Skip to content

Commit

Permalink
Merge pull request #16 from romandykyi/enforce-permissions
Browse files Browse the repository at this point in the history
Enforce Permissions
  • Loading branch information
romandykyi authored Mar 10, 2024
2 parents 07398df + 4eba435 commit ed89e51
Show file tree
Hide file tree
Showing 35 changed files with 2,035 additions and 977 deletions.
2 changes: 1 addition & 1 deletion AdvancedTodoList.Core/Dtos/TodoListMembersDtos.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public record TodoListMemberPreviewDto(int Id, ApplicationUserPreviewDto User, T
/// <summary>
/// DTO for adding a member to a to-do list.
/// </summary>
public record TodoListMemberAddDto(string UserId, int? RoleId = null);
public record TodoListMemberAddDto(string UserId);

/// <summary>
/// DTO for updating to-do list member's role.
Expand Down
2 changes: 1 addition & 1 deletion AdvancedTodoList.Core/Models/IHasOwner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ public interface IHasOwner
/// <summary>
/// Foreign key referencing the user who created this entity.
/// </summary>
public string? OwnerId { get; }
public string? OwnerId { get; set; }
}
30 changes: 13 additions & 17 deletions AdvancedTodoList.Core/Services/Auth/IPermissionsChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,23 @@ public interface IPermissionsChecker
/// Asynchronously checks whether the user is a member of the to-do list with
/// specified ID.
/// </summary>
/// <param name="userId">ID of the user.</param>
/// <param name="todoListId">ID of the to-do list.</param>
/// <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(string userId, string todoListId);
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="userId">ID of the user.</param>
/// <param name="todoListId">ID of the to-do list.</param>
/// <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(string userId, string todoListId, Func<RolePermissions, bool> permission);
Task<bool> HasPermissionAsync(TodoListContext context, Func<RolePermissions, bool> permission);

/// <summary>
/// Asynchronously checks whether the user can touch an entity.
Expand All @@ -42,31 +40,29 @@ public interface IPermissionsChecker
/// </remarks>
/// <typeparam name="TEntity">Type of the entity.</typeparam>
/// <typeparam name="TKey">Type of the unique identifier used by the entity.</typeparam>
/// <param name="userId">ID of the user whose permissions are achecked.</param>
/// <param name="todoListId">ID of the to-do list for which permission is checked.</param>
/// <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>(string userId, string todoListId,
TEntity entity, Func<RolePermissions, bool> permission)
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
/// defined by <paramref name="roleId"/>.
/// with the priority of <paramref name="rolePriority"/>.
/// </summary>
/// <param name="userId">ID of the user.</param>
/// <param name="todoListId">ID of the to-do list for which permission is checked.</param>
/// <param name="roleId">ID of the role.</param>
/// <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 role defined by <paramref name="roleId"/>; otherwise <see langword="false" />.
/// the <paramref name="rolePriority"/>; otherwise <see langword="false" />.
/// </returns>
Task<bool> HasPermissionOverRoleAsync(string userId, string todoListId,
int roleId, Func<RolePermissions, bool> permission);
Task<bool> HasPermissionOverRoleAsync(TodoListContext context, int rolePriority,
Func<RolePermissions, bool> permission);
}
53 changes: 20 additions & 33 deletions AdvancedTodoList.Core/Services/ITodoItemsService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using AdvancedTodoList.Core.Dtos;
using AdvancedTodoList.Core.Models.TodoLists;
using AdvancedTodoList.Core.Pagination;

namespace AdvancedTodoList.Core.Services;
Expand All @@ -12,76 +11,64 @@ public interface ITodoItemsService
/// <summary>
/// Retrieves a page of to-do list items of the list with the specified ID.
/// </summary>
/// <param name="todoListId">The ID of the to-do list which items will be retrieved.</param>
/// <param name="context">To-do list context.</param>
/// <param name="paginationParameters">Pagination parameters to use.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains a page of <see cref="TodoItemPreviewDto"/> objects or
/// <see langword="null" /> if the to-do list does not exist.
/// A task representing the asynchronous operation containing the result of operation.
/// </returns>
public Task<Page<TodoItemPreviewDto>?> GetItemsOfListAsync(string todoListId, PaginationParameters paginationParameters);
public Task<ServiceResponse<Page<TodoItemPreviewDto>>> GetItemsOfListAsync(
TodoListContext context, PaginationParameters paginationParameters);

