From b4800efb6dd5a44c43da0fa279a52ce11693ccf3 Mon Sep 17 00:00:00 2001 From: "jason.wang" Date: Sat, 19 Oct 2024 17:16:55 +0800 Subject: [PATCH 1/6] add affiliate employee --- .../Repositories/IBotSharpRepository.cs | 2 +- .../Users/IUserIdentity.cs | 3 ++ .../Users/IUserService.cs | 1 + .../BotSharp.Abstraction/Users/Models/User.cs | 1 + .../FileRepository/FileRepository.User.cs | 4 +- .../Users/Services/UserIdentity.cs | 9 ++++ .../Users/Services/UserService.cs | 54 ++++++++++++------- .../Repository/MongoRepository.User.cs | 8 +-- 8 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index de351649..abb144e4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -24,7 +24,7 @@ public interface IBotSharpRepository User? GetAffiliateUserByPhone(string phone) => throw new NotImplementedException(); User? GetUserById(string id) => throw new NotImplementedException(); List GetUserByIds(List ids) => throw new NotImplementedException(); - User? GetUserByAffiliateId(string affiliateId) => throw new NotImplementedException(); + List GetUsersByAffiliateId(string affiliateId) => throw new NotImplementedException(); User? GetUserByUserName(string userName) => throw new NotImplementedException(); void CreateUser(User user) => throw new NotImplementedException(); void UpdateUserVerified(string userId) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs index 347e7554..2c51b7b0 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs @@ -11,4 +11,7 @@ public interface IUserIdentity string? UserLanguage { get; } string? Phone { get; } string? AffiliateId { get; } + string? EmployeeId { get; } + string Type { get; } + string Role { get; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index 5a3315b7..1572771f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -9,6 +9,7 @@ public interface IUserService Task CreateUser(User user); Task ActiveUser(UserActivationModel model); Task GetAffiliateToken(string authorization); + Task GetAdminToken(string authorization); Task GetToken(string authorization); Task GetMyProfile(); Task VerifyUserNameExisting(string userName); diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs index cb48701f..69e41eca 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs @@ -22,6 +22,7 @@ public class User public string? VerificationCode { get; set; } public bool Verified { get; set; } public string? AffiliateId { get; set; } + public string? EmployeeId { get; set; } public bool IsDisabled { get; set; } public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; public DateTime CreatedTime { get; set; } = DateTime.UtcNow; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs index c8eb28dc..42d56d98 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs @@ -31,9 +31,9 @@ public partial class FileRepository return Users.Where(x => ids.Contains(x.Id) || (x.ExternalId != null && ids.Contains(x.ExternalId)))?.ToList() ?? new List(); } - public User? GetUserByAffiliateId(string affiliateId) + public List GetUsersByAffiliateId(string affiliateId) { - return Users.FirstOrDefault(x => x.AffiliateId == affiliateId); + return Users.Where(x => x.AffiliateId == affiliateId).ToList(); } public User? GetUserByUserName(string userName = null) diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs index 531fd286..9b7615af 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs @@ -72,4 +72,13 @@ public class UserIdentity : IUserIdentity [JsonPropertyName("affiliateId")] public string? AffiliateId => _claims?.FirstOrDefault(x => x.Type == "affiliateId")?.Value; + + [JsonPropertyName("employeeId")] + public string? EmployeeId => _claims?.FirstOrDefault(x => x.Type == "employeeId")?.Value; + + [JsonPropertyName("type")] + public string? Type => _claims?.FirstOrDefault(x => x.Type == "type")?.Value; + + [JsonPropertyName("role")] + public string? Role => _claims?.FirstOrDefault(x => x.Type == "role")?.Value; } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index bc6fc317..ef5a3d77 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -106,19 +106,14 @@ public class UserService : IUserService return true; } - public async Task GetAffiliateToken(string authorization) + public async Task GetAffiliateToken(string authorization) { var base64 = Encoding.UTF8.GetString(Convert.FromBase64String(authorization)); var (id, password) = base64.SplitAsTuple(":"); var db = _services.GetRequiredService(); var record = db.GetAffiliateUserByPhone(id); - if (record == null) - { - record = db.GetUserByPhone(id); - } - - var isCanLoginAffiliateRoleType = record != null && !record.IsDisabled && record.Type != UserType.Client; - if (!isCanLoginAffiliateRoleType) + var isCanLogin = record != null && !record.IsDisabled && record.Type == UserType.Affiliate; + if (!isCanLogin) { return default; } @@ -128,6 +123,35 @@ public class UserService : IUserService return default; } + return await Task.FromResult(BuildToken(record)); + } + + public async Task GetAdminToken(string authorization) + { + var base64 = Encoding.UTF8.GetString(Convert.FromBase64String(authorization)); + var (id, password) = base64.SplitAsTuple(":"); + var db = _services.GetRequiredService(); + var record = db.GetUserByPhone(id); + var isCanLogin = record != null && !record.IsDisabled + && record.Type == UserType.Internal && new List + { + UserRole.Root,UserRole.Admin + }.Contains(record.Role); + if (!isCanLogin) + { + return default; + } + + if (Utilities.HashTextMd5($"{password}{record.Salt}") != record.Password) + { + return default; + } + + return await Task.FromResult(BuildToken(record)); + } + + private Token BuildToken(User record) + { var accessToken = GenerateJwtToken(record); var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken); var token = new Token @@ -225,16 +249,7 @@ public class UserService : IUserService return default; } - var accessToken = GenerateJwtToken(record); - var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken); - var token = new Token - { - AccessToken = accessToken, - ExpireTime = jwt.Payload.Exp.Value, - TokenType = "Bearer", - Scope = "api" - }; - + var token = BuildToken(record); foreach (var hook in hooks) { hook.BeforeSending(token); @@ -258,7 +273,8 @@ public class UserService : IUserService new Claim("role", user.Role ?? UserRole.User), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim("phone", user.Phone ?? string.Empty), - new Claim("affiliateId", user.AffiliateId ?? string.Empty) + new Claim("affiliateId", user.AffiliateId ?? string.Empty), + new Claim("employeeId", user.EmployeeId ?? string.Empty) }; var validators = _services.GetServices(); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs index 9b6b62c1..f9e1a466 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs @@ -37,11 +37,11 @@ public partial class MongoRepository return users?.Any() == true ? users.Select(x => x.ToUser()).ToList() : new List(); } - public User? GetUserByAffiliateId(string affiliateId) + public List GetUsersByAffiliateId(string affiliateId) { - var user = _dc.Users.AsQueryable() - .FirstOrDefault(x => x.AffiliateId == affiliateId); - return user != null ? user.ToUser() : null; + var users = _dc.Users.AsQueryable() + .Where(x => x.AffiliateId == affiliateId).ToList(); + return users?.Any() == true ? users.Select(x => x.ToUser()).ToList() : new List(); } public User? GetUserByUserName(string userName) From 0623f3dbefc83c19229b797594890b80b2389856 Mon Sep 17 00:00:00 2001 From: "jason.wang" Date: Sat, 19 Oct 2024 18:49:18 +0800 Subject: [PATCH 2/6] fix issue by self test --- .../BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs | 3 ++- .../Repository/MongoRepository.User.cs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs index ffcd71a2..8526788b 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs @@ -19,9 +19,10 @@ public class UserDocument : MongoBase public string? VerificationCode { get; set; } public bool Verified { get; set; } public string? AffiliateId { get; set; } + public string? EmployeeId { get; set; } public bool IsDisabled { get; set; } public DateTime CreatedTime { get; set; } - public DateTime UpdatedTime { get; set; } + public DateTime UpdatedTime { get; set; } public User ToUser() { diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs index f9e1a466..ffdc78e6 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs @@ -71,6 +71,7 @@ public partial class MongoRepository VerificationCode = user.VerificationCode, Verified = user.Verified, AffiliateId = user.AffiliateId, + EmployeeId = user.EmployeeId, IsDisabled = user.IsDisabled, CreatedTime = DateTime.UtcNow, UpdatedTime = DateTime.UtcNow From a16b1e435881a86de0bb8bb9eca7209878bfbf74 Mon Sep 17 00:00:00 2001 From: "jason.wang" Date: Sun, 20 Oct 2024 13:02:13 +0800 Subject: [PATCH 3/6] fix issue by self test --- .../BotSharp.Core/Users/Services/UserIdentity.cs | 2 +- .../BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs index 9b7615af..9ee2238b 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs @@ -80,5 +80,5 @@ public class UserIdentity : IUserIdentity public string? Type => _claims?.FirstOrDefault(x => x.Type == "type")?.Value; [JsonPropertyName("role")] - public string? Role => _claims?.FirstOrDefault(x => x.Type == "role")?.Value; + public string? Role => _claims?.FirstOrDefault(x => x.Type == ClaimTypes.Role)?.Value; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs index 8526788b..a3d84e7d 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs @@ -22,7 +22,7 @@ public class UserDocument : MongoBase public string? EmployeeId { get; set; } public bool IsDisabled { get; set; } public DateTime CreatedTime { get; set; } - public DateTime UpdatedTime { get; set; } + public DateTime UpdatedTime { get; set; } public User ToUser() { @@ -41,6 +41,7 @@ public class UserDocument : MongoBase Type = Type, Role = Role, AffiliateId = AffiliateId, + EmployeeId = EmployeeId, IsDisabled = IsDisabled, VerificationCode = VerificationCode, Verified = Verified, From d591241efb875676d930c9c3ffb572d51b37f9f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Mon, 21 Oct 2024 17:05:36 +0800 Subject: [PATCH 4/6] support assistant message playload Assign payload when invoking function --- .../BotSharp.Core/Routing/RoutingService.InvokeFunction.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs index 8051b8b0..499c8981 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs @@ -53,6 +53,7 @@ public partial class RoutingService message.PostbackFunctionName = clonedMessage.PostbackFunctionName; message.CurrentAgentId = clonedMessage.CurrentAgentId; message.Content = clonedMessage.Content; + message.Payload = clonedMessage.Payload; message.StopCompletion = clonedMessage.StopCompletion; message.RichContent = clonedMessage.RichContent; message.Data = clonedMessage.Data; From 41709c95dddc9cf3a84bee106ee93feea40dcd67 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 21 Oct 2024 11:29:04 -0500 Subject: [PATCH 5/6] Fix compile issue. --- .../BotSharp.Core/Users/Services/UserService.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 9c1c8723..4e7c1248 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -161,7 +161,9 @@ public class UserService : IUserService return default; } - return await Task.FromResult(BuildToken(record)); + var (token, jwt) = BuildToken(record); + + return await Task.FromResult(token); } public async Task GetAdminToken(string authorization) @@ -185,10 +187,12 @@ public class UserService : IUserService return default; } - return await Task.FromResult(BuildToken(record)); + var (token, jwt) = BuildToken(record); + + return await Task.FromResult(token); } - private Token BuildToken(User record) + private (Token, JwtSecurityToken) BuildToken(User record) { var accessToken = GenerateJwtToken(record); var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken); @@ -199,7 +203,7 @@ public class UserService : IUserService TokenType = "Bearer", Scope = "api" }; - return token; + return (token, jwt); } public async Task GetToken(string authorization) @@ -287,7 +291,7 @@ public class UserService : IUserService return default; } - var token = BuildToken(record); + var (token, jwt) = BuildToken(record); foreach (var hook in hooks) { hook.UserAuthenticated(jwt); From f5d153f7d5af619d6682b50a6e7b9a76ff3e0eed Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 21 Oct 2024 14:46:09 -0500 Subject: [PATCH 6/6] add redis cache folder --- .../Infrastructures/SharpCacheAttribute.cs | 2 +- .../Users/Services/UserService.cs | 2 +- .../Filters/UserSingleLoginFilter.cs | 116 +++++++++--------- 3 files changed, 59 insertions(+), 61 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheAttribute.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheAttribute.cs index dd30f491..c8b29ecd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheAttribute.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheAttribute.cs @@ -71,7 +71,7 @@ public class SharpCacheAttribute : MoAttribute private string GetCacheKey(SharpCacheSettings settings, MethodContext context) { - var key = settings.Prefix + "-" + context.Method.Name; + var key = settings.Prefix + ":" + context.Method.Name; foreach (var arg in context.Arguments) { if (arg is null) diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 4e7c1248..e565a9b5 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -359,7 +359,7 @@ public class UserService : IUserService private string GetUserTokenExpiresCacheKey(string userId) { - return $"user_{userId}_token_expires"; + return $"user:{userId}_token_expires"; } public async Task GetUserTokenExpires() diff --git a/src/Infrastructure/BotSharp.OpenAPI/Filters/UserSingleLoginFilter.cs b/src/Infrastructure/BotSharp.OpenAPI/Filters/UserSingleLoginFilter.cs index e8db0801..dda71f48 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Filters/UserSingleLoginFilter.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Filters/UserSingleLoginFilter.cs @@ -1,80 +1,78 @@ using BotSharp.Abstraction.Users.Settings; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Configuration; using Microsoft.Net.Http.Headers; using System.IdentityModel.Tokens.Jwt; -namespace BotSharp.OpenAPI.Filters -{ - public class UserSingleLoginFilter : IAuthorizationFilter - { - private readonly IUserService _userService; - private readonly IServiceProvider _services; +namespace BotSharp.OpenAPI.Filters; - public UserSingleLoginFilter(IUserService userService, IServiceProvider services) +public class UserSingleLoginFilter : IAuthorizationFilter +{ + private readonly IUserService _userService; + private readonly IServiceProvider _services; + + public UserSingleLoginFilter(IUserService userService, IServiceProvider services) + { + _userService = userService; + _services = services; + } + + public void OnAuthorization(AuthorizationFilterContext context) + { + var isAllowAnonymous = context.ActionDescriptor.EndpointMetadata + .Any(em => em.GetType() == typeof(AllowAnonymousAttribute)); + + if (isAllowAnonymous) { - _userService = userService; - _services = services; + return; } - public void OnAuthorization(AuthorizationFilterContext context) + var bearerToken = GetBearerToken(context); + if (!string.IsNullOrWhiteSpace(bearerToken)) { - var isAllowAnonymous = context.ActionDescriptor.EndpointMetadata - .Any(em => em.GetType() == typeof(AllowAnonymousAttribute)); + var config = _services.GetRequiredService(); + var token = GetJwtToken(bearerToken); - if (isAllowAnonymous) + if (config.AllowMultipleDeviceLoginUserIds.Contains(token.Claims.First(x => x.Type == "nameid").Value)) { return; } - var bearerToken = GetBearerToken(context); - if (!string.IsNullOrWhiteSpace(bearerToken)) + var validTo = token.ValidTo.ToLongTimeString(); + var currentExpires = GetUserExpires().ToLongTimeString(); + + if (validTo != currentExpires) { - var config = _services.GetRequiredService(); - var token = GetJwtToken(bearerToken); - - if (config.AllowMultipleDeviceLoginUserIds.Contains(token.Claims.First(x => x.Type == "nameid").Value)) - { - return; - } - - var validTo = token.ValidTo.ToLongTimeString(); - var currentExpires = GetUserExpires().ToLongTimeString(); - - if (validTo != currentExpires) - { - Serilog.Log.Warning($"Token expired. Token expires at {validTo}, current expires at {currentExpires}"); - // login confict - context.Result = new ConflictResult(); - } + Serilog.Log.Warning($"Token expired. Token expires at {validTo}, current expires at {currentExpires}"); + // login confict + context.Result = new ConflictResult(); } } - - private string GetBearerToken(AuthorizationFilterContext context) - { - if (context.HttpContext.Request.Headers.TryGetValue(HeaderNames.Authorization, out var bearerToken) - && !string.IsNullOrWhiteSpace(bearerToken.ToString())) - { - var tokenType = bearerToken.ToString().Split(" ").First(); - if (tokenType == JwtBearerDefaults.AuthenticationScheme) - { - return bearerToken.ToString().Split(" ").Last(); - } - } - return null; - } - - private JwtSecurityToken GetJwtToken(string jwtToken) - { - var handler = new JwtSecurityTokenHandler(); - var token = handler.ReadJwtToken(jwtToken); - return token; - } - - private DateTime GetUserExpires() - { - return _userService.GetUserTokenExpires().GetAwaiter().GetResult(); - } + } + + private string GetBearerToken(AuthorizationFilterContext context) + { + if (context.HttpContext.Request.Headers.TryGetValue(HeaderNames.Authorization, out var bearerToken) + && !string.IsNullOrWhiteSpace(bearerToken.ToString())) + { + var tokenType = bearerToken.ToString().Split(" ").First(); + if (tokenType == JwtBearerDefaults.AuthenticationScheme) + { + return bearerToken.ToString().Split(" ").Last(); + } + } + return null; + } + + private JwtSecurityToken GetJwtToken(string jwtToken) + { + var handler = new JwtSecurityTokenHandler(); + var token = handler.ReadJwtToken(jwtToken); + return token; + } + + private DateTime GetUserExpires() + { + return _userService.GetUserTokenExpires().GetAwaiter().GetResult(); } }