public void Test_WithRoleWithOverlappingRules( )
        {
            long       userId      = 1;
            long       roleId      = 2;
            AccessRule accessRule1 = new AccessRule( );
            AccessRule accessRule2 = new AccessRule( );
            AccessRule accessRule3 = new AccessRule( );
            Mock <IUserRoleRepository> mockRoles = new Mock <IUserRoleRepository>(MockBehavior.Strict);
            Mock <IRuleRepository>     mockRules = new Mock <IRuleRepository>(MockBehavior.Strict);
            UserRuleSet userRuleSet;

            mockRoles.Setup(x => x.GetUserRoles(userId)).Returns(new HashSet <long> {
                roleId
            });
            mockRules.Setup(x => x.GetAccessRules(userId, Permissions.Read, null)).Returns(new List <AccessRule> {
                accessRule1, accessRule2
            });
            mockRules.Setup(x => x.GetAccessRules(roleId, Permissions.Read, null)).Returns(new List <AccessRule> {
                accessRule2, accessRule3
            });

            UserRuleSetProvider userRuleSetprovider = new UserRuleSetProvider(mockRoles.Object, mockRules.Object);

            userRuleSet = null;
            Assert.That(() => userRuleSet = userRuleSetprovider.GetUserRuleSet(userId, Permissions.Read), Throws.Nothing);
            Assert.That(userRuleSet, Is.Not.Null);
            Assert.That(userRuleSet.Key, Is.EqualTo(CryptoHelper.HashValues(new [] { accessRule1.Id, accessRule2.Id, accessRule3.Id }.OrderBy(id => id))));

            mockRoles.VerifyAll( );
            mockRules.VerifyAll( );
        }
        public void Test_IUserRoleRepository_ReturnsNull( )
        {
            long userId = 1;
            Mock <IUserRoleRepository> mockRoles = new Mock <IUserRoleRepository>(MockBehavior.Strict);
            Mock <IRuleRepository>     mockRules = new Mock <IRuleRepository>(MockBehavior.Strict);

            mockRoles.Setup(x => x.GetUserRoles(userId)).Returns((ISet <long>)null);

            UserRuleSetProvider userRuleSetprovider = new UserRuleSetProvider(mockRoles.Object, mockRules.Object);

            Assert.Throws <InvalidOperationException>(() => userRuleSetprovider.GetUserRuleSet(userId, Permissions.Read));
        }
        public void Test_TwoUsersSameTwoRoles_OneHasOwnRule( )
        {
            long       user1       = 1;
            long       user2       = 2;
            long       role1       = 3;
            long       role2       = 4;
            AccessRule accessRule1 = new AccessRule( );
            AccessRule accessRule2 = new AccessRule( );
            AccessRule accessRule3 = new AccessRule( );
            AccessRule accessRule4 = new AccessRule( );
            Mock <IUserRoleRepository> mockRoles = new Mock <IUserRoleRepository>(MockBehavior.Strict);
            Mock <IRuleRepository>     mockRules = new Mock <IRuleRepository>(MockBehavior.Strict);

            mockRoles.Setup(x => x.GetUserRoles(user1)).Returns(new HashSet <long> {
                role1, role2
            });
            mockRoles.Setup(x => x.GetUserRoles(user2)).Returns(new HashSet <long> {
                role1, role2
            });
            mockRules.Setup(x => x.GetAccessRules(user1, Permissions.Read, null)).Returns(new List <AccessRule> {
            });
            mockRules.Setup(x => x.GetAccessRules(user2, Permissions.Read, null)).Returns(new List <AccessRule> {
                accessRule4
            });
            mockRules.Setup(x => x.GetAccessRules(role1, Permissions.Read, null)).Returns(new List <AccessRule> {
                accessRule1
            });
            mockRules.Setup(x => x.GetAccessRules(role2, Permissions.Read, null)).Returns(new List <AccessRule> {
                accessRule2, accessRule3
            });

            UserRuleSetProvider userRuleSetprovider = new UserRuleSetProvider(mockRoles.Object, mockRules.Object);

            UserRuleSet userRuleSet1 = userRuleSetprovider.GetUserRuleSet(user1, Permissions.Read);
            UserRuleSet userRuleSet2 = userRuleSetprovider.GetUserRuleSet(user2, Permissions.Read);

            Assert.That(userRuleSet1, Is.Not.EqualTo(userRuleSet2));
        }
        public void Test_SingleRuleOnSubject( )
        {
            long       userId     = 1;
            AccessRule accessRule = new AccessRule( );
            Mock <IUserRoleRepository> mockRoles = new Mock <IUserRoleRepository>(MockBehavior.Strict);
            Mock <IRuleRepository>     mockRules = new Mock <IRuleRepository>(MockBehavior.Strict);
            UserRuleSet userRuleSet;

            mockRoles.Setup(x => x.GetUserRoles(userId)).Returns(new HashSet <long>( ));
            mockRules.Setup(x => x.GetAccessRules(userId, Permissions.Read, null)).Returns(new List <AccessRule> {
                accessRule
            });

            UserRuleSetProvider userRuleSetprovider = new UserRuleSetProvider(mockRoles.Object, mockRules.Object);

            userRuleSet = null;
            Assert.That(() => userRuleSet = userRuleSetprovider.GetUserRuleSet(userId, Permissions.Read), Throws.Nothing);
            Assert.That(userRuleSet, Is.Not.Null);

            mockRoles.VerifyAll( );
            mockRules.VerifyAll( );
        }
