checkpoint
This commit is contained in:
39
Gameboard.ShogiUI.Sockets/AnonymousSessionMiddleware.cs
Normal file
39
Gameboard.ShogiUI.Sockets/AnonymousSessionMiddleware.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
namespace Gameboard.ShogiUI.Sockets
|
||||||
|
{
|
||||||
|
namespace anonymous_session.Middlewares
|
||||||
|
{
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TODO: Use this example in the guest session logic instead of custom claims.
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -241,6 +241,21 @@ namespace Gameboard.ShogiUI.Sockets.Controllers
|
|||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize(Roles = "Admin, Shogi")]
|
||||||
|
[HttpDelete("{gameName}")]
|
||||||
|
public async Task<IActionResult> DeleteSession([FromRoute] string gameName)
|
||||||
|
{
|
||||||
|
var user = await ReadUserOrThrow();
|
||||||
|
if (user.IsAdmin)
|
||||||
|
{
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<Models.User> ReadUserOrThrow()
|
private async Task<Models.User> ReadUserOrThrow()
|
||||||
{
|
{
|
||||||
var user = await gameboardManager.ReadUser(User);
|
var user = await gameboardManager.ReadUser(User);
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ namespace Gameboard.ShogiUI.Sockets.Models
|
|||||||
|
|
||||||
public bool IsGuest => LoginPlatform == WhichLoginPlatform.Guest;
|
public bool IsGuest => LoginPlatform == WhichLoginPlatform.Guest;
|
||||||
|
|
||||||
|
public bool IsAdmin => LoginPlatform == WhichLoginPlatform.Microsoft && Id == "Hauth@live.com";
|
||||||
|
|
||||||
public User(string id, string displayName, WhichLoginPlatform platform)
|
public User(string id, string displayName, WhichLoginPlatform platform)
|
||||||
{
|
{
|
||||||
Id = id;
|
Id = id;
|
||||||
|
|||||||
@@ -30,169 +30,188 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Gameboard.ShogiUI.Sockets
|
namespace Gameboard.ShogiUI.Sockets
|
||||||
{
|
{
|
||||||
public class Startup
|
public class Startup
|
||||||
{
|
{
|
||||||
public Startup(IConfiguration configuration)
|
public Startup(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
Configuration = 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.
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddSingleton<IJoinByCodeHandler, JoinByCodeHandler>();
|
services.AddSingleton<IJoinByCodeHandler, JoinByCodeHandler>();
|
||||||
services.AddSingleton<ISocketConnectionManager, SocketConnectionManager>();
|
services.AddSingleton<ISocketConnectionManager, SocketConnectionManager>();
|
||||||
services.AddSingleton<ISocketTokenCache, SocketTokenCache>();
|
services.AddSingleton<ISocketTokenCache, SocketTokenCache>();
|
||||||
services.AddSingleton<IGameboardManager, GameboardManager>();
|
services.AddSingleton<IGameboardManager, GameboardManager>();
|
||||||
services.AddSingleton<IValidator<JoinByCodeRequest>, JoinByCodeRequestValidator>();
|
services.AddSingleton<IValidator<JoinByCodeRequest>, JoinByCodeRequestValidator>();
|
||||||
services.AddSingleton<IValidator<JoinGameRequest>, JoinGameRequestValidator>();
|
services.AddSingleton<IValidator<JoinGameRequest>, JoinGameRequestValidator>();
|
||||||
services.AddSingleton<ISocketService, SocketService>();
|
services.AddSingleton<ISocketService, SocketService>();
|
||||||
services.AddTransient<IGameboardRepository, GameboardRepository>();
|
services.AddTransient<IGameboardRepository, GameboardRepository>();
|
||||||
services.AddSingleton<IClaimsTransformation, ShogiUserClaimsTransformer>();
|
services.AddSingleton<IClaimsTransformation, ShogiUserClaimsTransformer>();
|
||||||
services.AddHttpClient("couchdb", c =>
|
services.AddHttpClient("couchdb", c =>
|
||||||
{
|
{
|
||||||
var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes("admin:admin"));
|
var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes("admin:admin"));
|
||||||
c.DefaultRequestHeaders.Add("Accept", "application/json");
|
c.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||||
c.DefaultRequestHeaders.Add("Authorization", $"Basic {base64}");
|
c.DefaultRequestHeaders.Add("Authorization", $"Basic {base64}");
|
||||||
|
|
||||||
var baseUrl = $"{Configuration["AppSettings:CouchDB:Url"]}/{Configuration["AppSettings:CouchDB:Database"]}/";
|
var baseUrl = $"{Configuration["AppSettings:CouchDB:Url"]}/{Configuration["AppSettings:CouchDB:Database"]}/";
|
||||||
c.BaseAddress = new Uri(baseUrl);
|
c.BaseAddress = new Uri(baseUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
services
|
services
|
||||||
.AddControllers()
|
.AddControllers()
|
||||||
.AddNewtonsoftJson(options =>
|
.AddNewtonsoftJson(options =>
|
||||||
{
|
{
|
||||||
options.SerializerSettings.Formatting = Formatting.Indented;
|
options.SerializerSettings.Formatting = Formatting.Indented;
|
||||||
options.SerializerSettings.ContractResolver = new DefaultContractResolver
|
options.SerializerSettings.ContractResolver = new DefaultContractResolver
|
||||||
{
|
{
|
||||||
NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = true }
|
NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = true }
|
||||||
};
|
};
|
||||||
options.SerializerSettings.Converters = new[] { new StringEnumConverter() };
|
options.SerializerSettings.Converters = new[] { new StringEnumConverter() };
|
||||||
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddAuthentication("CookieOrJwt")
|
services.AddAuthentication("CookieOrJwt")
|
||||||
.AddPolicyScheme("CookieOrJwt", "Either cookie or jwt", options =>
|
.AddPolicyScheme("CookieOrJwt", "Either cookie or jwt", options =>
|
||||||
{
|
{
|
||||||
options.ForwardDefaultSelector = context =>
|
options.ForwardDefaultSelector = context =>
|
||||||
{
|
{
|
||||||
var bearerAuth = context.Request.Headers["Authorization"].FirstOrDefault()?.StartsWith("Bearer ") ?? false;
|
var bearerAuth = context.Request.Headers["Authorization"].FirstOrDefault()?.StartsWith("Bearer ") ?? false;
|
||||||
return bearerAuth
|
return bearerAuth
|
||||||
? JwtBearerDefaults.AuthenticationScheme
|
? JwtBearerDefaults.AuthenticationScheme
|
||||||
: CookieAuthenticationDefaults.AuthenticationScheme;
|
: CookieAuthenticationDefaults.AuthenticationScheme;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.AddCookie(options =>
|
.AddCookie(options =>
|
||||||
{
|
{
|
||||||
options.Cookie.Name = "session-id";
|
options.Cookie.Name = "session-id";
|
||||||
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
|
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
|
||||||
options.Cookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
|
options.Cookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
|
||||||
options.SlidingExpiration = true;
|
options.SlidingExpiration = true;
|
||||||
})
|
})
|
||||||
.AddMicrosoftIdentityWebApi(Configuration);
|
.AddMicrosoftIdentityWebApi(Configuration);
|
||||||
|
|
||||||
services.AddSwaggerDocument(config =>
|
services.AddSwaggerDocument(config =>
|
||||||
{
|
{
|
||||||
config.AddSecurity("Bearer", new NSwag.OpenApiSecurityScheme
|
//config.AddSecurity("bearer", Enumerable.Empty<string>(), new NSwag.OpenApiSecurityScheme
|
||||||
{
|
//{
|
||||||
Type = NSwag.OpenApiSecuritySchemeType.OAuth2,
|
// Type = NSwag.OpenApiSecuritySchemeType.OAuth2,
|
||||||
Flow = NSwag.OpenApiOAuth2Flow.AccessCode,
|
// Flow = NSwag.OpenApiOAuth2Flow.Implicit,
|
||||||
AuthorizationUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
// Flows = new NSwag.OpenApiOAuthFlows
|
||||||
TokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token",
|
// {
|
||||||
Scopes = new Dictionary<string, string> { { "api://c1e94676-cab0-42ba-8b6c-9532b8486fff/access_as_user", "The scope" } },
|
// Implicit = new NSwag.OpenApiOAuthFlow
|
||||||
Scheme = "Bearer"
|
// {
|
||||||
});
|
// Scopes =
|
||||||
config.PostProcess = document =>
|
// }
|
||||||
{
|
// }
|
||||||
document.Info.Title = "Gameboard.ShogiUI.Sockets";
|
//});
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove default HttpClient logging.
|
// This just ensures anyone with a microsoft account can make API calls.
|
||||||
services.RemoveAll<IHttpMessageHandlerBuilderFilter>();
|
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<string, string> {
|
||||||
|
{ "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.
|
// Remove default HttpClient logging.
|
||||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ISocketService socketConnectionManager)
|
services.RemoveAll<IHttpMessageHandlerBuilderFilter>();
|
||||||
{
|
}
|
||||||
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);
|
|
||||||
|
|
||||||
if (env.IsDevelopment())
|
// 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)
|
||||||
app.UseDeveloperExceptionPage();
|
{
|
||||||
var client = PublicClientApplicationBuilder
|
var origins = new[] {
|
||||||
.Create(Configuration["AzureAd:ClientId"])
|
"http://localhost:3000", "https://localhost:3000",
|
||||||
.WithLogging(
|
"http://127.0.0.1:3000", "https://127.0.0.1:3000",
|
||||||
(level, message, pii) =>
|
"https://dev.lucaserver.space", "https://lucaserver.space"
|
||||||
{
|
};
|
||||||
|
var socketOptions = new WebSocketOptions();
|
||||||
|
foreach (var o in origins)
|
||||||
|
socketOptions.AllowedOrigins.Add(o);
|
||||||
|
|
||||||
},
|
if (env.IsDevelopment())
|
||||||
LogLevel.Verbose,
|
{
|
||||||
true,
|
app.UseDeveloperExceptionPage();
|
||||||
true
|
var client = PublicClientApplicationBuilder
|
||||||
)
|
.Create(Configuration["AzureAd:ClientId"])
|
||||||
.Build();
|
.WithLogging(
|
||||||
}
|
(level, message, pii) =>
|
||||||
else
|
{
|
||||||
{
|
Console.WriteLine(message);
|
||||||
app.UseHsts();
|
},
|
||||||
}
|
LogLevel.Verbose,
|
||||||
app
|
true,
|
||||||
.UseRequestResponseLogging()
|
true
|
||||||
.UseCors(opt => opt.WithOrigins(origins).AllowAnyMethod().AllowAnyHeader().WithExposedHeaders("Set-Cookie").AllowCredentials())
|
)
|
||||||
.UseRouting()
|
.Build();
|
||||||
.UseAuthentication()
|
}
|
||||||
.UseAuthorization()
|
else
|
||||||
.UseOpenApi()
|
{
|
||||||
.UseSwaggerUi3(config =>
|
app.UseHsts();
|
||||||
{
|
}
|
||||||
config.OAuth2Client = new NSwag.AspNetCore.OAuth2ClientSettings()
|
app
|
||||||
{
|
.UseRequestResponseLogging()
|
||||||
ClientId = "c1e94676-cab0-42ba-8b6c-9532b8486fff",
|
.UseCors(opt => opt.WithOrigins(origins).AllowAnyMethod().AllowAnyHeader().WithExposedHeaders("Set-Cookie").AllowCredentials())
|
||||||
UsePkceWithAuthorizationCodeGrant = true
|
.UseRouting()
|
||||||
};
|
.UseAuthentication()
|
||||||
//config.WithCredentials = true;
|
.UseAuthorization()
|
||||||
})
|
.UseOpenApi()
|
||||||
.UseWebSockets(socketOptions)
|
.UseSwaggerUi3(config =>
|
||||||
.UseEndpoints(endpoints =>
|
{
|
||||||
{
|
config.OAuth2Client = new NSwag.AspNetCore.OAuth2ClientSettings()
|
||||||
endpoints.MapControllers();
|
{
|
||||||
})
|
ClientId = "c1e94676-cab0-42ba-8b6c-9532b8486fff",
|
||||||
.Use(async (context, next) =>
|
UsePkceWithAuthorizationCodeGrant = true
|
||||||
{
|
};
|
||||||
if (context.WebSockets.IsWebSocketRequest)
|
//config.WithCredentials = true;
|
||||||
{
|
})
|
||||||
await socketConnectionManager.HandleSocketRequest(context);
|
.UseWebSockets(socketOptions)
|
||||||
}
|
.UseEndpoints(endpoints =>
|
||||||
else
|
{
|
||||||
{
|
endpoints.MapControllers();
|
||||||
await next();
|
})
|
||||||
}
|
.Use(async (context, next) =>
|
||||||
});
|
{
|
||||||
|
if (context.WebSockets.IsWebSocketRequest)
|
||||||
|
{
|
||||||
|
await socketConnectionManager.HandleSocketRequest(context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
|
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
Formatting = Formatting.Indented,
|
Formatting = Formatting.Indented,
|
||||||
ContractResolver = new DefaultContractResolver
|
ContractResolver = new DefaultContractResolver
|
||||||
{
|
{
|
||||||
NamingStrategy = new CamelCaseNamingStrategy
|
NamingStrategy = new CamelCaseNamingStrategy
|
||||||
{
|
{
|
||||||
ProcessDictionaryKeys = true
|
ProcessDictionaryKeys = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Converters = new[] { new StringEnumConverter() },
|
Converters = new[] { new StringEnumConverter() },
|
||||||
NullValueHandling = NullValueHandling.Ignore,
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
"Instance": "https://login.microsoftonline.com/",
|
"Instance": "https://login.microsoftonline.com/",
|
||||||
"ClientId": "c1e94676-cab0-42ba-8b6c-9532b8486fff",
|
"ClientId": "c1e94676-cab0-42ba-8b6c-9532b8486fff",
|
||||||
"TenantId": "common",
|
"TenantId": "common",
|
||||||
"Audience": "c1e94676-cab0-42ba-8b6c-9532b8486fff"
|
"Audience": "c1e94676-cab0-42ba-8b6c-9532b8486fff",
|
||||||
|
"ClientSecret": ""
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user