/// <summary>
/// Retrieves a to-do list item by its ID asynchronously.
/// </summary>
/// <param name="todoListId">The ID of the to-do list which contains the item.</param>
/// <param name="context">To-do list context.</param>
/// <param name="itemId">The ID of the to-do list item to retrieve.</param>
/// <returns>
/// A task representing the asynchronous operation. The task result contains
/// a <see cref="TodoItemGetByIdDto"/> object if the specified ID is found;
/// otherwise, returns <see langword="null"/>.
/// A task representing the asynchronous operation containing the result of operation.
/// </returns>
public Task<TodoItemGetByIdDto?> GetByIdAsync(string todoListId, int itemId);
public Task<ServiceResponse<TodoItemGetByIdDto>> GetByIdAsync(TodoListContext context, int itemId);

/// <summary>
/// Creates a new to-do list item asynchronously.
/// </summary>
/// <param name="todoListId">The ID of the to-do list to associate the item with.</param>
/// <param name="context">To-do list context.</param>
/// <param name="dto">The DTO containing information for creating the to-do list item.</param>
/// <param name="callerId">ID of the user who creates the to-do list item.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains the created <see cref="TodoItem"/> mapped to
/// <see cref="TodoItemGetByIdDto"/> or <see langword="null" /> if to-do list with ID
/// <paramref name="todoListId"/> does not exist.
/// A task representing the asynchronous operation containing the result of operation.
/// </returns>
public Task<TodoItemGetByIdDto?> CreateAsync(string todoListId, TodoItemCreateDto dto, string callerId);
public Task<ServiceResponse<TodoItemGetByIdDto>> CreateAsync(TodoListContext context, TodoItemCreateDto dto, string callerId);

/// <summary>
/// Edits a to-do list item asynchronously.
/// </summary>
/// <param name="todoListId">The ID of the to-do list which contains the item.</param>
/// <param name="context">To-do list context.</param>
/// <param name="itemId">The ID of the to-do list item to edit.</param>
/// <param name="dto">The DTO containing information for editing the to-do list item.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains <see langword="true"/> on success;
/// otherwise <see langword="false"/> if the entity was not found.
/// A task representing the asynchronous operation containing the result of operation.
/// </returns>
public Task<bool> EditAsync(string todoListId, int itemId, TodoItemCreateDto dto);
public Task<ServiceResponseStatus> EditAsync(TodoListContext context, int itemId, TodoItemCreateDto dto);

/// <summary>
/// Updates the state of a to-do list item asynchronously.
/// </summary>
/// <param name="todoListId">The ID of the to-do list which contains the item.</param>
/// <param name="context">To-do list context.</param>
/// <param name="itemId">The ID of the to-do list item to update the state.</param>
/// <param name="stateDto">The DTO which contains the state of the to-do item to set.</param></param>
/// <param name="stateDto">The DTO which contains the state of the to-do item to set.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains <see langword="true"/> on success;
/// otherwise <see langword="false"/> if the entity was not found.
/// A task representing the asynchronous operation containing the result of operation.
/// </returns>
public Task<bool> UpdateStateAsync(string todoListId, int itemId, TodoItemUpdateStateDto stateDto);
public Task<ServiceResponseStatus> UpdateStateAsync(TodoListContext context, int itemId, TodoItemUpdateStateDto stateDto);

/// <summary>
/// Deletes a to-do list item asynchronously.
/// </summary>
/// <param name="todoListId">The ID of the to-do list which contains the item.</param>
/// <param name="context">To-do list context.</param>
/// <param name="itemId">The ID of the to-do list item to delete.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains <see langword="true"/> on success;
/// otherwise <see langword="false"/> if the entity was not found.
/// A task representing the asynchronous operation containing the result of operation.
/// </returns>
public Task<bool> DeleteAsync(string todoListId, int itemId);
public Task<ServiceResponseStatus> DeleteAsync(TodoListContext context, int itemId);
}
61 changes: 34 additions & 27 deletions AdvancedTodoList.Core/Services/ITodoListDependantEntitiesService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using AdvancedTodoList.Core.Models;
using AdvancedTodoList.Core.Models.TodoLists;
using AdvancedTodoList.Core.Models.TodoLists.Members;
using AdvancedTodoList.Core.Pagination;
using AdvancedTodoList.Core.Specifications;

