/// <summary> /// Security challenge child handler /// </summary> public SecurityPolicyChildHandler(IRepositoryService <SecurityDevice> deviceRepository, IRepositoryService <SecurityApplication> applicationRepository, IRepositoryService <SecurityRole> roleRepository, IPolicyEnforcementService pepService, IPolicyInformationService pipService) { this.m_pip = pipService; this.m_pep = pepService; this.m_roleRepository = roleRepository; this.m_deviceRepository = deviceRepository; this.m_applicationRepository = applicationRepository; }
/// <summary> /// Create a token response /// </summary> private Stream CreateTokenResponse(IPrincipal oizPrincipal, IPrincipal clientPrincipal, EndpointReference appliesTo, IEnumerable <Claim> additionalClaims) { this.m_traceSource.TraceInformation("Will create new ClaimsPrincipal based on existing principal"); IRoleProviderService roleProvider = ApplicationContext.Current.GetService <IRoleProviderService>(); IPolicyInformationService pip = ApplicationContext.Current.GetService <IPolicyInformationService>(); IIdentityRefreshProviderService idp = ApplicationContext.Current.GetService <IIdentityRefreshProviderService>(); // TODO: Add configuration for expiry DateTime issued = DateTime.Parse((oizPrincipal as ClaimsPrincipal)?.FindFirst(ClaimTypes.AuthenticationInstant)?.Value ?? DateTime.Now.ToString("o")), expires = DateTime.Now.Add(this.m_configuration.ValidityTime); // System claims List <Claim> claims = new List <Claim>( roleProvider.GetAllRoles(oizPrincipal.Identity.Name).Select(r => new Claim(ClaimsIdentity.DefaultRoleClaimType, r)) ) { new Claim("iss", this.m_configuration.IssuerName), new Claim(ClaimTypes.Name, oizPrincipal.Identity.Name) }; // Additional claims claims.AddRange(additionalClaims); // Get policies var oizPrincipalPolicies = pip.GetActivePolicies(oizPrincipal); // Add grant if not exists if ((oizPrincipal as ClaimsPrincipal)?.FindFirst(ClaimTypes.Actor)?.Value == UserClassKeys.HumanUser.ToString()) { claims.AddRange(new Claim[] { //new Claim(ClaimTypes.AuthenticationInstant, issued.ToString("o")), new Claim(ClaimTypes.AuthenticationMethod, "OAuth2"), new Claim(OpenIzClaimTypes.OpenIzApplicationIdentifierClaim, (clientPrincipal as ClaimsPrincipal).FindFirst(ClaimTypes.Sid).Value) }); if ((oizPrincipal as ClaimsPrincipal)?.HasClaim(o => o.Type == OpenIzClaimTypes.OpenIzGrantedPolicyClaim) == true) { claims.AddRange((oizPrincipal as ClaimsPrincipal).FindAll(OpenIzClaimTypes.OpenIzGrantedPolicyClaim)); } else { claims.AddRange(oizPrincipalPolicies.Where(o => o.Rule == PolicyDecisionOutcomeType.Grant).Select(o => new Claim(OpenIzClaimTypes.OpenIzGrantedPolicyClaim, o.Policy.Oid))); } // Is the user elevated? If so, add claims for those policies if (claims.Exists(o => o.Type == OpenIzClaimTypes.XspaPurposeOfUseClaim)) { claims.AddRange(oizPrincipalPolicies.Where(o => o.Rule == PolicyDecisionOutcomeType.Elevate).Select(o => new Claim(OpenIzClaimTypes.OpenIzGrantedPolicyClaim, o.Policy.Oid))); } // Add Email address from idp claims.AddRange((oizPrincipal as ClaimsPrincipal).Claims.Where(o => o.Type == ClaimTypes.Email)); var tel = (oizPrincipal as ClaimsPrincipal).Claims.FirstOrDefault(o => o.Type == ClaimTypes.MobilePhone)?.Value; if (!String.IsNullOrEmpty(tel)) { claims.Add(new Claim("tel", tel)); } } // Name identifier claims.AddRange((oizPrincipal as ClaimsPrincipal).Claims.Where(o => o.Type == ClaimTypes.NameIdentifier)); // Find the nameid var nameId = claims.Find(o => o.Type == ClaimTypes.NameIdentifier); if (nameId != null) { claims.Remove(nameId); claims.Add(new Claim("sub", nameId.Value)); } var principal = new ClaimsPrincipal(new ClaimsIdentity(oizPrincipal.Identity, claims)); SigningCredentials credentials = this.CreateSigningCredentials(); // Generate security token var jwt = new JwtSecurityToken( signingCredentials: credentials, audience: appliesTo.Uri.ToString(), notBefore: issued, expires: expires, claims: claims ); JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); var encoder = handler.SignatureProviderFactory.CreateForSigning(credentials.SigningKey, credentials.SignatureAlgorithm); var refreshGrant = idp.CreateRefreshToken(oizPrincipal, expires.AddMinutes(10)); var refreshToken = String.Format("{0}.{1}", BitConverter.ToString(encoder.Sign(refreshGrant)).Replace("-", ""), BitConverter.ToString(refreshGrant).Replace("-", "")); WebOperationContext.Current.OutgoingResponse.ContentType = "application/json"; OAuthTokenResponse response = new OAuthTokenResponse() { TokenType = OAuthConstants.JwtTokenType, AccessToken = handler.WriteToken(jwt), ExpiresIn = (int)(expires.Subtract(DateTime.Now)).TotalMilliseconds, RefreshToken = refreshToken // TODO: Need to write a SessionProvider for this so we can keep track of refresh tokens }; return(this.CreateResponse(response)); }
/// <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> /// Gets the granted policies from the specified claims principal /// </summary> public static IEnumerable <IPolicyInstance> GetGrantedPolicies(this IClaimsPrincipal me, IPolicyInformationService pip) { return(me.Claims.Where(o => o.Type == SanteDBClaimTypes.SanteDBGrantedPolicyClaim).Select(o => new ClaimsPolicyInstance(me, pip.GetPolicy(o.Value)))); }