2009-04-07 11 views
53

現在、.NETベースのプロジェクトのユーザーの役割とアクセス許可を格納する方法を研究しています。これらのプロジェクトの一部はWebベースであり、一部はそうではありません。私は現在、私が探しているものを、一貫性のある移植可能な方法で、プロジェクトの種類間で達成するための最良の方法を見つけるのに苦労しています。Active Directoryを使用した.NETのユーザーグループと役割の管理

ここでは、Active Directoryを基本的なユーザー情報の単一の連絡先として活用することを検討しています。このため、各アプリケーションのユーザーは、既にActive Directoryに格納されており、そこでアクティブに維持されているため、カスタムデータベースを維持する必要はありません。また、可能であれば、独自のセキュリティモデル/コードを作成することは望ましくなく、Microsoftが提供するセキュリティアプリケーションブロックのような既存のものを使用したいと考えています。

一部のプロジェクトでは、読み取り、書き込み、またはアクセス権などの基本的な権限のみが必要です。他のプロジェクトでは、より複雑な権限が必要です。これらのアプリケーションのユーザーには、一部の領域にはアクセス権が与えられているが、他の領域にはアクセス権が与えられていない可能性があります。アプリの管理セクションでは、このアクセスを制御して定義します。ではなく、のADツールです。

現在、イントラネット上で認証を実行するために統合Windows認証を使用しています。これは基本的なユーザー情報を見つけるのに適しています。また、Active Directoryの役割プロバイダーを提供するためにASP.NETを拡張することができるので、ユーザーが所属するセキュリティグループを見つけることができます。しかし、私にこの方法が没落したように見えるのは、すべてがActive Directoryに格納されているため、事態が大きくなりすぎても維持するのが難しいことです。

この同じ行に沿って、Active Directory Lightweight Directory Servicesについても聞いたことがあります。これは、スキーマを拡張し、アプリケーション固有の属性とグループのみを追加できるようです。問題は、これがどのように行われるのか、それがどのように機能するかについては何も見つかりません。このインスタンスと対話する方法と新しいインスタンスを作成する方法について説明しているMSDNの記事がありますが、私の質問に答えるものはありません。

私の質問はあなたの経験に基づいて、私は正しい道を下っていますか?私が探していることは、Active Directoryだけで可能か、他のツールを使用する必要がありますか?


