/// <summary> /// Validate claims made by the requestor /// </summary> private IEnumerable <Claim> ValidateClaims(IPrincipal userPrincipal) { IPolicyDecisionService pdp = ApplicationContext.Current.GetService <IPolicyDecisionService>(); List <Claim> retVal = new List <Claim>(); // HACK: Find a better way to make claims // Claims are stored as X-OpenIZACS-Claim headers foreach (var itm in OpenIzClaimTypes.ExtractClaims(WebOperationContext.Current.IncomingRequest.Headers)) { // Claim allowed if (this.m_configuration.AllowedClientClaims == null || !this.m_configuration.AllowedClientClaims.Contains(itm.Type)) { throw new SecurityException(ApplicationContext.Current.GetLocaleString("SECE001")); } else { // Validate the claim var handler = OpenIzClaimTypes.GetHandler(itm.Type); if (handler == null || handler.Validate(userPrincipal, itm.Value)) { retVal.Add(itm); } else { throw new SecurityException(String.Format(ApplicationContext.Current.GetLocaleString("SECE002"), itm.Type)); } } } return(retVal); }
/// <summary> /// Data policy filter service with DI /// </summary> public DataPolicyFilterService(IConfigurationManager configurationManager, IPasswordHashingService passwordService, IPolicyDecisionService pdpService, IThreadPoolService threadPoolService, IDataCachingService dataCachingService, ISubscriptionExecutor subscriptionExecutor = null, IAdhocCacheService adhocCache = null) { this.m_hasher = passwordService; this.m_adhocCache = adhocCache; this.m_pdpService = pdpService; this.m_subscriptionExecutor = subscriptionExecutor; this.m_dataCachingService = dataCachingService; this.m_threadPool = threadPoolService; // Configuration load this.m_configuration = configurationManager.GetSection <DataPolicyFilterConfigurationSection>(); if (this.m_configuration == null) { this.m_tracer.TraceWarning("No data policy configuration exists. Setting all to HIDE"); this.m_configuration = new DataPolicyFilterConfigurationSection() { DefaultAction = ResourceDataPolicyActionType.Hide, Resources = new List <ResourceDataPolicyFilter>() }; } if (this.m_configuration.Resources != null) { foreach (var t in this.m_configuration.Resources) { if (typeof(Act).IsAssignableFrom(t.ResourceType.Type) || typeof(Entity).IsAssignableFrom(t.ResourceType.Type) || typeof(AssigningAuthority).IsAssignableFrom(t.ResourceType.Type)) { this.m_tracer.TraceInfo("Binding privacy action {0} to {1}", t.Action, t.ResourceType.Type); this.m_actions.TryAdd(t.ResourceType.Type, t); } } } }
/// <summary> /// PDP service /// </summary> public ApplicationServiceBehavior() { this.m_policyDecisionService = ApplicationServiceContext.Current.GetService <IPolicyDecisionService>(); this.m_monitorStateThread = new Thread(this.MonitorOnlineState) { IsBackground = true, Priority = ThreadPriority.Lowest, Name = "Online State Monitor" }; this.m_monitorStateThread.Start(); }
/// <summary> /// Change the user's password /// </summary> /// <param name="userName">User name.</param> /// <param name="newPassword">New password.</param> /// <param name="principal">Principal.</param> public void ChangePassword(string userName, string password, System.Security.Principal.IPrincipal principal) { // We must demand the change password permission try { IPolicyDecisionService pdp = ApplicationContext.Current.GetService <IPolicyDecisionService>(); if (userName != principal.Identity.Name && pdp.GetPolicyOutcome(principal, PolicyIdentifiers.ChangePassword) == OpenIZ.Core.Model.Security.PolicyGrantType.Deny) { throw new SecurityException("User cannot change specified users password"); } var conn = this.CreateConnection(); using (conn.Lock()) { var dbu = conn.Table <DbSecurityUser>().Where(o => o.UserName == userName).FirstOrDefault(); if (dbu == null) { throw new KeyNotFoundException(); } else { IPasswordHashingService hash = ApplicationContext.Current.GetService <IPasswordHashingService>(); dbu.PasswordHash = hash.ComputeHash(password); dbu.SecurityHash = Guid.NewGuid().ToString(); dbu.UpdatedByUuid = conn.Table <DbSecurityUser>().First(u => u.UserName == principal.Identity.Name).Uuid; dbu.UpdatedTime = DateTime.Now; conn.Update(dbu); this.SecurityAttributesChanged?.Invoke(this, new SecurityAuditDataEventArgs(dbu, "password")); } } } catch (Exception e) { this.SecurityAttributesChanged?.Invoke(this, new SecurityAuditDataEventArgs(new SecurityUser() { Key = Guid.Empty, UserName = userName }, "password") { Success = false }); this.m_tracer.TraceError("Error changing password for user {0} : {1}", userName, e); throw; } }
/// <summary> /// Change the user's password /// </summary> /// <param name="userName">User name.</param> /// <param name="newPassword">New password.</param> /// <param name="principal">Principal.</param> public void ChangePassword(string userName, string password, System.Security.Principal.IPrincipal principal) { // We must demand the change password permission IPolicyDecisionService pdp = ApplicationContext.Current.GetService <IPolicyDecisionService>(); if (!userName.Equals(principal.Identity.Name, StringComparison.OrdinalIgnoreCase)) { ApplicationServiceContext.Current.GetService <IPolicyEnforcementService>().Demand(PermissionPolicyIdentifiers.ChangePassword, principal); } // Password failed validation if (ApplicationServiceContext.Current.GetService <IPasswordValidatorService>()?.Validate(password) == false) { throw new DetectedIssueException(new DetectedIssue(DetectedIssuePriorityType.Error, "err.password.complexity", "Password does not meet complexity requirements", DetectedIssueKeys.SecurityIssue)); } try { var conn = this.CreateConnection(); using (conn.Lock()) { var dbu = conn.Table <DbSecurityUser>().Where(o => o.UserName.ToLower() == userName.ToLower()).FirstOrDefault(); if (dbu == null) { throw new KeyNotFoundException(); } else { IPasswordHashingService hash = ApplicationContext.Current.GetService <IPasswordHashingService>(); dbu.Password = hash.ComputeHash(password); dbu.SecurityHash = Guid.NewGuid().ToString(); dbu.UpdatedByUuid = conn.Table <DbSecurityUser>().First(u => u.UserName == principal.Identity.Name).Uuid; dbu.UpdatedTime = DateTime.Now; conn.Update(dbu); } } } catch (Exception e) { this.m_tracer.TraceError("Error changing password for user {0} : {1}", userName, e); throw new DataPersistenceException($"Error changing password for {userName}", e); } }
/// <summary> /// Change the PIN for the user /// </summary> /// <param name="userName">The name of the user to change the PIN for</param> /// <param name="pin">The PIN to change to</param> /// <remarks>Only the currently logged in credential can change a PIN number for themselves</remarks> public void ChangePin(string userName, byte[] pin, IPrincipal principal) { // We must demand the change password permission IPolicyDecisionService pdp = ApplicationContext.Current.GetService <IPolicyDecisionService>(); if (userName != principal.Identity.Name) { throw new SecurityException("Can only change PIN number of your own account"); } else if (pin.Length < 4 || pin.Length > 8 || pin.Any(o => o < 0 || o > 9)) { throw new ArgumentOutOfRangeException("PIN numbers must be between 4 and 8 digits"); } try { var conn = this.CreateConnection(); using (conn.Lock()) { var dbu = conn.Table <DbSecurityUser>().Where(o => o.UserName == userName).FirstOrDefault(); if (dbu == null) { throw new KeyNotFoundException(); } else { IPasswordHashingService hash = ApplicationContext.Current.GetService <IPasswordHashingService>(); dbu.PinHash = hash.ComputeHash(Encoding.UTF8.GetString(pin.Select(o => (byte)(o + 48)).ToArray(), 0, pin.Length)); dbu.SecurityHash = Guid.NewGuid().ToString(); dbu.UpdatedByUuid = conn.Table <DbSecurityUser>().First(u => u.UserName == AuthenticationContext.Current.Principal.Identity.Name).Uuid; dbu.UpdatedTime = DateTime.Now; conn.Update(dbu); } } } catch (Exception e) { this.m_tracer.TraceError("Error changing password for user {0} : {1}", userName, e); throw new DataPersistenceException($"Error changing password for {userName}", e); } }
/// <summary> /// Set the lockout /// </summary> public void SetLockout(string userName, bool v, IPrincipal principal) { IPolicyDecisionService pdp = ApplicationContext.Current.GetService <IPolicyDecisionService>(); ApplicationServiceContext.Current.GetService <IPolicyEnforcementService>().Demand(PermissionPolicyIdentifiers.AlterLocalIdentity, principal); try { var conn = this.CreateConnection(); using (conn.Lock()) { var userData = conn.Table <DbSecurityUser>().FirstOrDefault(o => o.UserName == userName); if (userData == null) { throw new KeyNotFoundException(userName); } else { if (v) { userData.Lockout = DateTime.MaxValue; } else { userData.Lockout = null; userData.InvalidLoginAttempts = 0; } conn.Update(userData); ApplicationServiceContext.Current.GetService <IDataCachingService>().Remove(userData.Key); } } } catch (Exception e) { this.m_tracer.TraceError("Error locking identity {0}", e); throw new DataPersistenceException($"Error locking identity {userName}", e); } }
/// <summary> /// DI constructor /// </summary> public DefaultPolicyEnforcementService(IPolicyDecisionService pdpService) : base(pdpService) { }
/// <summary> /// Create new service with adhoc cache /// </summary> public AdoPolicyInformationService(IPolicyEnforcementService pepService, IPolicyDecisionService pdpService, IAdhocCacheService adhocCache = null) { this.m_adhocCache = adhocCache; this.m_policyEnforcement = pepService; this.m_policyDecisionService = pdpService; }
/// <summary> /// Authenticate the device /// </summary> public IPrincipal Authenticate(string deviceId, string deviceSecret, AuthenticationMethod authMethod = AuthenticationMethod.Any) { var config = ApplicationContext.Current.Configuration.GetSection <SecurityConfigurationSection>(); if (!authMethod.HasFlag(AuthenticationMethod.Local)) { throw new InvalidOperationException("Identity provider only supports local auth"); } // Pre-event AuthenticatingEventArgs e = new AuthenticatingEventArgs(deviceId) { }; this.Authenticating?.Invoke(this, e); if (e.Cancel) { this.m_tracer.TraceWarning("Pre-Event hook indicates cancel {0}", deviceId); return(e.Principal); } IPrincipal retVal = null; try { // Connect to the db var connection = this.CreateConnection(); using (connection.Lock()) { // Password service IPasswordHashingService passwordHash = ApplicationContext.Current.GetService(typeof(IPasswordHashingService)) as IPasswordHashingService; DbSecurityDevice dbd = connection.Table <DbSecurityDevice>().FirstOrDefault(o => o.PublicId.ToLower() == deviceId.ToLower()); if (dbd == null) { throw new SecurityException(Strings.locale_authenticationFailure); } else if (config?.MaxInvalidLogins.HasValue == true && dbd.Lockout.HasValue && dbd.Lockout > DateTime.Now) { throw new SecurityException(Strings.locale_accountLocked); } else if (dbd.ObsoletionTime != null) { throw new SecurityException(Strings.locale_accountObsolete); } else if (!String.IsNullOrEmpty(deviceSecret) && passwordHash.ComputeHash(deviceSecret) != dbd.DeviceSecret) { dbd.InvalidAuthAttempts++; connection.Update(dbd); throw new SecurityException(Strings.locale_authenticationFailure); } else if (config?.MaxInvalidLogins.HasValue == true && dbd.InvalidAuthAttempts > config?.MaxInvalidLogins) { //s TODO: Make this configurable dbd.Lockout = DateTime.Now.AddSeconds(30 * (dbd.InvalidAuthAttempts - config.MaxInvalidLogins.Value)); connection.Update(dbd); throw new SecurityException(Strings.locale_accountLocked); } // TODO: Lacks login permission else { dbd.LastAuthTime = DateTime.Now; dbd.InvalidAuthAttempts = 0; connection.Update(dbd); IPolicyDecisionService pdp = ApplicationContext.Current.GetService <IPolicyDecisionService>(); IPolicyInformationService pip = ApplicationContext.Current.GetService <IPolicyInformationService>(); List <IClaim> additionalClaims = new List <IClaim>(); additionalClaims.AddRange(pip.GetPolicies(dbd).Where(o => o.Rule == PolicyGrantType.Grant).Select(o => new SanteDBClaim(SanteDBClaimTypes.SanteDBGrantedPolicyClaim, o.Policy.Oid))); additionalClaims.Add(new SanteDBClaim(SanteDBClaimTypes.SanteDBDeviceIdentifierClaim, dbd.Key.ToString())); additionalClaims.Add(new SanteDBClaim(SanteDBClaimTypes.SanteDBApplicationIdentifierClaim, ApplicationContext.Current.Application.Key.ToString())); // Local application only for SQLite // Create the principal retVal = new SQLitePrincipal(new SQLiteDeviceIdentity(dbd.PublicId, true, DateTime.Now, DateTime.Now.Add(config?.MaxLocalSession ?? new TimeSpan(0, 15, 0)), additionalClaims), new string[] { }); ApplicationServiceContext.Current.GetService <IPolicyEnforcementService>().Demand(PermissionPolicyIdentifiers.LoginAsService, retVal); } } // Post-event this.Authenticated?.Invoke(e, new AuthenticatedEventArgs(deviceId, retVal, true)); } catch (Exception ex) { this.m_tracer.TraceError("Error establishing device session ({1}): {0}", ex, deviceSecret); this.Authenticated?.Invoke(e, new AuthenticatedEventArgs(deviceId, retVal, false)); throw; } return(retVal); }
/// <summary> /// Creates a new instance with DI /// </summary> public ExemptablePolicyFilterService(IConfigurationManager configManager, IPasswordHashingService passwordService, IPolicyDecisionService pdpService, IThreadPoolService threadPoolService, IDataCachingService dataCachingService, IAdhocCacheService adhocCache = null, ISubscriptionExecutor subscriptionExecutor = null) : base(configManager, passwordService, pdpService, threadPoolService, dataCachingService, subscriptionExecutor, adhocCache) { }
/// <summary> /// Policy decision service /// </summary> public DefaultPolicyEnforcementService(IPolicyDecisionService pdpService) { this.m_pdpService = pdpService; }