Skip to content
Snippets Groups Projects
Commit 5e438f7e authored by esantangelo's avatar esantangelo
Browse files

merge from develop

parents 021c3d26 fc6434f8
No related branches found
No related tags found
3 merge requests!20merge from develop,!18Feature/chat signalr,!16Feature/chat signalr
Showing
with 705 additions and 49 deletions
################################################################################ ################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio. # This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################ ################################################################################
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -19,9 +20,12 @@ namespace Tsi1.Api.Controllers ...@@ -19,9 +20,12 @@ namespace Tsi1.Api.Controllers
{ {
private readonly ICourseService _courseService; private readonly ICourseService _courseService;
public CourseController(ICourseService courseService) private readonly IFileService _fileService;
public CourseController(ICourseService courseService, IFileService fileService)
{ {
_courseService = courseService; _courseService = courseService;
_fileService = fileService;
} }
[Authorize(Roles = UserTypes.Student + ", " + UserTypes.Professor)] [Authorize(Roles = UserTypes.Student + ", " + UserTypes.Professor)]
...@@ -54,6 +58,10 @@ namespace Tsi1.Api.Controllers ...@@ -54,6 +58,10 @@ namespace Tsi1.Api.Controllers
return BadRequest(result.Message); return BadRequest(result.Message);
} }
var path = Path.Combine(tenantId.ToString(), result.Data.Id.ToString());
_fileService.CreateFolder(path);
return Ok(); return Ok();
} }
...@@ -65,11 +73,38 @@ namespace Tsi1.Api.Controllers ...@@ -65,11 +73,38 @@ namespace Tsi1.Api.Controllers
var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value); var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var result = await _courseService.Matriculate(userId, courseId); var result = await _courseService.Matriculate(userId, courseId);
if (result.HasError) if (result.HasError)
{ {
return BadRequest(result.Message); return BadRequest(result.Message);
} }
if (result.Data == false)
{
return NotFound(result.Message);
}
return Ok();
}
[Authorize(Roles = UserTypes.Student)]
[HttpPost("DropOutFromCourse/{courseId}")]
public async Task<IActionResult> DropOutFromCourse(int courseId)
{
var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var result = await _courseService.DropOutFromCourse(userId, courseId);
if (result.HasError)
{
return BadRequest(result.Message);
}
if (result.Data == false)
{
return NotFound(result.Message);
}
return Ok(); return Ok();
} }
...@@ -78,12 +113,112 @@ namespace Tsi1.Api.Controllers ...@@ -78,12 +113,112 @@ namespace Tsi1.Api.Controllers
public async Task<IActionResult> AddProfessorToCourse(ProfessorCourseDto professorCourseDto) public async Task<IActionResult> AddProfessorToCourse(ProfessorCourseDto professorCourseDto)
{ {
var result = await _courseService.AddProfessorToCourse(professorCourseDto); var result = await _courseService.AddProfessorToCourse(professorCourseDto);
if (result.HasError)
{
return BadRequest(result.Message);
}
if (result.Data == false)
{
return NotFound(result.Message);
}
return Ok();
}
[Authorize(Roles = UserTypes.FacultyAdmin)]
[HttpPost("RemoveProfessorToCourse")]
public async Task<IActionResult> RemoveProfessorFromCourse(ProfessorCourseDto professorCourseDto)
{
var result = await _courseService.RemoveProfessorToCourse(professorCourseDto);
if (result.HasError)
{
return BadRequest(result.Message);
}
if (result.Data == false)
{
return NotFound(result.Message);
}
return Ok();
}
[Authorize(Roles = UserTypes.Student + ", " + UserTypes.Professor + ", " + UserTypes.FacultyAdmin)]
[HttpGet("GetAll")]
public async Task<IActionResult> GetAll()
{
var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
var result = await _courseService.GetAll(tenantId);
if (result.HasError) if (result.HasError)
{ {
return BadRequest(result.Message); return BadRequest(result.Message);
} }
return Ok(result.Data);
}
[Authorize(Roles = UserTypes.UdelarAdmin)]
[HttpGet("GetAll/{tenantId}")]
public async Task<IActionResult> GetAll(int tenantId)
{
var result = await _courseService.GetAll(tenantId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok(result.Data);
}
[Authorize(Roles = UserTypes.Professor + ", " + UserTypes.FacultyAdmin)]
[HttpPut("Modify/{courseId}")]
public async Task<IActionResult> Modify(int courseId, CourseCreateDto courseDto)
{
var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
courseDto.TenantId = tenantId;
var result = await _courseService.Modify(courseId, courseDto);
if (result.HasError)
{
return BadRequest(result.Message);
}
if (result.Data == false)
{
return NotFound(result.Message);
}
return Ok();
}
[Authorize(Roles = UserTypes.FacultyAdmin)]
[HttpDelete("Delete/{courseId}")]
public async Task<IActionResult> Delete(int courseId)
{
var result = await _courseService.Delete(courseId);
if (result.HasError)
{
return BadRequest(result.Message);
}
if (result.Data == null)
{
return NotFound(result.Message);
}
var path = Path.Combine(result.Data.TenantId.ToString(), result.Data.Id.ToString());
_fileService.DeleteFolder(path);
return Ok(); return Ok();
} }
} }
} }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Tsi1.BusinessLayer.Helpers;
using Tsi1.BusinessLayer.Interfaces;
namespace Tsi1.Api.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class FileController : ControllerBase
{
private readonly IFileService _fileService;
public FileController(IFileService fileService)
{
_fileService = fileService;
}
[Authorize(Roles = UserTypes.Professor)]
[HttpPost("Create/{courseId}")]
public async Task<IActionResult> Create(IFormFile file, int courseId)
{
var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
var result = await _fileService.Create(file, tenantId, courseId);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok(result.Data);
}
}
}
...@@ -17,9 +17,12 @@ namespace Tsi1.Api.Controllers ...@@ -17,9 +17,12 @@ namespace Tsi1.Api.Controllers
{ {
private readonly ITenantService _tenantService; private readonly ITenantService _tenantService;
public TenantController(ITenantService tenantService) private readonly IFileService _fileService;
public TenantController(ITenantService tenantService, IFileService fileService)
{ {
_tenantService = tenantService; _tenantService = tenantService;
_fileService = fileService;
} }
[Authorize(Roles = UserTypes.UdelarAdmin)] [Authorize(Roles = UserTypes.UdelarAdmin)]
...@@ -47,6 +50,76 @@ namespace Tsi1.Api.Controllers ...@@ -47,6 +50,76 @@ namespace Tsi1.Api.Controllers
return BadRequest(result.Message); return BadRequest(result.Message);
} }
_fileService.CreateFolder(result.Data.Id.ToString());
return Ok(result.Data);
}
[Authorize(Roles = UserTypes.UdelarAdmin)]
[HttpPut("Modify/{tenantId}")]
public async Task<IActionResult> Modify(int tenantId, TenantCreateDto tenantDto)
{
var result = await _tenantService.Modify(tenantId, tenantDto);
if (result.HasError)
{
return BadRequest(result.Message);
}
if (result.Data == false)
{
return NotFound(result.Message);
}
return Ok();
}
[Authorize(Roles = UserTypes.UdelarAdmin)]
[HttpDelete("Delete/{tenantId}")]
public async Task<IActionResult> Delete(int tenantId)
{
var result = await _tenantService.Delete(tenantId);
if (result.HasError)
{
return BadRequest(result.Message);
}
if (result.Data == false)
{
return NotFound(result.Message);
}
_fileService.DeleteFolder(tenantId.ToString());
return Ok();
}
[Authorize(Roles = UserTypes.UdelarAdmin)]
[HttpGet("FacultyList")]
public async Task<IActionResult> FacultyList()
{
var result = await _tenantService.FacultyList();
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok(result.Data);
}
[Authorize(Roles = UserTypes.UdelarAdmin)]
[HttpGet("CourseList")]
public async Task<IActionResult> CourseList()
{
var result = await _tenantService.CourseList();
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok(result.Data); return Ok(result.Data);
} }
} }
......
...@@ -23,21 +23,27 @@ namespace Tsi1.Api.Controllers ...@@ -23,21 +23,27 @@ namespace Tsi1.Api.Controllers
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly IUserTypeService _userTypeService; private readonly IUserTypeService _userTypeService;
private readonly ITenantService _tenantService; private readonly ITenantService _tenantService;
private readonly IEmailService _emailService;
public UserController(IJwtAuthManager jwtAuthManager, IUserService userService,
IUserTypeService userTypeService, ITenantService tenantService) public UserController(
IJwtAuthManager jwtAuthManager,
IUserService userService,
IUserTypeService userTypeService,
ITenantService tenantService,
IEmailService emailService)
{ {
_jwtAuthManager = jwtAuthManager; _jwtAuthManager = jwtAuthManager;
_userService = userService; _userService = userService;
_userTypeService = userTypeService; _userTypeService = userTypeService;
_tenantService = tenantService; _tenantService = tenantService;
_emailService = emailService;
} }
[AllowAnonymous] [AllowAnonymous]
[HttpPost("Login")] [HttpPost("Login")]
public async Task<IActionResult> Login(LoginRequest request) public async Task<IActionResult> Login(LoginRequest request)
{ {
var resultSplit = request.UserName.Split("@"); var resultSplit = request.Username.Split("@");
if (resultSplit.Count() != 2) if (resultSplit.Count() != 2)
{ {
...@@ -45,9 +51,7 @@ namespace Tsi1.Api.Controllers ...@@ -45,9 +51,7 @@ namespace Tsi1.Api.Controllers
} }
var userName = resultSplit[0]; var userName = resultSplit[0];
var tenantName = resultSplit[1]; var tenantName = resultSplit[1];
var tenantId = await _tenantService.GetByName(tenantName); var tenantId = await _tenantService.GetByName(tenantName);
if (tenantId.HasError) if (tenantId.HasError)
...@@ -56,7 +60,6 @@ namespace Tsi1.Api.Controllers ...@@ -56,7 +60,6 @@ namespace Tsi1.Api.Controllers
} }
var result = await _userService.Authenticate(userName, request.Password, tenantId.Data); var result = await _userService.Authenticate(userName, request.Password, tenantId.Data);
if (result.HasError) if (result.HasError)
{ {
return BadRequest(result.Message); return BadRequest(result.Message);
...@@ -110,68 +113,178 @@ namespace Tsi1.Api.Controllers ...@@ -110,68 +113,178 @@ namespace Tsi1.Api.Controllers
} }
} }
[Authorize(Roles = UserTypes.FacultyAdmin)] [Authorize(Roles = UserTypes.FacultyAdmin + ", " + UserTypes.UdelarAdmin)]
[HttpPost("Register")] [HttpPost("Register")]
public async Task<IActionResult> Register(UserRegisterDto dto) public async Task<IActionResult> Register(UserRegisterDto dto, [FromQuery] int? tenantId = null)
{ {
var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value); var myUserType = HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Role).Value;
if (myUserType == UserTypes.UdelarAdmin && tenantId == null)
{
return BadRequest(string.Format(ErrorMessages.TenantDoesNotExist, tenantId));
}
if (myUserType == UserTypes.FacultyAdmin)
{
tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
}
var userTypeResult = await _userTypeService.GetById(dto.UserTypeId); var userTypeResult = await _userTypeService.GetById(dto.UserTypeId);
if (userTypeResult.HasError)
{
return BadRequest(userTypeResult.Message);
}
var userType = userTypeResult.Data;
if (myUserType == UserTypes.UdelarAdmin &&
(userType.Name == UserTypes.Student ||
userType.Name == UserTypes.Professor))
{
return BadRequest(string.Format(ErrorMessages.InvalidUserType, userType.Name));
}
if (myUserType == UserTypes.FacultyAdmin &&
(userType.Name == UserTypes.UdelarAdmin ||
userType.Name == UserTypes.FacultyAdmin))
{
return BadRequest(string.Format(ErrorMessages.InvalidUserType, userType.Name));
}
var userServiceResult = await _userService.Create(dto, userType.Name, (int) tenantId);
if (userServiceResult.HasError)
{
BadRequest(userServiceResult.Message);
}
return Ok();
}
[Authorize(Roles = UserTypes.FacultyAdmin + ", " + UserTypes.UdelarAdmin)]
[HttpPut("Modify/{userId}")]
public async Task<IActionResult> Modify(UserModifyDto dto, int userId)
{
var myUserType = HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Role).Value;
var userTypeResult = await _userService.GetUserType(userId);
if (userTypeResult.HasError) if (userTypeResult.HasError)
{ {
return BadRequest(userTypeResult.Message); return BadRequest(userTypeResult.Message);
} }
if (myUserType == UserTypes.FacultyAdmin)
{
var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
var userTenant = await _userService.GetTenant(userId);
if (userTenant.HasError)
{
return BadRequest(userTenant.Message);
}
if (userTenant.Data != tenantId)
{
return BadRequest("No se puede modificar un usuario de otra facultad");
}
}
var userType = userTypeResult.Data; var userType = userTypeResult.Data;
if (userType.Name == UserTypes.UdelarAdmin || if (myUserType == UserTypes.UdelarAdmin &&
userType.Name == UserTypes.FacultyAdmin) (userType.Name == UserTypes.Student ||
userType.Name == UserTypes.Professor))
{
return BadRequest(string.Format(ErrorMessages.InvalidUserType, userType.Name));
}
if (myUserType == UserTypes.FacultyAdmin &&
(userType.Name == UserTypes.UdelarAdmin ||
userType.Name == UserTypes.FacultyAdmin))
{ {
return BadRequest(string.Format(ErrorMessages.InvalidUserType, userType.Name)); return BadRequest(string.Format(ErrorMessages.InvalidUserType, userType.Name));
} }
var userServiceResult = await _userService.Create(dto, userType.Name, tenantId); var userServiceResult = await _userService.Modify(dto, userType.Name, userId);
if (userServiceResult.HasError) if (userServiceResult.HasError)
{ {
return BadRequest(userServiceResult.Message); return BadRequest(userServiceResult.Message);
} }
if (userServiceResult.Data == false)
{
return NotFound(userServiceResult.Message);
}
return Ok(); return Ok();
} }
[Authorize(Roles = UserTypes.UdelarAdmin)] [Authorize(Roles = UserTypes.FacultyAdmin + ", " + UserTypes.UdelarAdmin)]
[HttpPost("RegisterAdmin/{tenantId}")] [HttpDelete("Delete/{userId}")]
public async Task<IActionResult> RegisterAdmin(UserRegisterDto dto, int tenantId) public async Task<IActionResult> Delete(int userId)
{ {
var userTypeResult = await _userTypeService.GetById(dto.UserTypeId); var myUserType = HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Role).Value;
if (myUserType == UserTypes.FacultyAdmin)
{
var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
var userTenant = await _userService.GetTenant(userId);
if (userTenant.HasError)
{
return BadRequest(userTenant.Message);
}
if (userTenant.Data != tenantId)
{
return BadRequest("No se puede borrar un usuario de otra facultad");
}
}
var userTypeResult = await _userService.GetUserType(userId);
if (userTypeResult.HasError) if (userTypeResult.HasError)
{ {
BadRequest(userTypeResult.Message); return BadRequest(userTypeResult.Message);
} }
var userType = userTypeResult.Data; var userType = userTypeResult.Data;
if (userType.Name == UserTypes.Student || if (myUserType == UserTypes.UdelarAdmin &&
userType.Name == UserTypes.Professor) (userType.Name == UserTypes.Student ||
userType.Name == UserTypes.Professor))
{ {
return BadRequest(string.Format(ErrorMessages.InvalidUserType, userType.Name)); return BadRequest(string.Format(ErrorMessages.InvalidUserType, userType.Name));
} }
var userServiceResult = await _userService.Create(dto, userType.Name, tenantId); if (myUserType == UserTypes.FacultyAdmin &&
(userType.Name == UserTypes.UdelarAdmin ||
userType.Name == UserTypes.FacultyAdmin))
{
return BadRequest(string.Format(ErrorMessages.InvalidUserType, userType.Name));
}
var userServiceResult = await _userService.Delete(userId);
if (userServiceResult.HasError) if (userServiceResult.HasError)
{ {
BadRequest(userServiceResult.Message); return BadRequest(userServiceResult.Message);
}
if (userServiceResult.Data == false)
{
return NotFound(userServiceResult.Message);
} }
return Ok(); return Ok();
} }
[Authorize(Roles = UserTypes.Student + ", " + UserTypes.Professor + ", " + UserTypes.FacultyAdmin)]
[Authorize(Roles = UserTypes.Student + ", " + UserTypes.Professor)]
[HttpGet("GetAll")] [HttpGet("GetAll")]
public async Task<IActionResult> GetAll() public async Task<IActionResult> GetAll()
{ {
...@@ -217,5 +330,108 @@ namespace Tsi1.Api.Controllers ...@@ -217,5 +330,108 @@ namespace Tsi1.Api.Controllers
return Ok(result.Data); return Ok(result.Data);
} }
[AllowAnonymous]
[HttpGet("ForgotPassword/{username}")]
public async Task<IActionResult> ForgotPassword(string username)
{
var resultSplit = username.Split("@");
if (resultSplit.Count() != 2)
{
return BadRequest(ErrorMessages.InvalidUsername);
}
username = resultSplit[0];
var tenantName = resultSplit[1];
var tenantId = await _tenantService.GetByName(tenantName);
if (tenantId.HasError)
{
return BadRequest(tenantId.Message);
}
var userResult = await _userService.GetByUsername(username, tenantId.Data);
if (userResult.HasError)
{
return BadRequest(userResult.Message);
}
var code = _jwtAuthManager.GenerateVerificationCode(username, DateTime.Now);
var result = await _emailService.SendVerificationCode(userResult.Data.Email, code);
if (result.HasError)
{
return BadRequest("Ha ocurrido un error");
}
return Ok();
}
[AllowAnonymous]
[HttpGet("VerificationCode/{username}/{code}")]
public async Task<IActionResult> VerificationCode(string username, int code)
{
var resultSplit = username.Split("@");
if (resultSplit.Count() != 2)
{
return BadRequest(ErrorMessages.InvalidUsername);
}
username = resultSplit[0];
var tenantName = resultSplit[1];
var tenantId = await _tenantService.GetByName(tenantName);
if (tenantId.HasError)
{
return BadRequest(tenantId.Message);
}
if (!_jwtAuthManager.ValidateVerificationCode(username, code))
{
return BadRequest("Código de verificación incorrecto");
}
var userResult = await _userService.GetByUsername(username, tenantId.Data);
if (userResult.HasError)
{
return BadRequest(userResult.Message);
}
var user = userResult.Data;
var claims = new[]
{
new Claim("Id", user.Id.ToString()),
new Claim("Username", user.Username),
new Claim("TenantId", user.TenantId.ToString()),
new Claim(ClaimTypes.Role, user.UserType.Name)
};
var jwtResult = _jwtAuthManager.GenerateTokens(user.Username, claims, DateTime.Now);
return Ok(new LoginResult
{
Id = user.Id,
UserName = user.Username,
Role = user.UserType.Name,
AccessToken = jwtResult.AccessToken,
RefreshToken = jwtResult.RefreshToken.TokenString
});
}
[HttpPost("RestorePassword")]
public async Task<IActionResult> RestorePassword(RestorePasswordDto dto)
{
var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var result = await _userService.UpdatePassword(userId, dto.Password);
if (result.HasError)
{
return BadRequest(result.Message);
}
return Ok();
}
} }
} }
...@@ -14,5 +14,8 @@ namespace Tsi1.Api.Infrastructure ...@@ -14,5 +14,8 @@ namespace Tsi1.Api.Infrastructure
void RemoveExpiredRefreshTokens(DateTime now); void RemoveExpiredRefreshTokens(DateTime now);
void RemoveRefreshTokenByUserName(string userName); void RemoveRefreshTokenByUserName(string userName);
(ClaimsPrincipal, JwtSecurityToken) DecodeJwtToken(string token); (ClaimsPrincipal, JwtSecurityToken) DecodeJwtToken(string token);
int GenerateVerificationCode(string username, DateTime now);
bool ValidateVerificationCode(string username, int code);
public void RemoveExpiredVerificationCodes(DateTime now);
} }
} }
...@@ -15,6 +15,7 @@ namespace Tsi1.Api.Infrastructure ...@@ -15,6 +15,7 @@ namespace Tsi1.Api.Infrastructure
{ {
public IImmutableDictionary<string, RefreshToken> UsersRefreshTokensReadOnlyDictionary => _usersRefreshTokens.ToImmutableDictionary(); public IImmutableDictionary<string, RefreshToken> UsersRefreshTokensReadOnlyDictionary => _usersRefreshTokens.ToImmutableDictionary();
private readonly ConcurrentDictionary<string, RefreshToken> _usersRefreshTokens; // can store in a database or a distributed cache private readonly ConcurrentDictionary<string, RefreshToken> _usersRefreshTokens; // can store in a database or a distributed cache
private readonly ConcurrentDictionary<string, VerificationCode> _usersVerificationCodes;
private readonly JwtTokenConfig _jwtTokenConfig; private readonly JwtTokenConfig _jwtTokenConfig;
private readonly byte[] _secret; private readonly byte[] _secret;
...@@ -22,6 +23,7 @@ namespace Tsi1.Api.Infrastructure ...@@ -22,6 +23,7 @@ namespace Tsi1.Api.Infrastructure
{ {
_jwtTokenConfig = jwtTokenConfig; _jwtTokenConfig = jwtTokenConfig;
_usersRefreshTokens = new ConcurrentDictionary<string, RefreshToken>(); _usersRefreshTokens = new ConcurrentDictionary<string, RefreshToken>();
_usersVerificationCodes = new ConcurrentDictionary<string, VerificationCode>();
_secret = Encoding.ASCII.GetBytes(jwtTokenConfig.Secret); _secret = Encoding.ASCII.GetBytes(jwtTokenConfig.Secret);
} }
...@@ -118,6 +120,36 @@ namespace Tsi1.Api.Infrastructure ...@@ -118,6 +120,36 @@ namespace Tsi1.Api.Infrastructure
return (principal, validatedToken as JwtSecurityToken); return (principal, validatedToken as JwtSecurityToken);
} }
public int GenerateVerificationCode(string username, DateTime now)
{
var code = new Random().Next(1000000);
var verficationCode = new VerificationCode()
{
Username = username,
Code = code,
ExpireAt = now.AddMinutes(_jwtTokenConfig.VerificationCodeExpiration)
};
_usersVerificationCodes.AddOrUpdate(username, verficationCode, (s, t) => verficationCode);
return code;
}
public bool ValidateVerificationCode(string username, int code)
{
return _usersVerificationCodes.TryGetValue(username, out _);
}
public void RemoveExpiredVerificationCodes(DateTime now)
{
var expiredCodes = _usersVerificationCodes.Where(x => x.Value.ExpireAt < now).ToList();
foreach (var expiredCode in expiredCodes)
{
_usersVerificationCodes.TryRemove(expiredCode.Key, out _);
}
}
private static string GenerateRefreshTokenString() private static string GenerateRefreshTokenString()
{ {
var randomNumber = new byte[32]; var randomNumber = new byte[32];
......
...@@ -4,19 +4,11 @@ namespace Tsi1.Api.Infrastructure ...@@ -4,19 +4,11 @@ namespace Tsi1.Api.Infrastructure
{ {
public class JwtTokenConfig public class JwtTokenConfig
{ {
[JsonPropertyName("secret")]
public string Secret { get; set; } public string Secret { get; set; }
[JsonPropertyName("issuer")]
public string Issuer { get; set; } public string Issuer { get; set; }
[JsonPropertyName("audience")]
public string Audience { get; set; } public string Audience { get; set; }
[JsonPropertyName("accessTokenExpiration")]
public int AccessTokenExpiration { get; set; } public int AccessTokenExpiration { get; set; }
[JsonPropertyName("refreshTokenExpiration")]
public int RefreshTokenExpiration { get; set; } public int RefreshTokenExpiration { get; set; }
public int VerificationCodeExpiration { get; set; }
} }
} }
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Tsi1.Api.Infrastructure
{
public class JwtVerificationCodeCache : IHostedService, IDisposable
{
private Timer _timer;
private readonly IJwtAuthManager _jwtAuthManager;
public JwtVerificationCodeCache(IJwtAuthManager jwtAuthManager)
{
_jwtAuthManager = jwtAuthManager;
}
public Task StartAsync(CancellationToken stoppingToken)
{
// remove expired refresh tokens from cache every minute
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_jwtAuthManager.RemoveExpiredVerificationCodes(DateTime.Now);
}
public Task StopAsync(CancellationToken stoppingToken)
{
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
}
...@@ -11,7 +11,7 @@ namespace Tsi1.Api.Models ...@@ -11,7 +11,7 @@ namespace Tsi1.Api.Models
{ {
[Required] [Required]
[JsonPropertyName("username")] [JsonPropertyName("username")]
public string UserName { get; set; } public string Username { get; set; }
[Required] [Required]
[JsonPropertyName("password")] [JsonPropertyName("password")]
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Tsi1.Api.Models
{
public class VerificationCode
{
public string Username { get; set; }
public int Code { get; set; }
public DateTime ExpireAt { get; set; }
}
}
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AutoMapper; using AutoMapper;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
...@@ -48,6 +52,8 @@ namespace Tsi1.Api ...@@ -48,6 +52,8 @@ namespace Tsi1.Api
var postgreSqlSection = isElasticCloud ? "PostgreSqlCloud" : "PostgreSql"; var postgreSqlSection = isElasticCloud ? "PostgreSqlCloud" : "PostgreSql";
var mongoDbSection = isElasticCloud ? "Tsi1DatabaseSettingsCloud" : "Tsi1DatabaseSettings"; var mongoDbSection = isElasticCloud ? "Tsi1DatabaseSettingsCloud" : "Tsi1DatabaseSettings";
var jwtTokenConfig = Configuration.GetSection("jwtTokenConfig").Get<JwtTokenConfig>();
services.AddDbContext<Tsi1Context>(x => x.UseNpgsql(Configuration.GetConnectionString(postgreSqlSection))); services.AddDbContext<Tsi1Context>(x => x.UseNpgsql(Configuration.GetConnectionString(postgreSqlSection)));
services.Configure<Tsi1DatabaseSettings>( services.Configure<Tsi1DatabaseSettings>(
...@@ -56,6 +62,13 @@ namespace Tsi1.Api ...@@ -56,6 +62,13 @@ namespace Tsi1.Api
services.AddSingleton<ITsi1DatabaseSettings>(sp => services.AddSingleton<ITsi1DatabaseSettings>(sp =>
sp.GetRequiredService<IOptions<Tsi1DatabaseSettings>>().Value); sp.GetRequiredService<IOptions<Tsi1DatabaseSettings>>().Value);
services.AddSingleton(jwtTokenConfig);
services.AddSingleton<IJwtAuthManager, JwtAuthManager>();
services.AddHostedService<JwtRefreshTokenCache>();
services.AddHostedService<JwtVerificationCodeCache>();
services.AddSingleton<IMessageService, MessageService>(); services.AddSingleton<IMessageService, MessageService>();
services.AddScoped<IUserService, UserService>(); services.AddScoped<IUserService, UserService>();
...@@ -65,14 +78,19 @@ namespace Tsi1.Api ...@@ -65,14 +78,19 @@ namespace Tsi1.Api
services.AddScoped<IPostService, PostService>(); services.AddScoped<IPostService, PostService>();
services.AddScoped<IPostMessageService, PostMessageService>(); services.AddScoped<IPostMessageService, PostMessageService>();
services.AddScoped<ITenantService, TenantService>(); services.AddScoped<ITenantService, TenantService>();
services.AddScoped<IFileService, FileService>();
services.Configure<MailSettings>(Configuration.GetSection("MailSettings")); services.Configure<MailSettings>(Configuration.GetSection("MailSettings"));
services.AddScoped<IEmailService, EmailService>(); services.AddScoped<IEmailService, EmailService>();
services.AddCors(); services.AddCors();
var jwtTokenConfig = Configuration.GetSection("jwtTokenConfig").Get<JwtTokenConfig>(); services.AddAuthorization(options =>
services.AddSingleton(jwtTokenConfig); {
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
services.AddAuthentication(x => services.AddAuthentication(x =>
{ {
...@@ -112,9 +130,6 @@ namespace Tsi1.Api ...@@ -112,9 +130,6 @@ namespace Tsi1.Api
}; };
}); });
services.AddSingleton<IJwtAuthManager, JwtAuthManager>();
services.AddHostedService<JwtRefreshTokenCache>();
services.AddSwaggerGen(c => services.AddSwaggerGen(c =>
{ {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Tsi1 api", Version = "v1" }); c.SwaggerDoc("v1", new OpenApiInfo { Title = "Tsi1 api", Version = "v1" });
...@@ -145,7 +160,7 @@ namespace Tsi1.Api ...@@ -145,7 +160,7 @@ namespace Tsi1.Api
mc.AddProfile(new MappingProfile()); mc.AddProfile(new MappingProfile());
}); });
IMapper mapper = mappingConfig.CreateMapper(); var mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper); services.AddSingleton(mapper);
} }
...@@ -172,6 +187,16 @@ namespace Tsi1.Api ...@@ -172,6 +187,16 @@ namespace Tsi1.Api
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
var provider = new FileExtensionContentTypeProvider();
app.UseStaticFiles(new StaticFileOptions()
{
ContentTypeProvider = provider,
FileProvider = new PhysicalFileProvider(
Path.Combine(env.ContentRootPath, "StaticFiles")),
RequestPath = "/static"
}
);
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers(); endpoints.MapControllers();
......
...@@ -14,5 +14,9 @@ ...@@ -14,5 +14,9 @@
<ProjectReference Include="..\Tsi1.BusinessLayer\Tsi1.BusinessLayer.csproj" /> <ProjectReference Include="..\Tsi1.BusinessLayer\Tsi1.BusinessLayer.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="StaticFiles\" />
</ItemGroup>
</Project> </Project>
...@@ -14,12 +14,13 @@ ...@@ -14,12 +14,13 @@
"ConnectionString": "mongodb://mongo:27017", "ConnectionString": "mongodb://mongo:27017",
"DatabaseName": "Tsi1Db" "DatabaseName": "Tsi1Db"
}, },
"jwtTokenConfig": { "JwtTokenConfig": {
"secret": "1234567890123456789", "Secret": "1234567890123456789",
"issuer": "https://localhost:5000", "Issuer": "https://localhost:5000",
"audience": "https://localhost:5000", "Audience": "https://localhost:5000",
"accessTokenExpiration": 20, "AccessTokenExpiration": 20,
"refreshTokenExpiration": 60 "RefreshTokenExpiration": 60,
"VerificationCodeExpiration": 5
}, },
"MailSettings": { "MailSettings": {
"Mail": "tsi1.grupo2.2020@gmail.com", "Mail": "tsi1.grupo2.2020@gmail.com",
......
...@@ -8,5 +8,6 @@ namespace Tsi1.BusinessLayer.Dtos ...@@ -8,5 +8,6 @@ namespace Tsi1.BusinessLayer.Dtos
{ {
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public int StudentQuantity { get; set; }
} }
} }
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.BusinessLayer.Dtos
{
public class FileDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Path { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.BusinessLayer.Dtos
{
public class RestorePasswordDto
{
public string Password { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.BusinessLayer.Dtos
{
public class TenantCourseDto
{
public TenantCourseDto()
{
Courses = new List<CoursePreviewDto>();
}
public int Id { get; set; }
public string Name { get; set; }
public List<CoursePreviewDto> Courses { get; set; }
}
}
...@@ -9,5 +9,7 @@ namespace Tsi1.BusinessLayer.Dtos ...@@ -9,5 +9,7 @@ namespace Tsi1.BusinessLayer.Dtos
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public int StudentQuantity { get; set; }
} }
} }
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Tsi1.BusinessLayer.Dtos
{
public class UserModifyDto
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string IdentityCard { get; set; }
public int Age { get; set; }
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment