public BlobChangeToken(string blobName, CloudBlobContainer container, AzureBlobContentOptions options) { BlobName = blobName; _container = container; _options = options; _lastModifiedUtc = _prevModifiedUtc = DateTime.UtcNow; }
public BlobChangesWatcher(IOptions <AzureBlobContentOptions> options) { _options = options.Value; if (CloudStorageAccount.TryParse(_options.ConnectionString, out CloudStorageAccount cloudStorageAccount)) { var cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient(); _container = cloudBlobClient.GetContainerReference(_options.Container); } }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMemoryCache(); services.AddResponseCaching(); services.Configure <StorefrontOptions>(Configuration.GetSection("VirtoCommerce")); //The IHttpContextAccessor service is not registered by default //https://github.com/aspnet/Hosting/issues/793 services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton <IWorkContextAccessor, WorkContextAccessor>(); services.AddSingleton <IUrlBuilder, UrlBuilder>(); services.AddSingleton <IStorefrontUrlBuilder, StorefrontUrlBuilder>(); services.AddSingleton <IStoreService, StoreService>(); services.AddSingleton <ICurrencyService, CurrencyService>(); services.AddSingleton <ISlugRouteService, SlugRouteService>(); services.AddSingleton <IMemberService, MemberService>(); services.AddSingleton <ICustomerOrderService, CustomerOrderService>(); services.AddSingleton <IQuoteService, QuoteService>(); services.AddSingleton <ISubscriptionService, SubscriptionService>(); services.AddSingleton <ICatalogService, CatalogService>(); services.AddSingleton <IInventoryService, InventoryService>(); services.AddSingleton <IPricingService, PricingService>(); services.AddSingleton <ITaxEvaluator, TaxEvaluator>(); services.AddSingleton <IPromotionEvaluator, PromotionEvaluator>(); services.AddSingleton <IDynamicContentEvaluator, DynamicContentEvaluator>(); services.AddSingleton <IMarketingService, MarketingService>(); services.AddSingleton <IStaticContentService, StaticContentService>(); services.AddSingleton <IMenuLinkListService, MenuLinkListServiceImpl>(); services.AddSingleton <IStaticContentItemFactory, StaticContentItemFactory>(); services.AddSingleton <IApiChangesWatcher, ApiChangesWatcher>(); services.AddSingleton <AssociationRecommendationsProvider>(); services.AddSingleton <CognitiveRecommendationsProvider>(); services.AddSingleton <IRecommendationProviderFactory, RecommendationProviderFactory>(provider => new RecommendationProviderFactory(provider.GetService <AssociationRecommendationsProvider>(), provider.GetService <CognitiveRecommendationsProvider>())); services.AddTransient <IQuoteRequestBuilder, QuoteRequestBuilder>(); services.AddSingleton <IBlobChangesWatcher, BlobChangesWatcher>(); services.AddTransient <ICartBuilder, CartBuilder>(); services.AddTransient <ICartService, CartService>(); services.AddTransient <AngularAntiforgeryCookieResultFilter>(); services.AddTransient <AnonymousUserForStoreAuthorizationFilter>(); //Register events framework dependencies services.AddSingleton(new InProcessBus()); services.AddSingleton <IEventPublisher>(provider => provider.GetService <InProcessBus>()); services.AddSingleton <IHandlerRegistrar>(provider => provider.GetService <InProcessBus>()); //Cache services.AddSingleton <IStorefrontMemoryCache, StorefrontMemoryCache>(); //Register platform API clients services.AddPlatformEndpoint(options => { Configuration.GetSection("VirtoCommerce:Endpoint").Bind(options); }); services.AddSingleton <ICountriesService, FileSystemCountriesService>(); services.Configure <FileSystemCountriesOptions>(options => { options.FilePath = HostingEnvironment.MapPath("~/countries.json"); }); var contentConnectionString = BlobConnectionString.Parse(Configuration.GetConnectionString("ContentConnectionString")); if (contentConnectionString.Provider.EqualsInvariant("AzureBlobStorage")) { var azureBlobOptions = new AzureBlobContentOptions(); Configuration.GetSection("VirtoCommerce:AzureBlobStorage").Bind(azureBlobOptions); services.AddAzureBlobContent(options => { options.Container = contentConnectionString.RootPath; options.ConnectionString = contentConnectionString.ConnectionString; options.PollForChanges = azureBlobOptions.PollForChanges; options.ChangesPollingInterval = azureBlobOptions.ChangesPollingInterval; }); } else { var fileSystemBlobOptions = new FileSystemBlobContentOptions(); Configuration.GetSection("VirtoCommerce:FileSystemBlobStorage").Bind(fileSystemBlobOptions); services.AddFileSystemBlobContent(options => { options.Path = HostingEnvironment.MapPath(contentConnectionString.RootPath); }); } //Identity overrides for use remote user storage services.AddScoped <IUserStore <User>, UserStoreStub>(); services.AddScoped <IRoleStore <Role>, UserStoreStub>(); services.AddScoped <UserManager <User>, CustomUserManager>(); services.AddScoped <SignInManager <User>, CustomSignInManager>(); //Resource-based authorization that requires API permissions for some operations services.AddSingleton <IAuthorizationHandler, CanImpersonateAuthorizationHandler>(); services.AddSingleton <IAuthorizationHandler, CanReadContentItemAuthorizationHandler>(); services.AddSingleton <IAuthorizationHandler, OnlyRegisteredUserAuthorizationHandler>(); services.AddSingleton <IAuthorizationHandler, AnonymousUserForStoreAuthorizationHandler>(); // register the AuthorizationPolicyProvider which dynamically registers authorization policies for each permission defined in the platform services.AddSingleton <IAuthorizationPolicyProvider, PermissionAuthorizationPolicyProvider>(); //Storefront authorization handler for policy based on permissions services.AddSingleton <IAuthorizationHandler, PermissionAuthorizationHandler>(); services.AddSingleton <IAuthorizationHandler, CanEditOrganizationResourceAuthorizationHandler>(); services.AddSingleton <IAuthorizationHandler, CanAccessOrderAuthorizationHandler>(); services.AddAuthorization(options => { options.AddPolicy(CanImpersonateAuthorizationRequirement.PolicyName, policy => policy.Requirements.Add(new CanImpersonateAuthorizationRequirement())); options.AddPolicy(CanReadContentItemAuthorizeRequirement.PolicyName, policy => policy.Requirements.Add(new CanReadContentItemAuthorizeRequirement())); options.AddPolicy(CanEditOrganizationResourceAuthorizeRequirement.PolicyName, policy => policy.Requirements.Add(new CanEditOrganizationResourceAuthorizeRequirement())); options.AddPolicy(OnlyRegisteredUserAuthorizationRequirement.PolicyName, policy => policy.Requirements.Add(new OnlyRegisteredUserAuthorizationRequirement())); options.AddPolicy(AnonymousUserForStoreAuthorizationRequirement.PolicyName, policy => policy.Requirements.Add(new AnonymousUserForStoreAuthorizationRequirement())); options.AddPolicy(CanAccessOrderAuthorizationRequirement.PolicyName, policy => policy.Requirements.Add(new CanAccessOrderAuthorizationRequirement())); }); var auth = services.AddAuthentication(); var facebookSection = Configuration.GetSection("Authentication:Facebook"); if (facebookSection.GetChildren().Any()) { auth.AddFacebook(facebookOptions => { facebookSection.Bind(facebookOptions); }); } var googleSection = Configuration.GetSection("Authentication:Google"); if (googleSection.GetChildren().Any()) { auth.AddGoogle(googleOptions => { googleSection.Bind(googleOptions); }); } var githubSection = Configuration.GetSection("Authentication:Github"); if (githubSection.GetChildren().Any()) { auth.AddGitHub(GitHubAuthenticationOptions => { githubSection.Bind(GitHubAuthenticationOptions); }); } var stackexchangeSection = Configuration.GetSection("Authentication:Stackexchange"); if (stackexchangeSection.GetChildren().Any()) { auth.AddStackExchange(StackExchangeAuthenticationOptions => { stackexchangeSection.Bind(StackExchangeAuthenticationOptions); }); } //This line is required in order to use the old Identity V2 hashes to prevent rehashes passwords for platform users which login in the storefront //and it can lead to platform access denied for them. (TODO: Need to remove after platform migration to .NET Core) services.Configure <PasswordHasherOptions>(option => option.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2); services.Configure <IdentityOptions>(Configuration.GetSection("IdentityOptions")); services.Configure <CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme, Configuration.GetSection("CookieAuthenticationOptions")); services.AddIdentity <User, Role>(options => { }).AddDefaultTokenProviders(); services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); // The Tempdata provider cookie is not essential. Make it essential // so Tempdata is functional when tracking is disabled. services.Configure <CookieTempDataProviderOptions>(options => { options.Cookie.IsEssential = true; }); services.Replace(ServiceDescriptor.Transient <CookieAuthenticationHandler, CustomCookieAuthenticationHandler>()); //Add Liquid view engine services.AddLiquidViewEngine(options => { Configuration.GetSection("VirtoCommerce:LiquidThemeEngine").Bind(options); }); var snapshotProvider = services.BuildServiceProvider(); services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN"); services.AddMvc(options => { //Workaround to avoid 'Null effective policy causing exception' (on logout) //https://github.com/aspnet/Mvc/issues/7809 //TODO: Try to remove in ASP.NET Core 2.2 options.AllowCombiningAuthorizeFilters = false; // Thus we disable anonymous users based on "Store:AllowAnonymous" store option options.Filters.AddService <AnonymousUserForStoreAuthorizationFilter>(); options.CacheProfiles.Add("Default", new CacheProfile() { Duration = (int)TimeSpan.FromHours(1).TotalSeconds, VaryByHeader = "host" }); options.CacheProfiles.Add("None", new CacheProfile() { NoStore = true, Location = ResponseCacheLocation.None }); options.Filters.AddService(typeof(AngularAntiforgeryCookieResultFilter)); // To include only Api controllers to swagger document options.Conventions.Add(new ApiExplorerApiControllersConvention()); // Use the routing logic of ASP.NET Core 2.1 or earlier: options.EnableEndpointRouting = false; }).AddJsonOptions(options => { options.SerializerSettings.ContractResolver = new DefaultContractResolver() { NamingStrategy = new CamelCaseNamingStrategy() }; options.SerializerSettings.DefaultValueHandling = DefaultValueHandling.Include; options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore; options.SerializerSettings.Converters.Add(new CartTypesJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new MoneyJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new CurrencyJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new OrderTypesJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new RecommendationJsonConverter(snapshotProvider.GetService <IRecommendationProviderFactory>())); //Force serialize MutablePagedList type as array, instead of dictionary options.SerializerSettings.Converters.Add(new MutablePagedListAsArrayJsonConverter(options.SerializerSettings)); //Converter for providing back compatibility with old themes was used CustomerInfo type which has contained user and contact data in the single type. //May be removed when all themes will fixed to new User type with nested Contact property. options.SerializerSettings.Converters.Add(new UserBackwardCompatibilityJsonConverter(options.SerializerSettings)); }).AddViewOptions(options => { options.ViewEngines.Add(snapshotProvider.GetService <ILiquidViewEngine>()); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); //Register event handlers via reflection services.RegisterAssembliesEventHandlers(typeof(Startup)); services.AddApplicationInsightsTelemetry(); services.AddApplicationInsightsExtensions(Configuration); //https://github.com/aspnet/HttpAbstractions/issues/315 //Changing the default html encoding options, to not encode non-Latin characters services.Configure <WebEncoderOptions>(options => options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All)); services.Configure <HstsOptions>(options => { options.IncludeSubDomains = true; options.MaxAge = TimeSpan.FromDays(30); }); // Register the Swagger generator, defining 1 or more Swagger documents services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new Info { Title = "Storefront REST API documentation", Version = "v1" }); c.DescribeAllEnumsAsStrings(); c.IgnoreObsoleteProperties(); c.IgnoreObsoleteActions(); // To include 401 response type to actions that requires Authorization c.OperationFilter <AuthResponsesOperationFilter>(); c.OperationFilter <OptionalParametersFilter>(); c.OperationFilter <FileResponseTypeFilter>(); // Workaround of problem with int default value for enum parameter: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/868 c.ParameterFilter <EnumDefaultValueParameterFilter>(); // To avoid errors with repeating type names c.CustomSchemaIds(type => (Attribute.GetCustomAttribute(type, typeof(SwaggerSchemaIdAttribute)) as SwaggerSchemaIdAttribute)?.Id ?? type.FriendlyId()); }); services.AddResponseCompression(); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMemoryCache(); services.AddResponseCaching(); services.Configure <StorefrontOptions>(Configuration.GetSection("VirtoCommerce")); //The IHttpContextAccessor service is not registered by default //https://github.com/aspnet/Hosting/issues/793 services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton <IWorkContextAccessor, WorkContextAccessor>(); services.AddSingleton <IUrlBuilder, UrlBuilder>(); services.AddSingleton <IStorefrontUrlBuilder, StorefrontUrlBuilder>(); services.AddSingleton <IStoreService, StoreService>(); services.AddSingleton <ICurrencyService, CurrencyService>(); services.AddSingleton <ISlugRouteService, SlugRouteService>(); services.AddSingleton <IMemberService, MemberService>(); services.AddSingleton <ICustomerOrderService, CustomerOrderService>(); services.AddSingleton <IQuoteService, QuoteService>(); services.AddSingleton <ISubscriptionService, SubscriptionService>(); services.AddSingleton <ICatalogService, CatalogService>(); services.AddSingleton <IInventoryService, InventoryService>(); services.AddSingleton <IPricingService, PricingService>(); services.AddSingleton <ITaxEvaluator, TaxEvaluator>(); services.AddSingleton <IPromotionEvaluator, PromotionEvaluator>(); services.AddSingleton <IMarketingService, MarketingService>(); services.AddSingleton <IStaticContentService, StaticContentService>(); services.AddSingleton <IMenuLinkListService, MenuLinkListServiceImpl>(); services.AddSingleton <IStaticContentItemFactory, StaticContentItemFactory>(); services.AddSingleton <IApiChangesWatcher, ApiChangesWatcher>(); services.AddSingleton <AssociationRecommendationsProvider>(); services.AddSingleton <CognitiveRecommendationsProvider>(); services.AddSingleton <IRecommendationProviderFactory, RecommendationProviderFactory>(provider => new RecommendationProviderFactory(provider.GetService <AssociationRecommendationsProvider>(), provider.GetService <CognitiveRecommendationsProvider>())); services.AddTransient <IQuoteRequestBuilder, QuoteRequestBuilder>(); services.AddSingleton <IBlobChangesWatcher, BlobChangesWatcher>(); services.AddTransient <ICartBuilder, CartBuilder>(); services.AddTransient <ICartService, CartService>(); //Register events framework dependencies services.AddSingleton(new InProcessBus()); services.AddSingleton <IEventPublisher>(provider => provider.GetService <InProcessBus>()); services.AddSingleton <IHandlerRegistrar>(provider => provider.GetService <InProcessBus>()); //Register platform API clients services.AddPlatformEndpoint(options => { Configuration.GetSection("VirtoCommerce:Endpoint").Bind(options); }); services.AddSingleton <ICountriesService, FileSystemCountriesService>(); services.Configure <FileSystemCountriesOptions>(options => { options.FilePath = HostingEnvironment.MapPath("~/countries.json"); }); var contentConnectionString = BlobConnectionString.Parse(Configuration.GetConnectionString("ContentConnectionString")); if (contentConnectionString.Provider.EqualsInvariant("AzureBlobStorage")) { var azureBlobOptions = new AzureBlobContentOptions(); Configuration.GetSection("VirtoCommerce:AzureBlobStorage").Bind(azureBlobOptions); services.AddAzureBlobContent(options => { options.Container = contentConnectionString.RootPath; options.ConnectionString = contentConnectionString.ConnectionString; options.PollForChanges = azureBlobOptions.PollForChanges; options.ChangesPoolingInterval = azureBlobOptions.ChangesPoolingInterval; }); } else { services.AddFileSystemBlobContent(options => { options.Path = HostingEnvironment.MapPath(contentConnectionString.RootPath); }); } //Identity overrides for use remote user storage services.AddSingleton <IUserStore <User>, UserStoreStub>(); services.AddSingleton <IUserClaimsPrincipalFactory <User>, UserPrincipalFactory>(); services.AddScoped <UserManager <User>, CustomUserManager>(); //Resource-based authorization that requires API permissions for some operations services.AddSingleton <IAuthorizationHandler, CanImpersonateAuthorizationHandler>(); services.AddSingleton <IAuthorizationHandler, CanReadContentItemAuthorizationHandler>(); // register the AuthorizationPolicyProvider which dynamically registers authorization policies for each permission defined in the platform services.AddSingleton <IAuthorizationPolicyProvider, PermissionAuthorizationPolicyProvider>(); //Storefront authorization handler for policy based on permissions services.AddSingleton <IAuthorizationHandler, PermissionAuthorizationHandler>(); services.AddSingleton <IAuthorizationHandler, CanEditOrganizationResourceAuthorizationHandler>(); services.AddAuthorization(options => { options.AddPolicy(CanImpersonateAuthorizationRequirement.PolicyName, policy => policy.Requirements.Add(new CanImpersonateAuthorizationRequirement())); options.AddPolicy(CanReadContentItemAuthorizeRequirement.PolicyName, policy => policy.Requirements.Add(new CanReadContentItemAuthorizeRequirement())); options.AddPolicy(CanEditOrganizationResourceAuthorizeRequirement.PolicyName, policy => policy.Requirements.Add(new CanEditOrganizationResourceAuthorizeRequirement())); }); var auth = services.AddAuthentication(); var facebookSection = Configuration.GetSection("Authentication:Facebook"); if (facebookSection.GetChildren().Any()) { auth.AddFacebook(facebookOptions => { facebookSection.Bind(facebookOptions); }); } var googleSection = Configuration.GetSection("Authentication:Google"); if (googleSection.GetChildren().Any()) { auth.AddGoogle(googleOptions => { googleSection.Bind(googleOptions); }); } var githubSection = Configuration.GetSection("Authentication:Github"); if (githubSection.GetChildren().Any()) { auth.AddGitHub(GitHubAuthenticationOptions => { githubSection.Bind(GitHubAuthenticationOptions); }); } var stackexchangeSection = Configuration.GetSection("Authentication:Stackexchange"); if (stackexchangeSection.GetChildren().Any()) { auth.AddStackExchange(StackExchangeAuthenticationOptions => { stackexchangeSection.Bind(StackExchangeAuthenticationOptions); }); } //This line is required in order to use the old Identity V2 hashes to prevent rehashes passwords for platform users which login in the storefront //and it can lead to platform access denied for them. (TODO: Need to remove after platform migration to .NET Core) services.Configure <PasswordHasherOptions>(option => option.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2); services.AddIdentity <User, IdentityRole>(options => { options.Password.RequiredLength = 8; options.Password.RequireLowercase = true; options.Password.RequireUppercase = true; options.Password.RequireDigit = false; options.Password.RequireNonAlphanumeric = false; options.Lockout.MaxFailedAccessAttempts = 5; options.Lockout.AllowedForNewUsers = true; options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15); }).AddDefaultTokenProviders(); services.ConfigureApplicationCookie(options => { // Cookie settings options.Cookie.HttpOnly = true; options.Cookie.Expiration = TimeSpan.FromDays(30); options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout options.AccessDeniedPath = "/error/AccessDenied"; options.SlidingExpiration = true; }); //Add Liquid view engine services.AddLiquidViewEngine(options => { Configuration.GetSection("VirtoCommerce:LiquidThemeEngine").Bind(options); }); var snapshotProvider = services.BuildServiceProvider(); services.AddMvc(options => { options.CacheProfiles.Add("Default", new CacheProfile() { Duration = (int)TimeSpan.FromHours(1).TotalSeconds, VaryByHeader = "host" }); }).AddJsonOptions(options => { options.SerializerSettings.DefaultValueHandling = DefaultValueHandling.Include; options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore; options.SerializerSettings.Converters.Add(new CartTypesJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new MoneyJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new CurrencyJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new OrderTypesJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new RecommendationJsonConverter(snapshotProvider.GetService <IRecommendationProviderFactory>())); //Converter for providing back compatibility with old themes was used CustomerInfo type which has contained user and contact data in the single type. //May be removed when all themes will fixed to new User type with nested Contact property. options.SerializerSettings.Converters.Add(new UserBackwardCompatibilityJsonConverter(options.SerializerSettings)); }).AddViewOptions(options => { options.ViewEngines.Add(snapshotProvider.GetService <ILiquidViewEngine>()); }); //Register event handlers via reflection services.RegisterAssembliesEventHandlers(typeof(Startup)); services.AddApplicationInsightsTelemetry(); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>(); services.Configure <PlatformOptions>(Configuration.GetSection("VirtoCommerce")); services.Configure <HangfireOptions>(Configuration.GetSection("VirtoCommerce:Jobs")); PlatformVersion.CurrentVersion = SemanticVersion.Parse(Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationVersion); services.AddPlatformServices(Configuration); services.AddSecurityServices(); var mvcBuilder = services.AddMvc().AddJsonOptions(options => { //Next line needs to represent custom derived types in the resulting swagger doc definitions. Because default SwaggerProvider used global JSON serialization settings //we should register this converter globally. options.SerializerSettings.ContractResolver = new PolymorphJsonContractResolver(); //Next line allow to use polymorph types as parameters in API controller methods options.SerializerSettings.Converters.Add(new PolymorphJsonConverter()); options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; options.SerializerSettings.Converters.Add(new StringEnumConverter()); options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.None; options.SerializerSettings.Formatting = Formatting.None; options.SerializerSettings.Error += (sender, args) => { // Expose any JSON serialization exception as HTTP error throw new JsonException(args.ErrorContext.Error.Message); }; options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; } ) .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); var modulesDiscoveryPath = Path.GetFullPath("Modules"); services.AddModules(mvcBuilder, options => { options.DiscoveryPath = modulesDiscoveryPath; options.ProbingPath = "App_Data/Modules"; }); services.Configure <ExternalModuleCatalogOptions>(Configuration.GetSection("ExternalModules")); services.AddExternalModules(); services.AddDbContext <SecurityDbContext>(options => { options.UseSqlServer(Configuration.GetConnectionString("VirtoCommerce")); // Register the entity sets needed by OpenIddict. // Note: use the generic overload if you need // to replace the default OpenIddict entities. options.UseOpenIddict(); }); services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddSecurityServices(options => { options.NonEditableUsers = new[] { "admin" }; }); services.AddIdentity <ApplicationUser, Role>() .AddEntityFrameworkStores <SecurityDbContext>() .AddDefaultTokenProviders(); // Configure Identity to use the same JWT claims as OpenIddict instead // of the legacy WS-Federation claims it uses by default (ClaimTypes), // which saves you from doing the mapping in your authorization controller. services.Configure <IdentityOptions>(options => { options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name; options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject; options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role; }); // Register the OAuth2 validation handler. services.AddAuthentication().AddOAuthValidation(); // Register the OpenIddict services. // Note: use the generic overload if you need // to replace the default OpenIddict entities. services.AddOpenIddict(options => { // Register the Entity Framework stores. options.AddEntityFrameworkCoreStores <SecurityDbContext>(); // Register the ASP.NET Core MVC binder used by OpenIddict. // Note: if you don't call this method, you won't be able to // bind OpenIdConnectRequest or OpenIdConnectResponse parameters. options.AddMvcBinders(); // Enable the authorization, logout, token and userinfo endpoints. options.EnableTokenEndpoint("/connect/token") .EnableUserinfoEndpoint("/api/security/userinfo"); // Note: the Mvc.Client sample only uses the code flow and the password flow, but you // can enable the other flows if you need to support implicit or client credentials. options.AllowPasswordFlow() .AllowRefreshTokenFlow() .AllowClientCredentialsFlow(); // Make the "client_id" parameter mandatory when sending a token request. //options.RequireClientIdentification(); // When request caching is enabled, authorization and logout requests // are stored in the distributed cache by OpenIddict and the user agent // is redirected to the same page with a single parameter (request_id). // This allows flowing large OpenID Connect requests even when using // an external authentication provider like Google, Facebook or Twitter. options.EnableRequestCaching(); // During development, you can disable the HTTPS requirement. options.DisableHttpsRequirement(); // Note: to use JWT access tokens instead of the default // encrypted format, the following lines are required: // options.UseJsonWebTokens(); //TODO: Replace to X.509 certificate options.AddEphemeralSigningKey(); }); services.Configure <IdentityOptions>(Configuration.GetSection("IdentityOptions")); //always return 401 instead of 302 for unauthorized requests services.ConfigureApplicationCookie(options => { options.Events.OnRedirectToLogin = context => { context.Response.StatusCode = 401; return(Task.CompletedTask); }; }); services.AddAuthorization(); // register the AuthorizationPolicyProvider which dynamically registers authorization policies for each permission defined in module manifest services.AddSingleton <IAuthorizationPolicyProvider, PermissionAuthorizationPolicyProvider>(); //Platform authorization handler for policies based on permissions services.AddSingleton <IAuthorizationHandler, PermissionAuthorizationHandler>(); // Add memory cache services services.AddMemoryCache(); //Add Smidge runtime bundling library configuration services.AddSmidge(Configuration.GetSection("smidge"), new PhysicalFileProvider(modulesDiscoveryPath)); services.AddSmidgeNuglify(); // Register the Swagger generator services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new Info { Title = "VirtoCommerce Solution REST API documentation", Version = "v1", Description = "For this sample, you can use the" , Contact = new Contact { Email = "*****@*****.**", Name = "Virto Commerce", Url = "http://virtocommerce.com" } }); c.TagActionsBy(api => api.GroupByModuleName(services)); c.DocInclusionPredicate((docName, api) => true); c.DescribeAllEnumsAsStrings(); c.IgnoreObsoleteProperties(); c.IgnoreObsoleteActions(); c.OperationFilter <FileResponseTypeFilter>(); c.OperationFilter <OptionalParametersFilter>(); c.OperationFilter <TagsFilter>(); c.DocumentFilter <TagsFilter>(); c.MapType <object>(() => new Schema { Type = "object" }); c.AddModulesXmlComments(services); }); //Add SignalR for push notifications services.AddSignalR(); var assetsProvider = Configuration.GetSection("Assets:Provider").Value; if (assetsProvider.EqualsInvariant("AzureBlobStorage")) { var azureBlobOptions = new AzureBlobContentOptions(); Configuration.GetSection("Assets:AzureBlobStorage").Bind(azureBlobOptions); services.AddAzureBlobProvider(options => { options.ConnectionString = azureBlobOptions.ConnectionString; options.CdnUrl = azureBlobOptions.CdnUrl; }); } else { var fileSystemBlobOptions = new FileSystemBlobContentOptions(); Configuration.GetSection("Assets:FileSystem").Bind(fileSystemBlobOptions); services.AddFileSystemBlobProvider(options => { options.RootPath = HostingEnvironment.MapPath(fileSystemBlobOptions.RootPath); options.PublicUrl = fileSystemBlobOptions.PublicUrl; }); } var hangfireOptions = new HangfireOptions(); Configuration.GetSection("VirtoCommerce:Hangfire").Bind(hangfireOptions); if (hangfireOptions.JobStorageType == HangfireJobStorageType.SqlServer) { services.AddHangfire(config => config.UseSqlServerStorage(Configuration.GetConnectionString("VirtoCommerce"))); } else { services.AddHangfire(config => config.UseMemoryStorage()); } }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMemoryCache(); services.AddResponseCaching(); services.Configure <StorefrontOptions>(Configuration.GetSection("VirtoCommerce")); services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN"); //The IHttpContextAccessor service is not registered by default //https://github.com/aspnet/Hosting/issues/793 services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton <IWorkContextAccessor, WorkContextAccessor>(); services.AddSingleton <IUrlBuilder, UrlBuilder>(); services.AddSingleton <IStorefrontUrlBuilder, StorefrontUrlBuilder>(); services.AddSingleton <IStoreService, StoreService>(); services.AddSingleton <ICurrencyService, CurrencyService>(); services.AddSingleton <ISlugRouteService, SlugRouteService>(); services.AddSingleton <IMemberService, MemberService>(); services.AddSingleton <ICustomerOrderService, CustomerOrderService>(); services.AddSingleton <IQuoteService, QuoteService>(); services.AddSingleton <ISubscriptionService, SubscriptionService>(); services.AddSingleton <ICatalogService, CatalogService>(); services.AddSingleton <IInventoryService, InventoryService>(); services.AddSingleton <IPricingService, PricingService>(); services.AddSingleton <ITaxEvaluator, TaxEvaluator>(); services.AddSingleton <IPromotionEvaluator, PromotionEvaluator>(); services.AddSingleton <IMarketingService, MarketingService>(); services.AddSingleton <IStaticContentService, StaticContentService>(); services.AddSingleton <IMenuLinkListService, MenuLinkListServiceImpl>(); services.AddSingleton <IStaticContentItemFactory, StaticContentItemFactory>(); services.AddSingleton <IApiChangesWatcher, ApiChangesWatcher>(); services.AddSingleton <AssociationRecommendationsProvider>(); services.AddSingleton <CognitiveRecommendationsProvider>(); services.AddSingleton <IRecommendationProviderFactory, RecommendationProviderFactory>(provider => new RecommendationProviderFactory(provider.GetService <AssociationRecommendationsProvider>(), provider.GetService <CognitiveRecommendationsProvider>())); services.AddTransient <IQuoteRequestBuilder, QuoteRequestBuilder>(); services.AddSingleton <IBlobChangesWatcher, BlobChangesWatcher>(); services.AddTransient <ICartBuilder, CartBuilder>(); services.AddTransient <ICartService, CartService>(); //Register events framework dependencies services.AddSingleton(new InProcessBus()); services.AddSingleton <IEventPublisher>(provider => provider.GetService <InProcessBus>()); services.AddSingleton <IHandlerRegistrar>(provider => provider.GetService <InProcessBus>()); //Register platform API clients services.AddPlatformEndpoint(options => { Configuration.GetSection("VirtoCommerce:Endpoint").Bind(options); }); services.AddSingleton <ICountriesService, FileSystemCountriesService>(); services.Configure <FileSystemCountriesOptions>(options => { options.FilePath = HostingEnvironment.MapPath("~/countries.json"); }); var contentConnectionString = BlobConnectionString.Parse(Configuration.GetConnectionString("ContentConnectionString")); if (contentConnectionString.Provider.EqualsInvariant("AzureBlobStorage")) { var azureBlobOptions = new AzureBlobContentOptions(); Configuration.GetSection("VirtoCommerce:AzureBlobStorage").Bind(azureBlobOptions); services.AddAzureBlobContent(options => { options.Container = contentConnectionString.RootPath; options.ConnectionString = contentConnectionString.ConnectionString; options.PollForChanges = azureBlobOptions.PollForChanges; options.ChangesPoolingInterval = azureBlobOptions.ChangesPoolingInterval; }); } else { var fileSystemBlobOptions = new FileSystemBlobContentOptions(); Configuration.GetSection("VirtoCommerce:FileSystemBlobStorage").Bind(fileSystemBlobOptions); services.AddFileSystemBlobContent(options => { options.Path = HostingEnvironment.MapPath(contentConnectionString.RootPath); }); } //Identity overrides for use remote user storage services.AddScoped <IUserStore <User>, UserStoreStub>(); services.AddScoped <IRoleStore <Role>, UserStoreStub>(); services.AddScoped <UserManager <User>, CustomUserManager>(); services.AddScoped <SignInManager <User>, CustomSignInManager>(); //Resource-based authorization that requires API permissions for some operations services.AddSingleton <IAuthorizationHandler, CanImpersonateAuthorizationHandler>(); services.AddSingleton <IAuthorizationHandler, CanReadContentItemAuthorizationHandler>(); services.AddSingleton <IAuthorizationHandler, OnlyRegisteredUserAuthorizationHandler>(); // register the AuthorizationPolicyProvider which dynamically registers authorization policies for each permission defined in the platform services.AddSingleton <IAuthorizationPolicyProvider, PermissionAuthorizationPolicyProvider>(); //Storefront authorization handler for policy based on permissions services.AddSingleton <IAuthorizationHandler, PermissionAuthorizationHandler>(); services.AddSingleton <IAuthorizationHandler, CanEditOrganizationResourceAuthorizationHandler>(); services.AddAuthorization(options => { options.AddPolicy(CanImpersonateAuthorizationRequirement.PolicyName, policy => policy.Requirements.Add(new CanImpersonateAuthorizationRequirement())); options.AddPolicy(CanReadContentItemAuthorizeRequirement.PolicyName, policy => policy.Requirements.Add(new CanReadContentItemAuthorizeRequirement())); options.AddPolicy(CanEditOrganizationResourceAuthorizeRequirement.PolicyName, policy => policy.Requirements.Add(new CanEditOrganizationResourceAuthorizeRequirement())); options.AddPolicy(OnlyRegisteredUserAuthorizationRequirement.PolicyName, policy => policy.Requirements.Add(new OnlyRegisteredUserAuthorizationRequirement())); }); var auth = services.AddAuthentication(); var facebookSection = Configuration.GetSection("Authentication:Facebook"); if (facebookSection.GetChildren().Any()) { auth.AddFacebook(facebookOptions => { facebookSection.Bind(facebookOptions); }); } var googleSection = Configuration.GetSection("Authentication:Google"); if (googleSection.GetChildren().Any()) { auth.AddGoogle(googleOptions => { googleSection.Bind(googleOptions); }); } var githubSection = Configuration.GetSection("Authentication:Github"); if (githubSection.GetChildren().Any()) { auth.AddGitHub(GitHubAuthenticationOptions => { githubSection.Bind(GitHubAuthenticationOptions); }); } var stackexchangeSection = Configuration.GetSection("Authentication:Stackexchange"); if (stackexchangeSection.GetChildren().Any()) { auth.AddStackExchange(StackExchangeAuthenticationOptions => { stackexchangeSection.Bind(StackExchangeAuthenticationOptions); }); } //This line is required in order to use the old Identity V2 hashes to prevent rehashes passwords for platform users which login in the storefront //and it can lead to platform access denied for them. (TODO: Need to remove after platform migration to .NET Core) services.Configure <PasswordHasherOptions>(option => option.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2); services.Configure <IdentityOptions>(Configuration.GetSection("IdentityOptions")); services.Configure <CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme, Configuration.GetSection("CookieAuthenticationOptions")); services.AddIdentity <User, Role>(options => { }).AddDefaultTokenProviders(); services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); // The Tempdata provider cookie is not essential. Make it essential // so Tempdata is functional when tracking is disabled. services.Configure <CookieTempDataProviderOptions>(options => { options.Cookie.IsEssential = true; }); services.Replace(ServiceDescriptor.Transient <CookieAuthenticationHandler, CustomCookieAuthenticationHandler>()); //Add Liquid view engine services.AddLiquidViewEngine(options => { Configuration.GetSection("VirtoCommerce:LiquidThemeEngine").Bind(options); }); var snapshotProvider = services.BuildServiceProvider(); services.AddMvc(options => { //Workaround to avoid 'Null effective policy causing exception' (on logout) //https://github.com/aspnet/Mvc/issues/7809 //TODO: Try to remove in ASP.NET Core 2.2 options.AllowCombiningAuthorizeFilters = false; options.CacheProfiles.Add("Default", new CacheProfile() { Duration = (int)TimeSpan.FromHours(1).TotalSeconds, VaryByHeader = "host" }); }).AddJsonOptions(options => { options.SerializerSettings.DefaultValueHandling = DefaultValueHandling.Include; options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore; options.SerializerSettings.Converters.Add(new CartTypesJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new MoneyJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new CurrencyJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new OrderTypesJsonConverter(snapshotProvider.GetService <IWorkContextAccessor>())); options.SerializerSettings.Converters.Add(new RecommendationJsonConverter(snapshotProvider.GetService <IRecommendationProviderFactory>())); //Converter for providing back compatibility with old themes was used CustomerInfo type which has contained user and contact data in the single type. //May be removed when all themes will fixed to new User type with nested Contact property. options.SerializerSettings.Converters.Add(new UserBackwardCompatibilityJsonConverter(options.SerializerSettings)); }).AddViewOptions(options => { options.ViewEngines.Add(snapshotProvider.GetService <ILiquidViewEngine>()); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); //Register event handlers via reflection services.RegisterAssembliesEventHandlers(typeof(Startup)); services.AddApplicationInsightsTelemetry(); services.AddApplicationInsightsExtensions(Configuration); services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN"); }