public BooksEntityApp() : base("BookStore", CurrentVersion) { //Areas var booksArea = this.AddArea("books"); var infoArea = this.AddArea("info"); var loginArea = this.AddArea("login"); //main module MainModule = new BooksModule(booksArea); //Standard modules var dbInfoModule = new DbInfoModule(infoArea); //Job exec module - disabled for now //var jobExecModule = new Modules.JobExecution.JobExecutionModule(booksArea); // LoginModule var loginStt = new LoginModuleSettings(passwordExpirationPeriod: TimeSpan.FromDays(180)); loginStt.RequiredPasswordStrength = PasswordStrength.Medium; loginStt.DefaultEmailFrom = "*****@*****.**"; var loginModule = new LoginModule(loginArea, loginStt); // Setup encrypted data module. var encrModule = new EncryptedDataModule(booksArea); // Use TestGenerateCryptoKeys test in BasicTests project to generate // and print crypto keys for all algorithms. var cryptoKey = "8E487AD4C490AC43DF15D33AB654E5A222A02C9C904BC51E48C4FE5B7D86F90A"; var cryptoBytes = HexUtil.HexToByteArray(cryptoKey); encrModule.AddChannel(cryptoBytes); //creates default channel Instance = this; }
public static void InitApp() { Util.Check(!_initFailed, "App initialization failed. Cannot run tests. See other tests output for failure details."); if(BooksApp != null) return; try { //force randomization of schema update SQLs, to test that they will put in correct order anyway DbModelUpdater.Test_RandomizeInitialSchemaUpdatesOrder = true; //Check if Reset was called; if Driver is null, we are running in Test Explorer mode if(Driver == null) SetupForTestExplorerMode(); //Reset Db and drop schema objects var resetDb = ConfigurationManager.AppSettings["ResetDatabase"] == "true"; if(resetDb) Vita.UnitTests.Common.TestUtil.DropSchemaObjects(DbSettings); //Setup model, initialize Books module, create database model, update schema ------------------------------------------------- BooksApp = new BooksEntityApp(LoginCryptoKey); BooksApp.LogPath = LogFilePath; BooksApp.ActivationLogPath = ActivationLogFilePath; BooksApp.CacheSettings.CacheEnabled = CacheEnabled; NotificationListener = new NotificationListener(BooksApp, blockAll: true); //SmtpMock for testing password reset and other processes BooksApp.Init(); //Now connect the main app BooksApp.ConnectTo(DbSettings); //if we have logging app as a separate app - we need to connect it too. // NOTE: good pracice to connect LoggingApp before we connect the main app, so it can log main database update scripts // but it should work anyway. var logDbSettings = new DbSettings(Driver, DbSettings.ModelConfig.Options, LogConnectionString); BooksApp.LoggingApp.ConnectTo(logDbSettings); Thread.Yield(); CreateSampleData(); } catch(ClientFaultException cfx) { Debug.WriteLine("Validation errors: \r\n" + cfx.ToString()); throw; } catch(Exception sx) { _initFailed = true; //Unit test framework shows only ex message, not details; let's write specifics into debug output - it will be shown in test failure report Debug.WriteLine("app init encountered errors: "); Debug.WriteLine(sx.ToLogString()); throw; } }
public static void InitImpl() { if(BooksApp != null) return; LogFilePath = ConfigurationManager.AppSettings["LogFilePath"]; DeleteLocalLogFile(); var protectedSection = (NameValueCollection)ConfigurationManager.GetSection("protected"); var loginCryptoKey = protectedSection["LoginInfoCryptoKey"]; var connString = protectedSection["MsSqlConnectionString"]; var logConnString = protectedSection["MsSqlLogConnectionString"]; BooksApp = new BooksEntityApp(loginCryptoKey); //Add mock email/sms service NotificationListener = new NotificationListener(BooksApp, blockAll: true); //Set magic captcha in login settings, so we can pass captcha in unit tests var loginStt = BooksApp.GetConfig<Vita.Modules.Login.LoginModuleSettings>(); loginStt.MagicCaptcha = "Magic"; BooksApp.Init(); //connect to database var driver = MsSqlDbDriver.Create(connString); var dbOptions = MsSqlDbDriver.DefaultMsSqlDbOptions; var dbSettings = new DbSettings(driver, dbOptions, connString, upgradeMode: DbUpgradeMode.Always); // schemas); var resetDb = ConfigurationManager.AppSettings["ResetDatabase"] == "true"; if(resetDb) Vita.UnitTests.Common.TestUtil.DropSchemaObjects(dbSettings); BooksApp.ConnectTo(dbSettings); var logDbSettings = new DbSettings(driver, dbOptions, logConnString); BooksApp.LoggingApp.ConnectTo(logDbSettings); BooksApp.LoggingApp.LogPath = LogFilePath; TestUtil.DeleteAllData(BooksApp, exceptEntities: new [] {typeof(IDbInfo), typeof(IDbModuleInfo)}); TestUtil.DeleteAllData(BooksApp.LoggingApp); SampleDataGenerator.CreateUnitTestData(BooksApp); var serviceUrl = ConfigurationManager.AppSettings["ServiceUrl"]; StartService(serviceUrl); Client = new WebApiClient(serviceUrl); Client.InnerHandler.AllowAutoRedirect = false; //we need it for Redirect test }
//Prepares for full run with a specified server internal static void Reset(TestConfig config) { if(BooksApp != null) BooksApp.Flush(); Thread.Sleep(100); //to allow log dump of buffered messages DeleteLogFiles(); //it will happen only once WriteLog("\r\n------------------------ " + config.ToString() + "---------------------------------------------\r\n\r\n"); ServerType = config.ServerType; CacheEnabled = config.EnableCache; UseBatchMode = config.UseBatchMode; BooksApp = null; _initFailed = false; var protectedSection = (NameValueCollection)ConfigurationManager.GetSection("protected"); //Load connection string ConnectionString = ReplaceBinFolderToken(protectedSection[ServerType + "ConnectionString"]); Util.Check(!string.IsNullOrEmpty(ConnectionString), "Connection string not found for server: {0}.", ServerType); LogConnectionString = ReplaceBinFolderToken(protectedSection[ServerType + "LogConnectionString"]); LogConnectionString = LogConnectionString ?? ConnectionString; LoginCryptoKey = protectedSection["LoginInfoCryptoKey"]; Driver = ToolHelper.CreateDriver(ServerType, ConnectionString); var dbOptions = ToolHelper.GetDefaultOptions(ServerType); if (config.UseStoredProcs) dbOptions |= DbOptions.UseStoredProcs; else dbOptions &= ~DbOptions.UseStoredProcs; if (config.UseBatchMode) dbOptions |= DbOptions.UseBatchMode; else dbOptions &= ~DbOptions.UseBatchMode; // dbOptions |= DbOptions.ForceArraysAsLiterals; -- just to test this flag DbSettings = new DbSettings(Driver, dbOptions, ConnectionString, upgradeMode: DbUpgradeMode.Always); //Test: remap login schema into login2 DbSettings.ModelConfig.MapSchema("login", "login2"); }
public static void TearDown() { //You should do this normally - shutdown the entity store // but in this test app it would take too long time for all tests (re-init database for each test class) // so by default running without it #if FULL_SHUTDOWN if (BooksApp != null) BooksApp.Shutdown(); BooksApp = null; #endif if (BooksApp != null) BooksApp.Flush(); }
//LoginModule is used to retrieve some authorization roles of login module and add them to book store role(s) public BooksAuthorization(BooksEntityApp app) { _app = app; var loginRoles = app.GetModule <LoginModule>().Roles; // Data filters // 'userid' value will be automatically injected by runtime when evaluating lambdas var userDataFilter = new AuthorizationFilter("UserData"); userDataFilter.Add <IUser, Guid>((u, userId) => u.Id == userId); userDataFilter.Add <IBookOrder, Guid>((bo, userId) => bo.User.Id == userId); userDataFilter.Add <IBookOrderLine, Guid>((ol, userId) => ol.Order.User.Id == userId); userDataFilter.Add <IBookReview, Guid>((r, userId) => r.User.Id == userId); // Author data filter. var authorDataFilter = new AuthorizationFilter("AuthorData"); // Simple case. IAuthor record is matched by filter if IAuthor.User.Id == Current-user-Id // Note that IAuthor.User might be null - but it's OK to use 'a.User.Id' - the lambda is rewritten // with extra safe conditionals to allow these expressions authorDataFilter.Add <IAuthor, Guid>((a, userId) => a.User.Id == userId); // More complex case. We provide an expression that checks if userId matches Id of any author of a book. // Option 1 - checking book.Authors list, this requires loading list of ALL authors // authorDataFilter.Add<IBook, Guid>((b, userId) => (b.Authors.Any(a => a.User.Id == userId))); // Option 2 - Making LINQ query, using Exists<> helper method, it loads a single record authorDataFilter.Add <IBook, OperationContext, Guid>((b, ctx, userId) => ctx.Exists <IBookAuthor>( ba => ba.Author.User.Id == userId && ba.Book.Id == b.Id)); // BookOrder filter for adjusting order by StoreManager var adjustOrderFilter = new AuthorizationFilter("OrderToAdjust"); adjustOrderFilter.Add <IBookOrder, Guid>((bo, adjustedOrderId) => bo.Id == adjustedOrderId); adjustOrderFilter.Add <IBookOrderLine, Guid>((line, adjustedOrderId) => line.Order.Id == adjustedOrderId); // Customer/users data filter. CustomerSupport role allows access only to users who are Customers or Authors (not employees) // Here we demo use of data filter expression for pre-filtering users in queries. // When customer support person queries users, the filter 'u.Type==UserType.Customer' will be automatically injected into the LINQ query // We also add another filter for authors - the final filter will be OR of both var custSupportUserFilter = new AuthorizationFilter("CustomerSupportUsers"); custSupportUserFilter.Add <IUser>(u => u.Type == UserType.Customer, FilterUse.Entities | FilterUse.Query); // just for testing filter combinations, we add another filter for users that are authors. // both filters will be combined (using OR), and whenever customer support person queries users, he will get only Customer and Author users var custSupportAuthorUserFilter = new AuthorizationFilter("CustomerSupportAuthorUsers"); custSupportAuthorUserFilter.Add <IUser>(u => u.Type == UserType.Author, FilterUse.All); //Entity resources var books = new EntityGroupResource("Books", typeof(IBook), typeof(IBookAuthor), typeof(IAuthor), typeof(IImage)); var publishers = new EntityGroupResource("Publishers", typeof(IPublisher)); var orders = new EntityGroupResource("Orders", typeof(IBookOrder), typeof(IBookOrderLine)); var reviews = new EntityGroupResource("BookReviews", typeof(IBookReview)); var users = new EntityGroupResource("Users", typeof(IUser)); var authorEditData = new EntityGroupResource("AuthorEditData"); authorEditData.Add(typeof(IAuthor), "Bio"); authorEditData.Add <IBook>(b => b.Description, b => b.Abstract); //authorEditData.Add(typeof(IBook), "Description,Abstract"); //-- alternative way var coupons = new EntityGroupResource("Coupons", typeof(ICoupon)); var couponAppliedOn = new EntityGroupResource("CouponAppliedOn"); couponAppliedOn.Add <ICoupon>(c => c.AppliedOn); //Permissions var browseBooks = new EntityGroupPermission("BrowseBooks", AccessType.Read, books, publishers); var createOrders = new EntityGroupPermission("CreateOrders", AccessType.CRUD, orders); var viewOrders = new EntityGroupPermission("ViewOrders", AccessType.Read, orders); var browseReviews = new EntityGroupPermission("BrowseReviews", AccessType.Read, reviews); var writeReviews = new EntityGroupPermission("WriteReviews", AccessType.CRUD, reviews); var editBooks = new EntityGroupPermission("EditBooks", AccessType.CRUD, books); var deleteReviews = new EntityGroupPermission("DeleteReviews", AccessType.Read | AccessType.Delete, reviews); var editPublishers = new EntityGroupPermission("EditPublishers", AccessType.CRUD, publishers); var editByAuthor = new EntityGroupPermission("EditByAuthor", AccessType.Update, authorEditData); var editUsers = new EntityGroupPermission("EditUsers", AccessType.CRUD, users); var viewUsers = new EntityGroupPermission("ViewUsers", AccessType.Read, users); var adjustOrder = new EntityGroupPermission("AdjustOrder", AccessType.Read | AccessType.Update | AccessType.Delete, orders); // We grant Peek permission here for Coupons. The app code can find a coupon by code, but user cannot see it // (no READ permission) or see any coupons. User receives coupon in email, and uses it when buying a book. // System looks up the coupon code and applies the discount. But user cannot never see any coupons in the system. var lookupCoupon = new EntityGroupPermission("LookupCoupon", AccessType.Peek, coupons); //permission to update ICoupon.AppliedOn property - we set it when user uses coupon var useCoupon = new EntityGroupPermission("UseCoupon", AccessType.UpdateStrict, couponAppliedOn); var manageCoupons = new EntityGroupPermission("ManageCoupons", AccessType.CRUD, coupons); //Activities var browsing = new Activity("Browsing", browseBooks, browseReviews); var shopping = new Activity("Shopping", createOrders, lookupCoupon, useCoupon); var writingReviews = new Activity("Reviewing", writeReviews); var editingUserInfo = new Activity("EditingUserInfo", editUsers); var viewingUserInfo = new Activity("ViewingUserInfo", editUsers); var viewingOrders = new Activity("ViewingOrders", viewOrders, lookupCoupon); var bookEditing = new Activity("BookEditing", editBooks); var editingByAuthor = new Activity("EditingByAuthor", editByAuthor); var managingStore = new Activity("ManagingStore", editPublishers, manageCoupons); var moderatingReviews = new Activity("ModeratingReviews", deleteReviews); var adjustingOrders = new Activity("AdjustingOrders", adjustOrder); // Controller permissions CallUserAccountController = new ObjectAccessPermission("CallUserAccountController", AccessType.ApiAll, typeof(Vita.Samples.BookStore.Api.UserAccountController)); //Roles //Browse books and reviews; AnonymousUser = new Role("AnonymousUser", browsing); // Customer - view/edit orders only for current user; edit reviews only created by current user Customer = new Role("Customer"); Customer.AddChildRoles(AnonymousUser, loginRoles.SelfServiceEditor); Customer.Grant(CallUserAccountController); Customer.Grant(userDataFilter, shopping, writingReviews, editingUserInfo); BookEditor = new Role("BookEditor", browsing, bookEditing); Author = new Role("Author"); Author.ChildRoles.Add(Customer); //author can act as a customer and buy a book //We save the grant in static field, to explicitly enable the activity at runtime for limited scope AuthorEditGrant = Author.GrantDynamic(authorDataFilter, editingByAuthor); //Customer support can view orders and users (only users that are customers!) CustomerSupport = new Role("CustomerSupport", viewingOrders); CustomerSupport.Grant(custSupportUserFilter, viewingUserInfo); CustomerSupport.Grant(custSupportAuthorUserFilter, viewingUserInfo); //Store manager StoreManager = new Role("StoreManager", browsing, managingStore, moderatingReviews); // Store manager is able to adjust orders, but only in the context of dynamic (explicitly started) activity // When adjusting activity starts, it saves order Id in user context under AdjustedOrderIdKey. // All records being edited are then verified against this order Id. // This garantees that during adjustment editing we modify only data for the order that we started adjustment for. ManagerAdjustOrderGrant = StoreManager.GrantDynamic(adjustOrderFilter, adjustingOrders, AdjustedOrderIdKey); //Add permission to access LoginAdministration functions, calendar entities StoreManager.AddChildRoles(loginRoles.LoginAdministrator); StoreManager.ChildRoles.Add(Vita.Modules.Logging.LoggingAuthorizationRoles.Instance.LogDataViewerRole); }
//LoginModule is used to retrieve some authorization roles of login module and add them to book store role(s) public BooksAuthorization(BooksEntityApp app, LoginModule loginModule) { _app = app; // Data filters // 'userid' value will be automatically injected by runtime when evaluating lambdas var userDataFilter = new AuthorizationFilter("UserData"); userDataFilter.Add<IUser, Guid>((u, userId) => u.Id == userId); userDataFilter.Add<IBookOrder, Guid>((bo, userId) => bo.User.Id == userId); userDataFilter.Add<IBookOrderLine, Guid>((ol, userId) => ol.Order.User.Id == userId); userDataFilter.Add<IBookReview, Guid>((r, userId) => r.User.Id == userId); // Author data filter. var authorDataFilter = new AuthorizationFilter("AuthorData"); // Simple case. IAuthor record is matched by filter if IAuthor.User.Id == Current-user-Id // Note that IAuthor.User might be null - but it's OK to use 'a.User.Id' - the lambda is rewritten // with extra safe conditionals to allow these expressions authorDataFilter.Add<IAuthor, Guid>((a, userId) => a.User.Id == userId); // More complex case. We provide an expression that checks if userId matches Id of any author of a book. // Option 1 - checking book.Authors list, this requires loading list of ALL authors // authorDataFilter.Add<IBook, Guid>((b, userId) => (b.Authors.Any(a => a.User.Id == userId))); // Option 2 - Making LINQ query, using Exists<> helper method, it loads a single record authorDataFilter.Add<IBook, OperationContext, Guid>((b, ctx, userId) => ctx.Exists<IBookAuthor>( ba => ba.Author.User.Id == userId && ba.Book.Id == b.Id)); // BookOrder filter for adjusting order by StoreManager var adjustOrderFilter = new AuthorizationFilter("OrderToAdjust"); adjustOrderFilter.Add<IBookOrder, Guid>((bo, adjustedOrderId) => bo.Id == adjustedOrderId); adjustOrderFilter.Add<IBookOrderLine, Guid>((line, adjustedOrderId) => line.Order.Id == adjustedOrderId); // Customer/users data filter. CustomerSupport role allows access only to users who are Customers or Authors (not employees) // Here we demo use of data filter expression for pre-filtering users in queries. // When customer support person queries users, the filter 'u.Type==UserType.Customer' will be automatically injected into the LINQ query // We also add another filter for authors - the final filter will be OR of both var custSupportUserFilter = new AuthorizationFilter("CustomerSupportUsers"); custSupportUserFilter.Add<IUser>(u => u.Type == UserType.Customer, FilterUse.Entities | FilterUse.Query); // just for testing filter combinations, we add another filter for users that are authors. // both filters will be combined (using OR), and whenever customer support person queries users, he will get only Customer and Author users var custSupportAuthorUserFilter = new AuthorizationFilter("CustomerSupportAuthorUsers"); custSupportAuthorUserFilter.Add<IUser>(u => u.Type == UserType.Author, FilterUse.All); //Entity resources var books = new EntityGroupResource("Books", typeof(IBook), typeof(IBookAuthor), typeof(IAuthor), typeof(IImage)); var publishers = new EntityGroupResource("Publishers", typeof(IPublisher)); var orders = new EntityGroupResource("Orders", typeof(IBookOrder), typeof(IBookOrderLine)); var reviews = new EntityGroupResource("BookReviews", typeof(IBookReview)); var users = new EntityGroupResource("Users", typeof(IUser)); var authorEditData = new EntityGroupResource("AuthorEditData"); authorEditData.Add(typeof(IAuthor), "Bio"); authorEditData.Add<IBook>(b => b.Description, b => b.Abstract); //authorEditData.Add(typeof(IBook), "Description,Abstract"); //-- alternative way var coupons = new EntityGroupResource("Coupons", typeof(ICoupon)); var couponAppliedOn = new EntityGroupResource("CouponAppliedOn"); couponAppliedOn.Add<ICoupon>(c => c.AppliedOn); //Permissions var browseBooks = new EntityGroupPermission("BrowseBooks", AccessType.Read, books, publishers); var createOrders = new EntityGroupPermission("CreateOrders", AccessType.CRUD, orders); var viewOrders = new EntityGroupPermission("ViewOrders", AccessType.Read, orders); var browseReviews = new EntityGroupPermission("BrowseReviews", AccessType.Read, reviews); var writeReviews = new EntityGroupPermission("WriteReviews", AccessType.CRUD, reviews); var editBooks = new EntityGroupPermission("EditBooks", AccessType.CRUD, books); var deleteReviews = new EntityGroupPermission("DeleteReviews", AccessType.Read | AccessType.Delete, reviews); var editPublishers = new EntityGroupPermission("EditPublishers", AccessType.CRUD, publishers); var editByAuthor = new EntityGroupPermission("EditByAuthor", AccessType.Update, authorEditData); var editUsers = new EntityGroupPermission("EditUsers", AccessType.CRUD, users); var viewUsers = new EntityGroupPermission("ViewUsers", AccessType.Read, users); var adjustOrder = new EntityGroupPermission("AdjustOrder", AccessType.Read | AccessType.Update | AccessType.Delete, orders); // We grant Peek permission here for Coupons. The app code can find a coupon by code, but user cannot see it // (no READ permission) or see any coupons. User receives coupon in email, and uses it when buying a book. // System looks up the coupon code and applies the discount. But user cannot never see any coupons in the system. var lookupCoupon = new EntityGroupPermission("LookupCoupon", AccessType.Peek, coupons); //permission to update ICoupon.AppliedOn property - we set it when user uses coupon var useCoupon = new EntityGroupPermission("UseCoupon", AccessType.UpdateStrict, couponAppliedOn); var manageCoupons = new EntityGroupPermission("ManageCoupons", AccessType.CRUD, coupons); //Activities var browsing = new Activity("Browsing", browseBooks, browseReviews); var shopping = new Activity("Shopping", createOrders, lookupCoupon, useCoupon); var writingReviews = new Activity("Reviewing", writeReviews); var editingUserInfo = new Activity("EditingUserInfo", editUsers); var viewingUserInfo = new Activity("ViewingUserInfo", editUsers); var viewingOrders = new Activity("ViewingOrders", viewOrders, lookupCoupon); var bookEditing = new Activity("BookEditing", editBooks); var editingByAuthor = new Activity("EditingByAuthor", editByAuthor); var managingStore = new Activity("ManagingStore", editPublishers, manageCoupons); var moderatingReviews = new Activity("ModeratingReviews", deleteReviews); var adjustingOrders = new Activity("AdjustingOrders", adjustOrder); // Controller permissions CallUserAccountController = new ObjectAccessPermission("CallUserAccountController", AccessType.ApiAll, typeof(Vita.Samples.BookStore.Api.UserAccountController)); //Roles //Browse books and reviews; AnonymousUser = new Role("AnonymousUser", browsing); // Customer - view/edit orders only for current user; edit reviews only created by current user Customer = new Role("Customer"); Customer.ChildRoles.Add(AnonymousUser); Customer.Grant(CallUserAccountController); Customer.Grant(userDataFilter, shopping, writingReviews, editingUserInfo); BookEditor = new Role("BookEditor", browsing, bookEditing); Author = new Role("Author"); Author.ChildRoles.Add(Customer); //author can act as a customer and buy a book //We save the grant in static field, to explicitly enable the activity at runtime for limited scope AuthorEditGrant = Author.GrantDynamic(authorDataFilter, editingByAuthor); //Customer support can view orders and users (only users that are customers!) CustomerSupport = new Role("CustomerSupport", viewingOrders); CustomerSupport.Grant(custSupportUserFilter, viewingUserInfo); CustomerSupport.Grant(custSupportAuthorUserFilter, viewingUserInfo); //Store manager StoreManager = new Role("StoreManager", browsing, managingStore, moderatingReviews); // Store manager is able to adjust orders, but only in the context of dynamic (explicitly started) activity // When adjusting activity starts, it saves order Id in user context under AdjustedOrderIdKey. // All records being edited are then verified against this order Id. // This garantees that during adjustment editing we modify only data for the order that we started adjustment for. ManagerAdjustOrderGrant = StoreManager.GrantDynamic(adjustOrderFilter, adjustingOrders, AdjustedOrderIdKey); //Add permission to access LoginAdministration API controller StoreManager.ChildRoles.Add(loginModule.Authorization.LoginAdministrator); StoreManager.ChildRoles.Add(Vita.Modules.Logging.LoggingAuthorizationRoles.Instance.LogDataViewerRole); }