diff --git a/src/GameIdeas/GameIdeas.Shared/Exceptions/EnvironmentVariableMissingException.cs b/src/GameIdeas/GameIdeas.Shared/Exceptions/EnvironmentVariableMissingException.cs new file mode 100644 index 0000000..50b7746 --- /dev/null +++ b/src/GameIdeas/GameIdeas.Shared/Exceptions/EnvironmentVariableMissingException.cs @@ -0,0 +1,3 @@ +namespace GameIdeas.Shared.Exceptions; + +public class EnvironmentVariableMissingException(string message) : Exception(message); \ No newline at end of file diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Extensions/ServiceCollectionExtension.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Extensions/ServiceCollectionExtension.cs new file mode 100644 index 0000000..8714909 --- /dev/null +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Extensions/ServiceCollectionExtension.cs @@ -0,0 +1,47 @@ +using GameIdeas.Shared.Exceptions; +using GameIdeas.Shared.Options; + +namespace GameIdeas.WebAPI.Extensions; + +public static class ServiceCollectionExtension +{ + public static IServiceCollection AddGameIdeasOptions(this IServiceCollection services) + { +#if DEBUG + var dictionary = LoadEnvironmentVariable("../../../../.env"); +#else + var dictionary = LoadEnvironmentVariable("../.env"); +#endif + + services.Configure(options => + { + options.DbHost = GetEnvVar("DB_HOST", dictionary); + options.DbUsername = GetEnvVar("DB_USERNAME", dictionary); + options.DbPassword = GetEnvVar("DB_PASSWORD", dictionary); + options.DbDatabase = GetEnvVar("DB_DATABASE", dictionary); + options.JwtKey = GetEnvVar("JWT_KEY", dictionary); + options.JwtIssuer = GetEnvVar("JWT_ISSUER", dictionary); + options.JwtAudience = GetEnvVar("JWT_AUDIENCE", dictionary); + }); + + return services; + } + + private static string GetEnvVar(string name, Dictionary dictionary) + { + return Environment.GetEnvironmentVariable(name) + ?? dictionary.GetValueOrDefault(name) + ?? throw new EnvironmentVariableMissingException($"Missing environment variable with key: {name}"); + } + + private static Dictionary LoadEnvironmentVariable(string filePath) + { + if (!File.Exists(filePath)) + return []; + + return File.ReadAllLines(filePath) + .Select(line => line.Split('=', StringSplitOptions.RemoveEmptyEntries)) + .Where(parts => parts.Length == 2) + .ToDictionary(parts => parts[0], parts => parts[1]); + } +} \ No newline at end of file diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/GameIdeas.WebAPI.csproj b/src/GameIdeas/Server/GameIdeas.WebAPI/GameIdeas.WebAPI.csproj index f776445..32e89b5 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/GameIdeas.WebAPI.csproj +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/GameIdeas.WebAPI.csproj @@ -28,8 +28,4 @@ - - - - diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Program.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Program.cs index 449dd0b..a691143 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Program.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Program.cs @@ -10,37 +10,18 @@ using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using System.Text; +using GameIdeas.WebAPI.Extensions; var builder = WebApplication.CreateBuilder(args); var services = builder.Services; -#if DEBUG -LoadEnvironmentVariable("../../../../.env"); -#else -LoadEnvironmentVariable("../.env"); -#endif - -Action dbContextOptions = options => -{ - options.UseNpgsql( - GetConnectionString(), - npgOption => - { - npgOption.CommandTimeout(60); - npgOption.MigrationsAssembly("GameIdeas.WebAPI"); - }); -}; - -// Add services to the container. -services.AddDbContext(dbContextOptions); +services.AddGameIdeasOptions(); +services.AddDbContext(ContextOptions); services.AddIdentity() .AddEntityFrameworkStores() .AddDefaultTokenProviders(); -var jwtKey = Environment.GetEnvironmentVariable("JWT_KEY") - ?? throw new ArgumentNullException(message: "Invalid key for JWT token", null); - services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; @@ -86,7 +67,6 @@ services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); services.AddControllers(); -// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi services.AddOpenApi(); services.AddCors(option => option.AddDefaultPolicy(policy => @@ -118,6 +98,16 @@ app.UseAuthorization(); app.MapControllers(); app.Run(); +return; + +void ContextOptions(DbContextOptionsBuilder options) +{ + options.UseNpgsql(GetConnectionString(), npgOption => + { + npgOption.CommandTimeout(60); + npgOption.MigrationsAssembly("GameIdeas.WebAPI"); + }); +} async Task LoadTranslations() { @@ -145,21 +135,3 @@ string GetConnectionString() return $"Host={host};Username={login};Password={pass};Database={database}"; } - -static void LoadEnvironmentVariable(string filePath) -{ - if (!File.Exists(filePath)) - return; - - foreach (var line in File.ReadAllLines(filePath)) - { - var parts = line.Split( - '=', - StringSplitOptions.RemoveEmptyEntries); - - if (parts.Length != 2) - continue; - - Environment.SetEnvironmentVariable(parts[0], parts[1]); - } -} diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserReadService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserReadService.cs index 6b53fff..8dd196e 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserReadService.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserReadService.cs @@ -11,12 +11,15 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using GameIdeas.Shared.Exceptions; +using GameIdeas.Shared.Options; +using Microsoft.Extensions.Options; namespace GameIdeas.WebAPI.Services.Users; public class UserReadService( UserManager userManager, GameIdeasContext context, + IOptions options, IMapper mapper) : IUserReadService { public async Task> GetRoles() @@ -125,14 +128,11 @@ public class UserReadService( 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 authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.Value.JwtKey)); var token = new JwtSecurityToken( - issuer: Environment.GetEnvironmentVariable("JWT_ISSUER"), - audience: Environment.GetEnvironmentVariable("JWT_AUDIENCE"), + issuer: options.Value.JwtIssuer, + audience: options.Value.JwtAudience, expires: DateTime.Now.AddHours(GlobalConstants.JWT_DURATION_HOUR), claims: authClaims, signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)