Add authentication and authorization (#21)
Reviewed-on: #21
This commit was merged in pull request #21.
This commit is contained in:
@@ -37,6 +37,9 @@ Store your favorite games, intelligent game add, store game files and data, mana
|
||||
| DB_USERNAME | Username for the database |
|
||||
| DB_PASSWORD | Plain password for the database |
|
||||
| DB_DATABASE | Name of the database |
|
||||
| JWT_KEY | Key for your jwt tokens |
|
||||
| JWT_ISSUER | Your domain name |
|
||||
| JWT_AUDIENCE | Your domain name |
|
||||
|
||||
<!-- ## Installation
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@using GameIdeas.BlazorApp.Layouts
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<CascadingAuthenticationState>
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
@@ -11,4 +13,6 @@
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
</Router>
|
||||
</CascadingAuthenticationState>
|
||||
|
||||
|
||||
@@ -9,10 +9,13 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Blazored.FluentValidation" Version="2.2.0" />
|
||||
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
||||
<PackageReference Include="FluentValidation" Version="11.11.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.2" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.2" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
using GameIdeas.Shared.Dto;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Helpers;
|
||||
|
||||
public static class GameHelper
|
||||
{
|
||||
public static void WriteTrackingDto(GameDetailDto game)
|
||||
public static void WriteTrackingDto(GameDetailDto game, AuthenticationState authState)
|
||||
{
|
||||
game.CreationUserId = 100000;
|
||||
if (authState == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(authState), "Authentication state missing");
|
||||
}
|
||||
|
||||
var userId = authState.User.FindFirstValue(ClaimTypes.Sid);
|
||||
|
||||
if (userId == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(authState), "user state missing");
|
||||
}
|
||||
|
||||
game.CreationUserId = userId;
|
||||
game.CreationDate = DateTime.Now;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,10 @@ using GameIdeas.BlazorApp.Shared.Components.Select.Models;
|
||||
using GameIdeas.BlazorApp.Shared.Components.Slider;
|
||||
using GameIdeas.Shared.Dto;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.JSInterop;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.Games.Components;
|
||||
|
||||
@@ -14,6 +16,7 @@ public partial class GameCreationForm
|
||||
{
|
||||
[Inject] private IJSRuntime Js { get; set; } = default!;
|
||||
[Inject] private IGameGateway GameGateway { get; set; } = default!;
|
||||
[Inject] private AuthenticationStateProvider AuthenticationState { get; set; } = default!;
|
||||
[CascadingParameter] private Popup? Popup { get; set; }
|
||||
[Parameter] public CategoriesDto? Categories { get; set; }
|
||||
[Parameter] public EventCallback OnSubmit { get; set; }
|
||||
@@ -33,7 +36,6 @@ public partial class GameCreationForm
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await Js.InvokeVoidAsync("resizeGameForm");
|
||||
|
||||
}
|
||||
|
||||
private void HandleOnCancel()
|
||||
@@ -52,7 +54,9 @@ public partial class GameCreationForm
|
||||
{
|
||||
IsLoading = true;
|
||||
|
||||
GameHelper.WriteTrackingDto(GameDto);
|
||||
var authState = await AuthenticationState.GetAuthenticationStateAsync();
|
||||
GameHelper.WriteTrackingDto(GameDto, authState);
|
||||
|
||||
var gameId = await GameGateway.CreateGame(GameDto);
|
||||
|
||||
if (gameId != 0)
|
||||
@@ -68,6 +72,7 @@ public partial class GameCreationForm
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
@using GameIdeas.BlazorApp.Pages.Games
|
||||
@using GameIdeas.BlazorApp.Shared.Components.Account
|
||||
@using GameIdeas.BlazorApp.Pages.User
|
||||
@using GameIdeas.BlazorApp.Shared.Components.Select
|
||||
@using GameIdeas.BlazorApp.Shared.Components.Select.Models
|
||||
@using GameIdeas.BlazorApp.Shared.Models
|
||||
@using GameIdeas.Resources
|
||||
@using GameIdeas.Shared.Constants
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
|
||||
@inherits ComponentBase
|
||||
|
||||
@@ -15,7 +17,8 @@
|
||||
@ChildContent
|
||||
|
||||
<div class="account-add-container">
|
||||
<div class="add-container">
|
||||
<AuthorizeView Roles="@GlobalConstants.ADMIN_MEMBER">
|
||||
<Authorized>
|
||||
<div class="add-buttons">
|
||||
<div class="first-button button">
|
||||
<svg class="button-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
@@ -31,14 +34,9 @@
|
||||
</div>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="account-container">
|
||||
<div class="icon-container" @onclick=HandleAccountClicked>
|
||||
<svg class="account-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12,19.2C9.5,19.2 7.29,17.92 6,16C6.03,14 10,12.9 12,12.9C14,12.9 17.97,14 18,16C16.71,17.92 14.5,19.2 12,19.2M12,5A3,3 0 0,1 15,8A3,3 0 0,1 12,11A3,3 0 0,1 9,8A3,3 0 0,1 12,5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<AccountSettings @ref="AccountSettings" />
|
||||
</div>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<UserMenu />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using GameIdeas.BlazorApp.Shared.Components.Account;
|
||||
using GameIdeas.BlazorApp.Shared.Components.Select;
|
||||
using GameIdeas.BlazorApp.Shared.Components.Select.Models;
|
||||
using GameIdeas.BlazorApp.Shared.Models;
|
||||
@@ -18,7 +17,6 @@ public partial class GameHeader : ComponentBase
|
||||
{ AddType.Auto, ResourcesKey.AutoAdd }
|
||||
};
|
||||
|
||||
private AccountSettings? AccountSettings;
|
||||
private Select<KeyValuePair<AddType, string>, object>? SelectListAdd;
|
||||
private SelectParams<KeyValuePair<AddType, string>, object> SelectParams = new();
|
||||
|
||||
@@ -26,7 +24,7 @@ public partial class GameHeader : ComponentBase
|
||||
{
|
||||
SelectParams = new()
|
||||
{
|
||||
Items = AddTypes.ToList(),
|
||||
Items = [.. AddTypes],
|
||||
GetItemLabel = item => item.Value
|
||||
};
|
||||
|
||||
@@ -43,9 +41,4 @@ public partial class GameHeader : ComponentBase
|
||||
SelectListAdd?.Close();
|
||||
await AddTypeChanged.InvokeAsync(values.FirstOrDefault().Key);
|
||||
}
|
||||
|
||||
private void HandleAccountClicked()
|
||||
{
|
||||
AccountSettings?.Toggle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-container img {
|
||||
@@ -21,10 +22,6 @@
|
||||
max-width: 85%;
|
||||
}
|
||||
|
||||
.icon-container:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.account-add-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -32,15 +29,12 @@
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.add-container {
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.add-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background: var(--violet);
|
||||
border-radius: var(--small-radius);
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.button {
|
||||
@@ -71,7 +65,3 @@
|
||||
background: var(--violet-selected);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.account-icon {
|
||||
fill: var(--line);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
@using Blazored.FluentValidation
|
||||
|
||||
<EditForm EditContext="EditContext" OnSubmit="HandleLoginSubmit">
|
||||
<FluentValidationValidator />
|
||||
<div class="login-form">
|
||||
<div class="login-field">
|
||||
<div class="input-title">@ResourcesKey.EnterUsername</div>
|
||||
<InputText class="input-text"
|
||||
@bind-Value="UserDto.Username" />
|
||||
</div>
|
||||
<div class="login-field">
|
||||
<div class="input-title">@ResourcesKey.EnterPassword</div>
|
||||
<InputText class="input-text"
|
||||
@bind-Value="UserDto.Password" />
|
||||
</div>
|
||||
<div class="login-field">
|
||||
<button class="login-button" type="submit" disabled="@IsLoading">
|
||||
@if (IsLoading)
|
||||
{
|
||||
<div class="loading"></div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ResourcesKey.Login
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
@@ -0,0 +1,44 @@
|
||||
using GameIdeas.BlazorApp.Pages.User.Gateways;
|
||||
using GameIdeas.Shared.Dto;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.User.Components;
|
||||
|
||||
public partial class Login
|
||||
{
|
||||
[Parameter] public IAuthGateway AuthGateway { get; set; } = default!;
|
||||
|
||||
private EditContext? EditContext;
|
||||
private UserDto UserDto = new();
|
||||
private bool IsLoading = false;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
EditContext = new EditContext(UserDto);
|
||||
}
|
||||
|
||||
private async Task HandleLoginSubmit()
|
||||
{
|
||||
if (EditContext?.Validate() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
await AuthGateway.Login(UserDto);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
UserDto.Password = string.Empty;
|
||||
EditContext?.Validate();
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,4 @@
|
||||
.account-setting-content {
|
||||
overflow: hidden;
|
||||
border-radius: var(--big-radius);
|
||||
position: fixed;
|
||||
animation-name: fade-in;
|
||||
animation-duration: 0.4s;
|
||||
border: 2px solid var(--input-selected);
|
||||
background: var(--dropdown-content);
|
||||
right: 10px;
|
||||
margin-top: 4px;
|
||||
z-index: var(--index-floating);
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
.login-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px 8px;
|
||||
@@ -53,7 +36,7 @@
|
||||
|
||||
.login-button:hover {
|
||||
background: var(--violet-selected);
|
||||
cursor:pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-button:disabled {
|
||||
@@ -70,26 +53,3 @@
|
||||
animation: loading 1s linear infinite;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.settings-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 0 6px;
|
||||
border-bottom: 2px solid var(--line);
|
||||
}
|
||||
|
||||
.settings-element {
|
||||
max-width: 140px;
|
||||
height: 40px;
|
||||
padding: 0 26px;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.settings-element:hover {
|
||||
background: var(--line)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using FluentValidation;
|
||||
using GameIdeas.Shared.Dto;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Shared.Components.Account;
|
||||
namespace GameIdeas.BlazorApp.Pages.User.Components;
|
||||
|
||||
public class LoginValidator : AbstractValidator<LoginDto>
|
||||
public class LoginValidator : AbstractValidator<UserDto>
|
||||
{
|
||||
public LoginValidator()
|
||||
{
|
||||
@@ -0,0 +1,38 @@
|
||||
using GameIdeas.BlazorApp.Services;
|
||||
using GameIdeas.BlazorApp.Shared.Constants;
|
||||
using GameIdeas.BlazorApp.Shared.Exceptions;
|
||||
using GameIdeas.Resources;
|
||||
using GameIdeas.Shared.Dto;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.User.Gateways;
|
||||
|
||||
public class AuthGateway(IHttpClientService httpClient,
|
||||
AuthenticationStateProvider stateProvider) : IAuthGateway
|
||||
{
|
||||
public async Task<bool> Login(UserDto userDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var token = await httpClient.PostAsync<TokenDto>(Endpoints.Auth.Login, userDto);
|
||||
await ((JwtAuthenticationStateProvider)stateProvider).NotifyUserAuthenticationAsync(token!.Token!);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new AuthenticationUserException(ResourcesKey.UserLoginFailed);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Logout()
|
||||
{
|
||||
try
|
||||
{
|
||||
await ((JwtAuthenticationStateProvider)stateProvider).NotifyUserLogoutAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new AuthenticationUserException(ResourcesKey.UserLogoutFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using GameIdeas.Shared.Dto;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.User.Gateways;
|
||||
|
||||
public interface IAuthGateway
|
||||
{
|
||||
Task<bool> Login(UserDto userDto);
|
||||
Task Logout();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
@using GameIdeas.BlazorApp.Pages.User.Components
|
||||
@using GameIdeas.BlazorApp.Shared.Components.BackdropFilter
|
||||
@using GameIdeas.BlazorApp.Shared.Constants
|
||||
@using GameIdeas.Shared.Constants
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
|
||||
<div class="menu">
|
||||
<div class="icon" @onclick=HandleAccountClicked>
|
||||
@Icons.Account
|
||||
</div>
|
||||
<div class="container">
|
||||
@if (ContentVisile)
|
||||
{
|
||||
<div class="content">
|
||||
<AuthorizeView Roles="@GlobalConstants.ADMIN_MEMBER">
|
||||
<Authorized>
|
||||
<div class="menu-element">
|
||||
@ResourcesKey.CategoriesManager
|
||||
</div>
|
||||
<span class="line"></span>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<AuthorizeView Roles="@GlobalConstants.ADMINISTRATOR">
|
||||
<Authorized>
|
||||
<div class="menu-element">
|
||||
@ResourcesKey.UserManager
|
||||
</div>
|
||||
<span class="line"></span>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<div class="menu-element" @onclick="HandleLogoutClicked">
|
||||
@ResourcesKey.Logout
|
||||
</div>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<Login AuthGateway="AuthGateway" />
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BackdropFilter AllowBodyScroll=true CloseOnClick=true Color="BackdropFilterColor.Transparent"
|
||||
IsVisible="ContentVisile" OnClick="HandleBackdropFilterClicked" />
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using GameIdeas.BlazorApp.Pages.User.Gateways;
|
||||
using GameIdeas.BlazorApp.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.User;
|
||||
|
||||
public partial class UserMenu
|
||||
{
|
||||
[Inject] private IAuthGateway AuthGateway { get; set; } = default!;
|
||||
|
||||
private bool ContentVisile = false;
|
||||
|
||||
private async Task HandleLogoutClicked()
|
||||
{
|
||||
await AuthGateway.Logout();
|
||||
ContentVisile = false;
|
||||
}
|
||||
|
||||
private void HandleAccountClicked()
|
||||
{
|
||||
ContentVisile = true;
|
||||
}
|
||||
|
||||
private void HandleBackdropFilterClicked()
|
||||
{
|
||||
ContentVisile = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
.menu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon ::deep svg {
|
||||
fill: var(--line);
|
||||
}
|
||||
|
||||
.container {
|
||||
right: 0;
|
||||
position: absolute;
|
||||
margin-top: 4px;
|
||||
z-index: var(--index-dropdown);
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow: hidden;
|
||||
border-radius: var(--big-radius);
|
||||
border: 2px solid var(--input-selected);
|
||||
background: var(--dropdown-content);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 0 6px;
|
||||
border-bottom: 2px solid var(--input-selected);
|
||||
}
|
||||
|
||||
.menu-element {
|
||||
height: 32px;
|
||||
padding: 0 20px;
|
||||
align-content: center;
|
||||
text-wrap: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-element:hover {
|
||||
background: var(--input-selected)
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
using System.Net.Http.Json;
|
||||
using Blazored.LocalStorage;
|
||||
using GameIdeas.BlazorApp;
|
||||
using GameIdeas.BlazorApp.Pages.Games.Gateways;
|
||||
using GameIdeas.BlazorApp.Pages.User.Gateways;
|
||||
using GameIdeas.BlazorApp.Services;
|
||||
using GameIdeas.Resources;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
var services = builder.Services;
|
||||
|
||||
builder.RootComponents.Add<App>("#app");
|
||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
|
||||
@@ -15,7 +20,7 @@ UriBuilder uriBuilder = new(builder.HostEnvironment.BaseAddress)
|
||||
Port = 8000
|
||||
};
|
||||
|
||||
builder.Services.AddHttpClient(
|
||||
services.AddHttpClient(
|
||||
"GameIdeas.WebAPI",
|
||||
client =>
|
||||
{
|
||||
@@ -23,11 +28,18 @@ builder.Services.AddHttpClient(
|
||||
client.Timeout = TimeSpan.FromMinutes(3);
|
||||
});
|
||||
|
||||
builder.Services.AddScoped<IHttpClientService, HttpClientService>();
|
||||
builder.Services.AddScoped<IGameGateway, GameGateway>();
|
||||
services.AddBlazoredLocalStorage();
|
||||
services.AddAuthorizationCore();
|
||||
|
||||
builder.Services.AddSingleton<TranslationService>();
|
||||
builder.Services.AddSingleton<Translations>();
|
||||
services.AddScoped<AuthenticationStateProvider, JwtAuthenticationStateProvider>();
|
||||
|
||||
services.AddScoped<IHttpClientService, HttpClientService>();
|
||||
|
||||
services.AddScoped<IAuthGateway, AuthGateway>();
|
||||
services.AddScoped<IGameGateway, GameGateway>();
|
||||
|
||||
services.AddSingleton<TranslationService>();
|
||||
services.AddSingleton<Translations>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
@@ -3,10 +3,15 @@ using System.Net.Http.Headers;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json;
|
||||
using System.Text;
|
||||
using Blazored.LocalStorage;
|
||||
using GameIdeas.Shared.Constants;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Services;
|
||||
|
||||
public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory) : IHttpClientService
|
||||
public class HttpClientService(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
ILoggerFactory loggerFactory,
|
||||
ILocalStorageService localStorage) : IHttpClientService
|
||||
{
|
||||
private readonly HttpClient httpClient = httpClientFactory.CreateClient("GameIdeas.WebAPI");
|
||||
private readonly ILogger<HttpClientService> logger = loggerFactory.CreateLogger<HttpClientService>();
|
||||
@@ -25,6 +30,8 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
|
||||
|
||||
public async Task<T?> PostAsync<T>(string url, object data)
|
||||
{
|
||||
await SetAuthorizationHeader();
|
||||
|
||||
var jsonContent = JsonSerializer.Serialize(data, _optionsCamelCase);
|
||||
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
||||
var response = await httpClient.PostAsync(url, content);
|
||||
@@ -32,8 +39,11 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
|
||||
return await GetResultValue<T>(response, ResourcesKey.ErrorWhenPostingData);
|
||||
}
|
||||
|
||||
|
||||
public async Task<T?> PutAsync<T>(string url, object data)
|
||||
{
|
||||
await SetAuthorizationHeader();
|
||||
|
||||
var jsonContent = JsonSerializer.Serialize(data, _optionsCamelCase);
|
||||
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
||||
var response = await httpClient.PutAsync(url, content);
|
||||
@@ -43,6 +53,7 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
|
||||
|
||||
public async Task<T?> DeleteAsync<T>(string? url)
|
||||
{
|
||||
await SetAuthorizationHeader();
|
||||
var response = await httpClient.DeleteAsync(url);
|
||||
|
||||
return await GetResultValue<T>(response, ResourcesKey.ErrorWhenDeletingData);
|
||||
@@ -50,6 +61,7 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
|
||||
|
||||
public async Task<T?> FetchDataAsync<T>(string? url)
|
||||
{
|
||||
await SetAuthorizationHeader();
|
||||
var response = await httpClient.GetAsync(url);
|
||||
|
||||
return await GetResultValue<T>(response, ResourcesKey.ErrorWhenFetchingData);
|
||||
@@ -57,6 +69,7 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
|
||||
|
||||
public async Task<byte[]?> FetchBytesAsync(string? url)
|
||||
{
|
||||
await SetAuthorizationHeader();
|
||||
var response = await httpClient.GetAsync(url);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
@@ -71,6 +84,7 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
|
||||
|
||||
public async Task<Stream?> FetchStreamAsync(string? url)
|
||||
{
|
||||
await SetAuthorizationHeader();
|
||||
var response = await httpClient.GetAsync(url);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
@@ -84,6 +98,8 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
|
||||
|
||||
public async Task<T?> PostFileAsync<T>(string? url, Stream fileStream, string fileName, string contentType)
|
||||
{
|
||||
await SetAuthorizationHeader();
|
||||
|
||||
using var content = new MultipartFormDataContent();
|
||||
|
||||
var streamContent = new StreamContent(fileStream);
|
||||
@@ -122,4 +138,11 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
|
||||
throw new HttpRequestException(
|
||||
$"{errorMessage} + StatusCode: {response.StatusCode} + Reason: {response.ReasonPhrase}");
|
||||
}
|
||||
|
||||
private async Task SetAuthorizationHeader()
|
||||
{
|
||||
var token = await localStorage.GetItemAsStringAsync(GlobalConstants.LS_AUTH_STORAGE_KEY);
|
||||
httpClient.DefaultRequestHeaders.Authorization =
|
||||
new AuthenticationHeaderValue("bearer", token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using Blazored.LocalStorage;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using System.Security.Claims;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using GameIdeas.Shared.Constants;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Services;
|
||||
|
||||
public class JwtAuthenticationStateProvider(ILocalStorageService localStorage) : AuthenticationStateProvider
|
||||
{
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
var savedToken = await localStorage.GetItemAsStringAsync(GlobalConstants.LS_AUTH_STORAGE_KEY);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(savedToken))
|
||||
{
|
||||
try
|
||||
{
|
||||
var token = new JwtSecurityTokenHandler().ReadJwtToken(savedToken);
|
||||
|
||||
return new AuthenticationState(
|
||||
new ClaimsPrincipal(new ClaimsIdentity(token.Claims, "jwt"))
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await localStorage.RemoveItemAsync(GlobalConstants.LS_AUTH_STORAGE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
|
||||
}
|
||||
|
||||
public async Task NotifyUserAuthenticationAsync(string token)
|
||||
{
|
||||
await localStorage.SetItemAsStringAsync(GlobalConstants.LS_AUTH_STORAGE_KEY, token);
|
||||
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
}
|
||||
|
||||
public async Task NotifyUserLogoutAsync()
|
||||
{
|
||||
await localStorage.RemoveItemAsync(GlobalConstants.LS_AUTH_STORAGE_KEY);
|
||||
|
||||
var nobody = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(nobody)));
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
@using GameIdeas.Resources
|
||||
@using Blazored.FluentValidation;
|
||||
|
||||
<div class="account-setting-container" tabindex="1000">
|
||||
<div class="account-setting-content @(ContentVisile ? string.Empty : "invisible")">
|
||||
@if (!IsLogin)
|
||||
{
|
||||
<EditForm EditContext="EditContext" OnSubmit="HandleLoginSubmit">
|
||||
<FluentValidationValidator />
|
||||
<div class="login-form">
|
||||
<div class="login-field">
|
||||
<div class="input-title">@ResourcesKey.EnterUsername</div>
|
||||
<InputText class="input-text"
|
||||
@bind-Value="LoginDto.Username" />
|
||||
</div>
|
||||
<div class="login-field">
|
||||
<div class="input-title">@ResourcesKey.EnterPassword</div>
|
||||
<InputText class="input-text"
|
||||
@bind-Value="LoginDto.Password" />
|
||||
</div>
|
||||
<div class="login-field">
|
||||
<button class="login-button" type="submit" disabled="@IsLoading">
|
||||
@if (IsLoading)
|
||||
{
|
||||
<div class="loading"></div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ResourcesKey.Login
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="settings-list">
|
||||
<div class="settings-element">
|
||||
@ResourcesKey.UserManager
|
||||
</div>
|
||||
<span class="line"></span>
|
||||
<div class="settings-element" @onclick="HandleLogoutClicked">
|
||||
@ResourcesKey.Logout
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,47 +0,0 @@
|
||||
using GameIdeas.Shared.Dto;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Shared.Components.Account;
|
||||
|
||||
public partial class AccountSettings
|
||||
{
|
||||
private bool ContentVisile = false;
|
||||
private EditContext? EditContext;
|
||||
private LoginDto LoginDto = new();
|
||||
private bool IsLoading = false;
|
||||
private bool IsLogin = true;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
EditContext = new EditContext(LoginDto);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
ContentVisile = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
ContentVisile = !ContentVisile;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task HandleLoginSubmit()
|
||||
{
|
||||
if (EditContext?.Validate() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsLoading = true;
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
Close();
|
||||
IsLoading = false;
|
||||
}
|
||||
private void HandleLogoutClicked()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
@@ -15,4 +15,9 @@ public static class Endpoints
|
||||
{
|
||||
public static readonly string AllCategories = "api/Category/All";
|
||||
}
|
||||
|
||||
public static class Auth
|
||||
{
|
||||
public static readonly string Login = "api/User/Login";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,8 @@ public static class Icons
|
||||
public readonly static MarkupString Game = new(OpenBraket +
|
||||
"<path d=\"M6,7H18A5,5 0 0,1 23,12A5,5 0 0,1 18,17C16.36,17 14.91,16.21 14,15H10C9.09,16.21 7.64,17 6,17A5,5 0 0,1 1,12A5,5 0 0,1 6,7M19.75,9.5A1.25,1.25 0 0,0 18.5,10.75A1.25,1.25 0 0,0 19.75,12A1.25,1.25 0 0,0 21,10.75A1.25,1.25 0 0,0 19.75,9.5M17.25,12A1.25,1.25 0 0,0 16,13.25A1.25,1.25 0 0,0 17.25,14.5A1.25,1.25 0 0,0 18.5,13.25A1.25,1.25 0 0,0 17.25,12M5,9V11H3V13H5V15H7V13H9V11H7V9H5Z\">" +
|
||||
CloseBraket);
|
||||
|
||||
public readonly static MarkupString Account = new(OpenBraket +
|
||||
"<path d=\"M12,19.2C9.5,19.2 7.29,17.92 6,16C6.03,14 10,12.9 12,12.9C14,12.9 17.97,14 18,16C16.71,17.92 14.5,19.2 12,19.2M12,5A3,3 0 0,1 15,8A3,3 0 0,1 12,11A3,3 0 0,1 9,8A3,3 0 0,1 12,5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z\" />" +
|
||||
CloseBraket);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace GameIdeas.BlazorApp.Shared.Exceptions;
|
||||
|
||||
public class AuthenticationUserException(string message) : Exception(message);
|
||||
@@ -11,6 +11,7 @@ public class Translations (TranslationService translationService)
|
||||
public string EnterUsername => translationService.Translate(nameof(EnterUsername));
|
||||
public string EnterPassword => translationService.Translate(nameof(EnterPassword));
|
||||
public string UserManager => translationService.Translate(nameof(UserManager));
|
||||
public string CategoriesManager => translationService.Translate(nameof(CategoriesManager));
|
||||
public string Filters => translationService.Translate(nameof(Filters));
|
||||
public string LastAdd => translationService.Translate(nameof(LastAdd));
|
||||
public string Research => translationService.Translate(nameof(Research));
|
||||
@@ -48,6 +49,11 @@ public class Translations (TranslationService translationService)
|
||||
public string MinStorageSpaceFormat => translationService.Translate(nameof(MinStorageSpaceFormat));
|
||||
public string MaxStorageSpaceFormat => translationService.Translate(nameof(MaxStorageSpaceFormat));
|
||||
public string MinMaxStorageSpaceFormat => translationService.Translate(nameof(MinMaxStorageSpaceFormat));
|
||||
public string UserArgumentsNull => translationService.Translate(nameof(UserArgumentsNull));
|
||||
public string InvalidToken => translationService.Translate(nameof(InvalidToken));
|
||||
public string UserUnauthorized => translationService.Translate(nameof(UserUnauthorized));
|
||||
public string UserLoginFailed => translationService.Translate(nameof(UserLoginFailed));
|
||||
public string UserLogoutFailed => translationService.Translate(nameof(UserLogoutFailed));
|
||||
}
|
||||
|
||||
public static class ResourcesKey
|
||||
@@ -67,6 +73,7 @@ public static class ResourcesKey
|
||||
public static string EnterUsername => _instance?.EnterUsername ?? throw new InvalidOperationException("ResourcesKey.EnterUsername is not initialized.");
|
||||
public static string EnterPassword => _instance?.EnterPassword ?? throw new InvalidOperationException("ResourcesKey.EnterPassword is not initialized.");
|
||||
public static string UserManager => _instance?.UserManager ?? throw new InvalidOperationException("ResourcesKey.UserManager is not initialized.");
|
||||
public static string CategoriesManager => _instance?.CategoriesManager ?? throw new InvalidOperationException("ResourcesKey.CategoriesManager is not initialized.");
|
||||
public static string Filters => _instance?.Filters ?? throw new InvalidOperationException("ResourcesKey.Filters is not initialized.");
|
||||
public static string LastAdd => _instance?.LastAdd ?? throw new InvalidOperationException("ResourcesKey.LastAdd is not initialized.");
|
||||
public static string Research => _instance?.Research ?? throw new InvalidOperationException("ResourcesKey.Research is not initialized.");
|
||||
@@ -104,4 +111,9 @@ public static class ResourcesKey
|
||||
public static string MinStorageSpaceFormat => _instance?.MinStorageSpaceFormat ?? throw new InvalidOperationException("ResourcesKey.MinStorageSpaceFormat is not initialized.");
|
||||
public static string MaxStorageSpaceFormat => _instance?.MaxStorageSpaceFormat ?? throw new InvalidOperationException("ResourcesKey.MaxStorageSpaceFormat is not initialized.");
|
||||
public static string MinMaxStorageSpaceFormat => _instance?.MinMaxStorageSpaceFormat ?? throw new InvalidOperationException("ResourcesKey.MinMaxStorageSpaceFormat is not initialized.");
|
||||
public static string UserArgumentsNull => _instance?.UserArgumentsNull ?? throw new InvalidOperationException("ResourcesKey.UserArgumentsNull is not initialized.");
|
||||
public static string InvalidToken => _instance?.InvalidToken ?? throw new InvalidOperationException("ResourcesKey.InvalidToken is not initialized.");
|
||||
public static string UserUnauthorized => _instance?.UserUnauthorized ?? throw new InvalidOperationException("ResourcesKey.UserUnauthorized is not initialized.");
|
||||
public static string UserLoginFailed => _instance?.UserLoginFailed ?? throw new InvalidOperationException("ResourcesKey.UserLoginFailed is not initialized.");
|
||||
public static string UserLogoutFailed => _instance?.UserLogoutFailed ?? throw new InvalidOperationException("ResourcesKey.UserLogoutFailed is not initialized.");
|
||||
}
|
||||
@@ -2,6 +2,18 @@
|
||||
|
||||
public class GlobalConstants
|
||||
{
|
||||
public static int NUMBER_PER_PAGE = 50;
|
||||
public readonly static Guid ADMINISTRATOR_ID = Guid.Parse("{06CA5CB7-6DE5-4A73-9DDD-8E2D5CCDF104}");
|
||||
public readonly static Guid ADMINISTRATOR_USER_ID = Guid.Parse("{2AB56FCB-0CDE-4DAE-AC9C-FC7635B0D18A}");
|
||||
public readonly static Guid MEMBER_ID = Guid.Parse("{BCE14DEA-1748-4A76-8485-ADEE83DF5EFD}");
|
||||
|
||||
public const string ADMINISTRATOR = "Administrateur";
|
||||
public const string MEMBER = "Membre";
|
||||
public const string ADMIN_MEMBER = $"{ADMINISTRATOR}, {MEMBER}";
|
||||
|
||||
public const int JWT_DURATION_HOUR = 12;
|
||||
|
||||
public const int NUMBER_PER_PAGE = 50;
|
||||
|
||||
public const string LS_AUTH_STORAGE_KEY = "authToken";
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ public class GameDetailDto
|
||||
public string? Title { get; set; }
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
public DateTime? CreationDate { get; set; }
|
||||
public int CreationUserId { get; set; }
|
||||
public string CreationUserId { get; set; } = string.Empty;
|
||||
public DateTime? ModificationDate { get; set; }
|
||||
public int? ModificationUserId { get; set; }
|
||||
public string? ModificationUserId { get; set; }
|
||||
public double? StorageSpace { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int Interest { get; set; } = 3;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using GameIdeas.Shared.Enum;
|
||||
|
||||
namespace GameIdeas.Shared.Dto;
|
||||
|
||||
public class LoginDto
|
||||
{
|
||||
public int? Id { get; set; }
|
||||
public string? Username { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public Role? Role { get; set; }
|
||||
}
|
||||
7
src/GameIdeas/GameIdeas.Shared/Dto/TokenDto.cs
Normal file
7
src/GameIdeas/GameIdeas.Shared/Dto/TokenDto.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace GameIdeas.Shared.Dto;
|
||||
|
||||
public class TokenDto
|
||||
{
|
||||
public string? Token { get; set; }
|
||||
public DateTime? Expiration { get; set; }
|
||||
}
|
||||
@@ -7,5 +7,5 @@ public class UserDto
|
||||
public int? Id { get; set; }
|
||||
public string? Username { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public Role? Role { get; set; }
|
||||
public string? RoleId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace GameIdeas.Shared.Enum;
|
||||
|
||||
public enum Role
|
||||
{
|
||||
Guest = 1,
|
||||
Member = 2,
|
||||
Administrator = 3
|
||||
}
|
||||
@@ -6,4 +6,8 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -15,9 +15,9 @@ public partial class Game
|
||||
public string Title { get; set; } = null!;
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
public int CreationUserId { get; set; }
|
||||
public string CreationUserId { get; set; } = null!;
|
||||
public DateTime? ModificationDate { get; set; }
|
||||
public int? ModificationUserId { get; set; }
|
||||
public string? ModificationUserId { get; set; }
|
||||
public double? StorageSpace { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int Interest { get; set; }
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
namespace GameIdeas.Shared.Model;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
public partial class User
|
||||
namespace GameIdeas.Shared.Model;
|
||||
|
||||
public partial class User : IdentityUser
|
||||
{
|
||||
public User()
|
||||
{
|
||||
@@ -8,11 +10,6 @@ public partial class User
|
||||
ModificationGames = new HashSet<Game>();
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public string Username { get; set; } = null!;
|
||||
public string Password { get; set; } = null!;
|
||||
public int Role { get; set; }
|
||||
|
||||
public virtual ICollection<Game> CreationGames { get; set; }
|
||||
public virtual ICollection<Game> ModificationGames { get; set; }
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameIdeas.Shared", "GameIde
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client", "Client", "{9598BBAF-CC9F-4F43-82B2-40F57296C9F0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameIdeas.WebAPI.Tests", "Server\GameIdeas.WebAPI.Tests\GameIdeas.WebAPI.Tests.csproj", "{D7B46EB2-590D-4A39-90C6-4D553FC4309A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -37,6 +39,10 @@ Global
|
||||
{9D6D5C6D-AD66-4353-88CC-638887C42477}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9D6D5C6D-AD66-4353-88CC-638887C42477}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9D6D5C6D-AD66-4353-88CC-638887C42477}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D7B46EB2-590D-4A39-90C6-4D553FC4309A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D7B46EB2-590D-4A39-90C6-4D553FC4309A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D7B46EB2-590D-4A39-90C6-4D553FC4309A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D7B46EB2-590D-4A39-90C6-4D553FC4309A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -44,6 +50,7 @@ Global
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{ABBADA2F-9017-49A2-AEB9-AC2DB7D70831} = {9598BBAF-CC9F-4F43-82B2-40F57296C9F0}
|
||||
{61C3985E-15DF-4127-9D1F-CAE39F0ADD17} = {F59BED34-9473-436A-A91A-23510A4E0E87}
|
||||
{D7B46EB2-590D-4A39-90C6-4D553FC4309A} = {F59BED34-9473-436A-A91A-23510A4E0E87}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6380DD77-53E4-4F3B-BB45-FAD2263D1511}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="MSTest" Version="3.6.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\GameIdeas.Shared\GameIdeas.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
17
src/GameIdeas/Server/GameIdeas.WebAPI.Tests/IdentityTest.cs
Normal file
17
src/GameIdeas/Server/GameIdeas.WebAPI.Tests/IdentityTest.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using GameIdeas.Shared.Model;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace GameIdeas.WebAPI.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class IdentityTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void GetPasswordHash()
|
||||
{
|
||||
var hasher = new PasswordHasher<User>();
|
||||
var hash = hasher.HashPassword(null!, "GameIdeas");
|
||||
Console.WriteLine(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
|
||||
@@ -1,9 +1,10 @@
|
||||
using GameIdeas.Shared.Model;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace GameIdeas.WebAPI.Context;
|
||||
|
||||
public class GameIdeasContext : DbContext
|
||||
public class GameIdeasContext : IdentityDbContext<User>
|
||||
{
|
||||
public GameIdeasContext(DbContextOptions<GameIdeasContext> option)
|
||||
: base(option)
|
||||
@@ -12,7 +13,6 @@ public class GameIdeasContext : DbContext
|
||||
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
||||
}
|
||||
|
||||
public virtual DbSet<User> Users { get; set; } = null!;
|
||||
public virtual DbSet<Developer> Developers { get; set; } = null!;
|
||||
public virtual DbSet<Platform> Platforms { get; set; } = null!;
|
||||
public virtual DbSet<Property> Properties { get; set; } = null!;
|
||||
@@ -27,28 +27,51 @@ public class GameIdeasContext : DbContext
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<User>(entity => {
|
||||
entity.ToTable("User");
|
||||
modelBuilder.Entity<Developer>(entity => {
|
||||
entity.ToTable("Developer");
|
||||
|
||||
entity.Property(e => e.Id)
|
||||
.UseIdentityByDefaultColumn()
|
||||
.HasIdentityOptions(startValue: 100000);
|
||||
entity.HasIndex(e => e.Name)
|
||||
.IsUnique();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Developer>(entity => entity.ToTable("Developer"));
|
||||
modelBuilder.Entity<Platform>(entity => {
|
||||
entity.ToTable("Platform");
|
||||
|
||||
modelBuilder.Entity<Platform>(entity => entity.ToTable("Platform"));
|
||||
entity.HasIndex(e => e.Label)
|
||||
.IsUnique();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Property>(entity => entity.ToTable("Property"));
|
||||
modelBuilder.Entity<Property>(entity =>
|
||||
{
|
||||
entity.ToTable("Property");
|
||||
|
||||
modelBuilder.Entity<Publisher>(entity => entity.ToTable("Publisher"));
|
||||
entity.HasIndex(e => e.Label)
|
||||
.IsUnique();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Tag>(entity => entity.ToTable("Tag"));
|
||||
modelBuilder.Entity<Publisher>(entity =>
|
||||
{
|
||||
entity.ToTable("Publisher");
|
||||
|
||||
entity.HasIndex(e => e.Name)
|
||||
.IsUnique();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Tag>(entity =>
|
||||
{
|
||||
entity.ToTable("Tag");
|
||||
|
||||
entity.HasIndex(e => e.Label)
|
||||
.IsUnique();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Game>(entity =>
|
||||
{
|
||||
entity.ToTable("Game");
|
||||
|
||||
entity.HasIndex(e => e.Title)
|
||||
.IsUnique();
|
||||
|
||||
entity.HasIndex(e => e.CreationUserId);
|
||||
|
||||
entity.HasIndex(e => e.ModificationUserId);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using GameIdeas.Shared.Dto;
|
||||
using GameIdeas.Shared.Constants;
|
||||
using GameIdeas.Shared.Dto;
|
||||
using GameIdeas.WebAPI.Services.Games;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace GameIdeas.WebAPI.Controllers;
|
||||
@@ -42,6 +44,7 @@ public class GameController(
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(Roles = GlobalConstants.ADMIN_MEMBER)]
|
||||
[HttpPost("Create")]
|
||||
public async Task<ActionResult<int>> CreateGame([FromBody] GameDetailDto game)
|
||||
{
|
||||
@@ -57,6 +60,7 @@ public class GameController(
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(Roles = GlobalConstants.ADMIN_MEMBER)]
|
||||
[HttpPut("Update")]
|
||||
public async Task<ActionResult<int>> UpdateGame([FromBody] GameDetailDto game)
|
||||
{
|
||||
@@ -72,6 +76,7 @@ public class GameController(
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(Roles = GlobalConstants.ADMIN_MEMBER)]
|
||||
[HttpDelete("Delete/{id:int}")]
|
||||
public async Task<ActionResult<bool>> DeleteGame(int id)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using GameIdeas.Shared.Dto;
|
||||
using GameIdeas.WebAPI.Exceptions;
|
||||
using GameIdeas.WebAPI.Services.Users;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace GameIdeas.WebAPI.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class UserController(
|
||||
IUserService userService,
|
||||
ILoggerFactory loggerFactory) : Controller
|
||||
{
|
||||
private readonly ILogger<UserController> logger = loggerFactory.CreateLogger<UserController>();
|
||||
|
||||
[HttpPost("Login")]
|
||||
public async Task<ActionResult<TokenDto>> Login([FromBody] UserDto model)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(await userService.Login(model));
|
||||
}
|
||||
catch (UserInvalidException e)
|
||||
{
|
||||
logger.LogInformation(e, "Missing informations for authentication");
|
||||
return StatusCode(406, e.Message);
|
||||
}
|
||||
catch (UserUnauthorizedException e)
|
||||
{
|
||||
logger.LogWarning(e, "Authentication invalid with there informations");
|
||||
return Unauthorized(e.Message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Internal error while search games");
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace GameIdeas.WebAPI.Exceptions;
|
||||
|
||||
public class UserInvalidException (string message) : Exception(message);
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace GameIdeas.WebAPI.Exceptions;
|
||||
|
||||
public class UserUnauthorizedException(string message) : Exception(message);
|
||||
@@ -7,6 +7,7 @@
|
||||
"EnterUsername": "Nom d'utilisateur",
|
||||
"EnterPassword": "Mot de passe",
|
||||
"UserManager": "Gestion des utilisateurs",
|
||||
"CategoriesManager": "Gestion des catégories",
|
||||
"Filters": "Les filtres",
|
||||
"LastAdd": "Les ajouts récents",
|
||||
"Research": "Rechercher",
|
||||
@@ -43,5 +44,10 @@
|
||||
"ErrorStorageSpaceLabel": "Erreur lors de la génération des label de l'espace de stockage",
|
||||
"MinStorageSpaceFormat": "Jusqu'à {0}",
|
||||
"MaxStorageSpaceFormat": "Plus de {0}",
|
||||
"MinMaxStorageSpaceFormat": "{0} à {1}"
|
||||
"MinMaxStorageSpaceFormat": "{0} à {1}",
|
||||
"UserArgumentsNull": "Nom d'utilisateur ou mot de passe invalide",
|
||||
"InvalidToken": "Le token JWT est invalide",
|
||||
"UserUnauthorized": "Utilisateur non authorisé",
|
||||
"UserLoginFailed": "Authentification de l'utilisateur échoué",
|
||||
"UserLogoutFailed": "Déconnection de l'utilisateur échoué"
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>c5ccfd3a-f458-4660-b6c4-81fcc2513737</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -12,6 +13,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.4">
|
||||
|
||||
@@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace GameIdeas.WebAPI.Migrations
|
||||
{
|
||||
[DbContext(typeof(GameIdeasContext))]
|
||||
[Migration("20250409225125_InitialCreate")]
|
||||
[Migration("20250420153030_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -39,6 +39,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Developer", (string)null);
|
||||
});
|
||||
|
||||
@@ -53,11 +56,12 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<int>("CreationUserId")
|
||||
.HasColumnType("integer");
|
||||
b.Property<string>("CreationUserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("text");
|
||||
@@ -66,13 +70,13 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime?>("ModificationDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<int?>("ModificationUserId")
|
||||
.HasColumnType("integer");
|
||||
b.Property<string>("ModificationUserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime?>("ReleaseDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<double?>("StorageSpace")
|
||||
.HasColumnType("double precision");
|
||||
@@ -87,6 +91,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasIndex("ModificationUserId");
|
||||
|
||||
b.HasIndex("Title")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Game", (string)null);
|
||||
});
|
||||
|
||||
@@ -182,6 +189,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Label")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Platform", (string)null);
|
||||
});
|
||||
|
||||
@@ -199,6 +209,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Label")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Property", (string)null);
|
||||
});
|
||||
|
||||
@@ -216,6 +229,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Publisher", (string)null);
|
||||
});
|
||||
|
||||
@@ -233,32 +249,206 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Label")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Tag", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
NpgsqlPropertyBuilderExtensions.HasIdentityOptions(b.Property<int>("Id"), 100000L, null, null, null, null, null);
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Role")
|
||||
.HasColumnType("integer");
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Username")
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("User", (string)null);
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Game", b =>
|
||||
@@ -372,6 +562,57 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
b.Navigation("Tag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Developer", b =>
|
||||
{
|
||||
b.Navigation("GameDevelopers");
|
||||
@@ -12,6 +12,45 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "text", nullable: false),
|
||||
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
PasswordHash = table.Column<string>(type: "text", nullable: true),
|
||||
SecurityStamp = table.Column<string>(type: "text", nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
|
||||
PhoneNumber = table.Column<string>(type: "text", nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Developer",
|
||||
columns: table => new
|
||||
@@ -78,19 +117,109 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "User",
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:IdentitySequenceOptions", "'100000', '1', '', '', 'False', '1'")
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Username = table.Column<string>(type: "text", nullable: false),
|
||||
Password = table.Column<string>(type: "text", nullable: false),
|
||||
Role = table.Column<int>(type: "integer", nullable: false)
|
||||
RoleId = table.Column<string>(type: "text", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_User", x => x.Id);
|
||||
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
UserId = table.Column<string>(type: "text", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||
ProviderKey = table.Column<string>(type: "text", nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
|
||||
UserId = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "text", nullable: false),
|
||||
RoleId = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "text", nullable: false),
|
||||
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Value = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
@@ -101,11 +230,11 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
.Annotation("Npgsql:IdentitySequenceOptions", "'100000', '1', '', '', 'False', '1'")
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Title = table.Column<string>(type: "text", nullable: false),
|
||||
ReleaseDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
CreationDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
CreationUserId = table.Column<int>(type: "integer", nullable: false),
|
||||
ModificationDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
ModificationUserId = table.Column<int>(type: "integer", nullable: true),
|
||||
ReleaseDate = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
CreationDate = table.Column<DateTime>(type: "timestamp without time zone", nullable: false, defaultValueSql: "now()"),
|
||||
CreationUserId = table.Column<string>(type: "text", nullable: false),
|
||||
ModificationDate = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||
ModificationUserId = table.Column<string>(type: "text", nullable: true),
|
||||
StorageSpace = table.Column<double>(type: "double precision", nullable: true),
|
||||
Description = table.Column<string>(type: "text", nullable: true),
|
||||
Interest = table.Column<int>(type: "integer", nullable: false)
|
||||
@@ -114,14 +243,14 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
{
|
||||
table.PrimaryKey("PK_Game", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Game_User_CreationUserId",
|
||||
name: "FK_Game_AspNetUsers_CreationUserId",
|
||||
column: x => x.CreationUserId,
|
||||
principalTable: "User",
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_Game_User_ModificationUserId",
|
||||
name: "FK_Game_AspNetUsers_ModificationUserId",
|
||||
column: x => x.ModificationUserId,
|
||||
principalTable: "User",
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
@@ -246,6 +375,49 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
table: "AspNetRoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "AspNetRoles",
|
||||
column: "NormalizedName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserClaims_UserId",
|
||||
table: "AspNetUserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserLogins_UserId",
|
||||
table: "AspNetUserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserRoles_RoleId",
|
||||
table: "AspNetUserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedUserName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Developer_Name",
|
||||
table: "Developer",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Game_CreationUserId",
|
||||
table: "Game",
|
||||
@@ -256,6 +428,12 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
table: "Game",
|
||||
column: "ModificationUserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Game_Title",
|
||||
table: "Game",
|
||||
column: "Title",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_GameDeveloper_DeveloperId",
|
||||
table: "GameDeveloper",
|
||||
@@ -280,11 +458,50 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
name: "IX_GameTag_TagId",
|
||||
table: "GameTag",
|
||||
column: "TagId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Platform_Label",
|
||||
table: "Platform",
|
||||
column: "Label",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Property_Label",
|
||||
table: "Property",
|
||||
column: "Label",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Publisher_Name",
|
||||
table: "Publisher",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tag_Label",
|
||||
table: "Tag",
|
||||
column: "Label",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "GameDeveloper");
|
||||
|
||||
@@ -300,6 +517,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
migrationBuilder.DropTable(
|
||||
name: "GameTag");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Developer");
|
||||
|
||||
@@ -319,7 +539,7 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
name: "Tag");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "User");
|
||||
name: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
||||
663
src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/20250420160158_SeedDefaultUser.Designer.cs
generated
Normal file
663
src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/20250420160158_SeedDefaultUser.Designer.cs
generated
Normal file
@@ -0,0 +1,663 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using GameIdeas.WebAPI.Context;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace GameIdeas.WebAPI.Migrations
|
||||
{
|
||||
[DbContext(typeof(GameIdeasContext))]
|
||||
[Migration("20250420160158_SeedDefaultUser")]
|
||||
partial class SeedDefaultUser
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Developer", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Developer", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Game", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
NpgsqlPropertyBuilderExtensions.HasIdentityOptions(b.Property<int>("Id"), 100000L, null, null, null, null, null);
|
||||
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("CreationUserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Interest")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime?>("ModificationDate")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<string>("ModificationUserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime?>("ReleaseDate")
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<double?>("StorageSpace")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreationUserId");
|
||||
|
||||
b.HasIndex("ModificationUserId");
|
||||
|
||||
b.HasIndex("Title")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Game", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.GameDeveloper", b =>
|
||||
{
|
||||
b.Property<int>("GameId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("DeveloperId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("GameId", "DeveloperId");
|
||||
|
||||
b.HasIndex("DeveloperId");
|
||||
|
||||
b.ToTable("GameDeveloper", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.GamePlatform", b =>
|
||||
{
|
||||
b.Property<int>("GameId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("PlatformId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("GameId", "PlatformId");
|
||||
|
||||
b.HasIndex("PlatformId");
|
||||
|
||||
b.ToTable("GamePlatform", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.GameProperty", b =>
|
||||
{
|
||||
b.Property<int>("GameId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("PropertyId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("GameId", "PropertyId");
|
||||
|
||||
b.HasIndex("PropertyId");
|
||||
|
||||
b.ToTable("GameProperty", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.GamePublisher", b =>
|
||||
{
|
||||
b.Property<int>("GameId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("PublisherId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("GameId", "PublisherId");
|
||||
|
||||
b.HasIndex("PublisherId");
|
||||
|
||||
b.ToTable("GamePublisher", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.GameTag", b =>
|
||||
{
|
||||
b.Property<int>("GameId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("TagId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("GameId", "TagId");
|
||||
|
||||
b.HasIndex("TagId");
|
||||
|
||||
b.ToTable("GameTag", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Platform", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Label")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Label")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Platform", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Property", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Label")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Label")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Property", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Publisher", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Publisher", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Tag", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Label")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Label")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Tag", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Game", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.User", "CreationUser")
|
||||
.WithMany("CreationGames")
|
||||
.HasForeignKey("CreationUserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GameIdeas.Shared.Model.User", "ModificationUser")
|
||||
.WithMany("ModificationGames")
|
||||
.HasForeignKey("ModificationUserId");
|
||||
|
||||
b.Navigation("CreationUser");
|
||||
|
||||
b.Navigation("ModificationUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.GameDeveloper", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.Developer", "Developer")
|
||||
.WithMany("GameDevelopers")
|
||||
.HasForeignKey("DeveloperId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GameIdeas.Shared.Model.Game", "Game")
|
||||
.WithMany("GameDevelopers")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Developer");
|
||||
|
||||
b.Navigation("Game");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.GamePlatform", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.Game", "Game")
|
||||
.WithMany("GamePlatforms")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GameIdeas.Shared.Model.Platform", "Platform")
|
||||
.WithMany("GamePlatforms")
|
||||
.HasForeignKey("PlatformId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Game");
|
||||
|
||||
b.Navigation("Platform");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.GameProperty", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.Game", "Game")
|
||||
.WithMany("GameProperties")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GameIdeas.Shared.Model.Property", "Property")
|
||||
.WithMany("GameProperties")
|
||||
.HasForeignKey("PropertyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Game");
|
||||
|
||||
b.Navigation("Property");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.GamePublisher", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.Game", "Game")
|
||||
.WithMany("GamePublishers")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GameIdeas.Shared.Model.Publisher", "Publisher")
|
||||
.WithMany("GamePublishers")
|
||||
.HasForeignKey("PublisherId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Game");
|
||||
|
||||
b.Navigation("Publisher");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.GameTag", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.Game", "Game")
|
||||
.WithMany("GameTags")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GameIdeas.Shared.Model.Tag", "Tag")
|
||||
.WithMany("GameTags")
|
||||
.HasForeignKey("TagId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Game");
|
||||
|
||||
b.Navigation("Tag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Developer", b =>
|
||||
{
|
||||
b.Navigation("GameDevelopers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Game", b =>
|
||||
{
|
||||
b.Navigation("GameDevelopers");
|
||||
|
||||
b.Navigation("GamePlatforms");
|
||||
|
||||
b.Navigation("GameProperties");
|
||||
|
||||
b.Navigation("GamePublishers");
|
||||
|
||||
b.Navigation("GameTags");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Platform", b =>
|
||||
{
|
||||
b.Navigation("GamePlatforms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Property", b =>
|
||||
{
|
||||
b.Navigation("GameProperties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Publisher", b =>
|
||||
{
|
||||
b.Navigation("GamePublishers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Tag", b =>
|
||||
{
|
||||
b.Navigation("GameTags");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.User", b =>
|
||||
{
|
||||
b.Navigation("CreationGames");
|
||||
|
||||
b.Navigation("ModificationGames");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using GameIdeas.Shared.Constants;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace GameIdeas.WebAPI.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SeedDefaultUser : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.InsertData(
|
||||
table: "AspNetRoles",
|
||||
columns: ["Id", "Name", "NormalizedName", "ConcurrencyStamp"],
|
||||
values: new object[,]
|
||||
{
|
||||
{
|
||||
GlobalConstants.ADMINISTRATOR_ID.ToString(),
|
||||
GlobalConstants.ADMINISTRATOR,
|
||||
GlobalConstants.ADMINISTRATOR.Normalize(),
|
||||
Guid.NewGuid().ToString()
|
||||
},
|
||||
{
|
||||
GlobalConstants.MEMBER_ID.ToString(),
|
||||
GlobalConstants.MEMBER,
|
||||
GlobalConstants.MEMBER.Normalize(),
|
||||
Guid.NewGuid().ToString()
|
||||
}
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "AspNetUsers",
|
||||
columns:
|
||||
[
|
||||
"Id", "UserName", "NormalizedUserName", "EmailConfirmed", "PhoneNumberConfirmed", "TwoFactorEnabled"
|
||||
, "PasswordHash", "SecurityStamp", "ConcurrencyStamp", "AccessFailedCount", "LockoutEnabled"
|
||||
],
|
||||
values:
|
||||
[
|
||||
GlobalConstants.ADMINISTRATOR_USER_ID.ToString(), "admin", "ADMIN",
|
||||
false, false, false,
|
||||
"AQAAAAIAAYagAAAAEOGx7MFBLpS7awda0ww6jsfXsnhsUjYd4gDK9DaGvQv0X9UZTuHStr5v5+t4Y1S+xg==",
|
||||
Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), 0, false
|
||||
]);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "AspNetUserRoles",
|
||||
columns: ["UserId", "RoleId"],
|
||||
values: [GlobalConstants.ADMINISTRATOR_USER_ID.ToString(), GlobalConstants.ADMINISTRATOR_ID.ToString()]);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql(@$"DELETE FROM ""AspNetUserRoles"" WHERE ""UserId"" = '{GlobalConstants.ADMINISTRATOR_USER_ID.ToString()}' AND ""RoleId"" = '{GlobalConstants.ADMINISTRATOR_ID.ToString()}'");
|
||||
migrationBuilder.DeleteData("AspNetUsers", "Id", GlobalConstants.ADMINISTRATOR_USER_ID.ToString());
|
||||
migrationBuilder.DeleteData("AspNetRoles", "Id", GlobalConstants.ADMINISTRATOR_ID.ToString());
|
||||
migrationBuilder.DeleteData("AspNetRoles", "Id", GlobalConstants.MEMBER_ID.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Developer", (string)null);
|
||||
});
|
||||
|
||||
@@ -50,11 +53,12 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<int>("CreationUserId")
|
||||
.HasColumnType("integer");
|
||||
b.Property<string>("CreationUserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("text");
|
||||
@@ -63,13 +67,13 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime?>("ModificationDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<int?>("ModificationUserId")
|
||||
.HasColumnType("integer");
|
||||
b.Property<string>("ModificationUserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime?>("ReleaseDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
.HasColumnType("timestamp without time zone");
|
||||
|
||||
b.Property<double?>("StorageSpace")
|
||||
.HasColumnType("double precision");
|
||||
@@ -84,6 +88,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasIndex("ModificationUserId");
|
||||
|
||||
b.HasIndex("Title")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Game", (string)null);
|
||||
});
|
||||
|
||||
@@ -179,6 +186,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Label")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Platform", (string)null);
|
||||
});
|
||||
|
||||
@@ -196,6 +206,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Label")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Property", (string)null);
|
||||
});
|
||||
|
||||
@@ -213,6 +226,9 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Publisher", (string)null);
|
||||
});
|
||||
|
||||
@@ -230,32 +246,206 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Label")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Tag", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
NpgsqlPropertyBuilderExtensions.HasIdentityOptions(b.Property<int>("Id"), 100000L, null, null, null, null, null);
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Role")
|
||||
.HasColumnType("integer");
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Username")
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("User", (string)null);
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Game", b =>
|
||||
@@ -369,6 +559,57 @@ namespace GameIdeas.WebAPI.Migrations
|
||||
b.Navigation("Tag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("GameIdeas.Shared.Model.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameIdeas.Shared.Model.Developer", b =>
|
||||
{
|
||||
b.Navigation("GameDevelopers");
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
using AutoMapper;
|
||||
using GameIdeas.Shared.Dto;
|
||||
using GameIdeas.Shared.Enum;
|
||||
using GameIdeas.Shared.Model;
|
||||
|
||||
namespace GameIdeas.WebAPI.Profiles;
|
||||
|
||||
public class UserProfile : Profile
|
||||
{
|
||||
public UserProfile()
|
||||
{
|
||||
CreateMap<User, UserDto>()
|
||||
.ForMember(d => d.Id, o => o.MapFrom(s => s.Id))
|
||||
.ForMember(d => d.Username, o => o.MapFrom(s => s.Username))
|
||||
.ForMember(d => d.Password, o => o.MapFrom(s => s.Password))
|
||||
.ForMember(d => d.Role, o => o.MapFrom(s => s.Role))
|
||||
.ReverseMap();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
using GameIdeas.Resources;
|
||||
using GameIdeas.Shared.Model;
|
||||
using GameIdeas.WebAPI.Context;
|
||||
using GameIdeas.WebAPI.Services.Categories;
|
||||
using GameIdeas.WebAPI.Services.Games;
|
||||
using GameIdeas.WebAPI.Services.Users;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var services = builder.Services;
|
||||
@@ -27,9 +33,38 @@ Action<DbContextOptionsBuilder> dbContextOptions = options =>
|
||||
// Add services to the container.
|
||||
services.AddDbContext<GameIdeasContext>(dbContextOptions);
|
||||
|
||||
services.AddIdentity<User, IdentityRole>()
|
||||
.AddEntityFrameworkStores<GameIdeasContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
var jwtKey = Environment.GetEnvironmentVariable("JWT_KEY")
|
||||
?? throw new ArgumentNullException(message: "Invalid key for JWT token", null);
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidIssuer = Environment.GetEnvironmentVariable("JWT_ISSUER"),
|
||||
ValidAudience = Environment.GetEnvironmentVariable("JWT_AUDIENCE"),
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey))
|
||||
};
|
||||
});
|
||||
|
||||
services.AddAuthorization();
|
||||
|
||||
services.AddSingleton<TranslationService>();
|
||||
services.AddSingleton<Translations>();
|
||||
|
||||
services.AddScoped<IUserService, UserService>();
|
||||
services.AddScoped<IGameReadService, GameReadService>();
|
||||
services.AddScoped<IGameWriteService, GameWriteService>();
|
||||
services.AddScoped<ICategoryService, CategoryService>();
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using GameIdeas.Shared.Dto;
|
||||
|
||||
namespace GameIdeas.WebAPI.Services.Users;
|
||||
|
||||
public interface IUserService
|
||||
{
|
||||
Task<TokenDto> Login(UserDto user);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using GameIdeas.Resources;
|
||||
using GameIdeas.Shared.Constants;
|
||||
using GameIdeas.Shared.Dto;
|
||||
using GameIdeas.Shared.Model;
|
||||
using GameIdeas.WebAPI.Exceptions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
|
||||
namespace GameIdeas.WebAPI.Services.Users;
|
||||
|
||||
public class UserService(UserManager<User> userManager) : IUserService
|
||||
{
|
||||
public async Task<TokenDto> Login(UserDto userDto)
|
||||
{
|
||||
if (userDto.Username == null || userDto.Password == null)
|
||||
throw new UserInvalidException(ResourcesKey.UserArgumentsNull);
|
||||
|
||||
var user = await userManager.FindByNameAsync(userDto.Username);
|
||||
|
||||
if (user == null || !await userManager.CheckPasswordAsync(user, userDto.Password))
|
||||
{
|
||||
throw new UserUnauthorizedException(ResourcesKey.UserUnauthorized);
|
||||
}
|
||||
|
||||
List<Claim> authClaims =
|
||||
[
|
||||
new Claim(ClaimTypes.Name, user.UserName ?? string.Empty),
|
||||
new Claim(ClaimTypes.Sid, user.Id),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
];
|
||||
|
||||
authClaims.AddRange((await userManager.GetRolesAsync(user))
|
||||
.Select(r => new Claim(ClaimTypes.Role, r)));
|
||||
|
||||
var jwtKey = Environment.GetEnvironmentVariable("JWT_KEY")
|
||||
?? throw new ArgumentNullException(message: ResourcesKey.InvalidToken, null);
|
||||
|
||||
var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey));
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: Environment.GetEnvironmentVariable("JWT_ISSUER"),
|
||||
audience: Environment.GetEnvironmentVariable("JWT_AUDIENCE"),
|
||||
expires: DateTime.Now.AddHours(GlobalConstants.JWT_DURATION_HOUR),
|
||||
claims: authClaims,
|
||||
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
|
||||
);
|
||||
|
||||
return new TokenDto
|
||||
{
|
||||
Token = new JwtSecurityTokenHandler().WriteToken(token),
|
||||
Expiration = token.ValidTo
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user