/// <summary>Populates ID property of provided <paramref name="principal"/>.</summary>
 /// <returns>Whether a new principal is inserted in database.</returns>
 private bool InsertPrincipalOrGetExisting(ref PrincipalInfo principal)
 {
     principal.ID = GetPrincipalID(principal.Name); // Retry reading existing principal after custom lock, maybe another concurrent command created the principal from AuthorizationDataLoader.
     if (principal.ID != default)
     {
         return(false);
     }
     try
     {
         var newPrincipal = _principalGenericRepository.CreateInstance();
         newPrincipal.ID   = Guid.NewGuid();
         newPrincipal.Name = principal.Name;
         _principalGenericRepository.Insert(newPrincipal);
         principal.ID = newPrincipal.ID;
         return(true);
     }
     catch (Exception ex) when(DuplicateNameInDatabase(ex))
     {
         _logger.Warning(() => $"Ignoring concurrent principal creation: {ex.GetType().Name}: {ex.Message}");
         principal.ID = GetPrincipalID(principal.Name); // Check if another concurrent command created the principal, but not from AuthorizationDataLoader (the custom lock was not used).
         if (principal.ID != default)
         {
             return(false);
         }
         else
         {
             throw new FrameworkException($"Cannot create the principal record for '{principal.Name}'.", ex);
         }
     }
 }
        /// <summary>Insert a principal with provided <see cref="PrincipalInfo.Name"/> to database, and populates ID property of <paramref name="principal"/> parameter.</summary>
        /// <remarks>If another concurrent process created a principal, this method will set the ID of the existing one.</remarks>
        public void SafeInsertPrincipal(ref PrincipalInfo principal)
        {
            string username = principal.Name; // Copy to be used for asynchronous logging.

            _logger.Info(() => $"Adding unregistered principal '{username}'. See {OptionsAttribute.GetConfigurationPath<RhetosAppOptions>()}:{nameof(RhetosAppOptions.AuthorizationAddUnregisteredPrincipals)} in configuration files.");
            var  userLock   = CreteCustomLock(username.ToUpper());
            bool newCreated = InsertPrincipalOrGetExisting(ref principal);

            if (!newCreated) // If new principal is created, other requests should wait for current transaction to be committed in order to read the new principal.
            {
                ReleaseCustomLock(userLock);
            }
        }
Пример #3
0
        public IList <bool> GetAuthorizations(IUserInfo userInfo, IList <Claim> requiredClaims)
        {
            PrincipalInfo      principal = userInfo.IsUserRecognized ? _authorizationData.GetPrincipal(userInfo.UserName) : null;
            IEnumerable <Guid> userRoles = GetUsersRoles(principal);

            IEnumerable <ClaimInfo>  claims          = GetClaims(requiredClaims);
            IEnumerable <Permission> userPermissions = GetUsersPermissions(claims, principal, userRoles);
            IList <bool>             userHasClaims   = GetUserHasClaims(claims, userPermissions);

            var roleNamesIndex = new Lazy <IDictionary <Guid, string> >(() => _authorizationData.GetRoles(userRoles));

            _logger.Trace(() => ReportRoles(userInfo, userRoles, roleNamesIndex));
            _logger.Trace(() => ReportPermissions(userInfo, principal, roleNamesIndex, userPermissions, claims, userHasClaims));

            return(userHasClaims);
        }
