/// <summary> /// Check if any of the queries grant access to all instances of the provided types. /// If so, update all results as true. /// </summary> /// <param name="entityType"></param> /// <param name="queries"></param> /// <param name="entities"></param> /// <param name="result"></param> /// <returns></returns> private static StructuredQuery CheckIfAnyQueryProvideAccessToAllInstancesOfType(long entityType, IEnumerable <AccessRuleQuery> queries, IList <EntityRef> entities, IDictionary <long, bool> result) { foreach (AccessRuleQuery accessRuleQuery in queries) { StructuredQuery structuredQuery = accessRuleQuery.Query; if (!QueryInspector.IsQueryUndamaged(structuredQuery)) { continue; // note: this gets logged by CheckAccessControlByQuery } bool queryGrantsAll = accessRuleQuery.DoesQueryGrantAllOfTypes(entityType); if (queryGrantsAll) { foreach (EntityRef entityRef in entities) { long id = entityRef.Id; if (result.ContainsKey(id)) { result[id] = true; } } return(structuredQuery); } } return(null); }
/// <summary> /// Determines, and caches, whether the access rule report refers to the current user (and would therefore give different results for different users). /// </summary> public bool DoesQueryReferToCurrentUser( ) { if (!_queryRefersToCurrentUser.HasValue) { _queryRefersToCurrentUser = QueryInspector.DoesQueryReferToCurrentUser(Query); } return(_queryRefersToCurrentUser.Value); }
/// <summary> /// Determines, and caches, whether the access rule report grants access to all instances of a specified type. /// </summary> public bool DoesQueryGrantAllOfTypes(long typeId) { bool result = _grantsAccessToAllOfType.GetOrAdd(typeId, typeId1 => { bool result1 = QueryInspector.DoesAccessRuleQueryGrantAllOfTypes(Query, new [] { typeId1 }); return(result1); }); return(result); }
/// <summary> /// Check whether the queries for the specified <paramref name="permission"/> allow the <paramref name="subjectId"/> /// access to <paramref name="entities"/> using the related security queries. Used for read, modify and delete /// permissions. /// </summary> /// <param name="subjectId"> /// The ID of the subject (user or role). /// </param> /// <param name="permission"> /// The permission (operation). This cannot be null. /// </param> /// <param name="entityType"> /// The type ID of the entities to check. This cannot be null. /// </param> /// <param name="entities"> /// The entities to check. This cannot be null or contain null. /// </param> /// <param name="allEntities"> /// All entities. /// </param> /// <param name="queryResultsCache"> /// The query results cache. /// </param> /// <param name="result"> /// The map of entity IDs to whether the relationship exists. /// </param> /// <exception cref="ArgumentNullException"> /// No argument can be null. /// </exception> internal void CheckAccessControlByQuery(long subjectId, EntityRef permission, long entityType, IList <EntityRef> entities, ISet <long> allEntities, IDictionary <long, ISet <long> > queryResultsCache, IDictionary <long, bool> result) { using (Profiler.MeasureAndSuppress("CheckAccessControlByQuery")) { if (permission == null) { throw new ArgumentNullException("permission"); } if (result == null) { throw new ArgumentNullException("result"); } if (entities == null) { throw new ArgumentNullException("entities"); } if (allEntities == null) { throw new ArgumentNullException("allEntities"); } if (queryResultsCache == null) { throw new ArgumentNullException("queryResultsCache"); } if (entities.Contains(null)) { throw new ArgumentException("Cannot check access for null entities", "entities"); } IEnumerable <AccessRuleQuery> queries; // Allow access to temporary IDs if (AllowAccessToTemporaryIds(result)) { return; } using (MessageContext messageContext = new MessageContext(EntityAccessControlService.MessageName)) { queries = QueryRepository.GetQueries(subjectId, permission, new[] { entityType }); QueryResult queryResult; // Check if any queries grant access to all instances of the type StructuredQuery shortCircuitQuery = CheckIfAnyQueryProvideAccessToAllInstancesOfType(entityType, queries, entities, result); if (shortCircuitQuery != null) { messageContext.Append( () => string.Format( "{0} allowed '{1}' access to entities '{2}' because it allows access to all instances of the type.", AccessControlDiagnosticsHelper.GetAccessRuleName(shortCircuitQuery), Permissions.GetPermissionByAlias(permission), string.Join(", ", entities.Select(x => x.ToString())))); return; } long securityOwnerRelId = WellKnownAliases.CurrentTenant.SecurityOwner; foreach (AccessRuleQuery accessRuleQuery in queries) { StructuredQuery structuredQuery = accessRuleQuery.Query; var allowedEntities = new HashSet <long>(); ISet <long> queryResultSet; if (!queryResultsCache.TryGetValue(accessRuleQuery.ReportId, out queryResultSet)) { var querySettings = new QuerySettings { SecureQuery = false, Hint = "security - " + Name }; bool filtered = false; if (allEntities.Count <= MaximumNumberOfFilteredEntities) { filtered = true; querySettings.SupportRootIdFilter = true; querySettings.RootIdFilterList = allEntities; } queryResult = null; try { using (MessageContext msg = new MessageContext("Reports", MessageContextBehavior.New)) { queryResult = Factory.QueryRunner.ExecuteQuery(structuredQuery, querySettings); } } catch (Exception ex) { AccessControlDiagnosticsHelper.WriteInvalidSecurityReportMessage(structuredQuery, messageContext, ex); } queryResultSet = new HashSet <long>(); if (queryResult != null && QueryInspector.IsQueryUndamaged(structuredQuery)) { foreach (DataRow dataRow in queryResult.DataTable.Rows) { var id = dataRow.Field <long>(0); if (filtered || allEntities.Contains(id)) { queryResultSet.Add(id); } } } else { if (queryResult != null) { AccessControlDiagnosticsHelper.WriteInvalidSecurityReportMessage(structuredQuery, messageContext); } } queryResultsCache[accessRuleQuery.ReportId] = queryResultSet; } foreach (EntityRef entityRef in entities) { if (queryResultSet.Contains(entityRef.Id) && result.ContainsKey(entityRef.Id)) { allowedEntities.Add(entityRef.Id); result[entityRef.Id] = true; } } // ReSharper disable AccessToForEachVariableInClosure // ReSharper disable SpecifyACultureInStringConversionExplicitly if (allowedEntities.Count > 0) { messageContext.Append( () => string.Format( "{0} allowed '{1}' access to entities '{2}' out of '{3}'", AccessControlDiagnosticsHelper.GetAccessRuleName(structuredQuery), Permissions.GetPermissionByAlias(permission), string.Join(", ", allowedEntities.Select(x => x.ToString())), string.Join(", ", entities.Select(x => x.ToString())))); } else { messageContext.Append( () => string.Format( "{0} returned no results for '{1}' access to entities '{2}'", AccessControlDiagnosticsHelper.GetAccessRuleName(structuredQuery), Permissions.GetPermissionByAlias(permission), string.Join(", ", entities.Select(x => x.ToString())))); } // ReSharper restore AccessToForEachVariableInClosure // ReSharper restore SpecifyACultureInStringConversionExplicitly // Set the cache invalidation information using (CacheContext cacheContext = CacheContext.GetContext()) { // ******************* TEMPORARY WORKAROUND *********************** // Until we properly implement filtering the invalidating relationships and fields by type // we will ignore invalidating on the security owner relationship cacheContext.RelationshipTypes.Add( StructuredQueryHelper.GetReferencedRelationships(structuredQuery).Where(er => er.Id != securityOwnerRelId).Select(er => er.Id)); cacheContext.FieldTypes.Add( StructuredQueryHelper.GetReferencedFields(structuredQuery, true, true).Select(er => er.Id)); } } } } }