/// <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); }