From 39862dfab6f861de7938a48394ae9d464ec1e6bf Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 14 Nov 2024 20:19:53 -0600 Subject: [PATCH] refine role auth --- .../Users/IUserService.cs | 2 +- .../Users/Models/UserAuthorization.cs | 19 +++++++++- .../Services/AgentService.DeleteAgent.cs | 5 ++- .../Services/AgentService.UpdateAgent.cs | 6 ++- .../Users/Services/UserService.cs | 37 ++++++++++--------- .../Controllers/AgentController.cs | 32 +++++----------- .../ViewModels/Agents/AgentViewModel.cs | 2 + .../Repository/MongoRepository.User.cs | 9 ++--- 8 files changed, 60 insertions(+), 52 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index ea20307a..abeb06d9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -10,7 +10,7 @@ public interface IUserService Task> GetUsers(UserFilter filter); Task GetUserDetails(string userId); Task IsAuthorizedUser(string userId); - Task GetUserAuthorizations(string? agentId = null); + Task GetUserAuthorizations(IEnumerable? agentIds = null); Task UpdateUser(User user, bool isUpdateUserAgents = false); Task CreateUser(User user); Task ActiveUser(UserActivationModel model); diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserAuthorization.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserAuthorization.cs index bf4ff025..56df6a7d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserAuthorization.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/UserAuthorization.cs @@ -4,5 +4,22 @@ public class UserAuthorization { public bool IsAdmin { get; set; } public IEnumerable Permissions { get; set; } = []; - public IEnumerable AgentActions { get; set; } = []; + public IEnumerable AgentActions { get; set; } = []; } + + +public static class UserAuthorizationExtension +{ + public static bool IsAgentActionAllowed(this UserAuthorization auth, string agentId, string targetAction) + { + if (auth == null || string.IsNullOrEmpty(agentId)) return false; + + if (auth.IsAdmin) return true; + + var found = auth.AgentActions.FirstOrDefault(x => x.AgentId == agentId); + if (found == null) return false; + + var actions = found.Actions ?? []; + return actions.Any(x => x == targetAction); + } +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.DeleteAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.DeleteAgent.cs index e8cd7573..6783bf91 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.DeleteAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.DeleteAgent.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Users.Enums; +using BotSharp.Abstraction.Users.Models; namespace BotSharp.Core.Agents.Services; @@ -7,9 +8,9 @@ public partial class AgentService public async Task DeleteAgent(string id) { var userService = _services.GetRequiredService(); - var auth = await userService.GetUserAuthorizations(id); + var auth = await userService.GetUserAuthorizations(new List { id }); - if (auth.IsAdmin || auth.AgentActions.Contains(UserAction.Edit)) + if (!auth.IsAgentActionAllowed(id, UserAction.Edit)) { return false; } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index c6b2a743..18afb27f 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -1,6 +1,7 @@ using BotSharp.Abstraction.Repositories.Enums; using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Users.Enums; +using BotSharp.Abstraction.Users.Models; using System.IO; namespace BotSharp.Core.Agents.Services; @@ -12,9 +13,10 @@ public partial class AgentService if (agent == null || string.IsNullOrEmpty(agent.Id)) return; var userService = _services.GetRequiredService(); - var auth = await userService.GetUserAuthorizations(agent.Id); + var auth = await userService.GetUserAuthorizations(new List { agent.Id }); + var allowEdit = auth.IsAgentActionAllowed(agent.Id, UserAction.Edit); - if (!auth.IsAdmin && !auth.AgentActions.Contains(UserAction.Edit)) + if (!allowEdit) { return; } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 94cf845c..e07d1be4 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -414,7 +414,7 @@ public class UserService : IUserService return user != null && UserConstant.AdminRoles.Contains(user.Role); } - public async Task GetUserAuthorizations(string? agentId = null) + public async Task GetUserAuthorizations(IEnumerable? agentIds = null) { var db = _services.GetRequiredService(); var user = db.GetUserById(_user.Id); @@ -422,32 +422,33 @@ public class UserService : IUserService if (user == null) return auth; - var permissions = user.Permissions; - - var role = db.GetRoles(new RoleFilter { Names = [ user.Role ] }).FirstOrDefault(); - if (role != null && !permissions.Any()) - { - permissions = role.Permissions ?? []; - } - auth.IsAdmin = UserConstant.AdminRoles.Contains(user.Role); + + var role = db.GetRoles(new RoleFilter { Names = [user.Role] }).FirstOrDefault(); + var permissions = user.Permissions?.Any() == true ? user.Permissions : role?.Permissions ?? []; auth.Permissions = permissions; - if (string.IsNullOrEmpty(agentId)) + if (agentIds == null || !agentIds.Any()) { return auth; } - var userAgent = db.GetUserDetails(user.Id)?.AgentActions?.FirstOrDefault(x => x.AgentId == agentId); - var actions = userAgent?.Actions ?? []; + var userAgents = db.GetUserDetails(user.Id)?.AgentActions? + .Where(x => agentIds.Contains(x.AgentId) && x.Actions.Any())?.Select(x => new UserAgent + { + AgentId = x.AgentId, + Actions = x.Actions + }).ToList() ?? []; - if (role != null && !actions.Any()) - { - var roleAgent = db.GetRoleDetails(role.Id)?.AgentActions?.FirstOrDefault(x => x.AgentId == agentId); - actions = roleAgent?.Actions ?? []; - } + var userAgentIds = userAgents.Select(x => x.AgentId).ToList(); + var roleAgents = db.GetRoleDetails(role?.Id)?.AgentActions? + .Where(x => !userAgentIds.Contains(x.AgentId))?.Select(x => new UserAgent + { + AgentId = x.AgentId, + Actions = x.Actions + })?.ToList() ?? []; - auth.AgentActions = actions; + auth.AgentActions = userAgents.Concat(roleAgents); return auth; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index 06cfc77e..5ce68c2a 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Users.Enums; namespace BotSharp.OpenAPI.Controllers; @@ -59,10 +58,12 @@ public class AgentController : ControllerBase } var userService = _services.GetRequiredService(); - var auth = await userService.GetUserAuthorizations(targetAgent.Id); + var auth = await userService.GetUserAuthorizations(new List { targetAgent.Id }); - targetAgent.Editable = auth.IsAdmin || auth.AgentActions.Contains(UserAction.Edit); - targetAgent.Chatable = auth.IsAdmin || auth.AgentActions.Contains(UserAction.Chat); + targetAgent.Editable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Edit); + targetAgent.Chatable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Chat); + targetAgent.Trainable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Train); + targetAgent.Evaluable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Evaluate); return targetAgent; } @@ -85,27 +86,14 @@ public class AgentController : ControllerBase }; } - var userAgents = new List(); - var user = await userService.GetUser(_user.Id); - if (!UserConstant.AdminRoles.Contains(user.Role)) - { - userAgents = await _agentService.GetUserAgents(user.Id); - } - + var auth = await userService.GetUserAuthorizations(pagedAgents.Items.Select(x => x.Id)); agents = pagedAgents?.Items?.Select(x => { - var chatable = true; - var editable = true; - if (!UserConstant.AdminRoles.Contains(user.Role)) - { - var actions = userAgents.FirstOrDefault(a => a.AgentId == x.Id)?.Actions ?? []; - chatable = actions.Contains(UserAction.Chat); - editable = actions.Contains(UserAction.Edit); - } - var model = AgentViewModel.FromAgent(x); - model.Editable = editable; - model.Chatable = chatable; + model.Editable = auth.IsAgentActionAllowed(x.Id, UserAction.Edit); + model.Chatable = auth.IsAgentActionAllowed(x.Id, UserAction.Chat); + model.Trainable = auth.IsAgentActionAllowed(x.Id, UserAction.Train); + model.Evaluable = auth.IsAgentActionAllowed(x.Id, UserAction.Evaluate); return model; })?.ToList() ?? []; diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index b368cde0..ca814177 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -48,6 +48,8 @@ public class AgentViewModel public bool Editable { get; set; } public bool Chatable { get; set; } + public bool Trainable { get; set; } + public bool Evaluable { get; set; } [JsonPropertyName("created_datetime")] public DateTime CreatedDateTime { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs index 374d2ab0..cb857aa0 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs @@ -43,22 +43,19 @@ public partial class MongoRepository public User? GetUserById(string id) { - var user = _dc.Users.AsQueryable() - .FirstOrDefault(x => x.Id == id || (x.ExternalId != null && x.ExternalId == id)); + var user = _dc.Users.AsQueryable().FirstOrDefault(x => x.Id == id || (x.ExternalId != null && x.ExternalId == id)); return user != null ? user.ToUser() : null; } public List GetUserByIds(List ids) { - var users = _dc.Users.AsQueryable() - .Where(x => ids.Contains(x.Id) || (x.ExternalId != null && ids.Contains(x.ExternalId))).ToList(); + var users = _dc.Users.AsQueryable().Where(x => ids.Contains(x.Id) || (x.ExternalId != null && ids.Contains(x.ExternalId))).ToList(); return users?.Any() == true ? users.Select(x => x.ToUser()).ToList() : new List(); } public List GetUsersByAffiliateId(string affiliateId) { - var users = _dc.Users.AsQueryable() - .Where(x => x.AffiliateId == affiliateId).ToList(); + var users = _dc.Users.AsQueryable().Where(x => x.AffiliateId == affiliateId).ToList(); return users?.Any() == true ? users.Select(x => x.ToUser()).ToList() : new List(); }