/// <summary> /// Establish provenance for the specified connection /// </summary> public static Guid EstablishProvenance(this DataContext me, IPrincipal principal, Guid?externalRef) { // First, we want to get the identities var cprincipal = (principal ?? AuthenticationContext.Current.Principal) as IClaimsPrincipal; DbSecurityProvenance retVal = new DbSecurityProvenance() { Key = me.ContextId, ApplicationKey = Guid.Parse(AuthenticationContext.SystemApplicationSid), ExternalSecurityObjectRefKey = externalRef, ExternalSecurityObjectRefType = externalRef != null ? (me.Count <DbSecurityUser>(o => o.Key == externalRef) > 0 ? "U" : "P") : null }; // Identities if (cprincipal != null) // claims principal? { foreach (var ident in cprincipal.Identities) { if (ident is DeviceIdentity) { retVal.DeviceKey = ident.GetKey(me); } else if (ident is Server.Core.Security.ApplicationIdentity) { retVal.ApplicationKey = ident.GetKey(me).Value; } else { retVal.UserKey = ident.GetKey(me); } } // Session identifier var sidClaim = cprincipal?.FindFirst(SanteDBClaimTypes.SanteDBSessionIdClaim)?.Value; Guid sid = Guid.Empty; if (Guid.TryParse(sidClaim, out sid)) { retVal.SessionKey = sid; } } else if (principal.Identity.Name == AuthenticationContext.SystemPrincipal.Identity.Name) { retVal.UserKey = Guid.Parse(AuthenticationContext.SystemUserSid); } else if (principal.Identity.Name == AuthenticationContext.AnonymousPrincipal.Identity.Name) { retVal.UserKey = Guid.Parse(AuthenticationContext.AnonymousUserSid); } // Context try { if (retVal.UserKey.ToString() == AuthenticationContext.SystemUserSid || retVal.UserKey.ToString() == AuthenticationContext.AnonymousUserSid) { retVal.Key = me.ContextId = retVal.UserKey.Value; } else { retVal = me.Insert(retVal); } } catch (Exception e) { s_traceSource.TraceWarning("Error creating context: {0}", e); throw new DataPersistenceException($"Error establishing provenance for {principal.Identity.Name}", e); } return(retVal.Key); }
/// <summary> /// Verify the identities are appropriately designed /// </summary> private void VerifyIdentities(DataContext context, Entity data, Guid?assertedCreator) { // Validate unique values for IDs DbSecurityProvenance provenance = null; var issues = new List <DetectedIssue>(); var priority = this.m_persistenceService.GetConfiguration().Validation.ValidationLevel; foreach (var id in data.Identifiers) { // Get ID var dbAuth = context.FirstOrDefault <DbAssigningAuthority>(o => o.Key == id.AuthorityKey); if (dbAuth == null) { dbAuth = context.FirstOrDefault <DbAssigningAuthority>(o => o.DomainName == id.Authority.DomainName); if (dbAuth != null) { id.AuthorityKey = dbAuth.Key; } } if (dbAuth == null) { issues.Add(new DetectedIssue(priority, "id.aa.notFound", $"Missing assigning authority with ID {String.Join(",", data.Identifiers.Select(o => o.AuthorityKey))}", DetectedIssueKeys.SafetyConcernIssue)); continue; } // Get this identifier records which is not owned by my record var ownedByOthers = context.Query <DbEntityIdentifier>( context.CreateSqlStatement() .SelectFrom(typeof(DbEntityIdentifier)) .Where <DbEntityIdentifier>(o => o.Value == id.Value && o.AuthorityKey == id.AuthorityKey && o.ObsoleteVersionSequenceId == null && o.SourceKey != data.Key) .And("NOT EXISTS (SELECT 1 FROM ent_rel_tbl WHERE src_ent_id = ? OR trg_ent_id = ? AND obslt_vrsn_seq_id IS NULL)", data.Key, data.Key) ).ToList(); var ownedByMe = context.Query <DbEntityIdentifier>( context.CreateSqlStatement() .SelectFrom(typeof(DbEntityIdentifier)) .Where <DbEntityIdentifier>(o => o.Value == id.Value && o.AuthorityKey == id.AuthorityKey && o.ObsoleteVersionSequenceId == null) .And("(ent_id = ? OR EXISTS (SELECT 1 FROM ent_rel_tbl WHERE src_ent_id = ? OR trg_ent_id = ? AND obslt_vrsn_seq_id IS NULL))", data.Key, data.Key, data.Key) ).ToList(); // Verify scope var scopes = context.Query <DbAuthorityScope>(o => o.AssigningAuthorityKey == dbAuth.Key); if (scopes.Any() && !scopes.Any(s => s.ScopeConceptKey == data.ClassConceptKey) && // This type of identifier is not allowed to be assigned to this type of object !ownedByOthers.Any() && !ownedByMe.Any()) // Unless it was already associated to another type of object related to me { issues.Add(new DetectedIssue(DetectedIssuePriorityType.Error, "id.target", $"Identifier of type {dbAuth.DomainName} cannot be assigned to object of type {data.ClassConceptKey}", DetectedIssueKeys.BusinessRuleViolationIssue)); } // If the identity domain is unique, and we've been asked to raid identifier uq issues if (dbAuth.IsUnique && this.m_persistenceService.GetConfiguration().Validation.IdentifierUniqueness&& ownedByOthers.Any()) { issues.Add(new DetectedIssue(priority, $"id.uniqueness", $"Identifier {id.Value} in domain {dbAuth.DomainName} violates unique constraint", DetectedIssueKeys.FormalConstraintIssue)); } if (dbAuth.AssigningApplicationKey.HasValue) // Must have permission { if (provenance == null) { provenance = context.FirstOrDefault <DbSecurityProvenance>(o => o.Key == data.CreatedByKey); } if (provenance.ApplicationKey != dbAuth.AssigningApplicationKey && // Established prov key assertedCreator != dbAuth.AssigningApplicationKey && // original prov key !ownedByMe.Any() && !ownedByOthers.Any()) // and has not already been assigned to me or anyone else (it is a new , unknown identifier) { issues.Add(new DetectedIssue(DetectedIssuePriorityType.Error, $"id.authority", $"Application {provenance.ApplicationKey} does not have permission to assign {dbAuth.DomainName}", DetectedIssueKeys.SecurityIssue)); } } if (!String.IsNullOrEmpty(dbAuth.ValidationRegex) && this.m_persistenceService.GetConfiguration().Validation.IdentifierFormat) // must be valid { var nonMatch = !new Regex(dbAuth.ValidationRegex).IsMatch(id.Value); if (nonMatch) { issues.Add(new DetectedIssue(priority, $"id.format", $"Identifier {id.Value} in domain {dbAuth.DomainName} failed format validation", DetectedIssueKeys.FormalConstraintIssue)); } } } // Validate issues if (issues.Any(o => o.Priority == DetectedIssuePriorityType.Error)) { throw new DetectedIssueException(issues); } else if (issues.Count > 0) { var extension = data.Extensions.FirstOrDefault(o => o.ExtensionTypeKey == ExtensionTypeKeys.DataQualityExtension); if (extension == null) { data.Extensions.Add(new EntityExtension(ExtensionTypeKeys.DataQualityExtension, typeof(DictionaryExtensionHandler), issues)); } else { var existingValues = extension.GetValue <List <DetectedIssue> >(); existingValues.AddRange(issues); extension.ExtensionValue = existingValues; } } }