Read Only Accounts (#2658)
This commit is contained in:
parent
4f5bb57085
commit
9c84e19960
17 changed files with 155 additions and 65 deletions
|
|
@ -35,7 +35,13 @@ public static class PolicyConstants
|
|||
/// Used to give a user ability to Login to their account
|
||||
/// </summary>
|
||||
public const string LoginRole = "Login";
|
||||
/// <summary>
|
||||
/// Restricts the ability to manage their account without an admin
|
||||
/// </summary>
|
||||
/// <remarks>This is used explicitly for Demo Server. Not sure why it would be used in another fashion</remarks>
|
||||
public const string ReadOnlyRole = "Read Only";
|
||||
|
||||
|
||||
public static readonly ImmutableArray<string> ValidRoles =
|
||||
ImmutableArray.Create(AdminRole, PlebRole, DownloadRole, ChangePasswordRole, BookmarkRole, ChangeRestrictionRole, LoginRole);
|
||||
ImmutableArray.Create(AdminRole, PlebRole, DownloadRole, ChangePasswordRole, BookmarkRole, ChangeRestrictionRole, LoginRole, ReadOnlyRole);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,10 +77,11 @@ public class AccountController : BaseApiController
|
|||
[HttpPost("reset-password")]
|
||||
public async Task<ActionResult> UpdatePassword(ResetPasswordDto resetPasswordDto)
|
||||
{
|
||||
_logger.LogInformation("{UserName} is changing {ResetUser}'s password", User.GetUsername(), resetPasswordDto.UserName);
|
||||
|
||||
var user = await _userManager.Users.SingleOrDefaultAsync(x => x.UserName == resetPasswordDto.UserName);
|
||||
if (user == null) return Ok(); // Don't report BadRequest as that would allow brute forcing to find accounts on system
|
||||
_logger.LogInformation("{UserName} is changing {ResetUser}'s password", User.GetUsername(), resetPasswordDto.UserName);
|
||||
if (User.IsInRole(PolicyConstants.ReadOnlyRole))
|
||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "permission-denied"));
|
||||
var isAdmin = User.IsInRole(PolicyConstants.AdminRole);
|
||||
|
||||
if (resetPasswordDto.UserName == User.GetUsername() && !(User.IsInRole(PolicyConstants.ChangePasswordRole) || isAdmin))
|
||||
|
|
@ -319,6 +320,7 @@ public class AccountController : BaseApiController
|
|||
public async Task<ActionResult<string>> ResetApiKey()
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername()) ?? throw new KavitaUnauthenticatedUserException();
|
||||
if (User.IsInRole(PolicyConstants.ReadOnlyRole)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "permission-denied"));
|
||||
user.ApiKey = HashUtil.ApiKey();
|
||||
|
||||
if (_unitOfWork.HasChanges() && await _unitOfWork.CommitAsync())
|
||||
|
|
@ -345,7 +347,7 @@ public class AccountController : BaseApiController
|
|||
public async Task<ActionResult> UpdateEmail(UpdateEmailDto? dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
if (user == null) return Unauthorized(await _localizationService.Translate(User.GetUserId(), "permission-denied"));
|
||||
if (user == null || User.IsInRole(PolicyConstants.ReadOnlyRole)) return Unauthorized(await _localizationService.Translate(User.GetUserId(), "permission-denied"));
|
||||
|
||||
if (dto == null || string.IsNullOrEmpty(dto.Email) || string.IsNullOrEmpty(dto.Password))
|
||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "invalid-payload"));
|
||||
|
|
@ -450,7 +452,7 @@ public class AccountController : BaseApiController
|
|||
if (user == null) return Unauthorized(await _localizationService.Translate(User.GetUserId(), "permission-denied"));
|
||||
|
||||
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
||||
if (!await _accountService.HasChangeRestrictionRole(user)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "permission-denied"));
|
||||
if (!await _accountService.CanChangeAgeRestriction(user)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "permission-denied"));
|
||||
|
||||
user.AgeRestriction = isAdmin ? AgeRating.NotApplicable : dto.AgeRating;
|
||||
user.AgeRestrictionIncludeUnknowns = isAdmin || dto.IncludeUnknowns;
|
||||
|
|
@ -898,7 +900,7 @@ public class AccountController : BaseApiController
|
|||
}
|
||||
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
if (!roles.Any(r => r is PolicyConstants.AdminRole or PolicyConstants.ChangePasswordRole))
|
||||
if (!roles.Any(r => r is PolicyConstants.AdminRole or PolicyConstants.ChangePasswordRole or PolicyConstants.ReadOnlyRole))
|
||||
return Unauthorized(await _localizationService.Translate(user.Id, "permission-denied"));
|
||||
|
||||
if (string.IsNullOrEmpty(user.Email) || !user.EmailConfirmed)
|
||||
|
|
@ -973,6 +975,7 @@ public class AccountController : BaseApiController
|
|||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
[Authorize("RequireAdminRole")]
|
||||
[HttpPost("resend-confirmation-email")]
|
||||
[EnableRateLimiting("Authentication")]
|
||||
public async Task<ActionResult<InviteUserResponse>> ResendConfirmationSendEmail([FromQuery] int userId)
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ public class SettingsController : BaseApiController
|
|||
/// Is the minimum information setup for Email to work
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Authorize]
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpGet("is-email-setup")]
|
||||
public async Task<ActionResult<bool>> IsEmailSetup()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ public class Volume : IEntityDate, IHasReadTimeEstimate
|
|||
{
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// A String representation of the volume number. Allows for floats.
|
||||
/// A String representation of the volume number. Allows for floats. Can also include a range (1-2).
|
||||
/// </summary>
|
||||
/// <remarks>For Books with Series_index, this will map to the Series Index.</remarks>
|
||||
public required string Name { get; set; }
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public interface IAccountService
|
|||
Task<IEnumerable<ApiException>> ValidateEmail(string email);
|
||||
Task<bool> HasBookmarkPermission(AppUser? user);
|
||||
Task<bool> HasDownloadPermission(AppUser? user);
|
||||
Task<bool> HasChangeRestrictionRole(AppUser? user);
|
||||
Task<bool> CanChangeAgeRestriction(AppUser? user);
|
||||
}
|
||||
|
||||
public class AccountService : IAccountService
|
||||
|
|
@ -128,14 +128,15 @@ public class AccountService : IAccountService
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the user have Change Restriction permission or admin rights
|
||||
/// Does the user have Change Restriction permission or admin rights and not Read Only
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> HasChangeRestrictionRole(AppUser? user)
|
||||
public async Task<bool> CanChangeAgeRestriction(AppUser? user)
|
||||
{
|
||||
if (user == null) return false;
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
if (roles.Contains(PolicyConstants.ReadOnlyRole)) return false;
|
||||
return roles.Contains(PolicyConstants.ChangePasswordRole) || roles.Contains(PolicyConstants.AdminRole);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue