namespace Unosquare.Swan.Networking.Ldap { using System; using System.Collections.Generic; using Exceptions; /// /// 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. /// public class LdapControl { /// /// Initializes a new instance of the class. /// Constructs a new LdapControl object using the specified values. /// /// The OID of the control, as a dotted string. /// True if the Ldap operation should be discarded if /// the control is not supported. False if /// the operation can be processed without the control. /// The control-specific data. /// An OID must be specified. public LdapControl(string oid, bool critical, sbyte[] values) { if (oid == null) { throw new ArgumentException("An OID must be specified"); } Asn1Object = new RfcControl( oid, new Asn1Boolean(critical), values == null ? null : new Asn1OctetString(values)); } /// /// Returns the identifier of the control. /// /// /// The identifier. /// public string Id => Asn1Object.ControlType.StringValue(); /// /// Returns whether the control is critical for the operation. /// /// /// true if critical; otherwise, false. /// public bool Critical => Asn1Object.Criticality.BooleanValue(); internal static RespControlVector RegisteredControls { get; } = new RespControlVector(5); internal RfcControl Asn1Object { get; } /// /// 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. /// /// The object identifier of the control. /// A class which can instantiate an LdapControl. public static void Register(string oid, Type controlClass) => RegisteredControls.RegisterResponseControl(oid, controlClass); /// /// Returns the control-specific data of the object. /// /// /// The control-specific data of the object as a byte array, /// or null if the control has no data. /// public sbyte[] GetValue() => Asn1Object.ControlValue?.ByteValue(); internal void SetValue(sbyte[] controlValue) { Asn1Object.ControlValue = new Asn1OctetString(controlValue); } } /// /// Represents a simple bind request. /// /// public class LdapBindRequest : LdapMessage { /// /// Initializes a new instance of the class. /// Constructs a simple bind request. /// /// The Ldap protocol version, use Ldap_V3. /// Ldap_V2 is not supported. /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name. /// 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. public LdapBindRequest(int version, string dn, sbyte[] password) : base(LdapOperation.BindRequest, new RfcBindRequest(version, dn, password)) { } /// /// Retrieves the Authentication DN for a bind request. /// /// /// The authentication dn. /// public string AuthenticationDN => Asn1Object.RequestDn; /// public override string ToString() => Asn1Object.ToString(); } /// /// Encapsulates a continuation reference from an asynchronous search operation. /// /// internal class LdapSearchResultReference : LdapMessage { /// /// Initializes a new instance of the class. /// Constructs an LdapSearchResultReference object. /// /// The LdapMessage with a search reference. internal LdapSearchResultReference(RfcLdapMessage message) : base(message) { } /// /// Returns any URLs in the object. /// /// /// The referrals. /// public string[] Referrals { get { var references = ((RfcSearchResultReference)Message.Response).ToArray(); var srefs = new string[references.Length]; for (var 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)Message.Response).GetErrorMessage().StringValue(); public string MatchedDN => ((IRfcResponse)Message.Response).GetMatchedDN().StringValue(); public LdapStatusCode ResultCode => Message.Response is RfcSearchResultEntry || (IRfcResponse)Message.Response is RfcIntermediateResponse ? LdapStatusCode.Success : (LdapStatusCode)((IRfcResponse)Message.Response).GetResultCode().IntValue(); internal LdapException Exception { get; set; } internal void ChkResultCode() { if (Exception != null) { throw Exception; } switch (ResultCode) { case LdapStatusCode.Success: case LdapStatusCode.CompareTrue: case LdapStatusCode.CompareFalse: break; case LdapStatusCode.Referral: throw new LdapException( "Automatic referral following not enabled", LdapStatusCode.Referral, ErrorMessage); default: throw new LdapException(ResultCode.ToString().Humanize(), ResultCode, ErrorMessage, MatchedDN); } } } /// /// The RespControlVector class implements extends the /// existing Vector class so that it can be used to maintain a /// list of currently registered control responses. /// internal class RespControlVector : List { private readonly object _syncLock = new object(); public RespControlVector(int cap) : base(cap) { } public void RegisterResponseControl(string oid, Type controlClass) { lock (_syncLock) { Add(new RegisteredControl(this, oid, controlClass)); } } /// /// Inner class defined to create a temporary object to encapsulate /// all registration information about a response control. /// internal class RegisteredControl { public RegisteredControl(RespControlVector enclosingInstance, string oid, Type controlClass) { EnclosingInstance = enclosingInstance; MyOid = oid; MyClass = controlClass; } internal Type MyClass { get; } internal string MyOid { get; } private RespControlVector EnclosingInstance { get; } } } /// /// Represents and Ldap Bind Request. ///
    /// BindRequest ::= [APPLICATION 0] SEQUENCE {
    /// version                 INTEGER (1 .. 127),
    /// name                    LdapDN,
    /// authentication          AuthenticationChoice }
    /// 
/// /// internal sealed class RfcBindRequest : Asn1Sequence, IRfcRequest { private readonly sbyte[] _password; private static readonly Asn1Identifier Id = new Asn1Identifier(LdapOperation.BindRequest); public RfcBindRequest(int version, string name, sbyte[] password) : base(3) { _password = password; Add(new Asn1Integer(version)); Add(name); Add(new RfcAuthenticationChoice(password)); } public Asn1Integer Version { get => (Asn1Integer)Get(0); set => Set(0, value); } public Asn1OctetString Name { get => (Asn1OctetString)Get(1); set => Set(1, value); } public RfcAuthenticationChoice AuthenticationChoice { get => (RfcAuthenticationChoice)Get(2); set => Set(2, value); } public override Asn1Identifier GetIdentifier() => Id; public string GetRequestDN() => ((Asn1OctetString)Get(1)).StringValue(); } }