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);
        }
Пример #6
0
 public bool IsChildOf(LdapIdentity parent)
 {
     return(Name.EndsWith(parent.Name));
 }
Пример #7
0
        /// <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);
        }
Пример #8
0
        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);
        }
Пример #9
0
        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);
        }