Exemple #5
0
        /// <summary>
        /// Create cache keys
        /// </summary>
        /// <param name="query">The query</param>
        /// <param name="settings">The query settings</param>
        /// <param name="perUserKey">True if the key is for a specific user; or false if it is to be shared across users.</param>
        /// <returns>The cache key</returns>
        private CachingQueryRunnerKey CreateCacheKeyImpl(StructuredQuery query, QuerySettings settings, bool perUserKey)
        {
            // Get a user-set key
            // (Users may share the same report SQL if they have the same set of read-rules)
            UserRuleSet userRuleSet = null;

            if (settings.RunAsUser != 0)
            {
                userRuleSet = UserRuleSetProvider.GetUserRuleSet(settings.RunAsUser, Permissions.Read);
                if (userRuleSet == null)
                {
                    throw new InvalidOperationException("Expected userRuleSet");   // Assert false
                }
            }

            // Create cache key
            // Cached with userId = -1 if the user
            long userId = perUserKey ? settings.RunAsUser : ShareAcrossUsersCacheKey;
            var  key    = new CachingQueryRunnerKey(query, settings, userRuleSet, userId);

            return(key);
        }
Exemple #6
0
        /// <summary>
        /// Build the SQL, or collect it from cache.
        /// </summary>
        /// <param name="query"></param>
        /// <param name="settings"></param>
        /// <returns></returns>
        public QueryBuild BuildSql(StructuredQuery query, QuerySqlBuilderSettings settings)
        {
            // Validate
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }
            if (QuerySqlBuilder == null)
            {
                throw new InvalidOperationException("QuerySqlBuilder not set.");
            }
            if (settings == null)
            {
                settings = new QuerySqlBuilderSettings( );
            }

            // Check if query can even participate in cache
            if (!CachingQuerySqlBuilderKey.DoesRequestAllowForCaching(query, settings))
            {
                return(BuildSqlImpl(query, settings));
            }

            // Get a user-set key
            // (Users may share the same report SQL if they have the same set of read-rules)
            UserRuleSet userRuleSet = null;

            if (settings.RunAsUser != 0)
            {
                userRuleSet = UserRuleSetProvider.GetUserRuleSet(settings.RunAsUser, Permissions.Read);
                if (userRuleSet == null)
                {
                    throw new InvalidOperationException("Expected userRuleSet");   // Assert false
                }
            }

            // Create cache key
            CachingQuerySqlBuilderKey   key = new CachingQuerySqlBuilderKey(query, settings, userRuleSet);
            CachingQuerySqlBuilderValue cacheValue;

            using (MessageContext msg = new MessageContext("Reports"))
            {
                // Check for force recalculation
                if (settings.RefreshCachedSql)
                {
                    msg.Append(() => "CachingQuerySqlBuilder refreshed forced");
                    _cacheInvalidator.DebugInvalidations.Add(key);
                    Cache.Remove(key);
                }

                // In some circumstances, a result will be uncacheable, so we just return 'null' in the delegate instead.
                // However, on the first access, we will still be doing the calculation, so store it here.
                CachingQuerySqlBuilderValue calculatedOnThisAccess = null;

                // Check cache
                bool fromCache = TryGetOrAdd(key, msg, out cacheValue, callbackKey =>
                {
                    // This callback is called if we have a cache miss

                    using (CacheContext cacheContext = new CacheContext( ))
                    {
                        QueryBuild queryResult = BuildSqlImpl(query, settings);
                        cacheValue             = new CachingQuerySqlBuilderValue(query, queryResult);
                        calculatedOnThisAccess = cacheValue;

                        if (queryResult.SqlIsUncacheable)
                        {
                            return(null);
                        }
                        else
                        {
                            // Add the cache context entries to the appropriate CacheInvalidator
                            _cacheInvalidator.AddInvalidations(cacheContext, callbackKey);
                            return(cacheValue);
                        }
                    }
                });

                // cacheValue will be null if the result was uncacheable
                if (cacheValue == null)
                {
                    if (calculatedOnThisAccess != null)
                    {
                        // In this path, the result was uncacheable, so the cache returned a 'null',
                        // but it was the initial calculation run anyway, so we can get the value from callbackValue.
                        cacheValue = calculatedOnThisAccess;
                    }
                    else
                    {
                        // In this path, the result was uncacheable, but someone had asked previously, and stored
                        // the null, so we need to actually build the SQL again for this scenario.
                        // Note: don't need to do anything with cache context, because this cache is not participating.
                        // And if there's a parent context set, then the call to BuildSqlImpl will just talk directly to that context.
                        QueryBuild queryResult = BuildSqlImpl(query, settings);
                        cacheValue = new CachingQuerySqlBuilderValue(query, queryResult);
                    }
                }
                else if (fromCache && CacheContext.IsSet( ))
                {
                    // Add the already stored changes that should invalidate this cache
                    // entry to any outer or containing cache contexts.
                    using (CacheContext cacheContext = CacheContext.GetContext( ))
                    {
                        cacheContext.AddInvalidationsFor(_cacheInvalidator, key);
                    }
                }
            }

            if (cacheValue == null)
            {
                throw new Exception("Assert false");
            }

            // Mutate returned result to be suitable for current query
            QueryBuild result;

            if (cacheValue.OriginalQuery == query)
            {
                result = cacheValue.QueryResult;
            }
            else
            {
                result = MutateResultToMatchCurrentQuery(cacheValue, query);
            }

            return(result);
        }