void.cat/VoidCat/Services/Users/UserManager.cs

136 lines
4.2 KiB
C#
Raw Normal View History

2022-02-21 22:35:06 +00:00
using VoidCat.Model;
2022-09-08 09:41:31 +00:00
using VoidCat.Model.User;
2022-02-21 22:35:06 +00:00
using VoidCat.Services.Abstractions;
2022-09-08 09:41:31 +00:00
using VoidCat.Services.Users.Auth;
2022-02-21 22:35:06 +00:00
namespace VoidCat.Services.Users;
2022-09-08 09:41:31 +00:00
public class UserManager
2022-02-21 22:35:06 +00:00
{
private readonly IUserStore _store;
private readonly IEmailVerification _emailVerification;
2022-09-08 09:41:31 +00:00
private readonly IUserAuthTokenStore _tokenStore;
private readonly OAuthFactory _oAuthFactory;
2022-02-24 12:00:28 +00:00
private static bool _checkFirstRegister;
2022-02-21 22:35:06 +00:00
2022-09-08 09:41:31 +00:00
public UserManager(IUserStore store, IEmailVerification emailVerification, OAuthFactory oAuthFactory,
IUserAuthTokenStore tokenStore)
2022-02-21 22:35:06 +00:00
{
_store = store;
_emailVerification = emailVerification;
2022-09-08 09:41:31 +00:00
_oAuthFactory = oAuthFactory;
_tokenStore = tokenStore;
2022-02-21 22:35:06 +00:00
}
2022-02-24 12:00:28 +00:00
2022-09-08 09:41:31 +00:00
/// <summary>
/// Login an existing user with email/password
/// </summary>
/// <param name="email"></param>
/// <param name="password"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async ValueTask<InternalUser> Login(string email, string password)
2022-02-21 22:35:06 +00:00
{
var userId = await _store.LookupUser(email);
if (!userId.HasValue) throw new InvalidOperationException("User does not exist");
2022-06-08 16:17:53 +00:00
var user = await _store.GetPrivate(userId.Value);
2022-02-21 22:35:06 +00:00
if (!(user?.CheckPassword(password) ?? false)) throw new InvalidOperationException("User does not exist");
2022-02-24 12:00:28 +00:00
2022-09-08 09:41:31 +00:00
await HandleLogin(user);
2022-02-21 22:35:06 +00:00
return user;
}
2022-02-24 12:00:28 +00:00
2022-09-08 09:41:31 +00:00
/// <summary>
/// Register a new internal user with email/password
/// </summary>
/// <param name="email"></param>
/// <param name="password"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async ValueTask<InternalUser> Register(string email, string password)
2022-02-21 22:35:06 +00:00
{
var existingUser = await _store.LookupUser(email);
2022-06-08 16:17:53 +00:00
if (existingUser != Guid.Empty && existingUser != null)
throw new InvalidOperationException("User already exists");
2022-02-21 22:35:06 +00:00
2022-09-08 09:41:31 +00:00
var newUser = new InternalUser
2022-02-24 12:00:28 +00:00
{
2022-06-08 16:17:53 +00:00
Id = Guid.NewGuid(),
Email = email,
Password = password.HashPassword(),
2022-02-24 12:00:28 +00:00
Created = DateTimeOffset.UtcNow,
LastLogin = DateTimeOffset.UtcNow
};
2022-09-08 09:41:31 +00:00
await SetupNewUser(newUser);
return newUser;
}
/// <summary>
/// Start OAuth2 authorization flow
/// </summary>
/// <param name="provider"></param>
/// <returns></returns>
public Uri Authorize(string provider)
{
var px = _oAuthFactory.GetProvider(provider);
return px.Authorize();
}
/// <summary>
/// Login or Register with OAuth2 auth code
/// </summary>
/// <param name="code"></param>
/// <param name="provider"></param>
/// <returns></returns>
public async ValueTask<InternalUser> LoginOrRegister(string code, string provider)
{
var px = _oAuthFactory.GetProvider(provider);
var token = await px.GetToken(code);
var user = await px.GetUserDetails(token);
if (user == default)
{
throw new InvalidOperationException($"Could not load user profile from provider: {provider}");
}
var uid = await _store.LookupUser(user.Email);
if (uid.HasValue)
{
var existingUser = await _store.GetPrivate(uid.Value);
if (existingUser?.AuthType == AuthType.OAuth2)
{
return existingUser;
}
throw new InvalidOperationException("Auth failure, user type does not match!");
}
await SetupNewUser(user);
return user;
}
private async Task SetupNewUser(InternalUser newUser)
{
2022-02-24 12:00:28 +00:00
// automatically set first user to admin
if (!_checkFirstRegister)
{
_checkFirstRegister = true;
var users = await _store.ListUsers(new(0, 1));
if (users.TotalResults == 0)
{
newUser.Roles.Add(Roles.Admin);
}
}
2022-06-06 21:51:25 +00:00
2022-03-07 13:38:53 +00:00
await _store.Set(newUser.Id, newUser);
await _emailVerification.SendNewCode(newUser);
2022-09-08 09:41:31 +00:00
}
private async Task HandleLogin(InternalUser user)
{
user.LastLogin = DateTimeOffset.UtcNow;
await _store.UpdateLastLogin(user.Id, DateTime.UtcNow);
2022-02-21 22:35:06 +00:00
}
2022-06-06 21:51:25 +00:00
}