Compare commits
3 Commits
8b7e4b1e64
...
cd7fe934fd
Author | SHA1 | Date |
---|---|---|
Tudor Stanciu | cd7fe934fd | |
Tudor Stanciu | ba50e1b186 | |
Tudor Stanciu | 4e15b40bee |
|
@ -1,7 +1,7 @@
|
||||||
<Project>
|
<Project>
|
||||||
<Import Project="dependencies.props" />
|
<Import Project="dependencies.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>2.4.3</Version>
|
<Version>2.4.4</Version>
|
||||||
<Authors>Tudor Stanciu</Authors>
|
<Authors>Tudor Stanciu</Authors>
|
||||||
<Company>STA</Company>
|
<Company>STA</Company>
|
||||||
<PackageTags>Tuitio</PackageTags>
|
<PackageTags>Tuitio</PackageTags>
|
||||||
|
|
|
@ -123,4 +123,13 @@
|
||||||
◾ New versions of nuget packages have been released.
|
◾ New versions of nuget packages have been released.
|
||||||
</Content>
|
</Content>
|
||||||
</Note>
|
</Note>
|
||||||
|
<Note>
|
||||||
|
<Version>2.4.4</Version>
|
||||||
|
<Date>2023-05-03 19:02</Date>
|
||||||
|
<Content>
|
||||||
|
Added better token persistence in the database
|
||||||
|
◾ All tokens are persisted in the database after a login operation to recover them in case of a system crash or restart.
|
||||||
|
◾ Expired tokens are still deleted periodically.
|
||||||
|
</Content>
|
||||||
|
</Note>
|
||||||
</ReleaseNotes>
|
</ReleaseNotes>
|
|
@ -36,7 +36,7 @@ namespace Tuitio.Application.CommandHandlers
|
||||||
|
|
||||||
_logger.LogDebug($"Login succeeded for user '{command.UserName}'.");
|
_logger.LogDebug($"Login succeeded for user '{command.UserName}'.");
|
||||||
|
|
||||||
var result = new AccountLoginResult(loginResult.Raw, loginResult.Token.ExpiresIn);
|
var result = new AccountLoginResult(loginResult.Raw, loginResult.TokenExtension.Token.ExpiresIn);
|
||||||
return Envelope<AccountLoginResult>.Success(result);
|
return Envelope<AccountLoginResult>.Success(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,21 @@ namespace Tuitio.Application.Mappings
|
||||||
public MappingProfile()
|
public MappingProfile()
|
||||||
{
|
{
|
||||||
CreateMap<models.RecordIdentifier, dto.RecordIdentifier>();
|
CreateMap<models.RecordIdentifier, dto.RecordIdentifier>();
|
||||||
CreateMap<models.Token, dto.AuthorizationResult>();
|
|
||||||
|
CreateMap<models.TokenExtension, dto.AuthorizationResult>()
|
||||||
|
.ForMember(z => z.TokenId, src => src.MapFrom(z => z.Token.TokenId))
|
||||||
|
.ForMember(z => z.UserId, src => src.MapFrom(z => z.Token.UserId))
|
||||||
|
.ForMember(z => z.UserName, src => src.MapFrom(z => z.Token.UserName))
|
||||||
|
.ForMember(z => z.FirstName, src => src.MapFrom(z => z.Token.FirstName))
|
||||||
|
.ForMember(z => z.LastName, src => src.MapFrom(z => z.Token.LastName))
|
||||||
|
.ForMember(z => z.Email, src => src.MapFrom(z => z.Token.Email))
|
||||||
|
.ForMember(z => z.SecurityStamp, src => src.MapFrom(z => z.Token.SecurityStamp))
|
||||||
|
.ForMember(z => z.LockStamp, src => src.MapFrom(z => z.Token.LockStamp))
|
||||||
|
.ForMember(z => z.CreatedAt, src => src.MapFrom(z => z.Token.CreatedAt))
|
||||||
|
.ForMember(z => z.ExpiresIn, src => src.MapFrom(z => z.Token.ExpiresIn))
|
||||||
|
.ForMember(z => z.Claims, src => src.MapFrom(z => z.Token.Claims))
|
||||||
|
;
|
||||||
|
|
||||||
CreateMap<models.Account.LogoutResult, dto.AccountLogoutResult>();
|
CreateMap<models.Account.LogoutResult, dto.AccountLogoutResult>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,6 @@ namespace Tuitio.Application.Services.Abstractions
|
||||||
{
|
{
|
||||||
internal interface ITokenService
|
internal interface ITokenService
|
||||||
{
|
{
|
||||||
Token GenerateToken(AppUser user);
|
TokenExtension GenerateToken(AppUser user);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,6 +10,6 @@ namespace Tuitio.Application.Services.Abstractions
|
||||||
{
|
{
|
||||||
Task<LoginResult> Login(string userName, string password);
|
Task<LoginResult> Login(string userName, string password);
|
||||||
Task<LogoutResult> Logout(string tokenRaw);
|
Task<LogoutResult> Logout(string tokenRaw);
|
||||||
Token Authorize(string tokenRaw);
|
TokenExtension Authorize(string tokenRaw);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -43,7 +43,7 @@ namespace Tuitio.Application.Services
|
||||||
_logger.LogInformation($"BehaviorService: {activeTokens.Length} active tokens were found in database.");
|
_logger.LogInformation($"BehaviorService: {activeTokens.Length} active tokens were found in database.");
|
||||||
foreach (var token in activeTokens)
|
foreach (var token in activeTokens)
|
||||||
{
|
{
|
||||||
var storeToken = Token.Import(token.Token);
|
var storeToken = TokenExtension.Deserialize(token.TokenExtension);
|
||||||
_securityStore.Set(token.Token, storeToken);
|
_securityStore.Set(token.Token, storeToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,15 +18,14 @@ namespace Tuitio.Application.Services
|
||||||
_configProvider=configProvider;
|
_configProvider=configProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Token GenerateToken(AppUser user)
|
public TokenExtension GenerateToken(AppUser user)
|
||||||
{
|
{
|
||||||
var token = new Token(_configProvider.Token.ValidityInMinutes);
|
|
||||||
var claims = user.Claims?.ToDictionary();
|
var claims = user.Claims?.ToDictionary();
|
||||||
var userRoles = user.GetUserRoles().AsRecordIdentifiers();
|
var userRoles = user.GetUserRoles().AsRecordIdentifiers();
|
||||||
var userGroups = user.UserGroups?.Select(z => z.UserGroup).AsRecordIdentifiers();
|
var userGroups = user.UserGroups?.Select(z => z.UserGroup).AsRecordIdentifiers();
|
||||||
|
var token = Token.Initialize(_configProvider.Token.ValidityInMinutes, user.UserId, user.UserName, user.FirstName, user.LastName, user.Email, user.SecurityStamp, claims);
|
||||||
token.SetUserData(user.UserId, user.UserName, user.FirstName, user.LastName, user.Email, user.SecurityStamp, claims, userRoles, userGroups);
|
var extension = token.Extend(userRoles, userGroups);
|
||||||
return token;
|
return extension;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,30 +42,30 @@ namespace Tuitio.Application.Services
|
||||||
if (!valid)
|
if (!valid)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var token = _tokenService.GenerateToken(user);
|
var tokenExtension = _tokenService.GenerateToken(user);
|
||||||
var raw = token.Export();
|
var raw = tokenExtension.Token.Export();
|
||||||
_securityStore.Set(raw, token);
|
_securityStore.Set(raw, tokenExtension);
|
||||||
|
|
||||||
await _userRepository.UpdateUserAfterLogin(user, token, raw);
|
await _userRepository.UpdateUserAfterLogin(user, tokenExtension, raw);
|
||||||
|
|
||||||
var result = new LoginResult(token, raw);
|
var result = new LoginResult(tokenExtension, raw);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<LogoutResult> Logout(string tokenRaw)
|
public async Task<LogoutResult> Logout(string tokenRaw)
|
||||||
{
|
{
|
||||||
var token = _securityStore.Get(tokenRaw);
|
var tokenExtension = _securityStore.Get(tokenRaw);
|
||||||
if (token == null)
|
if (tokenExtension == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
await _userRepository.RemoveToken(token.TokenId);
|
await _userRepository.RemoveToken(tokenExtension.Token.TokenId);
|
||||||
_securityStore.Remove(tokenRaw);
|
_securityStore.Remove(tokenRaw);
|
||||||
|
|
||||||
var result = new LogoutResult(token.UserId, token.UserName, DateTime.UtcNow);
|
var result = new LogoutResult(tokenExtension.Token.UserId, tokenExtension.Token.UserName, DateTime.UtcNow);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Token Authorize(string tokenRaw)
|
public TokenExtension Authorize(string tokenRaw)
|
||||||
{
|
{
|
||||||
var token = _securityStore.Get(tokenRaw);
|
var token = _securityStore.Get(tokenRaw);
|
||||||
return token;
|
return token;
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
// Copyright (c) 2020 Tudor Stanciu
|
// Copyright (c) 2020 Tudor Stanciu
|
||||||
|
|
||||||
using System;
|
|
||||||
using Tuitio.Domain.Models;
|
using Tuitio.Domain.Models;
|
||||||
|
|
||||||
namespace Tuitio.Application.Stores
|
namespace Tuitio.Application.Stores
|
||||||
{
|
{
|
||||||
internal interface ITokenStore
|
internal interface ITokenStore
|
||||||
{
|
{
|
||||||
Token Get(string key);
|
TokenExtension Get(string key);
|
||||||
bool Set(string key, Token token);
|
bool Set(string key, TokenExtension token);
|
||||||
void Remove(string key);
|
void Remove(string key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,14 @@ namespace Tuitio.Application.Stores
|
||||||
{
|
{
|
||||||
internal class TokenStore : ITokenStore
|
internal class TokenStore : ITokenStore
|
||||||
{
|
{
|
||||||
private ConcurrentDictionary<string, Token> Tokens { get; }
|
private ConcurrentDictionary<string, TokenExtension> Tokens { get; }
|
||||||
|
|
||||||
public TokenStore()
|
public TokenStore()
|
||||||
{
|
{
|
||||||
Tokens = new ConcurrentDictionary<string, Token>();
|
Tokens = new ConcurrentDictionary<string, TokenExtension>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Token Get(string key)
|
public TokenExtension Get(string key)
|
||||||
{
|
{
|
||||||
var registered = Tokens.ContainsKey(key);
|
var registered = Tokens.ContainsKey(key);
|
||||||
if (!registered)
|
if (!registered)
|
||||||
|
@ -24,7 +24,7 @@ namespace Tuitio.Application.Stores
|
||||||
return Tokens[key];
|
return Tokens[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Set(string key, Token token)
|
public bool Set(string key, TokenExtension token)
|
||||||
{
|
{
|
||||||
var registered = Tokens.ContainsKey(key);
|
var registered = Tokens.ContainsKey(key);
|
||||||
if (registered)
|
if (registered)
|
||||||
|
|
|
@ -41,13 +41,16 @@ namespace Tuitio.Domain.Data.Repositories
|
||||||
.FirstOrDefaultAsync(z => z.UserId == userId);
|
.FirstOrDefaultAsync(z => z.UserId == userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateUserAfterLogin(AppUser user, Token token, string tokenRaw)
|
public async Task UpdateUserAfterLogin(AppUser user, TokenExtension tokenExtension, string tokenRaw)
|
||||||
{
|
{
|
||||||
|
var token = tokenExtension.Token;
|
||||||
|
var extension = tokenExtension.Serialize();
|
||||||
var userToken = new UserToken()
|
var userToken = new UserToken()
|
||||||
{
|
{
|
||||||
TokenId = token.TokenId,
|
TokenId = token.TokenId,
|
||||||
UserId = token.UserId,
|
UserId = token.UserId,
|
||||||
Token = tokenRaw,
|
Token = tokenRaw,
|
||||||
|
TokenExtension = extension,
|
||||||
ValidFrom = token.CreatedAt,
|
ValidFrom = token.CreatedAt,
|
||||||
ValidUntil = token.CreatedAt.AddSeconds(token.ExpiresIn)
|
ValidUntil = token.CreatedAt.AddSeconds(token.ExpiresIn)
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ begin
|
||||||
TokenId uniqueidentifier constraint PK_Token primary key,
|
TokenId uniqueidentifier constraint PK_Token primary key,
|
||||||
UserId int not null constraint FK_Token_AppUser foreign key references AppUser(UserId),
|
UserId int not null constraint FK_Token_AppUser foreign key references AppUser(UserId),
|
||||||
Token varchar(1000) not null,
|
Token varchar(1000) not null,
|
||||||
|
TokenExtension varchar(2000) not null,
|
||||||
ValidFrom datetime not null,
|
ValidFrom datetime not null,
|
||||||
ValidUntil datetime not null
|
ValidUntil datetime not null
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,6 +9,7 @@ namespace Tuitio.Domain.Entities
|
||||||
public Guid TokenId { get; set; }
|
public Guid TokenId { get; set; }
|
||||||
public int UserId { get; set; }
|
public int UserId { get; set; }
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
|
public string TokenExtension { get; set; }
|
||||||
public DateTime ValidFrom { get; set; }
|
public DateTime ValidFrom { get; set; }
|
||||||
public DateTime ValidUntil { get; set; }
|
public DateTime ValidUntil { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@ using System;
|
||||||
|
|
||||||
namespace Tuitio.Domain.Models.Account
|
namespace Tuitio.Domain.Models.Account
|
||||||
{
|
{
|
||||||
public record LoginResult(Token Token, string Raw);
|
public record LoginResult(TokenExtension TokenExtension, string Raw);
|
||||||
public record LogoutResult(int UserId, string UserName, DateTime LogoutDate);
|
public record LogoutResult(int UserId, string UserName, DateTime LogoutDate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,16 +23,10 @@ namespace Tuitio.Domain.Models
|
||||||
public long ExpiresIn { get; set; }
|
public long ExpiresIn { get; set; }
|
||||||
public Dictionary<string, string> Claims { get; set; }
|
public Dictionary<string, string> Claims { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public IEnumerable<RecordIdentifier> UserRoles { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public IEnumerable<RecordIdentifier> UserGroups { get; set; }
|
|
||||||
|
|
||||||
[Obsolete("This constructor is only used for deserialization and should not be used for any other purpose.")]
|
[Obsolete("This constructor is only used for deserialization and should not be used for any other purpose.")]
|
||||||
public Token() { }
|
public Token() { }
|
||||||
|
|
||||||
public Token(int validityInMinutes)
|
private Token(int validityInMinutes)
|
||||||
{
|
{
|
||||||
TokenId = Guid.NewGuid();
|
TokenId = Guid.NewGuid();
|
||||||
CreatedAt = DateTime.UtcNow;
|
CreatedAt = DateTime.UtcNow;
|
||||||
|
@ -40,17 +34,20 @@ namespace Tuitio.Domain.Models
|
||||||
ExpiresIn = validityInMinutes * 60; // seconds
|
ExpiresIn = validityInMinutes * 60; // seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUserData(int userId, string userName, string firstName, string lastName, string email, string securityStamp, Dictionary<string, string> claims, IEnumerable<RecordIdentifier> userRoles, IEnumerable<RecordIdentifier> userGroups)
|
public static Token Initialize(int validityInMinutes, int userId, string userName, string firstName, string lastName, string email, string securityStamp, Dictionary<string, string> claims)
|
||||||
{
|
{
|
||||||
UserId = userId;
|
var token = new Token(validityInMinutes)
|
||||||
UserName = userName;
|
{
|
||||||
FirstName = firstName;
|
UserId = userId,
|
||||||
LastName = lastName;
|
UserName = userName,
|
||||||
Email = email;
|
FirstName = firstName,
|
||||||
SecurityStamp = securityStamp;
|
LastName = lastName,
|
||||||
Claims = claims;
|
Email = email,
|
||||||
UserRoles = userRoles;
|
SecurityStamp = securityStamp,
|
||||||
UserGroups = userGroups;
|
Claims = claims
|
||||||
|
};
|
||||||
|
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Export()
|
public string Export()
|
||||||
|
@ -73,6 +70,9 @@ namespace Tuitio.Domain.Models
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TokenExtension Extend(IEnumerable<RecordIdentifier> userRoles, IEnumerable<RecordIdentifier> userGroups)
|
||||||
|
=> TokenExtension.Create(this, userRoles, userGroups);
|
||||||
|
|
||||||
private static bool ValidateTokenRaw(string tokenRaw)
|
private static bool ValidateTokenRaw(string tokenRaw)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(tokenRaw))
|
if (string.IsNullOrWhiteSpace(tokenRaw))
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (c) 2020 Tudor Stanciu
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Tuitio.Domain.Models
|
||||||
|
{
|
||||||
|
public class TokenExtension
|
||||||
|
{
|
||||||
|
public Token Token { get; set; }
|
||||||
|
public IEnumerable<RecordIdentifier> UserRoles { get; set; }
|
||||||
|
public IEnumerable<RecordIdentifier> UserGroups { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("This constructor is only used for deserialization and should not be used for any other purpose.")]
|
||||||
|
public TokenExtension() { }
|
||||||
|
|
||||||
|
private TokenExtension(Token token, IEnumerable<RecordIdentifier> userRoles, IEnumerable<RecordIdentifier> userGroups)
|
||||||
|
{
|
||||||
|
Token=token;
|
||||||
|
UserRoles=userRoles;
|
||||||
|
UserGroups=userGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TokenExtension Create(Token token, IEnumerable<RecordIdentifier> userRoles, IEnumerable<RecordIdentifier> userGroups)
|
||||||
|
=> new(token, userRoles, userGroups);
|
||||||
|
|
||||||
|
public string Serialize()
|
||||||
|
{
|
||||||
|
var text = JsonConvert.SerializeObject(this);
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TokenExtension Deserialize(string text)
|
||||||
|
{
|
||||||
|
var token = JsonConvert.DeserializeObject<TokenExtension>(text);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ namespace Tuitio.Domain.Repositories
|
||||||
{
|
{
|
||||||
Task<AppUser> GetUser(string userName, string password);
|
Task<AppUser> GetUser(string userName, string password);
|
||||||
Task<AppUser> GetFullUser(int userId);
|
Task<AppUser> GetFullUser(int userId);
|
||||||
Task UpdateUserAfterLogin(AppUser user, Token token, string tokenRaw);
|
Task UpdateUserAfterLogin(AppUser user, TokenExtension tokenExtension, string tokenRaw);
|
||||||
Task<UserToken[]> GetActiveTokens();
|
Task<UserToken[]> GetActiveTokens();
|
||||||
Task RemoveToken(Guid tokenId);
|
Task RemoveToken(Guid tokenId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,26 +59,26 @@ namespace Tuitio.Authentication
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthenticationTicket GetAuthenticationTicket(Token result)
|
private AuthenticationTicket GetAuthenticationTicket(TokenExtension result)
|
||||||
{
|
{
|
||||||
var claimCollection = new Dictionary<string, string>()
|
var claimCollection = new Dictionary<string, string>()
|
||||||
{
|
{
|
||||||
{ ClaimTypes.NameIdentifier, result.UserId.ToString() },
|
{ ClaimTypes.NameIdentifier, result.Token.UserId.ToString() },
|
||||||
{ ClaimTypes.Name, result.UserName },
|
{ ClaimTypes.Name, result.Token.UserName },
|
||||||
};
|
};
|
||||||
|
|
||||||
if (result.FirstName != null)
|
if (result.Token.FirstName != null)
|
||||||
claimCollection.Add(ClaimTypes.GivenName, result.FirstName);
|
claimCollection.Add(ClaimTypes.GivenName, result.Token.FirstName);
|
||||||
|
|
||||||
if (result.LastName != null)
|
if (result.Token.LastName != null)
|
||||||
claimCollection.Add(ClaimTypes.Surname, result.FirstName);
|
claimCollection.Add(ClaimTypes.Surname, result.Token.FirstName);
|
||||||
|
|
||||||
if (result.Email != null)
|
if (result.Token.Email != null)
|
||||||
claimCollection.Add(ClaimTypes.Email, result.Email);
|
claimCollection.Add(ClaimTypes.Email, result.Token.Email);
|
||||||
|
|
||||||
if (result.Claims != null && result.Claims.Any())
|
if (result.Token.Claims != null && result.Token.Claims.Any())
|
||||||
{
|
{
|
||||||
foreach (var claim in result.Claims)
|
foreach (var claim in result.Token.Claims)
|
||||||
{
|
{
|
||||||
if (claimCollection.ContainsKey(claim.Key))
|
if (claimCollection.ContainsKey(claim.Key))
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,8 +34,7 @@ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
|
|
||||||
ARG APP_VERSION=0.0.0.0
|
ARG APP_VERSION=0.0.0.0
|
||||||
ENV APP_VERSION=${APP_VERSION}
|
ENV APP_VERSION=${APP_VERSION}
|
||||||
|
ARG APP_DATE="-"
|
||||||
#Workaround to lower the TLS level in container for old sql server version
|
ENV APP_DATE=${APP_DATE}
|
||||||
RUN sed -i 's/TLSv1.2/TLSv1.0/g' /etc/ssl/openssl.cnf
|
|
||||||
|
|
||||||
ENTRYPOINT ["dotnet", "Tuitio.dll"]
|
ENTRYPOINT ["dotnet", "Tuitio.dll"]
|
|
@ -39,7 +39,7 @@ namespace Tuitio.Api
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Fatal(ex, "Tuitio API host terminated unexpectedly");
|
Log.Fatal(ex, "Tuitio API host terminated unexpectedly.");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,7 +68,7 @@ namespace Tuitio.Application.Tests
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var token = _tokenService.GenerateToken(_userMock);
|
var token = _tokenService.GenerateToken(_userMock);
|
||||||
var raw = token.Export();
|
var raw = token.Token.Export();
|
||||||
var extracted = Token.Import(raw);
|
var extracted = Token.Import(raw);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
|
@ -9,11 +9,11 @@ namespace Tuitio.Application.Tests
|
||||||
{
|
{
|
||||||
public class TokenStoreTests
|
public class TokenStoreTests
|
||||||
{
|
{
|
||||||
private Token GetMockedToken()
|
private TokenExtension GetMockedToken()
|
||||||
{
|
{
|
||||||
var token = new Token(1);
|
var token = Token.Initialize(1, 0, "test.tuitio", "tuitio", "user", "user.tuitio@lab.com", Guid.NewGuid().ToString(), null);
|
||||||
token.SetUserData(0, "test.tuitio", "tuitio", "user", "user.tuitio@lab.com", Guid.NewGuid().ToString(), null, null, null);
|
var extension = token.Extend(null, null);
|
||||||
return token;
|
return extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
@ -68,13 +68,13 @@ namespace Tuitio.Application.Tests
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.NotNull(result.Token);
|
Assert.NotNull(result.TokenExtension.Token);
|
||||||
Assert.NotEmpty(result.Raw);
|
Assert.NotEmpty(result.Raw);
|
||||||
Assert.Equal(userName, result.Token.UserName);
|
Assert.Equal(userName, result.TokenExtension.Token.UserName);
|
||||||
Assert.True(result.Token.TokenId != Guid.Empty, "Token id cannot be an empty guid.");
|
Assert.True(result.TokenExtension.Token.TokenId != Guid.Empty, "Token id cannot be an empty guid.");
|
||||||
Assert.NotEmpty(result.Token.LockStamp);
|
Assert.NotEmpty(result.TokenExtension.Token.LockStamp);
|
||||||
Assert.True(result.Token.ExpiresIn > 0, "Token expiration must be a positive number.");
|
Assert.True(result.TokenExtension.Token.ExpiresIn > 0, "Token expiration must be a positive number.");
|
||||||
Assert.True((DateTime.UtcNow - result.Token.CreatedAt).TotalMinutes <= 1, "Token creation date must be within the last minute.");
|
Assert.True((DateTime.UtcNow - result.TokenExtension.Token.CreatedAt).TotalMinutes <= 1, "Token creation date must be within the last minute.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -105,11 +105,11 @@ namespace Tuitio.Application.Tests
|
||||||
var loginResult = await _userService.Login(userName, password);
|
var loginResult = await _userService.Login(userName, password);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var userTokenFromDb = await dbContext.UserTokens.FirstOrDefaultAsync(z => z.TokenId == loginResult.Token.TokenId);
|
var userTokenFromDb = await dbContext.UserTokens.FirstOrDefaultAsync(z => z.TokenId == loginResult.TokenExtension.Token.TokenId);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(userTokenFromDb);
|
Assert.NotNull(userTokenFromDb);
|
||||||
Assert.True(loginResult.Token.TokenId != Guid.Empty, "Token id cannot be an empty guid.");
|
Assert.True(loginResult.TokenExtension.Token.TokenId != Guid.Empty, "Token id cannot be an empty guid.");
|
||||||
Assert.True((DateTime.UtcNow - userTokenFromDb.ValidFrom).TotalMinutes <= 1, "Token valid from date must be within the last minute.");
|
Assert.True((DateTime.UtcNow - userTokenFromDb.ValidFrom).TotalMinutes <= 1, "Token valid from date must be within the last minute.");
|
||||||
Assert.True(userTokenFromDb.ValidUntil > DateTime.UtcNow, "Token valid until date must be greater than the current date.");
|
Assert.True(userTokenFromDb.ValidUntil > DateTime.UtcNow, "Token valid until date must be greater than the current date.");
|
||||||
}
|
}
|
||||||
|
@ -141,11 +141,11 @@ namespace Tuitio.Application.Tests
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.Equal(userName, result.UserName);
|
Assert.Equal(userName, result.Token.UserName);
|
||||||
Assert.NotNull(result.SecurityStamp);
|
Assert.NotNull(result.Token.SecurityStamp);
|
||||||
Assert.NotNull(result.LockStamp);
|
Assert.NotNull(result.Token.LockStamp);
|
||||||
Assert.True(result.TokenId != Guid.Empty, "Token id cannot be an empty guid.");
|
Assert.True(result.Token.TokenId != Guid.Empty, "Token id cannot be an empty guid.");
|
||||||
Assert.True(result.ExpiresIn > 0, "Token expiration must be a positive number.");
|
Assert.True(result.Token.ExpiresIn > 0, "Token expiration must be a positive number.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
Loading…
Reference in New Issue