Expand All @@ -19,69 +20,75 @@ public interface ITodoListDependantEntitiesService<TEntity, TKey>
/// Retrieves a page of to-do list dependant entities mapped to <typeparamref name="TDto"/>.
/// </summary>
/// <remarks>
/// This method checks if <paramref name="todoListId"/> is valid, but doesn't filter by it.
/// This method checks if to-do list ID is valid, but doesn't filter by it.
/// Filtering should be done in <paramref name="specification"/>.
/// </remarks>
/// <param name="todoListId">The ID of the to-do list which items should be retrieved.</param>
/// <param name="context">To-do list context.</param>
/// <param name="specification">Specification to apply to entities.</param>
/// <param name="paginationParameters">Pagination parameters to use.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains a page of <see cref="TDto"/> objects or
/// <see langword="null" /> if the to-do list does not exist.
/// A task representing the asynchronous operation. The task contains
/// a result of the operation and the requested page on success.
/// </returns>
public Task<Page<TDto>?> GetPageAsync<TDto>(string todoListId,
public Task<ServiceResponse<Page<TDto>>> GetPageAsync<TDto>(TodoListContext context,
ISpecification<TEntity> specification, PaginationParameters paginationParameters);

/// <summary>
/// Retrieves a to-do list dependant entity by its ID asynchronously and maps it to <typeparamref name="TDto"/>.
/// </summary>
/// <param name="todoListId">The ID of the to-do list which entity will be retrieved.</param>
/// <param name="context">To-do list context.</param>
/// <param name="entityId">The ID of the entity to retrieve.</param>
/// <typeparam name="TDto">DTO to map entity to.</typeparam>
/// <returns>
/// A task representing the asynchronous operation. The task result contains
/// a <typeparamref name="TDto"/> object if the specified ID is found;
/// otherwise, returns <see langword="null"/>.
/// A task representing the asynchronous operation. The task contains
/// a result of the operation and the requested dto on success.
/// </returns>
Task<TDto?> GetByIdAsync<TDto>(string todoListId, TKey entityId) where TDto : class;
Task<ServiceResponse<TDto>> GetByIdAsync<TDto>(TodoListContext context, TKey entityId) where TDto : class;

/// <summary>
/// Creates a new a to-do list dependant entity asynchronously from the DTO.
/// </summary>
/// <param name="todoListId">The ID of the to-do list to associate the entity with.</param>
/// <remarks>
/// If <typeparamref name="TEntity"/> implements the <see cref="IHasOwner"/> interface, then
/// this method will set the caller as an owner.
/// </remarks>
/// <param name="context">To-do list context.</param>
/// <param name="dto">The DTO containing information for creating the entity.</param>
/// <param name="permission">Optional accessor for the permission required for the user to perform the action.</param>
/// <returns>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains the created <typeparamref name="TEntity"/> mapped to
/// <typeparamref name="TOutputDto"/> or <see langword="null" /> if to-do list with ID
/// <paramref name="todoListId"/> does not exist.
/// A task representing the asynchronous operation. The task contains
/// a result of the operation and the created <typeparamref name="TEntity"/> mapped to
/// <typeparamref name="TOutputDto"/> on success.
/// </returns>
public Task<TOutputDto?> CreateAsync<TInputDto, TOutputDto>(string todoListId, TInputDto dto)
public Task<ServiceResponse<TOutputDto>> CreateAsync<TInputDto, TOutputDto>(
TodoListContext context, TInputDto dto, Func<RolePermissions, bool>? permission = null)
where TOutputDto : class;

/// <summary>
/// Edits a to-do list dependant entity asynchronously.
/// </summary>
/// <param name="todoListId">The ID of the to-do list which contains the entity.</param>
/// <param name="context">To-do list context.</param>
/// <param name="entityId">The ID of the entity to edit.</param>
/// <param name="dto">The DTO containing information for editing the entity.</param>
/// <param name="permission">Optional accessor for the permission required for the user to perform the action.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains <see langword="true"/> on success;
/// otherwise <see langword="false"/> if the entity or the to-do list was not found.
/// A task representing the asynchronous operation. The task contains
/// a result of the operation.
/// </returns>
public Task<bool> UpdateAsync<TDto>(string todoListId, TKey entityId, TDto dto);
public Task<ServiceResponseStatus> UpdateAsync<TDto>(TodoListContext context, TKey entityId,
TDto dto, Func<RolePermissions, bool>? permission = null);

/// <summary>
/// Deletes a to-do list dependant entity asynchronously.
/// </summary>
/// <param name="todoListId">The ID of the to-do list which contains the entity.</param>
/// <param name="context">To-do list context.</param>
/// <param name="entityId">The ID of the entity to delete.</param>
/// <param name="permission">Optional accessor for the permission required for the user to perform the action.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains <see langword="true"/> on success;
/// otherwise <see langword="false"/> if the entity was not found.
/// A task representing the asynchronous operation. The task contains
/// a result of the operation.
/// </returns>
public Task<bool> DeleteAsync(string todoListId, TKey entityId);
public Task<ServiceResponseStatus> DeleteAsync(TodoListContext context,
TKey entityId, Func<RolePermissions, bool>? permission = null);
}
25 changes: 11 additions & 14 deletions AdvancedTodoList.Core/Services/ITodoListMembersService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,42 @@ public interface ITodoListMembersService
/// <summary>
/// Gets a page with members of a to-do list asynchronously.
/// </summary>
/// <param name="todoListId">ID of the to-do list to add member to.</param>
/// <param name="context">To-do list context.</param>
/// <param name="paginationParameters">Pagination parameters to use.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains a page of <see cref="TodoListMemberPreviewDto"/> objects or
/// <see langword="null" /> if the to-do list does not exist.
/// A task representing the asynchronous operation containing the result of operation.
/// </returns>
Task<Page<TodoListMemberPreviewDto>?> GetMembersAsync(string todoListId, PaginationParameters paginationParameters);
Task<ServiceResponse<Page<TodoListMemberPreviewDto>>> GetMembersAsync(TodoListContext context, PaginationParameters paginationParameters);