他の方法は私がに見てきた:アプリケーション間でユーザーを管理するために[stackoverflow]

  • カスタムセキュリティモデルとデータベースの作成、複数のweb.configファイルを使用して

  • +0

    サンプルコードを追加しました。要求されたとおりに –

    答えて

    97

    あなたの認証にADを使用することは良い考えです。とにかく皆をそこに追加し、イントラネットユーザーは余分なログインの必要はありません。

    ASP.NETは、ADに対して認証できるプロバイダを使用することを許可していますが、グループメンバーシップのサポートを提供するものは何もありません(あなたが望むなら実装するのは簡単ですが、サンプルを提供することができます)。

    実際の問題は、ADグループを使用して各アプリ内で権限を定義したい場合です。

    もしそうなら、独自のRoleProvider for ASP.NETを作成するオプションがあります。これは、ApplicationServicesを介してWinFormsとWPFアプリケーションでも使用できます。このRoleProviderは、AD内のユーザーのIDを、自分のカスタムデータベースに格納できるアプリケーションごとのグループ/ロールにリンクすることができます。これらのロールの管理は、これらの管理者がADに特別な権限を持たなくても可能です。

    また、アプリの役割をADグループと組み合わせることもできます。そのため、ADのグローバルな「管理者」グループに属していれば、アプリの役割のメンバーシップに関係なくアプリケーションに完全なアクセス権が与えられます。逆に、彼らが解雇されたと言うADのグループまたはプロパティを持っている場合は、すべてのAppロールメンバーシップを無視してすべてのアクセスを制限することができます(HRはおそらく各アプリからそれらを削除しませんすべて!)。あなたが望むなら、あなたは、より多くを実装することができますが、あなただけのValidateUserメソッドを実装する必要があり、あなたのActiveDirectoryMembershipProviderのために、このオリジナル作品http://www.codeproject.com/Articles/28546/Active-Directory-Roles-Provider

    に基づく:

    注:

    サンプルコードは、要求として追加しました新しいAccountManagementネームスペースはこれを簡単にします:

    // assumes: using System.DirectoryServices.AccountManagement; 
    public override bool ValidateUser(string username, string password) 
    { 
        bool result = false; 
    
        try 
        { 
        using(var context = 
         new PrincipalContext(ContextType.Domain, "yourDomainName")) 
        { 
         result = context.ValidateCredentials(username, password); 
        } 
        } 
        catch(Exception ex) 
        { 
        // TODO: log exception 
        } 
    
        return result; 
    } 
    

    あなたの役割のプロバイダにとってはもう少し作業がありますあなたが除外したいグループ、除外したいユーザーなど、Googleを検索する際に発見した重要な問題です。

    これはおそらく完全なブログ記事の価値がありますが、これはセッション変数の検索を開始するのに役立ちます。完全なキャッシュ・サンプルが長すぎるため、パフォーマンスを向上させる方法のサンプルと同じです。

    using System; 
    using System.Collections.Generic; 
    using System.Collections.Specialized; 
    using System.Configuration.Provider; 
    using System.Diagnostics; 
    using System.DirectoryServices; 
    using System.DirectoryServices.AccountManagement; 
    using System.Linq; 
    using System.Web; 
    using System.Web.Hosting; 
    using System.Web.Security; 
    
    namespace MyApp.Security 
    { 
        public sealed class ActiveDirectoryRoleProvider : RoleProvider 
        { 
         private const string AD_FILTER = "(&(objectCategory=group)(|(groupType=-2147483646)(groupType=-2147483644)(groupType=-2147483640)))"; 
         private const string AD_FIELD = "samAccountName"; 
    
         private string _activeDirectoryConnectionString; 
         private string _domain; 
    
         // Retrieve Group Mode 
         // "Additive" indicates that only the groups specified in groupsToUse will be used 
         // "Subtractive" indicates that all Active Directory groups will be used except those specified in groupsToIgnore 
         // "Additive" is somewhat more secure, but requires more maintenance when groups change 
         private bool _isAdditiveGroupMode; 
    
         private List<string> _groupsToUse; 
         private List<string> _groupsToIgnore; 
         private List<string> _usersToIgnore; 
    
         #region Ignore Lists 
    
         // IMPORTANT - DEFAULT LIST OF ACTIVE DIRECTORY USERS TO "IGNORE" 
         //    DO NOT REMOVE ANY OF THESE UNLESS YOU FULLY UNDERSTAND THE SECURITY IMPLICATIONS 
         //    VERYIFY THAT ALL CRITICAL USERS ARE IGNORED DURING TESTING 
         private String[] _DefaultUsersToIgnore = new String[] 
         { 
          "Administrator", "TsInternetUser", "Guest", "krbtgt", "Replicate", "SERVICE", "SMSService" 
         }; 
    
         // IMPORTANT - DEFAULT LIST OF ACTIVE DIRECTORY DOMAIN GROUPS TO "IGNORE" 
         //    PREVENTS ENUMERATION OF CRITICAL DOMAIN GROUP MEMBERSHIP 
         //    DO NOT REMOVE ANY OF THESE UNLESS YOU FULLY UNDERSTAND THE SECURITY IMPLICATIONS 
         //    VERIFY THAT ALL CRITICAL GROUPS ARE IGNORED DURING TESTING BY CALLING GetAllRoles MANUALLY 
         private String[] _defaultGroupsToIgnore = new String[] 
          { 
           "Domain Guests", "Domain Computers", "Group Policy Creator Owners", "Guests", "Users", 
           "Domain Users", "Pre-Windows 2000 Compatible Access", "Exchange Domain Servers", "Schema Admins", 
           "Enterprise Admins", "Domain Admins", "Cert Publishers", "Backup Operators", "Account Operators", 
           "Server Operators", "Print Operators", "Replicator", "Domain Controllers", "WINS Users", 
           "DnsAdmins", "DnsUpdateProxy", "DHCP Users", "DHCP Administrators", "Exchange Services", 
           "Exchange Enterprise Servers", "Remote Desktop Users", "Network Configuration Operators", 
           "Incoming Forest Trust Builders", "Performance Monitor Users", "Performance Log Users", 
           "Windows Authorization Access Group", "Terminal Server License Servers", "Distributed COM Users", 
           "Administrators", "Everybody", "RAS and IAS Servers", "MTS Trusted Impersonators", 
           "MTS Impersonators", "Everyone", "LOCAL", "Authenticated Users" 
          }; 
         #endregion 
    
         /// <summary> 
         /// Initializes a new instance of the ADRoleProvider class. 
         /// </summary> 
         public ActiveDirectoryRoleProvider() 
         { 
          _groupsToUse = new List<string>(); 
          _groupsToIgnore = new List<string>(); 
          _usersToIgnore = new List<string>(); 
         } 
    
         public override String ApplicationName { get; set; } 
    
         /// <summary> 
         /// Initialize ADRoleProvider with config values 
         /// </summary> 
         /// <param name="name"></param> 
         /// <param name="config"></param> 
         public override void Initialize(String name, NameValueCollection config) 
         { 
          if (config == null) 
           throw new ArgumentNullException("config"); 
    
          if (String.IsNullOrEmpty(name)) 
           name = "ADRoleProvider"; 
    
          if (String.IsNullOrEmpty(config[ "description" ])) 
          { 
           config.Remove("description"); 
           config.Add("description", "Active Directory Role Provider"); 
          } 
    
          // Initialize the abstract base class. 
          base.Initialize(name, config); 
    
          _domain = ReadConfig(config, "domain"); 
          _isAdditiveGroupMode = (ReadConfig(config, "groupMode") == "Additive"); 
          _activeDirectoryConnectionString = ReadConfig(config, "connectionString"); 
    
          DetermineApplicationName(config); 
          PopulateLists(config); 
         } 
    
         private string ReadConfig(NameValueCollection config, string key) 
         { 
          if (config.AllKeys.Any(k => k == key)) 
           return config[ key ]; 
    
          throw new ProviderException("Configuration value required for key: " + key); 
         } 
    
         private void DetermineApplicationName(NameValueCollection config) 
         { 
          // Retrieve Application Name 
          ApplicationName = config[ "applicationName" ]; 
          if (String.IsNullOrEmpty(ApplicationName)) 
          { 
           try 
           { 
            string app = 
             HostingEnvironment.ApplicationVirtualPath ?? 
             Process.GetCurrentProcess().MainModule.ModuleName.Split('.').FirstOrDefault(); 
    
            ApplicationName = app != "" ? app : "/"; 
           } 
           catch 
           { 
            ApplicationName = "/"; 
           } 
          } 
    
          if (ApplicationName.Length > 256) 
           throw new ProviderException("The application name is too long."); 
         } 
    
         private void PopulateLists(NameValueCollection config) 
         { 
          // If Additive group mode, populate GroupsToUse with specified AD groups 
          if (_isAdditiveGroupMode && !String.IsNullOrEmpty(config[ "groupsToUse" ])) 
           _groupsToUse.AddRange(
            config[ "groupsToUse" ].Split(',').Select(group => group.Trim()) 
           ); 
    
          // Populate GroupsToIgnore List<string> with AD groups that should be ignored for roles purposes 
          _groupsToIgnore.AddRange(
           _defaultGroupsToIgnore.Select(group => group.Trim()) 
          ); 
    
          _groupsToIgnore.AddRange(
           (config[ "groupsToIgnore" ] ?? "").Split(',').Select(group => group.Trim()) 
          ); 
    
          // Populate UsersToIgnore ArrayList with AD users that should be ignored for roles purposes 
          string usersToIgnore = config[ "usersToIgnore" ] ?? ""; 
          _usersToIgnore.AddRange(
           _DefaultUsersToIgnore 
            .Select(value => value.Trim()) 
            .Union(
             usersToIgnore 
              .Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) 
              .Select(value => value.Trim()) 
            ) 
          ); 
         } 
    
         private void RecurseGroup(PrincipalContext context, string group, List<string> groups) 
         { 
          var principal = GroupPrincipal.FindByIdentity(context, IdentityType.SamAccountName, group); 
    
          if (principal == null) 
           return; 
    
          List<string> res = 
           principal 
            .GetGroups() 
            .ToList() 
            .Select(grp => grp.Name) 
            .ToList(); 
    
          groups.AddRange(res.Except(groups)); 
          foreach (var item in res) 
           RecurseGroup(context, item, groups); 
         } 
    
         /// <summary> 
         /// Retrieve listing of all roles to which a specified user belongs. 
         /// </summary> 
         /// <param name="username"></param> 
         /// <returns>String array of roles</returns> 
         public override string[] GetRolesForUser(string username) 
         { 
          string sessionKey = "groupsForUser:" + username; 
    
          if (HttpContext.Current != null && 
           HttpContext.Current.Session != null && 
           HttpContext.Current.Session[ sessionKey ] != null 
          ) 
           return ((List<string>) (HttpContext.Current.Session[ sessionKey ])).ToArray(); 
    
          using (PrincipalContext context = new PrincipalContext(ContextType.Domain, _domain)) 
          { 
           try 
           { 
            // add the users groups to the result 
            var groupList = 
             UserPrincipal 
              .FindByIdentity(context, IdentityType.SamAccountName, username) 
              .GetGroups() 
              .Select(group => group.Name) 
              .ToList(); 
    
            // add each groups sub groups into the groupList 
            foreach (var group in new List<string>(groupList)) 
             RecurseGroup(context, group, groupList); 
    
            groupList = groupList.Except(_groupsToIgnore).ToList(); 
    
            if (_isAdditiveGroupMode) 
             groupList = groupList.Join(_groupsToUse, r => r, g => g, (r, g) => r).ToList(); 
    
            if (HttpContext.Current != null) 
             HttpContext.Current.Session[ sessionKey ] = groupList; 
    
            return groupList.ToArray(); 
           } 
           catch (Exception ex) 
           { 
            // TODO: LogError("Unable to query Active Directory.", ex); 
            return new[] { "" }; 
           } 
          } 
         } 
    
         /// <summary> 
         /// Retrieve listing of all users in a specified role. 
         /// </summary> 
         /// <param name="rolename">String array of users</param> 
         /// <returns></returns> 
         public override string[] GetUsersInRole(String rolename) 
         { 
          if (!RoleExists(rolename)) 
           throw new ProviderException(String.Format("The role '{0}' was not found.", rolename)); 
    
          using (PrincipalContext context = new PrincipalContext(ContextType.Domain, _domain)) 
          { 
           try 
           { 
            GroupPrincipal p = GroupPrincipal.FindByIdentity(context, IdentityType.SamAccountName, rolename); 
    
            return (
    
             from user in p.GetMembers(true) 
             where !_usersToIgnore.Contains(user.SamAccountName) 
             select user.SamAccountName 
    
            ).ToArray(); 
           } 
           catch (Exception ex) 
           { 
            // TODO: LogError("Unable to query Active Directory.", ex); 
            return new[] { "" }; 
           } 
          } 
         } 
    
         /// <summary> 
         /// Determine if a specified user is in a specified role. 
         /// </summary> 
         /// <param name="username"></param> 
         /// <param name="rolename"></param> 
         /// <returns>Boolean indicating membership</returns> 
         public override bool IsUserInRole(string username, string rolename) 
         { 
          return GetUsersInRole(rolename).Any(user => user == username); 
         } 
    
         /// <summary> 
         /// Retrieve listing of all roles. 
         /// </summary> 
         /// <returns>String array of roles</returns> 
         public override string[] GetAllRoles() 
         { 
          string[] roles = ADSearch(_activeDirectoryConnectionString, AD_FILTER, AD_FIELD); 
    
          return (
    
           from role in roles.Except(_groupsToIgnore) 
           where !_isAdditiveGroupMode || _groupsToUse.Contains(role) 
           select role 
    
          ).ToArray(); 
         } 
    
         /// <summary> 
         /// Determine if given role exists 
         /// </summary> 
         /// <param name="rolename">Role to check</param> 
         /// <returns>Boolean indicating existence of role</returns> 
         public override bool RoleExists(string rolename) 
         { 
          return GetAllRoles().Any(role => role == rolename); 
         } 
    
         /// <summary> 
         /// Return sorted list of usernames like usernameToMatch in rolename 
         /// </summary> 
         /// <param name="rolename">Role to check</param> 
         /// <param name="usernameToMatch">Partial username to check</param> 
         /// <returns></returns> 
         public override string[] FindUsersInRole(string rolename, string usernameToMatch) 
         { 
          if (!RoleExists(rolename)) 
           throw new ProviderException(String.Format("The role '{0}' was not found.", rolename)); 
    
          return (
           from user in GetUsersInRole(rolename) 
           where user.ToLower().Contains(usernameToMatch.ToLower()) 
           select user 
    
          ).ToArray(); 
         } 
    
         #region Non Supported Base Class Functions 
    
         /// <summary> 
         /// AddUsersToRoles not supported. For security and management purposes, ADRoleProvider only supports read operations against Active Direcory. 
         /// </summary> 
         public override void AddUsersToRoles(string[] usernames, string[] rolenames) 
         { 
          throw new NotSupportedException("Unable to add users to roles. For security and management purposes, ADRoleProvider only supports read operations against Active Direcory."); 
         } 
    
         /// <summary> 
         /// CreateRole not supported. For security and management purposes, ADRoleProvider only supports read operations against Active Direcory. 
         /// </summary> 
         public override void CreateRole(string rolename) 
         { 
          throw new NotSupportedException("Unable to create new role. For security and management purposes, ADRoleProvider only supports read operations against Active Direcory."); 
         } 
    
         /// <summary> 
         /// DeleteRole not supported. For security and management purposes, ADRoleProvider only supports read operations against Active Direcory. 
         /// </summary> 
         public override bool DeleteRole(string rolename, bool throwOnPopulatedRole) 
         { 
          throw new NotSupportedException("Unable to delete role. For security and management purposes, ADRoleProvider only supports read operations against Active Direcory."); 
         } 
    
         /// <summary> 
         /// RemoveUsersFromRoles not supported. For security and management purposes, ADRoleProvider only supports read operations against Active Direcory. 
         /// </summary> 
         public override void RemoveUsersFromRoles(string[] usernames, string[] rolenames) 
         { 
          throw new NotSupportedException("Unable to remove users from roles. For security and management purposes, ADRoleProvider only supports read operations against Active Direcory."); 
         } 
         #endregion 
    
         /// <summary> 
         /// Performs an extremely constrained query against Active Directory. Requests only a single value from 
         /// AD based upon the filtering parameter to minimize performance hit from large queries. 
         /// </summary> 
         /// <param name="ConnectionString">Active Directory Connection String</param> 
         /// <param name="filter">LDAP format search filter</param> 
         /// <param name="field">AD field to return</param> 
         /// <returns>String array containing values specified by 'field' parameter</returns> 
         private String[] ADSearch(String ConnectionString, String filter, String field) 
         { 
          DirectorySearcher searcher = new DirectorySearcher 
          { 
           SearchRoot = new DirectoryEntry(ConnectionString), 
           Filter = filter, 
           PageSize = 500 
          }; 
          searcher.PropertiesToLoad.Clear(); 
          searcher.PropertiesToLoad.Add(field); 
    
          try 
          { 
           using (SearchResultCollection results = searcher.FindAll()) 
           { 
            List<string> r = new List<string>(); 
            foreach (SearchResult searchResult in results) 
            { 
             var prop = searchResult.Properties[ field ]; 
             for (int index = 0; index < prop.Count; index++) 
              r.Add(prop[ index ].ToString()); 
            } 
    
            return r.Count > 0 ? r.ToArray() : new string[ 0 ]; 
           } 
          } 
          catch (Exception ex) 
          { 
           throw new ProviderException("Unable to query Active Directory.", ex); 
          } 
         } 
        } 
    } 
    

    次のように、このためのサンプル設定サブセクションのエントリは次のようになります。

    <roleManager enabled="true" defaultProvider="ActiveDirectory"> 
        <providers> 
        <clear/> 
        <add 
         applicationName="MyApp" name="ActiveDirectory" 
         type="MyApp.Security.ActiveDirectoryRoleProvider" 
         domain="mydomain" groupMode="" connectionString="LDAP://myDirectoryServer.local/dc=mydomain,dc=local" 
        /> 
        </providers> 
    </roleManager> 
    

    やれやれ、それは多くのコードです!

    PS:上記の役割提供者のコア部分は他の人の作品に基づいていますが、私はリンクを手に入れませんでしたが、Googleを通じて見つけました。 LINQの使用とキャッシングのためのデータベースの必要性を取り除くために大幅に変更しました。

    +0

    これはまさに私が探していたようです。任意のサンプルコードを提供できますか?御時間ありがとうございます! –

    +7

    偉大な答え、あなたは私の尊敬を持っています – Ropstah

    +0

    この例では、ImpersonationContextを設定する必要がありますか?このようなことをやろうとしますが、Windows認証を使用します。 – hometoast

    関連する問題