Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions AdvancedTodoList.Application/AdvancedTodoList.Application.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.13.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.13.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AdvancedTodoList.Core\AdvancedTodoList.Core.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using AdvancedTodoList.Core.Models.Auth;

namespace AdvancedTodoList.Core.Dtos;
namespace AdvancedTodoList.Application.Dtos;

/// <summary>
/// Represents a minimal view DTO for <see cref="ApplicationUser" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace AdvancedTodoList.Core.Dtos;
namespace AdvancedTodoList.Application.Dtos;

/// <summary>
/// DTO for log in.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace AdvancedTodoList.Core.Dtos;
namespace AdvancedTodoList.Application.Dtos;

/// <summary>
/// Represents a DTO for invitation link view.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using AdvancedTodoList.Core.Models.TodoLists.Members;

namespace AdvancedTodoList.Core.Dtos;
namespace AdvancedTodoList.Application.Dtos;

/// <summary>
/// Represents an aggregate of a to-do list member with a role.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace AdvancedTodoList.Core.Dtos;
namespace AdvancedTodoList.Application.Dtos;

public record TodoItemCategoryCreateDto(string Name);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace AdvancedTodoList.Core.Dtos;
namespace AdvancedTodoList.Application.Dtos;

/// <summary>
/// DTO for creating/editing a to-do list.
Expand Down
37 changes: 37 additions & 0 deletions AdvancedTodoList.Application/Dtos/TodoListItemDtos.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using AdvancedTodoList.Core.Models.TodoLists;

namespace AdvancedTodoList.Application.Dtos;

/// <summary>
/// DTO for creating/editing a to-do list item.
/// </summary>
public record TodoItemCreateDto(
string Name, string Description, DateTime? DeadlineDate,
int Priority, int? CategoryId
);

/// <summary>
/// DTO for changing the state of a to-do list item.
/// </summary>
public record TodoItemUpdateStateDto(TodoItemState State);

/// <summary>
/// DTO for a full view of a to-do list item.
/// </summary>
public record TodoItemGetByIdDto(
int Id, string TodoListId, string Name,
string Description, DateTime? DeadlineDate,
TodoItemState State, int Priority,
ApplicationUserPreviewDto Owner,
TodoItemCategoryViewDto? Category
);

