From f1c01f411d7a54753f931d27ec269b4cf617cba7 Mon Sep 17 00:00:00 2001 From: mikebutrico Date: Sat, 16 Sep 2023 12:23:08 +0200 Subject: [PATCH 1/3] added ClaimsPrincipal to HasReachedPluginQuota to check email of current user against a whitelist set in the properties files --- Api/Controllers/PluginController.cs | 4 ++-- Api/Program.cs | 9 ++++++++- Application/Plugins/AdminWhitelist.cs | 12 ++++++++++++ Application/Plugins/IPluginRepository.cs | 3 ++- Application/Plugins/PluginRepository.cs | 19 ++++++++++++++----- Application/common/IBaseRepository.cs | 3 ++- 6 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 Application/Plugins/AdminWhitelist.cs diff --git a/Api/Controllers/PluginController.cs b/Api/Controllers/PluginController.cs index 1d60456..7a29a66 100644 --- a/Api/Controllers/PluginController.cs +++ b/Api/Controllers/PluginController.cs @@ -31,12 +31,12 @@ public async Task> CreatePlugin([FromBody] PluginCreateRequ { string userId = GetUserId(); - if (await pluginRepository.HasReachedPluginQuota(userId)) + if (await pluginRepository.HasReachedPluginQuota(userId,User)) return BadRequest("Max plugins reached"); var plugin = mapper.Map(request); plugin.UserId = userId; - var createdPlugin = await pluginRepository.Add(plugin, userId); + var createdPlugin = await pluginRepository.Add(plugin, userId,User); return CreatedAtAction(nameof(CreatePlugin), new { userId = createdPlugin.UserId, pluginId = createdPlugin.Id }, createdPlugin); } diff --git a/Api/Program.cs b/Api/Program.cs index 6ace094..a10ce5f 100644 --- a/Api/Program.cs +++ b/Api/Program.cs @@ -79,7 +79,7 @@ static void AddServices(WebApplicationBuilder builder, string version) var contactUrl = builder.Configuration.GetValue("ContactLogicApp:Url"); ArgumentNullException.ThrowIfNull(contactUrl); builder.Services.AddSingleton(new ContactSetting() { Url = contactUrl }); - + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -116,6 +116,13 @@ void AddConfigrations(WebApplicationBuilder builder) var gptSettings = new GPTSettings(); builder.Configuration.GetSection("GPTSettings").Bind(gptSettings); builder.Services.AddSingleton(gptSettings); + var AdminEmails = builder.Configuration.GetSection("AdminEmail").Get>(); + if (AdminEmails != null) + { + var adminWhitelist = new AdminWhitelist(); + adminWhitelist.emails = AdminEmails; + builder.Services.AddSingleton(adminWhitelist); + } builder.Services.AddSingleton(x => x.GetRequiredService().GetValue("FrontendDomain")!); //make this an obj } \ No newline at end of file diff --git a/Application/Plugins/AdminWhitelist.cs b/Application/Plugins/AdminWhitelist.cs new file mode 100644 index 0000000..f181c4f --- /dev/null +++ b/Application/Plugins/AdminWhitelist.cs @@ -0,0 +1,12 @@ +public class AdminWhitelist +{ + public List? emails { get; set; } + public bool Contains(string email) + { + if (email == null) + { + return false; + } + return this.emails != null && this.emails.Any(e => string.Equals(e, email, StringComparison.OrdinalIgnoreCase)); + } +} diff --git a/Application/Plugins/IPluginRepository.cs b/Application/Plugins/IPluginRepository.cs index 4fd59a9..f72d379 100644 --- a/Application/Plugins/IPluginRepository.cs +++ b/Application/Plugins/IPluginRepository.cs @@ -1,3 +1,4 @@ +using System.Security.Claims; using AiPlugin.Application.common; using AiPlugin.Domain.Plugin; @@ -6,5 +7,5 @@ namespace AiPlugin.Application.Plugins; public interface IPluginRepository : IBaseRepository { public Task> GetByUserId(string userid, CancellationToken cancellationToken = default); - public Task HasReachedPluginQuota(string userId); + public Task HasReachedPluginQuota(string userId, ClaimsPrincipal? user = null); } \ No newline at end of file diff --git a/Application/Plugins/PluginRepository.cs b/Application/Plugins/PluginRepository.cs index 0d1aee4..99dfafe 100644 --- a/Application/Plugins/PluginRepository.cs +++ b/Application/Plugins/PluginRepository.cs @@ -1,25 +1,28 @@ -using AiPlugin.Application.Plugins; +using System.Security.Claims; using System.Text.RegularExpressions; using AiPlugin.Domain.Plugin; using AiPlugin.Infrastructure; using Microsoft.EntityFrameworkCore; + namespace AiPlugin.Application.Plugins; public class PluginRepository : IPluginRepository { private readonly AiPluginDbContext dbContext; private readonly SubscriptionRepository subscriptionRepository; + private readonly AdminWhitelist adminWhitelist; - public PluginRepository(AiPluginDbContext dbContext, SubscriptionRepository subscriptionRepository) + public PluginRepository(AiPluginDbContext dbContext, SubscriptionRepository subscriptionRepository, AdminWhitelist adminWhitelist) { this.dbContext = dbContext; this.subscriptionRepository = subscriptionRepository; + this.adminWhitelist = adminWhitelist; } - public async Task Add(Plugin entity, string userId, CancellationToken cancellationToken = default) + public async Task Add(Plugin entity, string userId, ClaimsPrincipal? user = null, CancellationToken cancellationToken = default) { CheckPlugin(entity); - if (await HasReachedPluginQuota(userId)) + if (await HasReachedPluginQuota(userId, user)) { throw new Exception("Max plugins reached"); } @@ -70,8 +73,14 @@ public async Task Delete(Guid id, CancellationToken cancellationToken = default) await dbContext.SaveChangesAsync(cancellationToken); } - public async Task HasReachedPluginQuota(string userId) + public async Task HasReachedPluginQuota(string userId, ClaimsPrincipal? user = null) { + var userEmail = user?.FindFirst(ClaimTypes.Email)?.ToString(); + if (userEmail != null && adminWhitelist.Contains(userEmail)) + { + return false; + } + var isPremium = await subscriptionRepository.IsUserPremium(userId); return (await dbContext diff --git a/Application/common/IBaseRepository.cs b/Application/common/IBaseRepository.cs index 216705b..7f63f02 100644 --- a/Application/common/IBaseRepository.cs +++ b/Application/common/IBaseRepository.cs @@ -1,3 +1,4 @@ +using System.Security.Claims; using AiPlugin.Domain.Common; using AiPlugin.Domain.Plugin; @@ -5,7 +6,7 @@ namespace AiPlugin.Application.common; public interface IBaseRepository where T : EntityBase { - public Task Add(T entity, string userId, CancellationToken cancellationToken = default); + public Task Add(T entity, string userId,ClaimsPrincipal? user = null, CancellationToken cancellationToken = default); public Task Get(Guid id, CancellationToken cancellationToken = default); public Task Update(T entity, CancellationToken cancellationToken = default); public Task Delete(Guid id, CancellationToken cancellationToken = default); From afb28c87673d9711ab57c74e9f3c4ab08cc5dc50 Mon Sep 17 00:00:00 2001 From: mikebutrico Date: Sat, 16 Sep 2023 19:19:33 +0200 Subject: [PATCH 2/3] fixed max number of plugins being returned incorrectly for admins and consolidated the max plugins code in a function --- Api/Controllers/PluginController.cs | 2 +- Application/Plugins/IPluginRepository.cs | 1 + Application/Plugins/PluginRepository.cs | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Api/Controllers/PluginController.cs b/Api/Controllers/PluginController.cs index 7a29a66..4c9a34b 100644 --- a/Api/Controllers/PluginController.cs +++ b/Api/Controllers/PluginController.cs @@ -48,7 +48,7 @@ public async Task> GetPlugins() var plugins = await pluginRepository.GetByUserId(userId); var result = mapper.Map(plugins); - result.MaxPlugins = await subscriptionRepository.IsUserPremium(userId) ? 3 : 1; + result.MaxPlugins = await pluginRepository.maxPlugins(userId, User); return Ok(result); } diff --git a/Application/Plugins/IPluginRepository.cs b/Application/Plugins/IPluginRepository.cs index f72d379..5d68418 100644 --- a/Application/Plugins/IPluginRepository.cs +++ b/Application/Plugins/IPluginRepository.cs @@ -8,4 +8,5 @@ public interface IPluginRepository : IBaseRepository { public Task> GetByUserId(string userid, CancellationToken cancellationToken = default); public Task HasReachedPluginQuota(string userId, ClaimsPrincipal? user = null); + public Task maxPlugins(string userId, ClaimsPrincipal? user = null); } \ No newline at end of file diff --git a/Application/Plugins/PluginRepository.cs b/Application/Plugins/PluginRepository.cs index 99dfafe..b7d9691 100644 --- a/Application/Plugins/PluginRepository.cs +++ b/Application/Plugins/PluginRepository.cs @@ -75,20 +75,28 @@ public async Task Delete(Guid id, CancellationToken cancellationToken = default) public async Task HasReachedPluginQuota(string userId, ClaimsPrincipal? user = null) { - var userEmail = user?.FindFirst(ClaimTypes.Email)?.ToString(); + var userEmail = user?.FindFirst(ClaimTypes.Email)?.Value; if (userEmail != null && adminWhitelist.Contains(userEmail)) { return false; } - - var isPremium = await subscriptionRepository.IsUserPremium(userId); - + return (await dbContext .Plugins .Include(x => x.Sections) .Where(x => !x.isDeleted) .CountAsync(x => x.UserId == userId) - ) >= (isPremium ? 3 : 1); + ) >= await maxPlugins(userId,user); + } + public async Task maxPlugins(string userId, ClaimsPrincipal? user = null) + { + var userEmail = user?.FindFirst(ClaimTypes.Email)?.Value; + if (userEmail != null && adminWhitelist.Contains(userEmail)) + { + return 10000; + } + var isPremium = await subscriptionRepository.IsUserPremium(userId); + return isPremium ? 3 : 1; } private void CheckPlugin(Plugin entity) { From ac8f9b903636fbc66f8ea3ad5898cf455762843e Mon Sep 17 00:00:00 2001 From: mikebutrico Date: Sun, 17 Sep 2023 17:33:26 +0200 Subject: [PATCH 3/3] removed userId from pluginRepository.Add and passed the full user info instead, --- Api/Controllers/PluginController.cs | 4 ++-- Application/Plugins/IPluginRepository.cs | 2 +- Application/Plugins/PluginRepository.cs | 11 ++++++----- Application/common/IBaseRepository.cs | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Api/Controllers/PluginController.cs b/Api/Controllers/PluginController.cs index 4c9a34b..1e03444 100644 --- a/Api/Controllers/PluginController.cs +++ b/Api/Controllers/PluginController.cs @@ -31,12 +31,12 @@ public async Task> CreatePlugin([FromBody] PluginCreateRequ { string userId = GetUserId(); - if (await pluginRepository.HasReachedPluginQuota(userId,User)) + if (await pluginRepository.HasReachedPluginQuota(User)) return BadRequest("Max plugins reached"); var plugin = mapper.Map(request); plugin.UserId = userId; - var createdPlugin = await pluginRepository.Add(plugin, userId,User); + var createdPlugin = await pluginRepository.Add(plugin,User); return CreatedAtAction(nameof(CreatePlugin), new { userId = createdPlugin.UserId, pluginId = createdPlugin.Id }, createdPlugin); } diff --git a/Application/Plugins/IPluginRepository.cs b/Application/Plugins/IPluginRepository.cs index 5d68418..8d087ea 100644 --- a/Application/Plugins/IPluginRepository.cs +++ b/Application/Plugins/IPluginRepository.cs @@ -7,6 +7,6 @@ namespace AiPlugin.Application.Plugins; public interface IPluginRepository : IBaseRepository { public Task> GetByUserId(string userid, CancellationToken cancellationToken = default); - public Task HasReachedPluginQuota(string userId, ClaimsPrincipal? user = null); + public Task HasReachedPluginQuota(ClaimsPrincipal user); public Task maxPlugins(string userId, ClaimsPrincipal? user = null); } \ No newline at end of file diff --git a/Application/Plugins/PluginRepository.cs b/Application/Plugins/PluginRepository.cs index b7d9691..fa69b98 100644 --- a/Application/Plugins/PluginRepository.cs +++ b/Application/Plugins/PluginRepository.cs @@ -19,10 +19,10 @@ public PluginRepository(AiPluginDbContext dbContext, SubscriptionRepository subs this.adminWhitelist = adminWhitelist; } - public async Task Add(Plugin entity, string userId, ClaimsPrincipal? user = null, CancellationToken cancellationToken = default) + public async Task Add(Plugin entity, ClaimsPrincipal user, CancellationToken cancellationToken = default) { CheckPlugin(entity); - if (await HasReachedPluginQuota(userId, user)) + if (await HasReachedPluginQuota(user)) { throw new Exception("Max plugins reached"); } @@ -73,20 +73,21 @@ public async Task Delete(Guid id, CancellationToken cancellationToken = default) await dbContext.SaveChangesAsync(cancellationToken); } - public async Task HasReachedPluginQuota(string userId, ClaimsPrincipal? user = null) + public async Task HasReachedPluginQuota(ClaimsPrincipal user) { + var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? throw new UnauthorizedAccessException("UserId not found"); var userEmail = user?.FindFirst(ClaimTypes.Email)?.Value; if (userEmail != null && adminWhitelist.Contains(userEmail)) { return false; } - + return (await dbContext .Plugins .Include(x => x.Sections) .Where(x => !x.isDeleted) .CountAsync(x => x.UserId == userId) - ) >= await maxPlugins(userId,user); + ) >= await maxPlugins(userId, user); } public async Task maxPlugins(string userId, ClaimsPrincipal? user = null) { diff --git a/Application/common/IBaseRepository.cs b/Application/common/IBaseRepository.cs index 7f63f02..02ab195 100644 --- a/Application/common/IBaseRepository.cs +++ b/Application/common/IBaseRepository.cs @@ -6,7 +6,7 @@ namespace AiPlugin.Application.common; public interface IBaseRepository where T : EntityBase { - public Task Add(T entity, string userId,ClaimsPrincipal? user = null, CancellationToken cancellationToken = default); + public Task Add(T entity,ClaimsPrincipal user, CancellationToken cancellationToken = default); public Task Get(Guid id, CancellationToken cancellationToken = default); public Task Update(T entity, CancellationToken cancellationToken = default); public Task Delete(Guid id, CancellationToken cancellationToken = default);