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.
///