/// <summary>
        /// Synchronize security locally
        /// </summary>
        private void SynchronizeSecurity(string deviceSecret, string deviceName, IPrincipal principal)
        {
            // Create a security user and ensure they exist!
            var localPip      = ApplicationContext.Current.GetService <IOfflinePolicyInformationService>();
            var localIdp      = ApplicationContext.Current.GetService <IOfflineDeviceIdentityProviderService>();
            var sdPersistence = ApplicationContext.Current.GetService <IDataPersistenceService <SecurityDevice> >();

            if (localIdp != null &&
                !String.IsNullOrEmpty(deviceSecret) && principal is IClaimsPrincipal &&
                ApplicationContext.Current.ConfigurationPersister.IsConfigured)
            {
                using (AuthenticationContext.EnterContext(principal))
                {
                    IClaimsPrincipal cprincipal = principal as IClaimsPrincipal;
                    var amiPip = new AmiPolicyInformationService();

                    IIdentity localDeviceIdentity = null;
                    lock (this.m_lockObject)
                    {
                        try
                        {
                            localDeviceIdentity = localIdp.GetIdentity(deviceName);
                            Guid sid = Guid.Parse(cprincipal.FindFirst(SanteDBClaimTypes.SanteDBDeviceIdentifierClaim).Value);
                            if (localDeviceIdentity == null)
                            {
                                localDeviceIdentity = localIdp.CreateIdentity(sid, deviceName, deviceSecret, AuthenticationContext.SystemPrincipal);
                            }
                            else
                            {
                                localIdp.ChangeSecret(deviceName, deviceSecret, AuthenticationContext.SystemPrincipal);
                            }
                        }
                        catch (Exception ex)
                        {
                            this.m_tracer.TraceWarning("Insertion of local cache credential failed: {0}", ex);
                        }

                        // Ensure policies exist from the claim
                        foreach (var itm in cprincipal.Claims.Where(o => o.Type == SanteDBClaimTypes.SanteDBGrantedPolicyClaim))
                        {
                            if (localPip.GetPolicy(itm.Value) == null)
                            {
                                try
                                {
                                    var policy = amiPip.GetPolicy(itm.Value);
                                    localPip.CreatePolicy(policy, AuthenticationContext.SystemPrincipal);
                                }
                                catch (Exception e)
                                {
                                    this.m_tracer.TraceWarning("Cannot update local policy information : {0}", e.Message);
                                }
                            }
                        }

                        localPip.AddPolicies(localDeviceIdentity, PolicyGrantType.Grant, AuthenticationContext.SystemPrincipal, cprincipal.Claims.Where(o => o.Type == SanteDBClaimTypes.SanteDBGrantedPolicyClaim).Select(o => o.Value).ToArray());
                    }
                }
            }
        }
        /// <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;
            }
        }
