using System; using System.Collections.Generic; using Unosquare.Swan.Exceptions; namespace Unosquare.Swan.Networking.Ldap { /// /// 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, 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)); } /// /// Returns the identifier of the control. /// /// /// The identifier. /// public String Id => this.Asn1Object.ControlType.StringValue(); /// /// Returns whether the control is critical for the operation. /// /// /// true if critical; otherwise, false. /// public Boolean Critical => this.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() => this.Asn1Object.ControlValue?.ByteValue(); internal void SetValue(SByte[] controlValue) => this.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(Int32 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 => this.Asn1Object.RequestDn; /// public override String ToString() => this.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 { 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); } } } /// /// 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(Int32 cap) : base(cap) { } public void RegisterResponseControl(String oid, Type controlClass) { lock(this._syncLock) { this.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) { 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 = "")] 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 { [System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0052:Ungelesene private Member entfernen", Justification = "")] 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(); } }