/// <summary>
/// Adds a member to a to-do list asynchronously.
/// </summary>
/// <param name="todoListId">ID of the to-do list to add member to.</param>
/// <param name="context">To-do list context.</param>
/// <param name="dto">DTO that contains information needed for adding a member. Supossed to be valid.</param>
/// <returns>
/// A task representing the asynchronous operation containing the result of operation.
/// </returns>
Task<TodoListMemberServiceResult> AddMemberAsync(string todoListId, TodoListMemberAddDto dto);
Task<AddTodoListMemberServiceResult> AddMemberAsync(TodoListContext context, TodoListMemberAddDto dto);

/// <summary>
/// Updates a role of the member of a to-do list asynchronously.
/// </summary>
/// <param name="todoListId">ID of the to-do list.</param>
/// <param name="context">To-do list context.</param>
/// <param name="memberId">ID of the member.</param>
/// <param name="dto">DTO that contains information needed for updating a role.</param>
/// <returns>
/// A task representing the asynchronous operation containing the result of operation.
/// </returns>
Task<TodoListMemberServiceResult> UpdateMemberRoleAsync(string todoListId, int memberId, TodoListMemberUpdateRoleDto dto);
Task<TodoListMemberServiceResultStatus> UpdateMemberRoleAsync(TodoListContext context,
int memberId, TodoListMemberUpdateRoleDto dto);

/// <summary>
/// Removes a member from a to-do list asynchronously.
/// </summary>
/// <param name="todoListId">ID of the to-do list to remove member from.</param>
/// <param name="context">To-do list context.</param>
/// <param name="memberId">ID of the member.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// If user was removed successfully than <see langword="true"/> is returned;
/// otherwise <see langword="false" /> if the user or the to-do list was not found
/// A task representing the asynchronous operation containing the result of operation.
/// </returns>
Task<bool> RemoveMemberAsync(string todoListId, int memberId);
Task<ServiceResponseStatus> RemoveMemberAsync(TodoListContext context, int memberId);
}
Loading

0 comments on commit ed89e51

Please sign in to comment.