public static void BuildInfoProvider_EmptyBuildInfo() { using (var directory = new TemporaryDirectory()) { var mockWebHostEnvironment = new Mock <IWebHostEnvironment>(MockBehavior.Strict); mockWebHostEnvironment.Setup(_ => _.WebRootPath).Returns(directory.Location).Verifiable(); Directory.CreateDirectory(Path.Combine(directory.Location, "data")); File.WriteAllText(Path.Combine(directory.Location, @"data\BuildInfo.json"), "{}"); File.WriteAllText(Path.Combine(directory.Location, @"manifest.json"), JsonConvert.SerializeObject(Manifest)); var provider = new BuildInfoProvider(mockWebHostEnvironment.Object); Assert.Null(provider.BuildId); Assert.Null(provider.Changelist); Assert.NotNull(provider.Webclient); Assert.Equal(4, provider.Webclient.Count); Assert.Equal("39caf45e53fcc3060725", provider.Webclient["runtime"]); Assert.Equal("7b01886e415366cd2f5c", provider.Webclient["vendors~app"]); Assert.Equal("20c89b387dbdb102dba5", provider.Webclient["data~app"]); Assert.Equal("d8df654c29474da7b106", provider.Webclient["app"]); mockWebHostEnvironment.Verify(); } }
/// <summary> /// Writes out <c>BuildInfo</c> prior to build. /// </summary> public override void OnBeforeBuild() { GDXConfig config = GDXConfig.Get(); if (config == null || !config.developerBuildInfoEnabled) { return; } // Cache for destructor _enabled = true; try { string path = Path.Combine(Application.dataPath, config.developerBuildInfoPath); Platform.EnsureFileFolderHierarchyExists(path); File.WriteAllText(path, BuildInfoProvider.GetContent(config, false, Context.BuildConfigurationName)); BuildInfoProvider.CheckForAssemblyDefinition(); } catch (Exception e) { Debug.LogWarning(e); } }
/// <summary> /// Restores the default <c>BuildInfo</c> after a build process finishes. /// </summary> /// <param name="report">Build process reported information.</param> public void OnPostprocessBuild(BuildReport report) { GDXConfig config = GDXConfig.Get(); if (config == null || !config.developerBuildInfoEnabled) { return; } BuildInfoProvider.WriteDefaultFile(); }
public static void BuildInfoProvider_EmptyManifest() { using (var directory = new TemporaryDirectory()) { var mockWebHostEnvironment = new Mock <IWebHostEnvironment>(MockBehavior.Strict); mockWebHostEnvironment.Setup(_ => _.WebRootPath).Returns(directory.Location).Verifiable(); Directory.CreateDirectory(Path.Combine(directory.Location, "data")); File.WriteAllText(Path.Combine(directory.Location, @"data\BuildInfo.json"), JsonConvert.SerializeObject(BuildInfo)); File.WriteAllText(Path.Combine(directory.Location, @"manifest.json"), "{}"); var provider = new BuildInfoProvider(mockWebHostEnvironment.Object); Assert.Equal("SomeBuildId", provider.BuildId); Assert.Equal("SomeChangelist", provider.Changelist); Assert.NotNull(provider.Webclient); Assert.Equal(0, provider.Webclient.Count); mockWebHostEnvironment.Verify(); } }
/// <summary> /// Writes out <c>BuildInfo</c> prior to build. /// </summary> /// <param name="report">Build process reported information.</param> public void OnPreprocessBuild(BuildReport report) { GDXConfig config = GDXConfig.Get(); if (config == null || !config.developerBuildInfoEnabled) { return; } try { string path = Path.Combine(Application.dataPath, config.developerBuildInfoPath); Platform.EnsureFileFolderHierarchyExists(path); File.WriteAllText(path, BuildInfoProvider.GetContent(config, false, "Legacy")); BuildInfoProvider.CheckForAssemblyDefinition(); } catch (Exception e) { Debug.LogWarning(e); } }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // The DevelopmentStorageAccount will only work if you have the Storage emulator v4.3 installed: https://go.microsoft.com/fwlink/?linkid=717179&clcid=0x409 var storageConnectionString = this.configuration["Storage:ConnectionString"]; CloudStorageAccount storageAccount = null; if (!string.IsNullOrEmpty(storageConnectionString)) { storageAccount = CloudStorageAccount.Parse(storageConnectionString); } // Necessary to persist keys (like the ones used to generate auth cookies) // By default Azure Websites can persist keys across instances within a slot, but not across slots. // This means a slot swap will require users to re-log in. // See https://github.com/aspnet/Home/issues/466 and https://github.com/aspnet/DataProtection/issues/92 for details. var dataProtectionBuilder = services.AddDataProtection(); if (!string.IsNullOrEmpty(storageConnectionString)) { dataProtectionBuilder.PersistKeysToAzureBlobStorage(storageConnectionString, "key-container", "keys.xml"); } // Add Entity framework services. services.AddDbContext <ApplicationDbContext>(options => { options.UseSqlServer(this.configuration["Database:ConnectionString"]); // Register the entity sets needed by OpenIddict. options.UseOpenIddict(); }); services.AddIdentity <ApplicationUser, IdentityRole>(options => { // We need to disallow '@' since we need to disambiguate between user names and email addresses during log in options.User.AllowedUserNameCharacters = options.User.AllowedUserNameCharacters.Replace("@", string.Empty, StringComparison.Ordinal); options.User.RequireUniqueEmail = true; options.Password.RequiredLength = 4; options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; // 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. options.ClaimsIdentity.UserNameClaimType = OpenIddictConstants.Claims.Name; options.ClaimsIdentity.UserIdClaimType = OpenIddictConstants.Claims.Subject; options.ClaimsIdentity.EmailClaimType = OpenIddictConstants.Claims.Email; options.ClaimsIdentity.RoleClaimType = OpenIddictConstants.Claims.Role; }) .AddEntityFrameworkStores <ApplicationDbContext>() .AddDefaultTokenProviders(); // Register the OpenIddict services. services.AddOpenIddict() // Register the OpenIddict core services. .AddCore(options => { // Configure OpenIddict to use the Entity Framework Core stores and models. options.UseEntityFrameworkCore() .UseDbContext <ApplicationDbContext>(); }) // Register the OpenIddict server handler. .AddServer(options => { // Enable the token endpoint (required to use the password flow). options.SetTokenEndpointUris("/api/auth/token"); // Allow client applications to use the grant_type=password flow. options.AllowPasswordFlow() .AllowRefreshTokenFlow() .AllowCustomFlow(GoogleAssertionGrantHandler.GrantType) .AllowCustomFlow(FacebookAssertionGrantHandler.GrantType) .AllowCustomFlow(MicrosoftAssertionGrantHandler.GrantType); // Mark the "email", "profile" and "roles" scopes as supported scopes. options.RegisterScopes( OpenIddictConstants.Scopes.Email, OpenIddictConstants.Scopes.Profile, OpenIddictConstants.Scopes.Roles); // Accept anonymous clients (i.e clients that don't send a client_id). options.AcceptAnonymousClients(); // Use ASP.NET Core data protection options.UseDataProtection(); // Register the signing and encryption credentials. // Use ephemeral keys since we're using Data Protection to validate and issue tokens anyway, // but OpenIddict doesn't allow us to not register anything. options.AddEphemeralEncryptionKey() .AddEphemeralSigningKey(); // When rolling tokens are enabled, immediately // redeem the refresh token to prevent future reuse. options.UseRollingRefreshTokens(); // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. options.UseAspNetCore() .EnableTokenEndpointPassthrough(); }) // Register the OpenIddict validation components. .AddValidation(options => { // Import the configuration from the local OpenIddict server instance. options.UseLocalServer(); // Register the ASP.NET Core host. options.UseAspNetCore(); // Use ASP.NET Core data protection options.UseDataProtection(); }); services.Configure((AssertionGrantOptions options) => { options.AddAssertionGrantType <GoogleAssertionGrantHandler>(GoogleAssertionGrantHandler.GrantType); options.AddAssertionGrantType <FacebookAssertionGrantHandler>(FacebookAssertionGrantHandler.GrantType); options.AddAssertionGrantType <MicrosoftAssertionGrantHandler>(MicrosoftAssertionGrantHandler.GrantType); }); services.AddSingleton <GoogleAssertionGrantHandler>(); services.AddSingleton <FacebookAssertionGrantHandler>(); services.AddSingleton <MicrosoftAssertionGrantHandler>(); services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); }); var buildInfoProvider = new BuildInfoProvider(this.environment); services.AddApplicationInsightsTelemetry(new ApplicationInsightsServiceOptions { ApplicationVersion = buildInfoProvider.BuildId, DeveloperMode = this.environment.IsDevelopment(), }); services.AddCors(); services.AddControllers() .AddJsonOptions(options => { // Beautify by default for debuggability. When gzipping, this barely adds anything to the payload. options.JsonSerializerOptions.WriteIndented = true; // Omit nulls options.JsonSerializerOptions.IgnoreNullValues = true; // Use camel-casing for fields (lower case first character) options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; // Convert enum values to strings options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); }); // Allow IOptions<T> to be available through DI services.AddOptions(); // Container controlled registrations if (storageAccount != null) { services.AddSingleton <CloudTableClient>(_ => storageAccount.CreateCloudTableClient()); services.AddSingleton <ISiteNewsProvider, AzureStorageSiteNewsProvider>(); } else { services.AddSingleton <ISiteNewsProvider, InMemorySiteNewsProvider>(); } services.AddSingleton <GameData>(_ => GameData.Parse(Path.Combine(this.environment.WebRootPath, @"data\GameData.json"))); services.AddSingleton <HttpClient>(_ => new HttpClient()); services.AddSingleton <IAssertionGrantHandlerProvider, AssertionGrantHandlerProvider>(); services.AddSingleton <IBuildInfoProvider>(buildInfoProvider); services.AddSingleton <IEmailSender, EmailSender>(); services.AddSingleton <IOptions <PasswordHasherOptions>, PasswordHasherOptionsAccessor>(); // Per request registrations services.AddScoped <IClanManager, ClanManager>(); services.AddScoped <IDatabaseCommandFactory, DatabaseCommandFactory>(); services.AddScoped <IUserSettingsProvider, UserSettingsProvider>(); // configuration services.Configure <AuthenticationSettings>(options => this.configuration.GetSection("Authentication").Bind(options)); services.Configure <DatabaseSettings>(options => this.configuration.GetSection("Database").Bind(options)); services.Configure <EmailSenderSettings>(options => this.configuration.GetSection("EmailSender").Bind(options)); }
/// <summary> /// Draw the Build Info section of settings. /// </summary> /// <param name="settings">Serialized <see cref="GDXConfig" /> object to be modified.</param> public static void BuildInfo(SerializedObject settings) { const string sectionID = "GDX.Editor.Build.BuildInfoProvider"; GUI.enabled = true; bool buildInfoEnabled = Layout.CreateSettingsSection( sectionID, false, "BuildInfo Generation", $"{DocumentationUri}api/GDX.Editor.Build.BuildInfoProvider.html", settings.FindProperty("developerBuildInfoEnabled"), Content.BuildInfoEnabled); if (!Layout.GetCachedEditorBoolean(sectionID)) { return; } string buildInfoFile = Path.Combine(Application.dataPath, settings.FindProperty("developerBuildInfoPath").stringValue); if (!File.Exists(buildInfoFile)) { GUILayout.BeginVertical(Styles.InfoBoxStyle); GUILayout.BeginHorizontal(); GUILayout.Label( "There is currently no BuildInfo file in the target location. Would you like some default content written in its place?", Styles.WordWrappedLabelStyle); if (GUILayout.Button("Create Default", Styles.ButtonStyle)) { BuildInfoProvider.WriteDefaultFile(); AssetDatabase.ImportAsset("Assets/" + settings.FindProperty("developerBuildInfoPath").stringValue); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } // Only allow editing based on the feature being enabled GUI.enabled = buildInfoEnabled; EditorGUILayout.PropertyField(settings.FindProperty("developerBuildInfoPath"), Content.BuildInfoPath); EditorGUILayout.PropertyField(settings.FindProperty("developerBuildInfoNamespace"), Content.BuildInfoNamespace); EditorGUILayout.PropertyField(settings.FindProperty("developerBuildInfoAssemblyDefinition"), Content.BuildInfoAssemblyDefinition); GUILayout.Space(10); // Arguments (we're going to make sure they are forced to uppercase). GUILayout.Label("Build Arguments", Styles.SubSectionHeaderTextStyle); SerializedProperty buildNumberProperty = settings.FindProperty("developerBuildInfoBuildNumberArgument"); EditorGUILayout.PropertyField(buildNumberProperty, Content.BuildInfoBuildNumberArgument); if (buildNumberProperty.stringValue.HasLowerCase()) { buildNumberProperty.stringValue = buildNumberProperty.stringValue.ToUpper(); } SerializedProperty buildDescriptionProperty = settings.FindProperty("developerBuildInfoBuildDescriptionArgument"); EditorGUILayout.PropertyField(buildDescriptionProperty, Content.BuildInfoBuildDescriptionArgument); if (buildDescriptionProperty.stringValue.HasLowerCase()) { buildDescriptionProperty.stringValue = buildDescriptionProperty.stringValue.ToUpper(); } SerializedProperty buildChangelistProperty = settings.FindProperty("developerBuildInfoBuildChangelistArgument"); EditorGUILayout.PropertyField(buildChangelistProperty, Content.BuildInfoBuildChangelistArgument); if (buildChangelistProperty.stringValue.HasLowerCase()) { buildChangelistProperty.stringValue = buildChangelistProperty.stringValue.ToUpper(); } SerializedProperty buildTaskProperty = settings.FindProperty("developerBuildInfoBuildTaskArgument"); EditorGUILayout.PropertyField(buildTaskProperty, Content.BuildInfoBuildTaskArgument); if (buildTaskProperty.stringValue.HasLowerCase()) { buildTaskProperty.stringValue = buildTaskProperty.stringValue.ToUpper(); } SerializedProperty buildStreamProperty = settings.FindProperty("developerBuildInfoBuildStreamArgument"); EditorGUILayout.PropertyField(buildStreamProperty, Content.BuildInfoBuildStreamArgument); if (buildStreamProperty.stringValue.HasLowerCase()) { buildStreamProperty.stringValue = buildStreamProperty.stringValue.ToUpper(); } }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // The DevelopmentStorageAccount will only work if you have the Storage emulator v4.3 installed: https://go.microsoft.com/fwlink/?linkid=717179&clcid=0x409 var storageConnectionString = this.configuration["Storage:ConnectionString"]; var storageAccount = !string.IsNullOrEmpty(storageConnectionString) ? CloudStorageAccount.Parse(storageConnectionString) : null; // Nesessary to persist keys (like the ones used to generate auth cookies) // By default Azure Websites can persist keys across instances within a slot, but not across slots. // This means a slot swap will require users to re-log in. // See https://github.com/aspnet/Home/issues/466 and https://github.com/aspnet/DataProtection/issues/92 for details. var dataProtectionBuilder = services.AddDataProtection(); if (storageAccount != null) { var client = storageAccount.CreateCloudBlobClient(); var container = client.GetContainerReference("key-container"); // The container must exist before calling the DataProtection APIs. // The specific file within the container does not have to exist, // as it will be created on-demand. container.CreateIfNotExistsAsync().Wait(); dataProtectionBuilder.PersistKeysToAzureBlobStorage(container, "keys.xml"); } // Add Entity framework services. services.AddDbContext <ApplicationDbContext>(options => { options.UseSqlServer(this.configuration["Database:ConnectionString"]); // Register the entity sets needed by OpenIddict. options.UseOpenIddict(); }); services.AddIdentity <ApplicationUser, IdentityRole>(options => { // We need to disallow '@' since we need to disambiguate between user names and email addresses during log in options.User.AllowedUserNameCharacters = options.User.AllowedUserNameCharacters.Replace("@", string.Empty, StringComparison.Ordinal); options.User.RequireUniqueEmail = true; options.Password.RequiredLength = 4; options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; // 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. options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name; options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject; options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role; }) .AddEntityFrameworkStores <ApplicationDbContext>() .AddDefaultTokenProviders(); // Register the OpenIddict services. services.AddOpenIddict() // Register the OpenIddict core services. .AddCore(options => { // Configure OpenIddict to use the Entity Framework Core stores and models. options.UseEntityFrameworkCore() .UseDbContext <ApplicationDbContext>(); }) // Register the OpenIddict server handler. .AddServer(options => { // Register the ASP.NET Core MVC services used by OpenIddict. // Note: if you don't call this method, you won't be able to // bind OpenIdConnectRequest or OpenIdConnectResponse parameters. options.UseMvc(); // Enable the token endpoint (required to use the password flow). options.EnableTokenEndpoint("/api/auth/token"); // Allow client applications to use the grant_type=password flow. options.AllowPasswordFlow() .AllowRefreshTokenFlow() .AllowCustomFlow(GoogleAssertionGrantHandler.GrantType) .AllowCustomFlow(FacebookAssertionGrantHandler.GrantType) .AllowCustomFlow(MicrosoftAssertionGrantHandler.GrantType); // Mark the "email", "profile" and "roles" scopes as supported scopes. options.RegisterScopes( OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIddictConstants.Scopes.Roles); // 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(); // We don't want to specify a client_id when sending a token or revocation request. options.AcceptAnonymousClients(); // When rolling tokens are enabled, immediately // redeem the refresh token to prevent future reuse. options.UseRollingTokens(); }) // Register the OpenIddict validation handler. // Note: the OpenIddict validation handler is only compatible with the // default token format or with reference tokens and cannot be used with // JWT tokens. For JWT tokens, use the Microsoft JWT bearer handler. .AddValidation(); services.Configure((AssertionGrantOptions options) => { options.AddAssertionGrantType <GoogleAssertionGrantHandler>(GoogleAssertionGrantHandler.GrantType); options.AddAssertionGrantType <FacebookAssertionGrantHandler>(FacebookAssertionGrantHandler.GrantType); options.AddAssertionGrantType <MicrosoftAssertionGrantHandler>(MicrosoftAssertionGrantHandler.GrantType); }); services.AddSingleton <GoogleAssertionGrantHandler>(); services.AddSingleton <FacebookAssertionGrantHandler>(); services.AddSingleton <MicrosoftAssertionGrantHandler>(); services.AddAuthentication(); services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes(OAuthValidationDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); }); var buildInfoProvider = new BuildInfoProvider(this.environment); services.Configure((ApplicationInsightsServiceOptions options) => { options.ApplicationVersion = buildInfoProvider.BuildId; options.DeveloperMode = this.environment.IsDevelopment(); }); services.AddCors(); services.AddMvc(options => { ////var jsonFormatter = options.OutputFormatters.OfType<NewtonsoftJsonOutputFormatter>().Single(); // Allow the json formatter to handle requests from the browser ////jsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); }).AddNewtonsoftJson(options => { // Beautify by default for debuggability. When gzipping, this barely adds anything to the payload. options.SerializerSettings.Formatting = Formatting.Indented; // Omit nulls options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; // Use camel-casing for fields (lower case first character) options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // Convert enum values to strings options.SerializerSettings.Converters.Add(new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() }); }); // Allow IOptions<T> to be available through DI services.AddOptions(); // Container controlled registrations if (storageAccount != null) { services.AddSingleton <CloudTableClient>(_ => storageAccount.CreateCloudTableClient()); services.AddSingleton <CloudQueueClient>(_ => storageAccount.CreateCloudQueueClient()); services.AddSingleton <IUploadScheduler, AzureStorageUploadScheduler>(); services.AddSingleton <ISiteNewsProvider, AzureStorageSiteNewsProvider>(); } else { services.AddSingleton <IUploadScheduler, NoOpUploadScheduler>(); services.AddSingleton <ISiteNewsProvider, InMemorySiteNewsProvider>(); } services.AddSingleton <GameData>(_ => GameData.Parse(Path.Combine(this.environment.WebRootPath, @"data\GameData.json"))); services.AddSingleton <HttpClient>(_ => new HttpClient()); services.AddSingleton <IAssertionGrantHandlerProvider, AssertionGrantHandlerProvider>(); services.AddSingleton <IBuildInfoProvider>(buildInfoProvider); services.AddSingleton <IEmailSender, EmailSender>(); services.AddSingleton <IOptions <PasswordHasherOptions>, PasswordHasherOptionsAccessor>(); // Per request registrations services.AddScoped <IClanManager, ClanManager>(); services.AddScoped <IDatabaseCommandFactory, DatabaseCommandFactory>(); services.AddScoped <IUserSettingsProvider, UserSettingsProvider>(); // configuration services.Configure <AuthenticationSettings>(options => this.configuration.GetSection("Authentication").Bind(options)); services.Configure <DatabaseSettings>(options => this.configuration.GetSection("Database").Bind(options)); services.Configure <EmailSenderSettings>(options => this.configuration.GetSection("EmailSender").Bind(options)); }
public void True_GetContent_ForceDefaults() { string generateContent = BuildInfoProvider.GetContent(GDXConfig.Get(), true); Assert.IsTrue(generateContent.Contains(" public const int Changelist = 0;"), "Expected to find 'public const int Changelist = 0;'"); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // The DevelopmentStorageAccount will only work if you have the Storage emulator v4.3 installed: https://go.microsoft.com/fwlink/?linkid=717179&clcid=0x409 var storageConnectionString = this.Configuration["Storage:ConnectionString"]; var storageAccount = !string.IsNullOrEmpty(storageConnectionString) ? CloudStorageAccount.Parse(storageConnectionString) : null; // Nesessary to persist keys (like the ones used to generate auth cookies) // By default Azure Websites can persist keys across instances within a slot, but not across slots. // This means a slot swap will require users to re-log in. // See https://github.com/aspnet/Home/issues/466 and https://github.com/aspnet/DataProtection/issues/92 for details. var dataProtectionBuilder = services.AddDataProtection(); if (storageAccount != null) { var client = storageAccount.CreateCloudBlobClient(); var container = client.GetContainerReference("key-container"); // The container must exist before calling the DataProtection APIs. // The specific file within the container does not have to exist, // as it will be created on-demand. container.CreateIfNotExistsAsync().Wait(); dataProtectionBuilder.PersistKeysToAzureBlobStorage(container, "keys.xml"); } // Add Entity framework services. var connectionString = this.Configuration["Database:ConnectionString"]; Action <DbContextOptionsBuilder> useDatabase; switch (this.Configuration["Database:Kind"]) { case "SqlServer": { useDatabase = options => options.UseSqlServer(connectionString); break; } case "Sqlite": { useDatabase = options => options.UseSqlite(connectionString); break; } default: { throw new InvalidOperationException($"Invalid configuration for \"Database:Kind\": {this.Configuration["Database:Kind"]}"); } } services.AddDbContext <ApplicationDbContext>(options => { useDatabase(options); // Register the entity sets needed by OpenIddict. options.UseOpenIddict(); }); services.AddIdentity <ApplicationUser, IdentityRole>(options => { // We need to disallow '@' since we need to disambiguate between user names and email addresses during log in options.User.AllowedUserNameCharacters = options.User.AllowedUserNameCharacters.Replace("@", string.Empty, StringComparison.Ordinal); options.User.RequireUniqueEmail = true; options.Password.RequiredLength = 4; options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; // 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. options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name; options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject; options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role; }) .AddEntityFrameworkStores <ApplicationDbContext>() .AddDefaultTokenProviders(); // Register the OpenIddict services. services.AddOpenIddict(options => { // Register the Entity Framework stores. options.AddEntityFrameworkCoreStores <ApplicationDbContext>(); // 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 token endpoint (required to use the password flow). options.EnableTokenEndpoint("/api/auth/token"); // Allow client applications to use the grant_type=password flow. options.AllowPasswordFlow() .AllowRefreshTokenFlow(); // Allow Http on devbox if (this.Environment.IsDevelopment()) { options.DisableHttpsRequirement(); } }); this.ConfigureAuthentication(services); var buildInfoProvider = new BuildInfoProvider(this.Environment); services.AddApplicationInsightsTelemetry(this.Configuration); services.Configure((ApplicationInsightsServiceOptions options) => { options.ApplicationVersion = buildInfoProvider.BuildId; options.EnableAuthenticationTrackingJavaScript = true; }); services.AddCors(); services.AddMvc(options => { options.Filters.Add(typeof(MeasureLatencyFilter)); var jsonFormatter = options.OutputFormatters.OfType <JsonOutputFormatter>().Single(); // Allow the json formatter to handle requests from the browser jsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); }).AddJsonOptions(options => { // Beautify by default for debuggability. When gzipping, this barely adds anything to the payload. options.SerializerSettings.Formatting = Formatting.Indented; // Omit nulls options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; // Use camel-casing for fields (lower case first character) options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // Convert enum values to strings options.SerializerSettings.Converters.Add(new StringEnumConverter { CamelCaseText = true }); }); // Allow IOptions<T> to be available through DI services.AddOptions(); // Container controlled registrations if (storageAccount != null) { services.AddSingleton <CloudTableClient>(_ => storageAccount.CreateCloudTableClient()); services.AddSingleton <CloudQueueClient>(_ => storageAccount.CreateCloudQueueClient()); services.AddSingleton <IUploadScheduler, AzureStorageUploadScheduler>(); services.AddSingleton <ISiteNewsProvider, AzureStorageSiteNewsProvider>(); } else { services.AddSingleton <IUploadScheduler, NoOpUploadScheduler>(); services.AddSingleton <ISiteNewsProvider, InMemorySiteNewsProvider>(); } services.AddSingleton <GameData>(_ => GameData.Parse(Path.Combine(this.Environment.WebRootPath, @"data\GameData.json"))); services.AddSingleton <IBuildInfoProvider>(buildInfoProvider); services.AddSingleton <IEmailSender, EmailSender>(); services.AddSingleton <IOptions <PasswordHasherOptions>, PasswordHasherOptionsAccessor>(); // Per request registrations services.AddScoped <IContentManager, ContentManager>(); services.AddScoped <ICounterProvider, CounterProvider>(); services.AddScoped <IDatabaseCommandFactory, DatabaseCommandFactory>(); services.AddScoped <IUserSettingsProvider, UserSettingsProvider>(); // Configuration services.Configure <DatabaseSettings>(options => this.Configuration.GetSection("Database").Bind(options)); services.Configure <EmailSenderSettings>(options => this.Configuration.GetSection("EmailSender").Bind(options)); }