diff --git a/Gameboard.ShogiUI.Sockets/AnonymousSessionMiddleware.cs b/Gameboard.ShogiUI.Sockets/AnonymousSessionMiddleware.cs
new file mode 100644
index 0000000..f39e649
--- /dev/null
+++ b/Gameboard.ShogiUI.Sockets/AnonymousSessionMiddleware.cs
@@ -0,0 +1,39 @@
+namespace Gameboard.ShogiUI.Sockets
+{
+ namespace anonymous_session.Middlewares
+ {
+ using Microsoft.AspNetCore.Http;
+ using Microsoft.AspNetCore.Authentication;
+ using System.Security.Claims;
+
+ ///
+ /// TODO: Use this example in the guest session logic instead of custom claims.
+ ///
+ public class AnonymousSessionMiddleware
+ {
+ private readonly RequestDelegate _next;
+
+ public AnonymousSessionMiddleware(RequestDelegate next)
+ {
+ _next = next;
+ }
+
+ public async System.Threading.Tasks.Task InvokeAsync(HttpContext context)
+ {
+ if (!context.User.Identity.IsAuthenticated)
+ {
+ if (string.IsNullOrEmpty(context.User.FindFirstValue(ClaimTypes.Anonymous)))
+ {
+ var claim = new Claim(ClaimTypes.Anonymous, System.Guid.NewGuid().ToString());
+ context.User.AddIdentity(new ClaimsIdentity(new[] { claim }));
+
+ string scheme = Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme;
+ await context.SignInAsync(scheme, context.User, new AuthenticationProperties { IsPersistent = false });
+ }
+ }
+
+ await _next(context);
+ }
+ }
+ }
+}
diff --git a/Gameboard.ShogiUI.Sockets/Controllers/GameController.cs b/Gameboard.ShogiUI.Sockets/Controllers/GameController.cs
index db5f8b9..0f76e3b 100644
--- a/Gameboard.ShogiUI.Sockets/Controllers/GameController.cs
+++ b/Gameboard.ShogiUI.Sockets/Controllers/GameController.cs
@@ -241,6 +241,21 @@ namespace Gameboard.ShogiUI.Sockets.Controllers
return Ok();
}
+ [Authorize(Roles = "Admin, Shogi")]
+ [HttpDelete("{gameName}")]
+ public async Task DeleteSession([FromRoute] string gameName)
+ {
+ var user = await ReadUserOrThrow();
+ if (user.IsAdmin)
+ {
+ return Ok();
+ }
+ else
+ {
+ return Unauthorized();
+ }
+ }
+
private async Task ReadUserOrThrow()
{
var user = await gameboardManager.ReadUser(User);
diff --git a/Gameboard.ShogiUI.Sockets/Models/User.cs b/Gameboard.ShogiUI.Sockets/Models/User.cs
index 7c1a2fa..e633524 100644
--- a/Gameboard.ShogiUI.Sockets/Models/User.cs
+++ b/Gameboard.ShogiUI.Sockets/Models/User.cs
@@ -37,6 +37,8 @@ namespace Gameboard.ShogiUI.Sockets.Models
public bool IsGuest => LoginPlatform == WhichLoginPlatform.Guest;
+ public bool IsAdmin => LoginPlatform == WhichLoginPlatform.Microsoft && Id == "Hauth@live.com";
+
public User(string id, string displayName, WhichLoginPlatform platform)
{
Id = id;
diff --git a/Gameboard.ShogiUI.Sockets/Startup.cs b/Gameboard.ShogiUI.Sockets/Startup.cs
index 5650206..c735d0e 100644
--- a/Gameboard.ShogiUI.Sockets/Startup.cs
+++ b/Gameboard.ShogiUI.Sockets/Startup.cs
@@ -30,169 +30,188 @@ using System.Threading.Tasks;
namespace Gameboard.ShogiUI.Sockets
{
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
- public IConfiguration Configuration { get; }
+ public IConfiguration Configuration { get; }
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton, JoinByCodeRequestValidator>();
- services.AddSingleton, JoinGameRequestValidator>();
- services.AddSingleton();
- services.AddTransient();
- services.AddSingleton();
- services.AddHttpClient("couchdb", c =>
- {
- var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes("admin:admin"));
- c.DefaultRequestHeaders.Add("Accept", "application/json");
- c.DefaultRequestHeaders.Add("Authorization", $"Basic {base64}");
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton, JoinByCodeRequestValidator>();
+ services.AddSingleton, JoinGameRequestValidator>();
+ services.AddSingleton();
+ services.AddTransient();
+ services.AddSingleton();
+ services.AddHttpClient("couchdb", c =>
+ {
+ var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes("admin:admin"));
+ c.DefaultRequestHeaders.Add("Accept", "application/json");
+ c.DefaultRequestHeaders.Add("Authorization", $"Basic {base64}");
- var baseUrl = $"{Configuration["AppSettings:CouchDB:Url"]}/{Configuration["AppSettings:CouchDB:Database"]}/";
- c.BaseAddress = new Uri(baseUrl);
- });
+ var baseUrl = $"{Configuration["AppSettings:CouchDB:Url"]}/{Configuration["AppSettings:CouchDB:Database"]}/";
+ c.BaseAddress = new Uri(baseUrl);
+ });
- services
- .AddControllers()
- .AddNewtonsoftJson(options =>
- {
- options.SerializerSettings.Formatting = Formatting.Indented;
- options.SerializerSettings.ContractResolver = new DefaultContractResolver
- {
- NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = true }
- };
- options.SerializerSettings.Converters = new[] { new StringEnumConverter() };
- options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
- });
+ services
+ .AddControllers()
+ .AddNewtonsoftJson(options =>
+ {
+ options.SerializerSettings.Formatting = Formatting.Indented;
+ options.SerializerSettings.ContractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = true }
+ };
+ options.SerializerSettings.Converters = new[] { new StringEnumConverter() };
+ options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
+ });
- services.AddAuthentication("CookieOrJwt")
- .AddPolicyScheme("CookieOrJwt", "Either cookie or jwt", options =>
- {
- options.ForwardDefaultSelector = context =>
- {
- var bearerAuth = context.Request.Headers["Authorization"].FirstOrDefault()?.StartsWith("Bearer ") ?? false;
- return bearerAuth
- ? JwtBearerDefaults.AuthenticationScheme
- : CookieAuthenticationDefaults.AuthenticationScheme;
- };
- })
- .AddCookie(options =>
- {
- options.Cookie.Name = "session-id";
- options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
- options.Cookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
- options.SlidingExpiration = true;
- })
- .AddMicrosoftIdentityWebApi(Configuration);
+ services.AddAuthentication("CookieOrJwt")
+ .AddPolicyScheme("CookieOrJwt", "Either cookie or jwt", options =>
+ {
+ options.ForwardDefaultSelector = context =>
+ {
+ var bearerAuth = context.Request.Headers["Authorization"].FirstOrDefault()?.StartsWith("Bearer ") ?? false;
+ return bearerAuth
+ ? JwtBearerDefaults.AuthenticationScheme
+ : CookieAuthenticationDefaults.AuthenticationScheme;
+ };
+ })
+ .AddCookie(options =>
+ {
+ options.Cookie.Name = "session-id";
+ options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
+ options.Cookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
+ options.SlidingExpiration = true;
+ })
+ .AddMicrosoftIdentityWebApi(Configuration);
- services.AddSwaggerDocument(config =>
- {
- config.AddSecurity("Bearer", new NSwag.OpenApiSecurityScheme
- {
- Type = NSwag.OpenApiSecuritySchemeType.OAuth2,
- Flow = NSwag.OpenApiOAuth2Flow.AccessCode,
- AuthorizationUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
- TokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token",
- Scopes = new Dictionary { { "api://c1e94676-cab0-42ba-8b6c-9532b8486fff/access_as_user", "The scope" } },
- Scheme = "Bearer"
- });
- config.PostProcess = document =>
- {
- document.Info.Title = "Gameboard.ShogiUI.Sockets";
- };
- });
+ services.AddSwaggerDocument(config =>
+ {
+ //config.AddSecurity("bearer", Enumerable.Empty(), new NSwag.OpenApiSecurityScheme
+ //{
+ // Type = NSwag.OpenApiSecuritySchemeType.OAuth2,
+ // Flow = NSwag.OpenApiOAuth2Flow.Implicit,
+ // Flows = new NSwag.OpenApiOAuthFlows
+ // {
+ // Implicit = new NSwag.OpenApiOAuthFlow
+ // {
+ // Scopes =
+ // }
+ // }
+ //});
- // Remove default HttpClient logging.
- services.RemoveAll();
- }
+ // This just ensures anyone with a microsoft account can make API calls.
+ config.AddSecurity("bearer", new NSwag.OpenApiSecurityScheme
+ {
+ Type = NSwag.OpenApiSecuritySchemeType.OAuth2,
+ Flow = NSwag.OpenApiOAuth2Flow.Implicit,
+ AuthorizationUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
+ TokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token",
+ Scopes = new Dictionary {
+ { "api://c1e94676-cab0-42ba-8b6c-9532b8486fff/access_as_user", "The scope" },
+ { "api://c1e94676-cab0-42ba-8b6c-9532b8486fff/ShogiAdmin", "Admin scope" }
+ },
+ Scheme = "bearer",
+ BearerFormat = "JWT",
+ In = NSwag.OpenApiSecurityApiKeyLocation.Header,
+ });
+ config.PostProcess = document =>
+ {
+ document.Info.Title = "Gameboard.ShogiUI.Sockets";
+ };
+ });
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ISocketService socketConnectionManager)
- {
- var origins = new[] {
- "http://localhost:3000", "https://localhost:3000",
- "http://127.0.0.1:3000", "https://127.0.0.1:3000",
- "https://dev.lucaserver.space", "https://lucaserver.space"
- };
- var socketOptions = new WebSocketOptions();
- foreach (var o in origins)
- socketOptions.AllowedOrigins.Add(o);
+ // Remove default HttpClient logging.
+ services.RemoveAll();
+ }
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- var client = PublicClientApplicationBuilder
- .Create(Configuration["AzureAd:ClientId"])
- .WithLogging(
- (level, message, pii) =>
- {
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ISocketService socketConnectionManager)
+ {
+ var origins = new[] {
+ "http://localhost:3000", "https://localhost:3000",
+ "http://127.0.0.1:3000", "https://127.0.0.1:3000",
+ "https://dev.lucaserver.space", "https://lucaserver.space"
+ };
+ var socketOptions = new WebSocketOptions();
+ foreach (var o in origins)
+ socketOptions.AllowedOrigins.Add(o);
- },
- LogLevel.Verbose,
- true,
- true
- )
- .Build();
- }
- else
- {
- app.UseHsts();
- }
- app
- .UseRequestResponseLogging()
- .UseCors(opt => opt.WithOrigins(origins).AllowAnyMethod().AllowAnyHeader().WithExposedHeaders("Set-Cookie").AllowCredentials())
- .UseRouting()
- .UseAuthentication()
- .UseAuthorization()
- .UseOpenApi()
- .UseSwaggerUi3(config =>
- {
- config.OAuth2Client = new NSwag.AspNetCore.OAuth2ClientSettings()
- {
- ClientId = "c1e94676-cab0-42ba-8b6c-9532b8486fff",
- UsePkceWithAuthorizationCodeGrant = true
- };
- //config.WithCredentials = true;
- })
- .UseWebSockets(socketOptions)
- .UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
- })
- .Use(async (context, next) =>
- {
- if (context.WebSockets.IsWebSocketRequest)
- {
- await socketConnectionManager.HandleSocketRequest(context);
- }
- else
- {
- await next();
- }
- });
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ var client = PublicClientApplicationBuilder
+ .Create(Configuration["AzureAd:ClientId"])
+ .WithLogging(
+ (level, message, pii) =>
+ {
+ Console.WriteLine(message);
+ },
+ LogLevel.Verbose,
+ true,
+ true
+ )
+ .Build();
+ }
+ else
+ {
+ app.UseHsts();
+ }
+ app
+ .UseRequestResponseLogging()
+ .UseCors(opt => opt.WithOrigins(origins).AllowAnyMethod().AllowAnyHeader().WithExposedHeaders("Set-Cookie").AllowCredentials())
+ .UseRouting()
+ .UseAuthentication()
+ .UseAuthorization()
+ .UseOpenApi()
+ .UseSwaggerUi3(config =>
+ {
+ config.OAuth2Client = new NSwag.AspNetCore.OAuth2ClientSettings()
+ {
+ ClientId = "c1e94676-cab0-42ba-8b6c-9532b8486fff",
+ UsePkceWithAuthorizationCodeGrant = true
+ };
+ //config.WithCredentials = true;
+ })
+ .UseWebSockets(socketOptions)
+ .UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ })
+ .Use(async (context, next) =>
+ {
+ if (context.WebSockets.IsWebSocketRequest)
+ {
+ await socketConnectionManager.HandleSocketRequest(context);
+ }
+ else
+ {
+ await next();
+ }
+ });
- JsonConvert.DefaultSettings = () => new JsonSerializerSettings
- {
- Formatting = Formatting.Indented,
- ContractResolver = new DefaultContractResolver
- {
- NamingStrategy = new CamelCaseNamingStrategy
- {
- ProcessDictionaryKeys = true
- }
- },
- Converters = new[] { new StringEnumConverter() },
- NullValueHandling = NullValueHandling.Ignore,
- };
- }
- }
+ JsonConvert.DefaultSettings = () => new JsonSerializerSettings
+ {
+ Formatting = Formatting.Indented,
+ ContractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ ProcessDictionaryKeys = true
+ }
+ },
+ Converters = new[] { new StringEnumConverter() },
+ NullValueHandling = NullValueHandling.Ignore,
+ };
+ }
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets/appsettings.json b/Gameboard.ShogiUI.Sockets/appsettings.json
index 7abd699..119c533 100644
--- a/Gameboard.ShogiUI.Sockets/appsettings.json
+++ b/Gameboard.ShogiUI.Sockets/appsettings.json
@@ -16,7 +16,8 @@
"Instance": "https://login.microsoftonline.com/",
"ClientId": "c1e94676-cab0-42ba-8b6c-9532b8486fff",
"TenantId": "common",
- "Audience": "c1e94676-cab0-42ba-8b6c-9532b8486fff"
+ "Audience": "c1e94676-cab0-42ba-8b6c-9532b8486fff",
+ "ClientSecret": ""
},
"AllowedHosts": "*"
}
\ No newline at end of file