using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System.Linq; using System.Threading.Tasks; using Timeline.Entities; using Timeline.Models; namespace Timeline.Services { public class CreateTokenResult { public string Token { get; set; } public UserInfo UserInfo { get; set; } } public enum CreateUserResult { Success, AlreadyExists } public interface IUserService { /// /// Try to anthenticate with the given username and password. /// If success, create a token and return the user info. /// /// The username of the user to be anthenticated. /// The password of the user to be anthenticated. /// Return null if anthentication failed. An containing the created token and user info if anthentication succeeded. Task CreateToken(string username, string password); /// /// Verify the given token. /// If success, return the user info. /// /// The token to verify. /// Return null if verification failed. The user info if verification succeeded. Task VerifyToken(string token); Task CreateUser(string username, string password, string[] roles); } public class UserService : IUserService { private readonly ILogger _logger; private readonly DatabaseContext _databaseContext; private readonly IJwtService _jwtService; private readonly IPasswordService _passwordService; public UserService(ILogger logger, DatabaseContext databaseContext, IJwtService jwtService, IPasswordService passwordService) { _logger = logger; _databaseContext = databaseContext; _jwtService = jwtService; _passwordService = passwordService; } public async Task CreateToken(string username, string password) { var users = _databaseContext.Users.ToList(); var user = await _databaseContext.Users.Where(u => u.Name == username).SingleOrDefaultAsync(); if (user == null) { _logger.LogInformation($"Create token failed with invalid username. Username = {username} Password = {password} ."); return null; } var verifyResult = _passwordService.VerifyPassword(user.EncryptedPassword, password); if (verifyResult) { var userInfo = new UserInfo(user); return new CreateTokenResult { Token = _jwtService.GenerateJwtToken(user.Id, userInfo.Roles), UserInfo = userInfo }; } else { _logger.LogInformation($"Create token failed with invalid password. Username = {username} Password = {password} ."); return null; } } public async Task VerifyToken(string token) { var userId = _jwtService.VerifyJwtToken(token); if (userId == null) { _logger.LogInformation($"Verify token falied. Reason: invalid token. Token: {token} ."); return null; } var user = await _databaseContext.Users.Where(u => u.Id == userId.Value).SingleOrDefaultAsync(); if (user == null) { _logger.LogInformation($"Verify token falied. Reason: invalid user id. UserId: {userId} Token: {token} ."); return null; } return new UserInfo(user); } public async Task CreateUser(string username, string password, string[] roles) { var exists = (await _databaseContext.Users.Where(u => u.Name == username).ToListAsync()).Count != 0; if (exists) { return CreateUserResult.AlreadyExists; } await _databaseContext.Users.AddAsync(new User { Name = username, EncryptedPassword = _passwordService.HashPassword(password), RoleString = string.Join(',', roles) }); await _databaseContext.SaveChangesAsync(); return CreateUserResult.Success; } } }