BotSharp/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs

204 lines
6.3 KiB
C#
Raw Normal View History

2023-08-26 04:41:01 +00:00
using BotSharp.Abstraction.Repositories;
2023-06-11 23:46:02 +00:00
using BotSharp.Abstraction.Users.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
2024-01-31 15:15:41 +00:00
using NanoidDotNet;
2023-06-11 23:46:02 +00:00
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
namespace BotSharp.Core.Users.Services;
public class UserService : IUserService
{
private readonly IServiceProvider _services;
2023-06-17 02:42:35 +00:00
private readonly IUserIdentity _user;
2024-01-31 19:57:54 +00:00
private readonly ILogger _logger;
2023-06-11 23:46:02 +00:00
2024-01-31 19:57:54 +00:00
public UserService(IServiceProvider services, IUserIdentity user, ILogger<UserService> logger)
2023-06-11 23:46:02 +00:00
{
_services = services;
_user = user;
2024-01-31 19:57:54 +00:00
_logger = logger;
2023-06-11 23:46:02 +00:00
}
public async Task<User> CreateUser(User user)
{
2024-01-31 19:57:54 +00:00
if (string.IsNullOrEmpty(user.UserName))
{
// generate unique name
var name = user.Email.Split("@").First() + "-" + Nanoid.Generate("0123456789botsharp", 6);
user.UserName = name;
}
else
{
user.UserName = user.UserName.ToLower();
}
2023-08-10 04:53:22 +00:00
var db = _services.GetRequiredService<IBotSharpRepository>();
2024-01-31 19:57:54 +00:00
var record = db.GetUserByUserName(user.UserName);
2023-06-11 23:46:02 +00:00
if (record != null)
{
2023-09-07 22:04:34 +00:00
return record;
2023-06-11 23:46:02 +00:00
}
2024-01-31 19:57:54 +00:00
if (string.IsNullOrEmpty(user.Id))
2024-01-31 15:15:41 +00:00
{
2024-01-31 19:57:54 +00:00
user.Id = Guid.NewGuid().ToString();
2024-01-31 15:15:41 +00:00
}
2024-01-31 19:57:54 +00:00
record = user;
2024-02-14 23:45:28 +00:00
record.Email = user.Email?.ToLower();
2023-06-11 23:46:02 +00:00
record.Salt = Guid.NewGuid().ToString("N");
record.Password = Utilities.HashText(user.Password, record.Salt);
2023-09-02 05:10:46 +00:00
db.CreateUser(record);
2024-01-05 03:20:50 +00:00
_logger.LogWarning($"Created new user account: {record.Id} {record.UserName}");
2024-01-05 03:20:50 +00:00
Utilities.ClearCache();
2023-09-07 22:04:34 +00:00
return record;
2023-06-11 23:46:02 +00:00
}
public async Task<Token> GetToken(string authorization)
{
var base64 = Encoding.UTF8.GetString(Convert.FromBase64String(authorization));
2024-01-31 19:57:54 +00:00
var (id, password) = base64.SplitAsTuple(":");
2023-06-11 23:46:02 +00:00
2023-08-10 04:53:22 +00:00
var db = _services.GetRequiredService<IBotSharpRepository>();
2024-01-31 19:57:54 +00:00
var record = id.Contains("@") ? db.GetUserByEmail(id) : db.GetUserByUserName(id);
2024-01-31 15:15:41 +00:00
if (record == null)
2024-01-31 19:57:54 +00:00
{
record = db.GetUserByUserName(id);
}
2024-02-05 16:00:28 +00:00
var hooks = _services.GetServices<IAuthenticationHook>();
2024-01-31 19:57:54 +00:00
if (record == null || record.Source != "internal")
2024-01-31 15:15:41 +00:00
{
// check 3rd party user
2024-02-05 16:00:28 +00:00
foreach (var hook in hooks)
2024-01-31 15:15:41 +00:00
{
2024-02-05 16:00:28 +00:00
var user = await hook.Authenticate(id, password);
2024-01-31 19:57:54 +00:00
if (user == null)
{
continue;
}
if (string.IsNullOrEmpty(user.Source) || user.Source == "internal")
{
_logger.LogError($"Please set source name in the Authenticate hook.");
return null;
}
if (record == null)
2024-01-31 15:15:41 +00:00
{
// create a local user record
record = new User
{
UserName = user.UserName,
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
Source = user.Source,
2024-01-31 19:57:54 +00:00
ExternalId = user.ExternalId,
Password = user.Password,
2024-01-31 15:15:41 +00:00
};
await CreateUser(record);
}
2024-01-31 19:57:54 +00:00
break;
2024-01-31 15:15:41 +00:00
}
}
2023-06-11 23:46:02 +00:00
if (record == null)
{
return default;
}
2024-03-20 14:23:48 +00:00
#if !DEBUG
2023-06-11 23:46:02 +00:00
if (Utilities.HashText(password, record.Salt) != record.Password)
{
return default;
}
2024-03-20 14:23:48 +00:00
#endif
2023-06-11 23:46:02 +00:00
var accessToken = GenerateJwtToken(record);
var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken);
2024-02-05 16:00:28 +00:00
var token = new Token
2023-06-11 23:46:02 +00:00
{
AccessToken = accessToken,
ExpireTime = jwt.Payload.Exp.Value,
TokenType = "Bearer",
Scope = "api"
};
2024-02-05 16:00:28 +00:00
foreach (var hook in hooks)
{
hook.BeforeSending(token);
}
return token;
2023-06-11 23:46:02 +00:00
}
2023-09-07 22:04:34 +00:00
private string GenerateJwtToken(User user)
2023-06-11 23:46:02 +00:00
{
2024-01-31 19:57:54 +00:00
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.NameId, user.Id),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
2024-03-24 01:31:15 +00:00
new Claim(JwtRegisteredClaimNames.GivenName, user?.FirstName ?? string.Empty),
new Claim(JwtRegisteredClaimNames.FamilyName, user?.LastName ?? string.Empty),
2024-01-31 19:57:54 +00:00
new Claim("source", user.Source),
2024-03-24 01:31:15 +00:00
new Claim("external_id", user.ExternalId ?? string.Empty),
2024-01-31 19:57:54 +00:00
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var validators = _services.GetServices<IAuthenticationHook>();
foreach (var validator in validators)
{
validator.AddClaims(claims);
}
2023-06-11 23:46:02 +00:00
var config = _services.GetRequiredService<IConfiguration>();
var issuer = config["Jwt:Issuer"];
var audience = config["Jwt:Audience"];
var key = Encoding.ASCII.GetBytes(config["Jwt:Key"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
2024-01-31 19:57:54 +00:00
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddHours(2),
2023-06-11 23:46:02 +00:00
Issuer = issuer,
Audience = audience,
2024-01-31 19:57:54 +00:00
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
2024-02-14 23:45:28 +00:00
SecurityAlgorithms.HmacSha256Signature)
2023-06-11 23:46:02 +00:00
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
2024-01-31 15:15:41 +00:00
[MemoryCache(10 * 60, perInstanceCache: true)]
2023-06-11 23:46:02 +00:00
public async Task<User> GetMyProfile()
{
2023-08-10 04:53:22 +00:00
var db = _services.GetRequiredService<IBotSharpRepository>();
2024-02-15 18:11:56 +00:00
User user = default;
if (_user.UserName != null)
{
user = db.GetUserByUserName(_user.UserName);
}
else if (_user.Email != null)
{
user = db.GetUserByEmail(_user.Email);
}
2023-11-14 01:25:25 +00:00
return user;
}
2023-11-15 13:33:46 +00:00
[MemoryCache(10 * 60)]
2023-11-14 01:25:25 +00:00
public async Task<User> GetUser(string id)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var user = db.GetUserById(id);
2023-06-11 23:46:02 +00:00
return user;
}
}