/// <summary> /// Creates the filter function for reading, updating or deleting data from an enumeration of models /// </summary> /// <param name="operation">The database operation that is being performed</param> /// <param name="identityService">The identity service to fetch the user from</param> /// <param name="userManager">A userManager to pass to the ACLs</param> /// <param name="dbContext">A dbContext to pass to the ACLs</param> /// <param name="serviceProvider">Service provider to pass to the ACLs</param> /// <typeparam name="TModel">The type of the model to add security to</typeparam> /// <returns>An expression that can be user for the where condition of a linq query</returns> public static Expression <Func <TModel, bool> > CreateSecurityFilter <TModel>( DATABASE_OPERATION operation, IIdentityService identityService, UserManager <User> userManager, LactalisDBContext dbContext, IServiceProvider serviceProvider) where TModel : IOwnerAbstractModel, new() { identityService.RetrieveUserAsync().Wait(); var model = new TModel(); var userGroups = identityService.Groups; var userModelAcls = model.Acls.Where(x => userGroups.Contains(x.Group) || x.IsVisitorAcl); Expression <Func <TModel, bool> > baseRule = _ => false; var filter = Expression.OrElse(baseRule.Body, baseRule.Body); if (!userModelAcls.Any()) { // If we have no rules on this model then we should inherit from the model driven base rule Expression <Func <TModel, bool> > defaultFilter = _ => ALLOW_DEFAULT; filter = Expression.OrElse(filter, defaultFilter.Body); } else { // Otherwise combine the filter on acl with any existing filters var securityContext = new SecurityContext { DbContext = dbContext, UserManager = userManager, Groups = identityService.Groups, ServiceProvider = serviceProvider, }; IEnumerable <Expression <Func <TModel, bool> > > acls = null; switch (operation) { case DATABASE_OPERATION.READ: acls = userModelAcls.Select(acl => acl.GetReadConditions <TModel>(identityService.User, securityContext)); break; case DATABASE_OPERATION.UPDATE: acls = userModelAcls.Select(acl => acl.GetUpdateConditions <TModel>(identityService.User, securityContext)); break; case DATABASE_OPERATION.DELETE: acls = userModelAcls.Select(acl => acl.GetDeleteConditions <TModel>(identityService.User, securityContext)); break; default: break; } filter = acls.Aggregate(filter, (current, expression) => Expression.OrElse(current, expression.Body)); } var param = Expression.Parameter(typeof(TModel), "model"); var replacer = new ParameterReplacer(param); return(Expression.Lambda <Func <TModel, bool> >(replacer.Visit(filter), param)); }
public CrudTests() { _host = ServerBuilder.CreateServer(); _scope = _host.Services.CreateScope(); _serviceProvider = _scope.ServiceProvider; _database = _serviceProvider.GetRequiredService <LactalisDBContext>(); }
/// <summary> /// Creates the filter function for deleting data from an enumeration of models /// </summary> /// <param name="identityService"></param> /// <param name="userManager">A userManager to pass to the ACLs</param> /// <param name="dbContext">A dbContext to pass to the ACLs</param> /// <param name="serviceProvider">Service provider to pass to the ACLs</param> /// <typeparam name="TModel">The type of the model to add security to</typeparam> /// <returns>An expression that can be used for the where condition of a linq query</returns> public static Expression <Func <TModel, bool> > CreateDeleteSecurityFilter <TModel>( IIdentityService identityService, UserManager <User> userManager, LactalisDBContext dbContext, IServiceProvider serviceProvider) where TModel : IOwnerAbstractModel, new() { return(CreateSecurityFilter <TModel>(DATABASE_OPERATION.DELETE, identityService, userManager, dbContext, serviceProvider)); }
public static IQueryable <T> AddDeleteSecurityFiltering <T>( this IQueryable <T> queryable, IIdentityService identityService, UserManager <User> userManager, LactalisDBContext dbContext, IServiceProvider serviceProvider) where T : IOwnerAbstractModel, new() { return(queryable.Where(SecurityService.CreateDeleteSecurityFilter <T>(identityService, userManager, dbContext, serviceProvider))); }
public StartupTestFixture() { AppSettings = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddXmlFile("appsettings.Test.xml", optional: true, reloadOnChange: false) .AddEnvironmentVariables() .AddEnvironmentVariables("Lactalis_") .AddEnvironmentVariables($"Lactalis_Test_") .Build(); //load in site configuration var siteConfiguration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddIniFile("SiteConfig.ini", optional: true, reloadOnChange: false) .Build(); SiteSettings = new SiteSettings(); siteConfiguration.GetSection("site").Bind(SiteSettings); //load in the user configurations UserSettings = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddIniFile("UserConfig.ini", optional: true, reloadOnChange: false) .Build(); var superUserSettings = new UserSettings(); var testUserSettings = new UserSettings(); UserSettings.GetSection("super").Bind(superUserSettings); UserSettings.GetSection("test").Bind(testUserSettings); var baseUrlFromEnvironment = Environment.GetEnvironmentVariable("BASE_URL"); BaseUrl = baseUrlFromEnvironment ?? SiteSettings.BaseUrl; TestUsername = testUserSettings.Username; TestPassword = testUserSettings.Password; SuperUsername = superUserSettings.Username; SuperPassword = superUserSettings.Password; var dbConnectionString = AppSettings["ConnectionStrings:DbConnectionString"]; DbContextOptions = new DbContextOptionsBuilder<LactalisDBContext>() .UseNpgsql(dbConnectionString) .Options; PingServer.TestConnection(BaseUrl); using (var context = new LactalisDBContext(DbContextOptions, null, null)) { SuperOwnerId = context.Users.First(x => x.UserName == SuperUsername).Id; } }
public Guid CreateUser(bool isRegistered = true) { //setup the rest client var client = new RestClient { BaseUrl = new Uri($"{_configure.BaseUrl}/api/graphql") }; //setup the request var request = new RestRequest { Method = Method.POST, RequestFormat = DataFormat.Json }; //get the authorization token and adds the token to the request var loginToken = new LoginToken(_configure.BaseUrl, _configure.SuperUsername, _configure.SuperPassword); var authorizationToken = $"{loginToken.TokenType} {loginToken.AccessToken}"; request.AddHeader("Authorization", authorizationToken); request.AddHeader("Content-Type", "application/json"); request.AddHeader("Accept", "*\\*"); var query = QueryBuilder.CreateEntityQueryBuilder(new List <BaseEntity> { this }); request.AddParameter("text/json", query, ParameterType.RequestBody); var response = client.Execute(request); //valid ids returned and a valid response Assert.Equal(HttpStatusCode.OK, response.StatusCode); if (isRegistered) { // we will confirm their email, they are registered var configure = new StartupTestFixture(); using (var context = new LactalisDBContext(configure.DbContextOptions, null, null)) { context.Users.FirstOrDefault(x => x.UserName == EmailAddress).EmailConfirmed = true; context.SaveChanges(); } } return(Id); }
public CrudService( LactalisDBContext dbContext, UserManager <User> userManager, ISecurityService securityService, IIdentityService identityService, ILogger <CrudService> logger, IServiceProvider serviceProvider, IUploadStorageProvider storageProvider, IUserService userService, IAuditService auditService) { _dbContext = dbContext; _userManager = userManager; _securityService = securityService; _identityService = identityService; _logger = logger; _serviceProvider = serviceProvider; _storageProvider = storageProvider; _userService = userService; _auditService = auditService; }
public GraphqlFileTests() { _host = ServerBuilder.CreateServer(new ServerBuilderOptions { ConfigureServices = (collection, options) => { collection.AddScoped <IUploadStorageProvider, InMemoryFileProvider>(); } }); var serviceProvider = _host.Services.CreateScope().ServiceProvider; var httpContextAccessor = serviceProvider.GetRequiredService <IHttpContextAccessor>(); _dbContext = serviceProvider.GetRequiredService <LactalisDBContext>(); _storageProvider = serviceProvider.GetRequiredService <IUploadStorageProvider>(); _crudService = serviceProvider.GetRequiredService <ICrudService>(); _graphqlService = serviceProvider.GetRequiredService <IGraphQlService>(); _identityService = serviceProvider.GetRequiredService <IIdentityService>(); _fileController = serviceProvider.GetRequiredService <FileController>(); _fileController.ControllerContext.HttpContext = httpContextAccessor.HttpContext; }
public GraphQlService( ISchema schema, IDocumentExecuter executer, LactalisDBContext dataContext, ISecurityService securityService, UserManager <User> userManager, IUserService userService, ICrudService crudService, IServiceProvider serviceProvider, IIdentityService identityService, IAuditService auditService) { _schema = schema; _executer = executer; _dataContext = dataContext; _securityService = securityService; _userManager = userManager; _userService = userService; _crudService = crudService; _identityService = identityService; _serviceProvider = serviceProvider; _auditService = auditService; }
public static User GetUserFromDB(Guid id) { using var context = new LactalisDBContext(new StartupTestFixture().DbContextOptions, null, null); return(context.Users.FirstOrDefault(u => u.Id == id)); }
public async Task <List <string> > CheckDbSecurityChanges(IIdentityService identityService, LactalisDBContext dbContext) { await identityService.RetrieveUserAsync(); var securityContext = new SecurityContext { DbContext = dbContext, UserManager = _userManager, Groups = identityService.Groups, ServiceProvider = _serviceProvider, }; return(dbContext.ChangeTracker .Entries() .Where(entry => entry.Entity is IOwnerAbstractModel) .GroupBy(entry => entry.Entity.GetType()) .ToDictionary(grouping => grouping.Key, grouping => { var entityName = grouping.First().Entity.GetType().Name; var modelErrors = new List <string>(); // If there is no mutating actions then continue var actions = grouping.Select(e => e.State).ToList(); if (!(actions.Contains(EntityState.Added) || actions.Contains(EntityState.Deleted) || actions.Contains(EntityState.Modified))) { return modelErrors; } // Even though we have filtered this before we need this check for the compiler if (!(grouping.FirstOrDefault()?.Entity is IOwnerAbstractModel model)) { modelErrors.Add("Unknown model type"); return modelErrors; } // If the model has no security rules that apply to us then just fall back to the default rule var schemes = model.Acls .Where(scheme => identityService.Groups.Contains(scheme.Group) || scheme.Group == null || scheme.IsVisitorAcl) .ToList(); if (!schemes.Any()) { if (!ALLOW_DEFAULT) { modelErrors.Add($"No applicable schemes for this group on the model '{model.GetType()}'"); } return modelErrors; } // Group on each type of operation that is being performed on the entity var entityStates = grouping.GroupBy(entrySet => entrySet.State, (state, entityEntries) => { return new { State = state, Entities = entityEntries.Select(e => (IOwnerAbstractModel)e.Entity), }; }); // For each type of mutation call the rule on the acls that is applicable to that model foreach (var state in entityStates) { // Construct a list of auth functions to iterate over var authFunctionList = new List <Func <User, IEnumerable <IAbstractModel>, SecurityContext, bool> >(); AddAuthFunctions(authFunctionList, schemes, state.State); // Iterate over each of the auth functions, and work out weather we have permissions to do this // operation. Permissions are additive so we can use a bitwise or to represent the adding of // permissions var canOperate = false; foreach (var func in authFunctionList) { canOperate |= func(identityService.User, state.Entities, securityContext); // Once we hit a true we can break out of the loop since (true | anything == true) if (canOperate) { break; } } // After running the auth functions if we still have no permissions then throw an error message if (!canOperate) { AddError(modelErrors, state.State, entityName); } } return modelErrors; })