275 lines
9.7 KiB
C#
275 lines
9.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Unosquare.Swan.Exceptions;
|
|
|
|
namespace Unosquare.Swan.Networking.Ldap {
|
|
/// <summary>
|
|
/// Encapsulates optional additional parameters or constraints to be applied to
|
|
/// an Ldap operation.
|
|
/// When included with LdapConstraints or LdapSearchConstraints
|
|
/// on an LdapConnection or with a specific operation request, it is
|
|
/// sent to the server along with operation requests.
|
|
/// </summary>
|
|
public class LdapControl {
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="LdapControl"/> class.
|
|
/// Constructs a new LdapControl object using the specified values.
|
|
/// </summary>
|
|
/// <param name="oid">The OID of the control, as a dotted string.</param>
|
|
/// <param name="critical">True if the Ldap operation should be discarded if
|
|
/// the control is not supported. False if
|
|
/// the operation can be processed without the control.</param>
|
|
/// <param name="values">The control-specific data.</param>
|
|
/// <exception cref="ArgumentException">An OID must be specified.</exception>
|
|
public LdapControl(String oid, Boolean critical, SByte[] values) {
|
|
if(oid == null) {
|
|
throw new ArgumentException("An OID must be specified");
|
|
}
|
|
|
|
this.Asn1Object = new RfcControl(
|
|
oid,
|
|
new Asn1Boolean(critical),
|
|
values == null ? null : new Asn1OctetString(values));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the identifier of the control.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The identifier.
|
|
/// </value>
|
|
public String Id => this.Asn1Object.ControlType.StringValue();
|
|
|
|
/// <summary>
|
|
/// Returns whether the control is critical for the operation.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <c>true</c> if critical; otherwise, <c>false</c>.
|
|
/// </value>
|
|
public Boolean Critical => this.Asn1Object.Criticality.BooleanValue();
|
|
|
|
internal static RespControlVector RegisteredControls { get; } = new RespControlVector(5);
|
|
|
|
internal RfcControl Asn1Object {
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers a class to be instantiated on receipt of a control with the
|
|
/// given OID.
|
|
/// Any previous registration for the OID is overridden. The
|
|
/// controlClass must be an extension of LdapControl.
|
|
/// </summary>
|
|
/// <param name="oid">The object identifier of the control.</param>
|
|
/// <param name="controlClass">A class which can instantiate an LdapControl.</param>
|
|
public static void Register(String oid, Type controlClass)
|
|
=> RegisteredControls.RegisterResponseControl(oid, controlClass);
|
|
|
|
/// <summary>
|
|
/// Returns the control-specific data of the object.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// The control-specific data of the object as a byte array,
|
|
/// or null if the control has no data.
|
|
/// </returns>
|
|
public SByte[] GetValue() => this.Asn1Object.ControlValue?.ByteValue();
|
|
|
|
internal void SetValue(SByte[] controlValue) => this.Asn1Object.ControlValue = new Asn1OctetString(controlValue);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a simple bind request.
|
|
/// </summary>
|
|
/// <seealso cref="Unosquare.Swan.Networking.Ldap.LdapMessage" />
|
|
public class LdapBindRequest : LdapMessage {
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="LdapBindRequest"/> class.
|
|
/// Constructs a simple bind request.
|
|
/// </summary>
|
|
/// <param name="version">The Ldap protocol version, use Ldap_V3.
|
|
/// Ldap_V2 is not supported.</param>
|
|
/// <param name="dn">If non-null and non-empty, specifies that the
|
|
/// connection and all operations through it should
|
|
/// be authenticated with dn as the distinguished
|
|
/// name.</param>
|
|
/// <param name="password">If non-null and non-empty, specifies that the
|
|
/// connection and all operations through it should
|
|
/// be authenticated with dn as the distinguished
|
|
/// name and passwd as password.</param>
|
|
public LdapBindRequest(Int32 version, String dn, SByte[] password)
|
|
: base(LdapOperation.BindRequest, new RfcBindRequest(version, dn, password)) {
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the Authentication DN for a bind request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The authentication dn.
|
|
/// </value>
|
|
public String AuthenticationDN => this.Asn1Object.RequestDn;
|
|
|
|
/// <inheritdoc />
|
|
public override String ToString() => this.Asn1Object.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encapsulates a continuation reference from an asynchronous search operation.
|
|
/// </summary>
|
|
/// <seealso cref="Unosquare.Swan.Networking.Ldap.LdapMessage" />
|
|
internal class LdapSearchResultReference : LdapMessage {
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="LdapSearchResultReference"/> class.
|
|
/// Constructs an LdapSearchResultReference object.
|
|
/// </summary>
|
|
/// <param name="message">The LdapMessage with a search reference.</param>
|
|
internal LdapSearchResultReference(RfcLdapMessage message)
|
|
: base(message) {
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns any URLs in the object.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The referrals.
|
|
/// </value>
|
|
public String[] Referrals {
|
|
get {
|
|
Asn1Object[] references = ((RfcSearchResultReference)this.Message.Response).ToArray();
|
|
String[] srefs = new String[references.Length];
|
|
for(Int32 i = 0; i < references.Length; i++) {
|
|
srefs[i] = ((Asn1OctetString)references[i]).StringValue();
|
|
}
|
|
|
|
return srefs;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class LdapResponse : LdapMessage {
|
|
internal LdapResponse(RfcLdapMessage message)
|
|
: base(message) {
|
|
}
|
|
|
|
public String ErrorMessage => ((IRfcResponse)this.Message.Response).GetErrorMessage().StringValue();
|
|
|
|
public String MatchedDN => ((IRfcResponse)this.Message.Response).GetMatchedDN().StringValue();
|
|
|
|
public LdapStatusCode ResultCode => this.Message.Response is RfcSearchResultEntry ||
|
|
(IRfcResponse)this.Message.Response is RfcIntermediateResponse
|
|
? LdapStatusCode.Success
|
|
: (LdapStatusCode)((IRfcResponse)this.Message.Response).GetResultCode().IntValue();
|
|
|
|
internal LdapException Exception {
|
|
get; set;
|
|
}
|
|
|
|
internal void ChkResultCode() {
|
|
if(this.Exception != null) {
|
|
throw this.Exception;
|
|
}
|
|
|
|
switch(this.ResultCode) {
|
|
case LdapStatusCode.Success:
|
|
case LdapStatusCode.CompareTrue:
|
|
case LdapStatusCode.CompareFalse:
|
|
break;
|
|
case LdapStatusCode.Referral:
|
|
throw new LdapException(
|
|
"Automatic referral following not enabled",
|
|
LdapStatusCode.Referral,
|
|
this.ErrorMessage);
|
|
default:
|
|
throw new LdapException(this.ResultCode.ToString().Humanize(), this.ResultCode, this.ErrorMessage, this.MatchedDN);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The RespControlVector class implements extends the
|
|
/// existing Vector class so that it can be used to maintain a
|
|
/// list of currently registered control responses.
|
|
/// </summary>
|
|
internal class RespControlVector : List<RespControlVector.RegisteredControl> {
|
|
private readonly Object _syncLock = new Object();
|
|
|
|
public RespControlVector(Int32 cap)
|
|
: base(cap) {
|
|
}
|
|
|
|
public void RegisterResponseControl(String oid, Type controlClass) {
|
|
lock(this._syncLock) {
|
|
this.Add(new RegisteredControl(this, oid, controlClass));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inner class defined to create a temporary object to encapsulate
|
|
/// all registration information about a response control.
|
|
/// </summary>
|
|
internal class RegisteredControl {
|
|
public RegisteredControl(RespControlVector enclosingInstance, String oid, Type controlClass) {
|
|
this.EnclosingInstance = enclosingInstance;
|
|
this.MyOid = oid;
|
|
this.MyClass = controlClass;
|
|
}
|
|
|
|
internal Type MyClass {
|
|
get;
|
|
}
|
|
|
|
internal String MyOid {
|
|
get;
|
|
}
|
|
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "<Ausstehend>")]
|
|
private RespControlVector EnclosingInstance {
|
|
get;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents and Ldap Bind Request.
|
|
/// <pre>
|
|
/// BindRequest ::= [APPLICATION 0] SEQUENCE {
|
|
/// version INTEGER (1 .. 127),
|
|
/// name LdapDN,
|
|
/// authentication AuthenticationChoice }
|
|
/// </pre></summary>
|
|
/// <seealso cref="Unosquare.Swan.Networking.Ldap.Asn1Sequence" />
|
|
/// <seealso cref="IRfcRequest" />
|
|
internal sealed class RfcBindRequest
|
|
: Asn1Sequence, IRfcRequest {
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "<Ausstehend>")]
|
|
private readonly SByte[] _password;
|
|
private static readonly Asn1Identifier Id = new Asn1Identifier(LdapOperation.BindRequest);
|
|
|
|
public RfcBindRequest(Int32 version, String name, SByte[] password)
|
|
: base(3) {
|
|
this._password = password;
|
|
this.Add(new Asn1Integer(version));
|
|
this.Add(name);
|
|
this.Add(new RfcAuthenticationChoice(password));
|
|
}
|
|
|
|
public Asn1Integer Version {
|
|
get => (Asn1Integer)this.Get(0);
|
|
set => this.Set(0, value);
|
|
}
|
|
|
|
public Asn1OctetString Name {
|
|
get => (Asn1OctetString)this.Get(1);
|
|
set => this.Set(1, value);
|
|
}
|
|
|
|
public RfcAuthenticationChoice AuthenticationChoice {
|
|
get => (RfcAuthenticationChoice)this.Get(2);
|
|
set => this.Set(2, value);
|
|
}
|
|
|
|
public override Asn1Identifier GetIdentifier() => Id;
|
|
|
|
public String GetRequestDN() => ((Asn1OctetString)this.Get(1)).StringValue();
|
|
}
|
|
} |