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_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( ); }
/// <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); }
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)); }
/// <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); }