Пример #1
0
        /// <summary>
        /// Add a claim to the specified user
        /// </summary>
        public void AddClaim(string userName, Claim claim)
        {
            if (userName == null)
            {
                throw new ArgumentNullException(nameof(userName));
            }
            else if (claim == null)
            {
                throw new ArgumentNullException(nameof(claim));
            }


            try
            {
                using (var dataContext = this.m_configuration.Provider.GetWriteConnection())
                {
                    dataContext.Open();

                    var user = dataContext.FirstOrDefault <DbSecurityUser>(o => o.UserName.ToLower() == userName.ToLower());
                    if (user == null)
                    {
                        throw new KeyNotFoundException(userName);
                    }

                    lock (this.m_syncLock)
                    {
                        var existingClaim = dataContext.FirstOrDefault <DbUserClaim>(o => o.ClaimType == claim.Type && o.SourceKey == user.Key);

                        // Set the secret
                        if (existingClaim == null)
                        {
                            existingClaim = new DbUserClaim()
                            {
                                ClaimType  = claim.Type,
                                ClaimValue = claim.Value,
                                SourceKey  = user.Key
                            };
                            dataContext.Insert(existingClaim);
                        }
                        else
                        {
                            existingClaim.ClaimValue = claim.Value;
                            dataContext.Update(existingClaim);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                this.m_traceSource.TraceEvent(TraceEventType.Error, e.HResult, e.ToString());
                throw;
            }
        }
Пример #2
0
        /// <summary>
        /// Add a claim to the specified user
        /// </summary>
        public void AddClaim(string userName, IClaim claim, IPrincipal principal, TimeSpan?expire = null)
        {
            if (userName == null)
            {
                throw new ArgumentNullException(nameof(userName));
            }
            else if (claim == null)
            {
                throw new ArgumentNullException(nameof(claim));
            }

            this.VerifyPrincipal(principal, PermissionPolicyIdentifiers.AlterIdentity);

            try
            {
                using (var dataContext = this.m_configuration.Provider.GetWriteConnection())
                {
                    dataContext.Open();

                    var user = dataContext.FirstOrDefault <DbSecurityUser>(o => o.UserName.ToLower() == userName.ToLower());
                    if (user == null)
                    {
                        throw new KeyNotFoundException(userName);
                    }

                    lock (this.m_syncLock)
                    {
                        var existingClaim = dataContext.FirstOrDefault <DbUserClaim>(o => o.ClaimType == claim.Type && o.SourceKey == user.Key);

                        // Set the secret
                        if (existingClaim == null)
                        {
                            existingClaim = new DbUserClaim()
                            {
                                ClaimType  = claim.Type,
                                ClaimValue = claim.Value,
                                SourceKey  = user.Key
                            };
                            if (expire.HasValue)
                            {
                                existingClaim.ClaimExpiry = DateTime.Now.Add(expire.Value);
                            }
                            dataContext.Insert(existingClaim);
                        }
                        else
                        {
                            existingClaim.ClaimValue = claim.Value;
                            if (expire.HasValue)
                            {
                                existingClaim.ClaimExpiry = DateTime.Now.Add(expire.Value);
                            }
                            dataContext.Update(existingClaim);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                this.m_traceSource.TraceEvent(EventLevel.Error, e.ToString());
                throw;
            }
        }
Пример #3
0
        /// <summary>
        /// Authenticate the user using a TwoFactorAuthentication secret
        /// </summary>
        public IPrincipal Authenticate(string userName, string password, string tfaSecret)
        {
            // First, let's verify the TFA
            if (String.IsNullOrEmpty(userName))
            {
                throw new ArgumentNullException(nameof(userName));
            }
            else if (String.IsNullOrEmpty(tfaSecret))
            {
                throw new ArgumentNullException(nameof(tfaSecret));
            }

            // Authentication event args
            var evt = new AuthenticatingEventArgs(userName);

            this.Authenticating?.Invoke(this, evt);
            if (evt.Cancel)
            {
                throw new SecurityException("Authentication cancelled");
            }

            // Password hasher
            var hashingService = ApplicationServiceContext.Current.GetService <IPasswordHashingService>();

            tfaSecret = hashingService.ComputeHash(tfaSecret);

            // Try to authenticate
            try
            {
                using (var dataContext = this.m_configuration.Provider.GetWriteConnection())
                {
                    dataContext.Open();

                    using (var tx = dataContext.BeginTransaction())
                    {
                        try
                        {
                            var user = dataContext.FirstOrDefault <DbSecurityUser>(o => o.UserName.ToLower() == userName.ToLower());
                            if (user == null)
                            {
                                throw new KeyNotFoundException(userName);
                            }

                            var         claims     = dataContext.Query <DbUserClaim>(o => o.SourceKey == user.Key && (o.ClaimType == SanteDBClaimTypes.SanteDBOTAuthCode || o.ClaimType == SanteDBClaimTypes.SanteDBCodeAuth) && (!o.ClaimExpiry.HasValue || o.ClaimExpiry > DateTime.Now));
                            DbUserClaim tfaClaim   = claims.FirstOrDefault(o => o.ClaimType == SanteDBClaimTypes.SanteDBOTAuthCode),
                                        noPassword = claims.FirstOrDefault(o => o.ClaimType == SanteDBClaimTypes.SanteDBCodeAuth);

                            if (tfaClaim == null)
                            {
                                throw new InvalidOperationException("Cannot find appropriate claims for TFA");
                            }

                            // Expiry check
                            IClaimsPrincipal retVal = null;
                            if (String.IsNullOrEmpty(password) &&
                                Boolean.Parse(noPassword?.ClaimValue ?? "false") &&
                                tfaSecret == tfaClaim.ClaimValue) // TFA password hash sent as password, this is a password reset token - It will be set to expire ASAP
                            {
                                retVal = AdoClaimsIdentity.Create(user, true, "Tfa+LastPasswordHash").CreateClaimsPrincipal();
                                (retVal.Identity as IClaimsIdentity).AddClaim(new SanteDBClaim(SanteDBClaimTypes.PurposeOfUse, PurposeOfUseKeys.SecurityAdmin.ToString()));
                                (retVal.Identity as IClaimsIdentity).AddClaim(new SanteDBClaim(SanteDBClaimTypes.SanteDBScopeClaim, PermissionPolicyIdentifiers.LoginPasswordOnly));
                                (retVal.Identity as IClaimsIdentity).AddClaim(new SanteDBClaim(SanteDBClaimTypes.SanteDBScopeClaim, PermissionPolicyIdentifiers.ReadMetadata));
                                (retVal.Identity as IClaimsIdentity).RemoveClaim(retVal.FindFirst(SanteDBClaimTypes.Expiration));
                                (retVal.Identity as IClaimsIdentity).AddClaim(new SanteDBClaim(SanteDBClaimTypes.Expiration, DateTime.Now.AddMinutes(5).ToString("o"))); // Special case, passwoird
                            }
                            else if (!String.IsNullOrEmpty(password))
                            {
                                if (!user.TwoFactorEnabled || tfaSecret == tfaClaim.ClaimValue)
                                {
                                    retVal = this.Authenticate(userName, password) as IClaimsPrincipal;
                                }
                                else
                                {
                                    throw new AuthenticationException("TFA_MISMATCH");
                                }
                            }
                            else
                            {
                                throw new PolicyViolationException(new GenericPrincipal(new GenericIdentity(userName), new string[0]), PermissionPolicyIdentifiers.Login, PolicyGrantType.Deny);
                            }

                            // Now we want to fire authenticated
                            this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(userName, retVal, true));

                            tx.Commit();
                            return(retVal);
                        }
                        catch
                        {
                            tx.Rollback();
                            throw;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                this.m_traceSource.TraceEvent(EventLevel.Verbose, "Invalid credentials : {0}/{1}", userName, password);
                this.m_traceSource.TraceEvent(EventLevel.Error, e.ToString());
                this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(userName, null, false));
                throw;
            }
        }
Пример #4
0
        /// <summary>
        /// Creates a principal based on username and password
        /// </summary>
        internal static AdoClaimsIdentity Create(byte[] refreshToken)
        {
            try
            {
                Guid?userId = Guid.Empty;

                using (var dataContext = s_configuration.Provider.GetWriteConnection())
                {
                    dataContext.Open();

                    // Attempt to get a user
                    var         cvalue      = BitConverter.ToString(refreshToken).Replace("-", "");
                    DbUserClaim secretClaim = dataContext.FirstOrDefault <DbUserClaim>(o => o.ClaimType == AdoDataConstants.RefreshSecretClaimType && o.ClaimValue == cvalue);
                    if (secretClaim == null)
                    {
                        throw new SecurityException("Invalid refresh token");
                    }
                    DbUserClaim expiryClaim = dataContext.FirstOrDefault <DbUserClaim>(o => o.ClaimType == AdoDataConstants.RefreshExpiryClaimType && o.SourceKey == secretClaim.SourceKey);

                    if (expiryClaim == null || DateTimeOffset.Parse(expiryClaim.ClaimValue) < DateTimeOffset.Now)
                    {
                        throw new SecurityException($"Token expired {expiryClaim?.ClaimValue}");
                    }

                    // Grab the user and re-authenticate them
                    var user  = dataContext.SingleOrDefault <DbSecurityUser>(u => u.Key == secretClaim.SourceKey);
                    var roles = dataContext.Query <DbSecurityRole>(dataContext.CreateSqlStatement <DbSecurityRole>().SelectFrom()
                                                                   .InnerJoin <DbSecurityUserRole>(o => o.Key, o => o.RoleKey)
                                                                   .Where <DbSecurityUserRole>(o => o.UserKey == user.Key));

                    if (user.ObsoletionTime.HasValue)
                    {
                        throw new AuthenticationException("Security user is not active");
                    }
                    var userIdentity = new AdoClaimsIdentity(user, roles, true)
                    {
                        m_authenticationType = "Refresh"
                    };

                    // Is user allowed to login?
                    if (user.UserClass == UserClassKeys.HumanUser)
                    {
                        new PolicyPermission(System.Security.Permissions.PermissionState.Unrestricted, PermissionPolicyIdentifiers.Login, new GenericPrincipal(userIdentity, null)).Demand();
                    }
                    else if (user.UserClass == UserClassKeys.ApplicationUser)
                    {
                        new PolicyPermission(System.Security.Permissions.PermissionState.Unrestricted, PermissionPolicyIdentifiers.LoginAsService, new GenericPrincipal(userIdentity, null)).Demand();
                    }

                    return(userIdentity);
                }
            }
            catch (AuthenticationException)
            {
                // TODO: Audit this
                throw;
            }
            catch (SecurityException)
            {
                // TODO: Audit this
                throw;
            }
            catch (SqlException e)
            {
                switch (e.Number)
                {
                case 51900:
                    throw new AuthenticationException("Account is locked");

                case 51901:
                    throw new AuthenticationException("Invalid username/password");

                case 51902:
                    throw new AuthenticationException("User requires two-factor authentication");

                default:
                    throw e;
                }
            }
            catch (Exception e)
            {
                s_traceSource.TraceEvent(TraceEventType.Error, e.HResult, e.ToString());
                throw new Exception("Creating identity failed", e);
            }
        }
Пример #5
0
        /// <summary>
        /// Authenticate the user using a TwoFactorAuthentication secret
        /// </summary>
        public IPrincipal Authenticate(string userName, string password, string tfaSecret)
        {
            // First, let's verify the TFA
            if (String.IsNullOrEmpty(userName))
            {
                throw new ArgumentNullException(nameof(userName));
            }
            else if (String.IsNullOrEmpty(tfaSecret))
            {
                throw new ArgumentNullException(nameof(tfaSecret));
            }

            // Authentication event args
            var evt = new AuthenticatingEventArgs(userName);

            this.Authenticating?.Invoke(this, evt);
            if (evt.Cancel)
            {
                throw new SecurityException("Authentication cancelled");
            }

            // Password hasher
            var hashingService = ApplicationContext.Current.GetService <IPasswordHashingService>();

            tfaSecret = hashingService.EncodePassword(tfaSecret);

            // Try to authenticate
            try
            {
                using (var dataContext = this.m_configuration.Provider.GetWriteConnection())
                {
                    dataContext.Open();

                    using (var tx = dataContext.BeginTransaction())
                    {
                        try
                        {
                            var user = dataContext.FirstOrDefault <DbSecurityUser>(o => o.UserName.ToLower() == userName.ToLower());
                            if (user == null)
                            {
                                throw new KeyNotFoundException(userName);
                            }

                            var         claims     = dataContext.Query <DbUserClaim>(o => o.SourceKey == user.Key && (o.ClaimType == OpenIzClaimTypes.OpenIZTfaSecretClaim || o.ClaimType == OpenIzClaimTypes.OpenIZTfaSecretExpiry || o.ClaimType == OpenIzClaimTypes.OpenIZPasswordlessAuth));
                            DbUserClaim tfaClaim   = claims.FirstOrDefault(o => o.ClaimType == OpenIzClaimTypes.OpenIZTfaSecretClaim),
                                        tfaExpiry  = claims.FirstOrDefault(o => o.ClaimType == OpenIzClaimTypes.OpenIZTfaSecretExpiry),
                                        noPassword = claims.FirstOrDefault(o => o.ClaimType == OpenIzClaimTypes.OpenIZPasswordlessAuth);

                            if (tfaClaim == null || tfaExpiry == null)
                            {
                                throw new InvalidOperationException("Cannot find appropriate claims for TFA");
                            }

                            // Expiry check
                            ClaimsPrincipal retVal     = null;
                            DateTime        expiryDate = DateTime.Parse(tfaExpiry.ClaimValue);
                            if (expiryDate < DateTime.Now)
                            {
                                throw new SecurityException("TFA secret expired");
                            }
                            else if (String.IsNullOrEmpty(password) &&
                                     Boolean.Parse(noPassword?.ClaimValue ?? "false") &&
                                     tfaSecret == tfaClaim.ClaimValue) // Last known password hash sent as password, this is a password reset token - It will be set to expire ASAP
                            {
                                retVal = AdoClaimsIdentity.Create(user, true, "Tfa+LastPasswordHash").CreateClaimsPrincipal();
                                (retVal.Identity as ClaimsIdentity).AddClaim(new Claim(OpenIzClaimTypes.OpenIzGrantedPolicyClaim, PermissionPolicyIdentifiers.ChangePassword));
                                (retVal.Identity as ClaimsIdentity).RemoveClaim(retVal.FindFirst(ClaimTypes.Expiration));
                                // TODO: Add to configuration
                                (retVal.Identity as ClaimsIdentity).AddClaim(new Claim(ClaimTypes.Expiration, DateTime.Now.AddMinutes(5).ToString("o")));
                            }
                            else if (!String.IsNullOrEmpty(password))
                            {
                                retVal = this.Authenticate(userName, password) as ClaimsPrincipal;
                            }
                            else
                            {
                                throw new PolicyViolationException(PermissionPolicyIdentifiers.Login, PolicyDecisionOutcomeType.Deny);
                            }

                            // Now we want to fire authenticated
                            this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(userName, retVal, true));

                            tx.Commit();
                            return(retVal);
                        }
                        catch
                        {
                            tx.Rollback();
                            throw;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                this.m_traceSource.TraceEvent(TraceEventType.Verbose, e.HResult, "Invalid credentials : {0}/{1}", userName, password);
                this.m_traceSource.TraceEvent(TraceEventType.Error, e.HResult, e.ToString());
                this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(userName, null, false));
                throw;
            }
        }
        /// <summary>
        /// Authenticate a user using their challenge response
        /// </summary>
        public IPrincipal Authenticate(string userName, Guid challengeKey, string response, String tfa)
        {
            try
            {
                var authArgs = new AuthenticatingEventArgs(userName);
                this.Authenticating?.Invoke(this, authArgs);
                if (authArgs.Cancel)
                {
                    throw new SecurityException("Authentication cancelled");
                }

                var hashService  = ApplicationServiceContext.Current.GetService <IPasswordHashingService>();
                var responseHash = hashService.ComputeHash(response);

                // Connection to perform auth
                using (var context = this.m_configuration.Provider.GetWriteConnection())
                {
                    context.Open();
                    var query = context.CreateSqlStatement <DbSecurityUser>().SelectFrom(typeof(DbSecurityUser), typeof(DbSecurityUserChallengeAssoc))
                                .InnerJoin <DbSecurityUserChallengeAssoc>(o => o.Key, o => o.UserKey)
                                .Where(o => o.UserName.ToLower() == userName.ToLower() && o.ObsoletionTime == null)
                                .And <DbSecurityUserChallengeAssoc>(o => o.ExpiryTime > DateTimeOffset.Now);
                    var dbUser = context.FirstOrDefault <CompositeResult <DbSecurityUser, DbSecurityUserChallengeAssoc> >(query);

                    // User found?
                    if (dbUser == null)
                    {
                        throw new SecurityException("AUTH_INV");
                    }

                    // TFA?
                    if (!String.IsNullOrEmpty(tfa))
                    {
                        var tfaSecret = ApplicationServiceContext.Current.GetService <IPasswordHashingService>().ComputeHash(tfa);

                        var         claims   = context.Query <DbUserClaim>(o => o.SourceKey == dbUser.Object1.Key && (!o.ClaimExpiry.HasValue || o.ClaimExpiry > DateTimeOffset.Now)).ToList();
                        DbUserClaim tfaClaim = claims.FirstOrDefault(o => o.ClaimType == SanteDBClaimTypes.SanteDBOTAuthCode);
                        if (tfaClaim == null || !tfaSecret.Equals(tfaClaim.ClaimValue, StringComparison.OrdinalIgnoreCase))
                        {
                            throw new SecurityException("TFA_MISMATCH");
                        }
                    }

                    if (dbUser.Object1.Lockout > DateTimeOffset.Now)
                    {
                        throw new SecurityException("AUTH_LCK");
                    }
                    else if (dbUser.Object2.ChallengeResponse != responseHash || dbUser.Object1.Lockout.GetValueOrDefault() > DateTimeOffset.Now) // Increment invalid
                    {
                        dbUser.Object1.InvalidLoginAttempts++;
                        if (dbUser.Object1.InvalidLoginAttempts > this.m_securityConfiguration.GetSecurityPolicy <Int32>(SecurityPolicyIdentification.MaxInvalidLogins, 5))
                        {
                            dbUser.Object1.Lockout = DateTimeOffset.Now.Add(new TimeSpan(0, 0, dbUser.Object1.InvalidLoginAttempts.Value * 30));
                        }
                        dbUser.Object1.UpdatedByKey = Guid.Parse(AuthenticationContext.SystemUserSid);
                        dbUser.Object1.UpdatedTime  = DateTimeOffset.Now;

                        context.Update(dbUser.Object1);
                        if (dbUser.Object1.Lockout > DateTimeOffset.Now)
                        {
                            throw new AuthenticationException("AUTH_LCK");
                        }
                        else
                        {
                            throw new AuthenticationException("AUTH_INV");
                        }
                    }
                    else
                    {
                        var principal = AdoClaimsIdentity.Create(context, dbUser.Object1, true, "Secret=" + challengeKey.ToString()).CreateClaimsPrincipal();

                        this.m_policyEnforcementService.Demand(PermissionPolicyIdentifiers.Login, principal); // must still be allowed to login

                        (principal.Identity as IClaimsIdentity).AddClaim(new SanteDBClaim(SanteDBClaimTypes.PurposeOfUse, PurposeOfUseKeys.SecurityAdmin.ToString()));
                        (principal.Identity as IClaimsIdentity).AddClaim(new SanteDBClaim(SanteDBClaimTypes.SanteDBScopeClaim, PermissionPolicyIdentifiers.ReadMetadata));
                        (principal.Identity as IClaimsIdentity).AddClaim(new SanteDBClaim(SanteDBClaimTypes.SanteDBScopeClaim, PermissionPolicyIdentifiers.LoginPasswordOnly));

                        this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(userName, principal, true));
                        return(principal);
                    }
                }
            }
            catch (Exception e)
            {
                this.m_tracer.TraceError("Challenge authentication failed: {0}", e);
                this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(userName, null, false));
                throw new AuthenticationException($"Challenge authentication failed");
            }
        }
 public UserClaimEntity(DbUserClaim claim)
 {
     _claim = claim;
 }