protected virtual Task LoadAllUserGroups(LdapConnection connection, LdapIdentity domain, LdapProfile profile, ClientConfiguration clientConfig) { //already loaded from memberOf return(Task.CompletedTask); }
/// <summary> /// Verify User Name, Password, User Status and Policy against Active Directory /// </summary> public async Task <bool> VerifyCredential(string userName, string password, string ldapUri, PendingRequest request, ClientConfiguration clientConfig) { if (string.IsNullOrEmpty(userName)) { throw new ArgumentNullException(nameof(userName)); } if (string.IsNullOrEmpty(password)) { _logger.Error("Empty password provided for user '{user:l}'", userName); return(false); } if (string.IsNullOrEmpty(ldapUri)) { throw new ArgumentNullException(nameof(ldapUri)); } var user = LdapIdentity.ParseUser(userName); var bindDn = FormatBindDn(ldapUri, user, clientConfig); _logger.Debug($"Verifying user '{{user:l}}' credential and status at {ldapUri}", bindDn); try { using (var connection = new LdapConnection()) { //trust self-signed certificates on ldap server connection.TrustAllCertificates(); if (Uri.IsWellFormedUriString(ldapUri, UriKind.Absolute)) { var uri = new Uri(ldapUri); connection.Connect(uri.GetLeftPart(UriPartial.Authority)); } else { connection.Connect(ldapUri, 389); } //do not follow chase referrals connection.SetOption(LdapOption.LDAP_OPT_REFERRALS, IntPtr.Zero); await connection.BindAsync(LdapAuthType.Simple, new LdapCredential { UserName = bindDn, Password = password }); var domain = await WhereAmI(ldapUri, connection, clientConfig); _logger.Information($"User '{{user:l}}' credential and status verified successfully in {domain.Name}", user.Name); var profile = await LoadProfile(connection, domain, user, clientConfig); if (profile == null) { return(false); } var checkGroupMembership = !string.IsNullOrEmpty(clientConfig.ActiveDirectoryGroup); //user must be member of security group if (checkGroupMembership) { var isMemberOf = await IsMemberOf(connection, domain, user, profile, clientConfig.ActiveDirectoryGroup); if (!isMemberOf) { _logger.Warning($"User '{{user:l}}' is not member of '{clientConfig.ActiveDirectoryGroup}' group in {profile.BaseDn.Name}", user.Name); return(false); } _logger.Debug($"User '{{user:l}}' is member of '{clientConfig.ActiveDirectoryGroup}' group in {profile.BaseDn.Name}", user.Name); } var onlyMembersOfGroupMustProcess2faAuthentication = !string.IsNullOrEmpty(clientConfig.ActiveDirectory2FaGroup); //only users from group must process 2fa if (onlyMembersOfGroupMustProcess2faAuthentication) { var isMemberOf = await IsMemberOf(connection, domain, user, profile, clientConfig.ActiveDirectory2FaGroup); if (isMemberOf) { _logger.Debug($"User '{{user:l}}' is member of '{clientConfig.ActiveDirectory2FaGroup}' in {profile.BaseDn.Name}", user.Name); } else { _logger.Information($"User '{{user:l}}' is not member of '{clientConfig.ActiveDirectory2FaGroup}' in {profile.BaseDn.Name}", user.Name); request.Bypass2Fa = true; } } if (clientConfig.UseActiveDirectoryUserPhone) { request.UserPhone = profile.Phone; } if (clientConfig.UseActiveDirectoryMobileUserPhone) { request.UserPhone = profile.Mobile; } request.DisplayName = profile.DisplayName; request.EmailAddress = profile.Email; request.LdapAttrs = profile.LdapAttrs; if (profile.MemberOf != null) { request.UserGroups = profile.MemberOf.Select(dn => LdapIdentity.DnToCn(dn)).ToList(); } } return(true); //OK } catch (LdapException lex) { if (lex.Message != null) { var dataReason = ExtractErrorReason(lex.Message); if (dataReason != null) { _logger.Warning($"Verification user '{{user:l}}' at {ldapUri} failed: {dataReason}", user.Name); return(false); } } _logger.Error(lex, $"Verification user '{{user:l}}' at {ldapUri} failed", user.Name); } catch (Exception ex) { _logger.Error(ex, $"Verification user '{{user:l}}' at {ldapUri} failed", user.Name); } return(false); }
protected virtual async Task <bool> IsMemberOf(LdapConnection connection, LdapIdentity domain, LdapIdentity user, LdapProfile profile, string groupName) { var group = await FindValidGroup(connection, domain, groupName); if (group == null) { _logger.Warning($"Group '{groupName}' not exists in {domain.Name}"); return(false); } return(profile.MemberOf?.Any(g => g == group.Name) ?? false); }
protected async Task <LdapIdentity> FindValidGroup(LdapConnection connection, LdapIdentity domain, string groupName) { var group = LdapIdentity.ParseGroup(groupName); var searchFilter = $"(&({Names.ObjectClass}={Names.GroupClass})({Names.Identity(group)}={group.Name}))"; var response = await Query(connection, domain.Name, searchFilter, LdapSearchScope.LDAP_SCOPE_SUB, "DistinguishedName"); foreach (var entry in response) { var baseDn = LdapIdentity.BaseDn(entry.Dn); if (baseDn.Name == domain.Name) //only from user domain { var validatedGroup = new LdapIdentity { Name = entry.Dn, Type = IdentityType.DistinguishedName }; return(validatedGroup); } } return(null); }
protected virtual async Task <LdapProfile> LoadProfile(LdapConnection connection, LdapIdentity domain, LdapIdentity user, ClientConfiguration clientConfig) { var profile = new LdapProfile(); var queryAttributes = new List <string> { "DistinguishedName", "displayName", "mail", "telephoneNumber", "mobile", "memberOf" }; var ldapReplyAttributes = clientConfig.GetLdapReplyAttributes(); foreach (var ldapReplyAttribute in ldapReplyAttributes) { if (!profile.LdapAttrs.ContainsKey(ldapReplyAttribute)) { profile.LdapAttrs.Add(ldapReplyAttribute, null); queryAttributes.Add(ldapReplyAttribute); } } var searchFilter = $"(&(objectClass={Names.UserClass})({Names.Identity(user)}={user.Name}))"; _logger.Debug($"Querying user '{{user:l}}' in {domain.Name}", user.Name); var response = await Query(connection, domain.Name, searchFilter, LdapSearchScope.LDAP_SCOPE_SUB, queryAttributes.ToArray()); var entry = response.SingleOrDefault(); if (entry == null) { _logger.Error($"Unable to find user '{{user:l}}' in {domain.Name}", user.Name); return(null); } profile.BaseDn = LdapIdentity.BaseDn(entry.Dn); profile.DistinguishedName = entry.Dn; var attrs = entry.DirectoryAttributes; if (attrs.TryGetValue("displayName", out var displayNameAttr)) { profile.DisplayName = displayNameAttr.GetValue <string>(); } if (attrs.TryGetValue("mail", out var mailAttr)) { profile.Email = mailAttr.GetValue <string>(); } if (attrs.TryGetValue("telephoneNumber", out var phoneAttr)) { profile.Phone = phoneAttr.GetValue <string>(); } if (attrs.TryGetValue("mobile", out var mobileAttr)) { profile.Mobile = mobileAttr.GetValue <string>(); } if (attrs.TryGetValue("memberOf", out var memberOfAttr)) { profile.MemberOf = memberOfAttr.GetValues <string>().ToList(); } foreach (var key in profile.LdapAttrs.Keys.ToList()) //to list to avoid collection was modified exception { if (attrs.TryGetValue(key, out var attrValue)) { profile.LdapAttrs[key] = attrValue.GetValue <string>(); } else { _logger.Warning($"Can't load attribute '{key}' from user '{entry.Dn}'"); } } _logger.Debug($"User '{{user:l}}' profile loaded: {profile.DistinguishedName}", user.Name); if (clientConfig.ShouldLoadUserGroups()) { await LoadAllUserGroups(connection, domain, profile, clientConfig); } return(profile); }
public bool IsChildOf(LdapIdentity parent) { return(Name.EndsWith(parent.Name)); }
/// <summary> /// Verify User Name, Password, User Status and Policy against Active Directory /// </summary> public bool VerifyCredential(string userName, string password, PendingRequest request) { if (string.IsNullOrEmpty(userName)) { throw new ArgumentNullException(nameof(userName)); } if (string.IsNullOrEmpty(password)) { _logger.Error($"Empty password provided for user '{userName}'"); return(false); } var user = LdapIdentity.ParseUser(userName); var bindDn = FormatBindDn(user); _logger.Debug($"Verifying user '{bindDn}' credential and status at {_configuration.ActiveDirectoryDomain}"); try { using (var connection = new LdapConnection()) { //trust self-signed certificates on ldap server connection.TrustAllCertificates(); if (Uri.IsWellFormedUriString(_configuration.ActiveDirectoryDomain, UriKind.Absolute)) { var uri = new Uri(_configuration.ActiveDirectoryDomain); connection.Connect(uri.GetLeftPart(UriPartial.Authority)); } else { connection.Connect(_configuration.ActiveDirectoryDomain, 389); } //do not follow chase referrals connection.SetOption(LdapOption.LDAP_OPT_REFERRALS, IntPtr.Zero); connection.Bind(LdapAuthType.Simple, new LdapCredential { UserName = bindDn, Password = password }); var domain = WhereAmI(connection); _logger.Information($"User '{user.Name}' credential and status verified successfully in {domain.Name}"); var isProfileLoaded = LoadProfile(connection, domain, user, out var profile); if (!isProfileLoaded) { return(false); } var checkGroupMembership = !string.IsNullOrEmpty(_configuration.ActiveDirectoryGroup); //user must be member of security group if (checkGroupMembership) { var isMemberOf = IsMemberOf(connection, domain, user, profile, _configuration.ActiveDirectoryGroup); if (!isMemberOf) { _logger.Warning($"User '{user.Name}' is not member of '{_configuration.ActiveDirectoryGroup}' group in {profile.BaseDn.Name}"); return(false); } _logger.Debug($"User '{user.Name}' is member of '{_configuration.ActiveDirectoryGroup}' group in {profile.BaseDn.Name}"); } var onlyMembersOfGroupMustProcess2faAuthentication = !string.IsNullOrEmpty(_configuration.ActiveDirectory2FaGroup); //only users from group must process 2fa if (onlyMembersOfGroupMustProcess2faAuthentication) { var isMemberOf = IsMemberOf(connection, domain, user, profile, _configuration.ActiveDirectory2FaGroup); if (isMemberOf) { _logger.Debug($"User '{user.Name}' is member of '{_configuration.ActiveDirectory2FaGroup}' in {profile.BaseDn.Name}"); } else { _logger.Information($"User '{user.Name}' is not member of '{_configuration.ActiveDirectory2FaGroup}' in {profile.BaseDn.Name}"); request.Bypass2Fa = true; } } //check groups membership for radius reply conditional attributes foreach (var attribute in _configuration.RadiusReplyAttributes) { foreach (var value in attribute.Value.Where(val => val.UserGroupCondition != null)) { if (IsMemberOf(connection, domain, user, profile, value.UserGroupCondition)) { _logger.Information($"User '{user.Name}' is member of '{value.UserGroupCondition}' in {profile.BaseDn.Name}. Adding attribute '{attribute.Key}:{value.Value}' to reply"); request.UserGroups.Add(value.UserGroupCondition); } else { _logger.Debug($"User '{user.Name}' is not member of '{value.UserGroupCondition}' in {profile.BaseDn.Name}"); } } } if (_configuration.UseActiveDirectoryUserPhone) { request.UserPhone = profile.Phone; } if (_configuration.UseActiveDirectoryMobileUserPhone) { request.UserPhone = profile.Mobile; } request.DisplayName = profile.DisplayName; request.EmailAddress = profile.Email; } return(true); //OK } catch (LdapException lex) { if (lex.Message != null) { var dataReason = ExtractErrorReason(lex.Message); if (dataReason != null) { _logger.Warning($"Verification user '{user.Name}' at {_configuration.ActiveDirectoryDomain} failed: {dataReason}"); return(false); } } _logger.Error(lex, $"Verification user '{user.Name}' at {_configuration.ActiveDirectoryDomain} failed"); } catch (Exception ex) { _logger.Error(ex, $"Verification user '{user.Name}' at {_configuration.ActiveDirectoryDomain} failed"); } return(false); }
protected virtual bool IsMemberOf(LdapConnection connection, LdapIdentity domain, LdapIdentity user, LdapProfile profile, string groupName) { var isValidGroup = IsValidGroup(connection, domain, groupName, out var group); if (!isValidGroup) { _logger.Warning($"Group '{groupName}' not exists in {domain.Name}"); return(false); } return(profile.MemberOf?.Any(g => g == group.Name) ?? false); }
protected virtual bool LoadProfile(LdapConnection connection, LdapIdentity domain, LdapIdentity user, out LdapProfile profile) { profile = null; var attributes = new[] { "DistinguishedName", "displayName", "mail", "telephoneNumber", "mobile", "memberOf" }; var searchFilter = $"(&(objectClass={Names.UserClass})({Names.Identity(user)}={user.Name}))"; _logger.Debug($"Querying user '{user.Name}' in {domain.Name}"); var response = Query(connection, domain.Name, searchFilter, LdapSearchScope.LDAP_SCOPE_SUB, attributes); var entry = response.SingleOrDefault(); if (entry == null) { _logger.Error($"Unable to find user '{user.Name}' in {domain.Name}"); return(false); } profile = new LdapProfile { BaseDn = LdapIdentity.BaseDn(entry.Dn), DistinguishedName = entry.Dn, }; var attrs = entry.DirectoryAttributes; if (attrs.TryGetValue("displayName", out var displayNameAttr)) { profile.DisplayName = displayNameAttr.GetValue <string>(); } if (attrs.TryGetValue("mail", out var mailAttr)) { profile.Email = mailAttr.GetValue <string>(); } if (attrs.TryGetValue("telephoneNumber", out var phoneAttr)) { profile.Phone = phoneAttr.GetValue <string>(); } if (attrs.TryGetValue("mobile", out var mobileAttr)) { profile.Mobile = mobileAttr.GetValue <string>(); } if (attrs.TryGetValue("memberOf", out var memberOfAttr)) { profile.MemberOf = memberOfAttr.GetValues <string>().ToList(); } _logger.Debug($"User '{user.Name}' profile loaded: {profile.DistinguishedName}"); return(true); }