181 lines
5.2 KiB
C#
181 lines
5.2 KiB
C#
namespace AAIntegration.SimmonsBank.API.Services;
|
|
|
|
using AutoMapper;
|
|
using BCrypt.Net;
|
|
using AAIntegration.SimmonsBank.API.Entities;
|
|
using AAIntegration.SimmonsBank.API.Config;
|
|
using AAIntegration.SimmonsBank.API.Models.Users;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Microsoft.EntityFrameworkCore.Internal;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using System.Text;
|
|
using System.IdentityModel.Tokens.Jwt;
|
|
using System.Security.Claims;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
public interface IUserService
|
|
{
|
|
AuthenticateResponse Authenticate(AuthenticateRequest model);
|
|
void Register(RegisterRequest model);
|
|
IEnumerable<User> GetAll();
|
|
User GetById(int id);
|
|
void Update(int id, UserUpdateRequest model);
|
|
void Delete(int id);
|
|
Dictionary<string, int> GetAllApiKeys();
|
|
string GetUserApiKey(int id);
|
|
void InvalidateApiKey(string apiKey);
|
|
string CreateUserApiKey(int id);
|
|
}
|
|
|
|
public class UserService : IUserService
|
|
{
|
|
private DataContext _context;
|
|
private readonly IMapper _mapper;
|
|
private readonly IApiKeyService _apiKeyService;
|
|
private readonly IOptions<AppSettings> _appSettings;
|
|
|
|
public UserService(
|
|
DataContext context,
|
|
IMapper mapper,
|
|
IApiKeyService apiKeyService,
|
|
IOptions<AppSettings> appSettings)
|
|
{
|
|
_apiKeyService = apiKeyService;
|
|
_context = context;
|
|
_mapper = mapper;
|
|
_appSettings = appSettings;
|
|
}
|
|
|
|
public AuthenticateResponse Authenticate(AuthenticateRequest model)
|
|
{
|
|
var user = _context.Users.SingleOrDefault(x => x.Username == model.Username);
|
|
|
|
// validate
|
|
if (user == null || !BCrypt.Verify(model.Password, user.PasswordHash))
|
|
throw new AppException("Username or password is incorrect");
|
|
|
|
// Authentication successful
|
|
|
|
// Create Claims
|
|
var claims = new List<Claim>
|
|
{
|
|
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
|
new Claim(ClaimTypes.Name, user.Username),
|
|
};
|
|
|
|
// Create JWT Token
|
|
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.Value.Secret));
|
|
var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256Signature);
|
|
var tokeOptions = new JwtSecurityToken(
|
|
issuer: _appSettings.Value.APIUrl,
|
|
audience: _appSettings.Value.APIUrl,
|
|
claims: claims,
|
|
expires: DateTime.Now.AddDays(7),
|
|
signingCredentials: signinCredentials
|
|
);
|
|
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
|
|
|
|
var response = _mapper.Map<AuthenticateResponse>(user);
|
|
response.Token = tokenString;
|
|
return response;
|
|
}
|
|
|
|
public void Register(RegisterRequest model)
|
|
{
|
|
// validate username
|
|
if (_context.Users.Any(x => x.Username == model.Username))
|
|
throw new AppException("Username '" + model.Username + "' is already taken");
|
|
|
|
// validate email
|
|
if (_context.Users.Any(x => x.Email == model.Email))
|
|
throw new AppException("Email '" + model.Email + "' is already taken");
|
|
|
|
// map model to new user object
|
|
var user = _mapper.Map<User>(model);
|
|
|
|
// hash password
|
|
user.PasswordHash = BCrypt.HashPassword(model.Password);
|
|
|
|
// save user
|
|
_context.Users.Add(user);
|
|
_context.SaveChanges();
|
|
}
|
|
|
|
public IEnumerable<User> GetAll()
|
|
{
|
|
return _context.Users;
|
|
}
|
|
|
|
public User GetById(int id)
|
|
{
|
|
return getUser(id);
|
|
}
|
|
|
|
public void Update(int id, UserUpdateRequest model)
|
|
{
|
|
var user = getUser(id);
|
|
|
|
// validate
|
|
if (model.Email != user.Email && _context.Users.Any(x => x.Email == model.Email))
|
|
throw new AppException("User with the email '" + model.Email + "' already exists");
|
|
|
|
// hash password if it was entered
|
|
if (!string.IsNullOrEmpty(model.Password))
|
|
user.PasswordHash = BCrypt.HashPassword(model.Password);
|
|
|
|
// copy model to user and save
|
|
_mapper.Map(model, user);
|
|
_context.Users.Update(user);
|
|
_context.SaveChanges();
|
|
}
|
|
|
|
public void Delete(int id)
|
|
{
|
|
var user = getUser(id);
|
|
_context.Users.Remove(user);
|
|
_context.SaveChanges();
|
|
}
|
|
|
|
public string CreateUserApiKey(int id)
|
|
{
|
|
var user = getUser(id);
|
|
|
|
user.ApiKey = _apiKeyService.GenerateApiKey();
|
|
|
|
_context.Users.Update(user);
|
|
_context.SaveChanges();
|
|
|
|
return user.ApiKey;
|
|
}
|
|
|
|
public Dictionary<string, int> GetAllApiKeys()
|
|
{
|
|
return _context.Users
|
|
.Where(u => u.ApiKey != null)
|
|
.ToDictionary(u => u.ApiKey, u => u.Id);
|
|
}
|
|
|
|
public string GetUserApiKey(int id)
|
|
{
|
|
return this.getUser(id).ApiKey;
|
|
}
|
|
|
|
public void InvalidateApiKey(string apiKey)
|
|
{
|
|
User? user = _context.Users.FirstOrDefault(u => u.ApiKey == apiKey);
|
|
|
|
if (user is not null)
|
|
user.ApiKey = null;
|
|
}
|
|
|
|
// helper methods
|
|
|
|
private User getUser(int id)
|
|
{
|
|
var user = _context.Users.Find(id);
|
|
if (user == null) throw new KeyNotFoundException("User not found");
|
|
return user;
|
|
}
|
|
} |