/// <summary> /// Create the identity /// </summary> public IIdentity CreateIdentity(Guid sid, string name, string deviceSecret, IPrincipal principal) { try { ApplicationServiceContext.Current.GetService <IPolicyEnforcementService>().Demand(PermissionPolicyIdentifiers.AccessClientAdministrativeFunction, principal); ApplicationServiceContext.Current.GetService <IPolicyEnforcementService>().Demand(PermissionPolicyIdentifiers.CreateLocalIdentity, principal); var conn = this.CreateConnection(); IPasswordHashingService hash = ApplicationContext.Current.GetService <IPasswordHashingService>(); using (conn.Lock()) { DbSecurityDevice dbu = new DbSecurityDevice() { DeviceSecret = hash.ComputeHash(deviceSecret), PublicId = name, Key = sid, CreationTime = DateTime.Now, CreatedByUuid = conn.Table <DbSecurityUser>().FirstOrDefault(o => o.UserName == AuthenticationContext.Current?.Principal?.Identity?.Name)?.Uuid ?? Guid.Parse("fadca076-3690-4a6e-af9e-f1cd68e8c7e8").ToByteArray() }; conn.Insert(dbu); } return(new SQLiteDeviceIdentity(name, false)); } catch { throw; } }
/// <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> /// Authenticate this user based on the session /// </summary> /// <param name="session">The session for which authentication is being saught</param> /// <returns>The authenticated principal</returns> public IPrincipal Authenticate(ISession session) { try { var sessionId = new Guid(session.Id.Take(16).ToArray()); var adhocCache = ApplicationServiceContext.Current.GetService <IAdhocCacheService>(); var authCache = adhocCache?.Get <object[]>($"s{sessionId}"); using (var context = this.m_configuration.Provider.GetReadonlyConnection()) { context.Open(); var sql = context.CreateSqlStatement <DbSession>().SelectFrom(typeof(DbSession), typeof(DbSecurityApplication), typeof(DbSecurityUser), typeof(DbSecurityDevice)) .InnerJoin <DbSecurityApplication>(o => o.ApplicationKey, o => o.Key) .Join <DbSession, DbSecurityUser>("LEFT", o => o.UserKey, o => o.Key) .Join <DbSession, DbSecurityDevice>("LEFT", o => o.DeviceKey, o => o.Key) .Where <DbSession>(s => s.Key == sessionId); DbSecurityApplication securityApplication = null; DbSecurityDevice securityDevice = null; DbSecurityUser securityUser = null; DbSession securitySession = null; if (authCache == null) { var auth = context.FirstOrDefault <CompositeResult <DbSession, DbSecurityApplication, DbSecurityUser, DbSecurityDevice> >(sql); auth.Object2.Secret = null; auth.Object3.Password = null; auth.Object3.SecurityHash = null; auth.Object4.DeviceSecret = null; adhocCache?.Add($"s{sessionId}", auth.Values, new TimeSpan(0, 5, 0)); securityApplication = auth.Object2; securityDevice = auth.Object4; securityUser = auth.Object3; securitySession = auth.Object1; } else { securitySession = authCache[0] as DbSession ?? (authCache[0] as JObject).ToObject <DbSession>(); securityApplication = authCache[1] as DbSecurityApplication ?? (authCache[1] as JObject).ToObject <DbSecurityApplication>(); securityUser = authCache[2] as DbSecurityUser ?? (authCache[2] as JObject).ToObject <DbSecurityUser>(); securityDevice = authCache[3] as DbSecurityDevice ?? (authCache[3] as JObject).ToObject <DbSecurityDevice>(); } // Identities List <IClaimsIdentity> identities = new List <IClaimsIdentity>(3); if (securitySession == null) { throw new SecuritySessionException(SessionExceptionType.NotEstablished, "Session not found", null); } if (securitySession.NotAfter < DateTimeOffset.Now) { throw new SecuritySessionException(SessionExceptionType.Expired, "Session expired", null); } else if (securitySession.NotBefore > DateTimeOffset.Now) { throw new SecuritySessionException(SessionExceptionType.NotYetValid, "Session not yet valid", null); } if (securitySession.ApplicationKey != null) { identities.Add(new Server.Core.Security.ApplicationIdentity(securityApplication.Key, securityApplication.PublicId, true)); } if (securitySession.DeviceKey.HasValue) { identities.Add(new DeviceIdentity(securityDevice.Key, securityDevice.PublicId, true)); } var principal = securitySession.UserKey.GetValueOrDefault() == Guid.Empty ? new SanteDBClaimsPrincipal(identities) : AdoClaimsIdentity.Create(context, securityUser, true, "SESSION").CreateClaimsPrincipal(identities); identities.First().AddClaim(new SanteDBClaim(SanteDBClaimTypes.AuthenticationInstant, securitySession.NotBefore.ToString("o"))); identities.First().AddClaim(new SanteDBClaim(SanteDBClaimTypes.Expiration, securitySession.NotAfter.ToString("o"))); identities.First().AddClaim(new SanteDBClaim(SanteDBClaimTypes.SanteDBSessionIdClaim, securitySession.Key.ToString())); // Add claims from session foreach (var clm in session.Claims.Where(o => o.Type != SanteDBClaimTypes.Sid && o.Type != SanteDBClaimTypes.SanteDBApplicationIdentifierClaim && o.Type != SanteDBClaimTypes.SanteDBDeviceIdentifierClaim)) { identities.First().AddClaim(clm); } // TODO: Load additional claims made about the user on the session return(principal); } } catch (Exception e) { this.m_traceSource.TraceEvent(EventLevel.Verbose, "Invalid session auth: {0}", e.Message); this.m_traceSource.TraceEvent(EventLevel.Error, e.ToString()); this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(null, null, false)); throw new SecuritySessionException(SessionExceptionType.Other, $"Could not perform session authentication", e); } }