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