/// <summary>
/// DTO for a partial view of a to-do list item.
/// </summary>
public record TodoItemPreviewDto(
int Id, string TodoListId, string Name,
DateTime? DeadlineDate, TodoItemState State,
int Priority, ApplicationUserPreviewDto Owner,
TodoItemCategoryViewDto? Category
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace AdvancedTodoList.Core.Dtos;
namespace AdvancedTodoList.Application.Dtos;

/// <summary>
/// DTO for a minimal view of a to-do list member.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using AdvancedTodoList.Core.Models.TodoLists.Members;

namespace AdvancedTodoList.Core.Dtos;
namespace AdvancedTodoList.Application.Dtos;

/// <summary>
/// Represents a partial view for a role.
Expand Down
30 changes: 30 additions & 0 deletions AdvancedTodoList.Application/Mapping/MappingGlobalSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using AdvancedTodoList.Application.Dtos;
using AdvancedTodoList.Core.Models.TodoLists;
using AdvancedTodoList.Core.Models.TodoLists.Members;
using Mapster;

namespace AdvancedTodoList.Application.Mapping;

/// <summary>
/// Class that defines global mapping settings.
/// </summary>
public static class MappingGlobalSettings
{
/// <summary>
/// Apply global mapping settings.
/// </summary>
public static void Apply()
{
// Ignore null IDs
TypeAdapterConfig<TodoListMember, TodoListMemberPreviewDto>.NewConfig()
.IgnoreIf((src, dest) => src.RoleId == null, dest => dest.Role!);
TypeAdapterConfig<TodoItem, TodoItemGetByIdDto>.NewConfig()
.IgnoreIf((src, dest) => src.CategoryId == null, dest => dest.Category!);
TypeAdapterConfig<TodoItem, TodoItemPreviewDto>.NewConfig()
.IgnoreIf((src, dest) => src.CategoryId == null, dest => dest.Category!);

// Convert null strings into empty strings and trim strings
TypeAdapterConfig.GlobalSettings.Default
.AddDestinationTransform((string? dest) => dest != null ? dest.Trim() : string.Empty);
}
}
27 changes: 27 additions & 0 deletions AdvancedTodoList.Application/Options/AccessTokenOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace AdvancedTodoList.Application.Options;

/// <summary>
/// A class that contains access token options.
/// </summary>
public class AccessTokenOptions
{
/// <summary>
/// Valid audience of tokens.
/// </summary>
public string ValidAudience { get; set; } = null!;

/// <summary>
/// Valid issuer of tokens.
/// </summary>
public string ValidIssuer { get; set; } = null!;

/// <summary>
/// Seconds before token expires.
/// </summary>
public int ExpirationSeconds { get; set; }

/// <summary>
/// A secret key used for signing access tokens.
/// </summary>
public string SecretKey { get; set; } = null!;
}
17 changes: 17 additions & 0 deletions AdvancedTodoList.Application/Options/InvitationLinkOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace AdvancedTodoList.Application.Options;

/// <summary>
/// A class that contains invitation link options.
/// </summary>
public class InvitationLinkOptions
{
/// <summary>
/// Size of the refresh token in bytes.
/// </summary>
public int Size { get; set; }

/// <summary>
/// Days before token expires.
/// </summary>
public int ExpirationDays { get; set; }
}
17 changes: 17 additions & 0 deletions AdvancedTodoList.Application/Options/RefreshTokenOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace AdvancedTodoList.Application.Options;

/// <summary>
/// A class that contains refresh token options.
/// </summary>
public class RefreshTokenOptions
{
/// <summary>
/// Size of the refresh token in bytes.
/// </summary>
public int Size { get; set; }

/// <summary>
/// Days before token expires.
/// </summary>
public int ExpirationDays { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using AdvancedTodoList.Core.Models.Auth;

namespace AdvancedTodoList.Application.Services.Definitions.Auth;

/// <summary>
/// An interface for a service that manages access tokens.
/// </summary>
public interface IAccessTokensService
{
/// <summary>
/// Generates an access token for the user.
/// </summary>
/// <param name="user">User which will receive an access token.</param>
/// <returns>
/// A string that represents an access token.
/// </returns>
string GenerateAccessToken(ApplicationUser user);

/// <summary>
/// Validates an access token without checking expiration time and then returns
/// ID of the user stored in it asynchronously.
/// </summary>
/// <param name="accessToken">A string that represents an access token.</param>
/// <returns>
/// A user ID retrieved from the access token or <see langword="null" />, if validation failed.
/// </returns>
Task<string?> GetUserIdFromExpiredTokenAsync(string accessToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using AdvancedTodoList.Application.Dtos;

namespace AdvancedTodoList.Application.Services.Definitions.Auth;

/// <summary>
/// Interface for service that performs authentication operations.
/// </summary>
public interface IAuthService
{
/// <summary>
/// Logs a user in asynchronously.
/// </summary>
/// <param name="logInDto">Data required for logging in.</param>
/// <returns>
/// Returns a task representing the asynchronous operation, containing a
/// response with access and refresh tokens or null if authorization fails.
/// </returns>
Task<LogInResponse?> LogInAsync(LogInDto logInDto);

/// <summary>
/// Registers a new user asynchronously.
/// </summary>
/// <param name="registerDto">Data required for user registration.</param>
/// <returns>
/// Returns a task representing the asynchronous operation, containing the registration result.
/// </returns>
Task<RegisterResult> RegisterAsync(RegisterDto registerDto);

/// <summary>
/// Refreshes the access token asynchronously.
/// </summary>
/// <param name="refreshDto">Data required for token refresh.</param>
/// <returns>
/// Returns a task representing the asynchronous operation,
/// containing a response with access and refresh tokens or null if authorization fails.
/// </returns>
Task<LogInResponse?> RefreshAsync(RefreshDto refreshDto);

/// <summary>
/// Logs a user out asynchronously by revoking a refresh token.
/// </summary>
/// <param name="userId">ID of the caller.</param>
/// <param name="logOutDto">Data required for logging out.</param>
/// <returns>
/// Returns a task representing the asynchronous operation,
/// indicating the success or failure of the operation.
/// </returns>
Task<bool> LogOutAsync(string userId, LogOutDto logOutDto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using AdvancedTodoList.Core.Models;
using AdvancedTodoList.Core.Models.TodoLists.Members;

namespace AdvancedTodoList.Application.Services.Definitions.Auth;

/// <summary>
/// Interface for a service that checks user's permissions.
/// </summary>
public interface IPermissionsChecker
{
/// <summary>
/// Asynchronously checks whether the user is a member of the to-do list with
/// specified ID.
/// </summary>
/// <param name="context">To-do list context.</param>
/// <returns>
/// <see langword="true" /> if user is a member of the list; otherwise <see langword="false" />.
/// </returns>
Task<bool> IsMemberOfListAsync(TodoListContext context);

/// <summary>
/// Asynchronously checks whether the user is a member of the to-do list and
/// has a permission defined by the funciton <paramref name="permission"/>.
/// </summary>
/// <param name="context">To-do list context.</param>
/// <param name="permission">Function that should return <see langword="true"/> if user has required permission.</param>
/// <returns>
/// <see langword="true" /> if user is a member of the list and has required permission;
/// otherwise <see langword="false" />.
/// </returns>
Task<bool> HasPermissionAsync(TodoListContext context, Func<RolePermissions, bool> permission);

/// <summary>
/// Asynchronously checks whether the user can touch an entity.
/// </summary>
/// <remarks>
/// This method firstly checks whether <paramref name="entity"/> implements <see cref="IHasOwner"/>
/// interface and if yes, checks if the user is the owner of the entity and is a member of the to-do list;
/// otherwise the method checks if user has the permission defined by the function <paramref name="permission"/>.
/// </remarks>
/// <typeparam name="TEntity">Type of the entity.</typeparam>
/// <typeparam name="TKey">Type of the unique identifier used by the entity.</typeparam>
/// <param name="context">To-do list context.</param>
/// <param name="entity">ID of the entity.</param>
/// <param name="permission">Function that should return <see langword="true"/> if user has required permission.</param>
/// <returns>
/// <see langword="true"/> if user is either an owner of the entity and a member of a to-do list,
/// or he/she/they has permission defined by <paramref name="permission"/>; otherwise <see langword="false" />.
/// </returns>
Task<bool> CanTouchEntityAsync<TEntity, TKey>(TodoListContext context, TEntity entity,
Func<RolePermissions, bool> permission)
where TEntity : class, IEntity<TKey>
where TKey : IEquatable<TKey>;

/// <summary>
/// Asynchronously checks whether the user has a permission to change the role
/// with the priority of <paramref name="rolePriority"/>.
/// </summary>
/// <param name="context">To-do list context.</param>
/// <param name="rolePriority">ID of the role.</param>
/// <param name="permission">Function that should return <see langword="true"/> if user has required permission.</param>
/// <returns>
/// <see langword="true"/> if user has <paramref name="permission"/> and highest role priority than
/// the <paramref name="rolePriority"/>; otherwise <see langword="false" />.
/// </returns>
Task<bool> HasPermissionOverRoleAsync(TodoListContext context, int rolePriority,
Func<RolePermissions, bool> permission);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace AdvancedTodoList.Application.Services.Definitions.Auth;

/// <summary>
/// An interface for a service that manages refresh tokens.
/// </summary>
public interface IRefreshTokensService
{
/// <summary>
/// Generates a refresh token for the user and saves it asynchronously.
/// </summary>
/// <param name="userId">ID of the user who will receive the token.</param>
/// <returns>
/// A string that represents a refresh token or <see langword="null" /> if user does not exist.
/// </returns>
Task<string?> GenerateAsync(string userId);

/// <summary>
/// Revokes the refresh token of the user asynchronously
/// </summary>
/// <param name="userId">ID of the user whose token is being revoked.</param>
/// <param name="token">Value of the token to be revoked.</param>
/// <returns>
/// <see langword="true" /> on success; <see langword="false" /> otherwise.
/// </returns>
Task<bool> RevokeAsync(string userId, string token);

/// <summary>
/// Checks whether refresh token is valid asynchronously.
/// </summary>
/// <param name="userId">ID of the user whose token is being validated.</param>
/// <param name="token">Value of the token to be validated.</param>
/// <returns>
/// <see langword="true" /> if token is valid;
/// <see langword="false" /> otherwise.
/// </returns>
Task<bool> ValidateAsync(string userId, string token);
}
Loading