Example #3
0
        /// <summary>
        /// Synchronize the security settings
        /// </summary>
        /// <param name="password"></param>
        /// <param name="principal"></param>
        private void SynchronizeSecurity(string password, IPrincipal principal)
        {
            // Create a security user and ensure they exist!
            var localRp  = ApplicationContext.Current.GetService <IOfflineRoleProviderService>();
            var localPip = ApplicationContext.Current.GetService <IOfflinePolicyInformationService>();
            var localIdp = ApplicationContext.Current.GetService <IOfflineIdentityProviderService>();

            if (localRp == null || localPip == null || localIdp == null)
            {
                return;
            }

            if (!String.IsNullOrEmpty(password) && principal is IClaimsPrincipal &&
                ApplicationContext.Current.ConfigurationPersister.IsConfigured)
            {
                using (AuthenticationContext.EnterContext(principal))
                {
                    IClaimsPrincipal cprincipal = principal as IClaimsPrincipal;
                    var amiPip = new AmiPolicyInformationService();

                    // We want to impersonate SYSTEM
                    //AndroidApplicationContext.Current.SetPrincipal(cprincipal);

                    // Ensure policies exist from the claim
                    foreach (var itm in cprincipal.Claims.Where(o => o.Type == SanteDBClaimTypes.SanteDBGrantedPolicyClaim).ToArray())
                    {
                        if (localPip.GetPolicy(itm.Value) == null)
                        {
                            try
                            {
                                var policy = amiPip.GetPolicy(itm.Value);
                                localPip.CreatePolicy(policy, AuthenticationContext.SystemPrincipal);
                            }
                            catch (Exception e)
                            {
                                this.m_tracer.TraceWarning("Cannot update local policy information : {0}", e.Message);
                            }
                        }
                    }

                    // Ensure roles exist from the claim
                    var localRoles = localRp.GetAllRoles();
                    foreach (var itm in cprincipal.Claims.Where(o => o.Type == SanteDBClaimTypes.DefaultRoleClaimType).ToArray())
                    {
                        // Ensure policy exists
                        try
                        {
                            var amiPolicies = amiPip.GetPolicies(new SecurityRole()
                            {
                                Name = itm.Value
                            }).ToArray();
                            foreach (var pol in amiPolicies)
                            {
                                if (localPip.GetPolicy(pol.Policy.Oid) == null)
                                {
                                    var policy = amiPip.GetPolicy(pol.Policy.Oid);
                                    localPip.CreatePolicy(policy, AuthenticationContext.SystemPrincipal);
                                }
                            }

                            // Local role doesn't exist
                            if (!localRoles.Contains(itm.Value))
                            {
                                localRp.CreateRole(itm.Value, AuthenticationContext.SystemPrincipal);
                            }
                            localRp.AddPoliciesToRoles(amiPolicies, new String[] { itm.Value }, AuthenticationContext.SystemPrincipal);
                        }
                        catch (Exception e)
                        {
                            this.m_tracer.TraceWarning("Could not fetch / refresh policies: {0}", e.Message);
                        }
                    }

                    var localUser = ApplicationContext.Current.ConfigurationPersister.IsConfigured ? localIdp.GetIdentity(principal.Identity.Name) : null;

                    try
                    {
                        Guid sid = Guid.Parse(cprincipal.FindFirst(SanteDBClaimTypes.Sid).Value);
                        if (localUser == null)
                        {
                            localIdp.CreateIdentity(sid, principal.Identity.Name, password, AuthenticationContext.SystemPrincipal);
                        }
                        else
                        {
                            localIdp.ChangePassword(principal.Identity.Name, password, AuthenticationContext.SystemPrincipal);
                        }

                        // Copy security attributes
                        var localSu = ApplicationContext.Current.GetService <IDataPersistenceService <SecurityUser> >().Get(sid, null, true, AuthenticationContext.SystemPrincipal);
                        localSu.Email         = cprincipal.FindFirst(SanteDBClaimTypes.Email)?.Value;
                        localSu.PhoneNumber   = cprincipal.FindFirst(SanteDBClaimTypes.Telephone)?.Value;
                        localSu.LastLoginTime = DateTime.Now;
                        ApplicationContext.Current.GetService <IDataPersistenceService <SecurityUser> >().Update(localSu, TransactionMode.Commit, AuthenticationContext.SystemPrincipal);

                        // Add user to roles
                        // TODO: Remove users from specified roles?
                        localRp.AddUsersToRoles(new String[] { principal.Identity.Name }, cprincipal.Claims.Where(o => o.Type == SanteDBClaimTypes.DefaultRoleClaimType).Select(o => o.Value).ToArray(), AuthenticationContext.SystemPrincipal);
                        // Unlock the account
                        localIdp.SetLockout(principal.Identity.Name, false, principal);
                    }
                    catch (Exception ex)
                    {
                        this.m_tracer.TraceWarning("Insertion of local cache credential failed: {0}", ex);
                    }
                }
            }
        }