diff --git a/Wordle.Api/Wordle.Api/Controllers/TokenController.cs b/Wordle.Api/Wordle.Api/Controllers/TokenController.cs index 98429e39..e552acf4 100644 --- a/Wordle.Api/Wordle.Api/Controllers/TokenController.cs +++ b/Wordle.Api/Wordle.Api/Controllers/TokenController.cs @@ -58,7 +58,7 @@ public async Task GetToken([FromBody] UserCredentialsDto userCred new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new("userId", user.Id.ToString()), new("userName", user.UserName!.ToString().Substring(0,user.UserName.ToString().IndexOf("@"))), // Use the email as the username, but get rid of the email domain - new("MyThing", "Thing"), + new(Claims.Random, (new Random()).NextDouble().ToString()) }; // Retrieve all roles associated with the user diff --git a/Wordle.Api/Wordle.Api/Controllers/WordController.cs b/Wordle.Api/Wordle.Api/Controllers/WordController.cs index 8bcac50b..87b0eacd 100644 --- a/Wordle.Api/Wordle.Api/Controllers/WordController.cs +++ b/Wordle.Api/Wordle.Api/Controllers/WordController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Wordle.Api.Identity; using Wordle.Api.Services; namespace Wordle.Api.Controllers; @@ -26,13 +27,22 @@ public async Task GetWordOfDay(double offsetInHours = -7.0) return await wordOfTheDayService.GetWordOfTheDay(today); } - [Authorize] + [Authorize(Roles = Roles.Admin)] [HttpGet("WordOfTheDayHint")] public async Task GetWordOfDayHint(double offsetInHours = -7.0) { DateOnly today = DateOnly.FromDateTime(DateTime.UtcNow.AddHours(offsetInHours)); var wordOfTheDay = await wordOfTheDayService.GetWordOfTheDay(today); - return wordOfTheDay.Substring(0, 1) + "___" + wordOfTheDay.Substring(4,1); + return wordOfTheDay.Substring(0, 1) + "___" + wordOfTheDay.Substring(4, 1); } + + [HttpGet("SecuredRandomWord")] + [Authorize(Policy = Policies.RandomAdmin)] + public async Task GetSecuredRandomWord() + { + var randomWord = await wordOfTheDayService.GetRandomWord("sec"); + return randomWord.Text; + } + } diff --git a/Wordle.Api/Wordle.Api/Identity/Claims.cs b/Wordle.Api/Wordle.Api/Identity/Claims.cs new file mode 100644 index 00000000..6c06bbf4 --- /dev/null +++ b/Wordle.Api/Wordle.Api/Identity/Claims.cs @@ -0,0 +1,8 @@ +namespace Wordle.Api.Identity; + +public static class Claims +{ + public const string Random = "Random"; + public const string BirthDate = "BirthDate"; +} + diff --git a/Wordle.Api/Wordle.Api/Identity/IdentitySeed.cs b/Wordle.Api/Wordle.Api/Identity/IdentitySeed.cs index c334eb7f..53a7435e 100644 --- a/Wordle.Api/Wordle.Api/Identity/IdentitySeed.cs +++ b/Wordle.Api/Wordle.Api/Identity/IdentitySeed.cs @@ -23,6 +23,11 @@ private static async Task SeedRolesAsync(RoleManager roleManager) { await roleManager.CreateAsync(new IdentityRole(Roles.Admin)); } + // Seed Roles + if (!await roleManager.RoleExistsAsync(Roles.Awesome)) + { + await roleManager.CreateAsync(new IdentityRole(Roles.Awesome)); + } } private static async Task SeedAdminUserAsync(UserManager userManager) @@ -43,5 +48,21 @@ private static async Task SeedAdminUserAsync(UserManager userManager) await userManager.AddToRoleAsync(user, Roles.Admin); } } + + if (await userManager.FindByEmailAsync("Awesome@intellitect.com") == null) + { + AppUser user = new AppUser + { + UserName = "Awesome@intellitect.com", + Email = "Awesome@intellitect.com" + }; + + IdentityResult result = userManager.CreateAsync(user, "P@ssw0rd123").Result; + + if (result.Succeeded) + { + await userManager.AddToRoleAsync(user, Roles.Awesome); + } + } } } \ No newline at end of file diff --git a/Wordle.Api/Wordle.Api/Identity/Policies.cs b/Wordle.Api/Wordle.Api/Identity/Policies.cs new file mode 100644 index 00000000..c8432d3b --- /dev/null +++ b/Wordle.Api/Wordle.Api/Identity/Policies.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Authorization; +using System.Security.Claims; +using System.ComponentModel; + +namespace Wordle.Api.Identity; +public static class Policies +{ + public const string RandomAdmin = "RandomAdmin"; + + public static void RandomAdminPolicy(AuthorizationPolicyBuilder policy) + { + policy.RequireRole(Roles.Admin); + policy.RequireAssertion(context => + { + var random = context.User.Claims.FirstOrDefault(c => c.Type == Claims.Random); + if (Double.TryParse(random?.Value, out double result)) + { + return result > 0.5; + } + return false; + }); + } + +} \ No newline at end of file diff --git a/Wordle.Api/Wordle.Api/Identity/Roles.cs b/Wordle.Api/Wordle.Api/Identity/Roles.cs index 6ca22e9f..be1a2c3d 100644 --- a/Wordle.Api/Wordle.Api/Identity/Roles.cs +++ b/Wordle.Api/Wordle.Api/Identity/Roles.cs @@ -2,4 +2,5 @@ public static class Roles { public const string Admin = "Admin"; + public const string Awesome = "Awesome"; } \ No newline at end of file diff --git a/Wordle.Api/Wordle.Api/Models/Word.cs b/Wordle.Api/Wordle.Api/Models/Word.cs index 2f4e5abd..3e78e400 100644 --- a/Wordle.Api/Wordle.Api/Models/Word.cs +++ b/Wordle.Api/Wordle.Api/Models/Word.cs @@ -1,6 +1,8 @@ using System.ComponentModel.DataAnnotations; namespace Wordle.Api.Models; + + public class Word { public int WordId { get; set; } diff --git a/Wordle.Api/Wordle.Api/Program.cs b/Wordle.Api/Wordle.Api/Program.cs index 12e1efdc..d196997b 100644 --- a/Wordle.Api/Wordle.Api/Program.cs +++ b/Wordle.Api/Wordle.Api/Program.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; using System; using System.Text; using Wordle.Api; @@ -32,7 +33,33 @@ builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +builder.Services.AddSwaggerGen(config => + { + config.SwaggerDoc("v1", new OpenApiInfo { Title = "Wordle API", Version = "v1" }); + config.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.Http, + Scheme = "Bearer" + }); + config.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new List() + } + }); + } +); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -62,6 +89,17 @@ } ); +//Add Policies +builder.Services.AddAuthorization(options => +{ + options.AddPolicy(Policies.RandomAdmin, Policies.RandomAdminPolicy); + //options.AddPolicy("IsGrantPolicy", policy => policy.RequireRole("Grant")); + //options.AddPolicy(Policies.EditWord, Policies.EditWordPolicy); +}); + + + + var app = builder.Build(); using (var scope = app.Services.CreateScope()) diff --git a/Wordle.Api/Wordle.Api/Services/WordOfTheDayService.cs b/Wordle.Api/Wordle.Api/Services/WordOfTheDayService.cs index 73fed039..5aeb5300 100644 --- a/Wordle.Api/Wordle.Api/Services/WordOfTheDayService.cs +++ b/Wordle.Api/Wordle.Api/Services/WordOfTheDayService.cs @@ -15,14 +15,17 @@ public WordOfTheDayService(WordleDbContext db) Db = db; } - public async Task GetRandomWord() + public async Task GetRandomWord(string containtsText = "") { - var numberOfWords = await Db.Words.CountAsync(); + var numberOfWords = await Db.Words.CountAsync(f=>f.Text.Contains(containtsText)); Random random = new(); int randomIndex = random.Next(numberOfWords); - return await Db.Words.Skip(randomIndex).FirstAsync(); + return await Db.Words + .Where(f=>f.Text.Contains(containtsText)) + .Skip(randomIndex) + .FirstAsync(); } public async Task GetWordOfTheDay(DateOnly date)