Example #1
0
        /// <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));
        }
Example #2
0
 public CrudTests()
 {
     _host            = ServerBuilder.CreateServer();
     _scope           = _host.Services.CreateScope();
     _serviceProvider = _scope.ServiceProvider;
     _database        = _serviceProvider.GetRequiredService <LactalisDBContext>();
 }
Example #3
0
 /// <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));
 }
Example #4
0
 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;
			}
		}
Example #6
0
        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);
        }
Example #7
0
 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;
 }
Example #8
0
        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;
        }
Example #9
0
 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;
 }
Example #10
0
 public static User GetUserFromDB(Guid id)
 {
     using var context = new LactalisDBContext(new StartupTestFixture().DbContextOptions, null, null);
     return(context.Users.FirstOrDefault(u => u.Id == id));
 }
Example #11
0
        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;
            })