Example #1
0
        /// <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;
            }
        }
Example #2
0
        /// <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);
            }
        }