Пример #4
0
        public PrincipalInfo GetPrincipal(string username)
        {
            var principal = new PrincipalInfo {
                ID = GetPrincipalID(username), Name = username
            };

            if (principal.ID == default(Guid))
            {
                if (ShouldAddUnregisteredPrincipal())
                {
                    _logger.Info(() => "Adding unregistered principal '" + username + "'. See AuthorizationAddUnregisteredPrincipals in web.config.");
                    var newPrincipal = _principalGenericRepository.Value.CreateInstance();
                    newPrincipal.ID   = Guid.NewGuid();
                    newPrincipal.Name = username;
                    try
                    {
                        _principalGenericRepository.Value.Insert(newPrincipal);
                        principal.ID = newPrincipal.ID;
                    }
                    catch (System.Data.SqlClient.SqlException ex)
                    {
                        if (ex.Message.StartsWith("Cannot insert duplicate key row in object 'Common.Principal' with unique index 'IX_Principal_Name'"))
                        {
                            _logger.Info(() => "Ignoring concurrent principal creation: " + ex.GetType().Name + ": " + ex.Message);
                            principal.ID = GetPrincipalID(username);
                            if (principal.ID == default(Guid))
                            {
                                throw new FrameworkException("Cannot create the principal record for '" + username + "'.", ex);
                            }
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
                else
                {
                    _logger.Info($"There is no principal with the username '{username}' in Common.Principal.");
                    throw new UserException($"Your account '{username}' is not registered in the system. Please contact the system administrator.");
                }
            }
            return(principal);
        }
Пример #5
0
        public PrincipalInfo GetPrincipal(string username)
        {
            var principal = new PrincipalInfo {
                ID = GetPrincipalID(username), Name = username
            };

            if (principal.ID != default)
            {
                return(principal);
            }
            else if (_rhetosAppOptions.AuthorizationAddUnregisteredPrincipals)
            {
                _principalWriter.Value.SafeInsertPrincipal(ref principal);
                return(principal);
            }
            else
            {
                _logger.Warning($"There is no principal with the username '{username}' in Common.Principal.");
                throw new UserException($"Your account '{username}' is not registered in the system. Please contact the system administrator.");
            }
        }
Пример #6
0
        public PrincipalInfo GetPrincipal(string username)
        {
            var principal = new PrincipalInfo { ID = GetPrincipalID(username), Name = username };

            if (principal.ID == default(Guid))
            {
                if (ShouldAddUnregisteredPrincipal())
                {
                    _logger.Info(() => "Adding unregistered principal '" + username + "'. See AuthorizationAddUnregisteredPrincipals in web.config.");
                    var newPrincipal = _principalGenericRepository.Value.CreateInstance();
                    newPrincipal.ID = Guid.NewGuid();
                    newPrincipal.Name = username;
                    try
                    {
                        _principalGenericRepository.Value.Insert(newPrincipal);
                        principal.ID = newPrincipal.ID;
                    }
                    catch (System.Data.SqlClient.SqlException ex)
                    {
                        if (ex.Message.StartsWith("Cannot insert duplicate key row in object 'Common.Principal' with unique index 'IX_Principal_Name'"))
                        {
                            _logger.Info(() => "Ignoring concurrent principal creation: " + ex.GetType().Name + ": " + ex.Message);
                            principal.ID = GetPrincipalID(username);
                            if (principal.ID == default(Guid))
                                throw new FrameworkException("Cannot create the principal record for '" + username + "'.", ex);
                        }
                        else
                            throw;
                    }
                }
                else
                {
                    _logger.Error("There is no principal with the given username '" + username + "' in Common.Principal.");
                    throw new ClientException("There is no principal with the given username.");
                }
            }
            return principal;
        }
        /// <summary>
        /// Reporting is done in a function that returns a string, to avoid any performance impact when the trace log is not enabled.
        /// </summary>
        private string ReportPermissions(IUserInfo userInfo, PrincipalInfo principal, Lazy<IDictionary<Guid, string>> roleNamesIndex,
            IEnumerable<Permission> userPermissions, IEnumerable<ClaimInfo> claims, IEnumerable<bool> userHasClaims)
        {
            var report = new List<string>();

            // Create an index of permissions:

            var permissionsByClaim = new MultiDictionary<Guid, Permission>();
            foreach (var permission in userPermissions)
                permissionsByClaim.Add(permission.ClaimID, permission);

            // Analyze permissions for required claims:

            foreach (var claimResult in claims.Zip(userHasClaims, (Claim, UserHasIt) => new { Claim, UserHasIt }))
            {
                var claimPermissions = claimResult.Claim.ID != null
                    ? permissionsByClaim.Get(claimResult.Claim.ID.Value)
                    : new Permission[] { };

                var permissionsDescription = claimPermissions
                    .Select(permission => new
                        {
                            permission.IsAuthorized,
                            PrincipalOrRoleName = permission.PrincipalID != null
                                ? ("principal " + (permission.PrincipalID.Value == principal.ID ? principal.Name : permission.PrincipalID.Value.ToString()))
                                : ("role " + GetRoleNameSafe(permission.RoleID.Value, roleNamesIndex))
                        })
                    .ToList();

                var allowedFor = permissionsDescription.Where(p => p.IsAuthorized).Select(p => p.PrincipalOrRoleName).ToList();
                var deniedFor = permissionsDescription.Where(p => !p.IsAuthorized).Select(p => p.PrincipalOrRoleName).ToList();

                string explanation = "User " + userInfo.UserName + " " + (claimResult.UserHasIt ? "has" : "doesn't have")
                    + " claim '" + claimResult.Claim.Resource + " " + claimResult.Claim.Right + "'. It is ";

                if (deniedFor.Count != 0)
                    if (allowedFor.Count != 0)
                        explanation += "denied for " + string.Join(", ", deniedFor) + " and allowed for " + string.Join(", ", allowedFor) + " (deny overrides allow).";
                    else
                        explanation += "denied for " + string.Join(", ", deniedFor) + ".";
                else
                    if (allowedFor.Count != 0)
                        explanation += "allowed for " + string.Join(", ", allowedFor) + ".";
                    else
                        if (claimResult.Claim.ID == null)
                            explanation += "denied by default (the claim does not exist or is no longer active).";
                        else
                            explanation += "denied by default (no permissions defined).";

                report.Add(explanation);
            }

            return string.Join("\r\n", report);
        }
Пример #8
0
        /// <summary>Returns principal, ID instead of the principal name, if the name is not available.</summary>

        private static string GetPermissionsPrincipalNameSafe(Permission permission, PrincipalInfo principal)
        {
            return(principal != null && permission.PrincipalID == principal.ID
                ? principal.Name
                : permission.PrincipalID.Value.ToString());
        }
Пример #9
0
        /// <summary>
        /// Reporting is done in a function that returns a string, to avoid any performance impact when the trace log is not enabled.
        /// </summary>
        private string ReportPermissions(IUserInfo userInfo, PrincipalInfo principal, Lazy <IDictionary <Guid, string> > roleNamesIndex,
                                         IEnumerable <Permission> userPermissions, IEnumerable <ClaimInfo> claims, IEnumerable <bool> userHasClaims)
        {
            var report = new List <string>();

            // Create an index of permissions:

            var permissionsByClaim = new MultiDictionary <Guid, Permission>();

            foreach (var permission in userPermissions)
            {
                permissionsByClaim.Add(permission.ClaimID, permission);
            }

            // Analyze permissions for required claims:

            foreach (var claimResult in claims.Zip(userHasClaims, (Claim, UserHasIt) => new { Claim, UserHasIt }))
            {
                var claimPermissions = claimResult.Claim.ID != null
                    ? permissionsByClaim.Get(claimResult.Claim.ID.Value)
                    : Array.Empty <Permission>();

                var permissionsDescription = claimPermissions
                                             .Select(permission => new
                {
                    permission.IsAuthorized,
                    PrincipalOrRoleName = permission.PrincipalID != null
                                ? ("principal " + GetPermissionsPrincipalNameSafe(permission, principal))
                                : ("role " + GetRoleNameSafe(permission.RoleID.Value, roleNamesIndex))
                })
                                             .ToList();

                var allowedFor = permissionsDescription.Where(p => p.IsAuthorized).Select(p => p.PrincipalOrRoleName).ToList();
                var deniedFor  = permissionsDescription.Where(p => !p.IsAuthorized).Select(p => p.PrincipalOrRoleName).ToList();

                string explanation;
                if (deniedFor.Count != 0)
                {
                    if (allowedFor.Count != 0)
                    {
                        explanation = $"It is denied for {string.Join(", ", deniedFor)} and allowed for {string.Join(", ", allowedFor)} (deny overrides allow).";
                    }
                    else
                    {
                        explanation = $"It is denied for {string.Join(", ", deniedFor)}.";
                    }
                }
                else if (allowedFor.Count != 0)
                {
                    explanation = $"It is allowed for {string.Join(", ", allowedFor)}.";
                }
                else if (claimResult.Claim.ID == null)
                {
                    explanation = "It is denied by default (the claim does not exist or is no longer active).";
                }
                else
                {
                    explanation = "It is denied by default (no permissions defined).";
                }

                report.Add($"User {ReportUserNameOrAnonymous(userInfo)} {(claimResult.UserHasIt ? "has" : "doesn't have")} claim '{claimResult.Claim.Resource} {claimResult.Claim.Right}'. {explanation}");
            }

            return(string.Join("\r\n", report));
        }
Пример #10
0
        /// <summary>
        /// Reporting is done in a function that returns a string, to avoid any performance impact when the trace log is not enabled.
        /// </summary>
        private string ReportPermissions(IUserInfo userInfo, PrincipalInfo principal, Lazy <IDictionary <Guid, string> > roleNamesIndex,
                                         IEnumerable <Permission> userPermissions, IEnumerable <ClaimInfo> claims, IEnumerable <bool> userHasClaims)
        {
            var report = new List <string>();

            // Create an index of permissions:

            var permissionsByClaim = new MultiDictionary <Guid, Permission>();

            foreach (var permission in userPermissions)
            {
                permissionsByClaim.Add(permission.ClaimID, permission);
            }

            // Analyze permissions for required claims:

            foreach (var claimResult in claims.Zip(userHasClaims, (Claim, UserHasIt) => new { Claim, UserHasIt }))
            {
                var claimPermissions = claimResult.Claim.ID != null
                    ? permissionsByClaim.Get(claimResult.Claim.ID.Value)
                    : new Permission[]
                {
                };

                var permissionsDescription = claimPermissions
                                             .Select(permission => new
                {
                    permission.IsAuthorized,
                    PrincipalOrRoleName = permission.PrincipalID != null
                                ? ("principal " + (permission.PrincipalID.Value == principal.ID ? principal.Name : permission.PrincipalID.Value.ToString()))
                                : ("role " + GetRoleNameSafe(permission.RoleID.Value, roleNamesIndex))
                })
                                             .ToList();

                var allowedFor = permissionsDescription.Where(p => p.IsAuthorized).Select(p => p.PrincipalOrRoleName).ToList();
                var deniedFor  = permissionsDescription.Where(p => !p.IsAuthorized).Select(p => p.PrincipalOrRoleName).ToList();

                string explanation = "User " + userInfo.UserName + " " + (claimResult.UserHasIt ? "has" : "doesn't have")
                                     + " claim '" + claimResult.Claim.Resource + " " + claimResult.Claim.Right + "'. It is ";

                if (deniedFor.Count != 0)
                {
                    if (allowedFor.Count != 0)
                    {
                        explanation += "denied for " + string.Join(", ", deniedFor) + " and allowed for " + string.Join(", ", allowedFor) + " (deny overrides allow).";
                    }
                    else
                    {
                        explanation += "denied for " + string.Join(", ", deniedFor) + ".";
                    }
                }
                else
                if (allowedFor.Count != 0)
                {
                    explanation += "allowed for " + string.Join(", ", allowedFor) + ".";
                }
                else
                if (claimResult.Claim.ID == null)
                {
                    explanation += "denied by default (the claim does not exist or is no longer active).";
                }
                else
                {
                    explanation += "denied by default (no permissions defined).";
                }

                report.Add(explanation);
            }

            return(string.Join("\r\n", report));
        }