Ability to restrict a user's ability to change passwords (#1018)
* Implemented a new role "Change Password". This role allows you to change your own password. By default, all users will have it. A user can have it removed arbitrarliy. Removed components that are no longer going to be used. * Cleaned up some code
This commit is contained in:
parent
9d20343f4e
commit
6ee8320c2b
16 changed files with 48 additions and 174 deletions
|
@ -20,8 +20,12 @@ namespace API.Constants
|
|||
/// Used to give a user ability to download files from the server
|
||||
/// </summary>
|
||||
public const string DownloadRole = "Download";
|
||||
/// <summary>
|
||||
/// Used to give a user ability to change their own password
|
||||
/// </summary>
|
||||
public const string ChangePasswordRole = "Change Password";
|
||||
|
||||
public static readonly ImmutableArray<string> ValidRoles =
|
||||
ImmutableArray.Create(AdminRole, PlebRole, DownloadRole);
|
||||
ImmutableArray.Create(AdminRole, PlebRole, DownloadRole, ChangePasswordRole);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace API.Controllers
|
|||
_logger.LogInformation("{UserName} is changing {ResetUser}'s password", User.GetUsername(), resetPasswordDto.UserName);
|
||||
var user = await _userManager.Users.SingleAsync(x => x.UserName == resetPasswordDto.UserName);
|
||||
|
||||
if (resetPasswordDto.UserName != User.GetUsername() && !User.IsInRole(PolicyConstants.AdminRole))
|
||||
if (resetPasswordDto.UserName != User.GetUsername() && !(User.IsInRole(PolicyConstants.AdminRole) || User.IsInRole(PolicyConstants.ChangePasswordRole)))
|
||||
return Unauthorized("You are not permitted to this operation.");
|
||||
|
||||
var errors = await _accountService.ChangeUserPassword(user, resetPasswordDto.Password);
|
||||
|
@ -245,45 +245,6 @@ namespace API.Controllers
|
|||
f => (string) f.GetValue(null)).Values.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given roles to the user.
|
||||
/// </summary>
|
||||
/// <param name="updateRbsDto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("update-rbs")]
|
||||
public async Task<ActionResult> UpdateRoles(UpdateRbsDto updateRbsDto)
|
||||
{
|
||||
var user = await _userManager.Users
|
||||
.Include(u => u.UserPreferences)
|
||||
.SingleOrDefaultAsync(x => x.NormalizedUserName == updateRbsDto.Username.ToUpper());
|
||||
if (updateRbsDto.Roles.Contains(PolicyConstants.AdminRole) ||
|
||||
updateRbsDto.Roles.Contains(PolicyConstants.PlebRole))
|
||||
{
|
||||
return BadRequest("Invalid Roles");
|
||||
}
|
||||
|
||||
var existingRoles = (await _userManager.GetRolesAsync(user))
|
||||
.Where(s => s != PolicyConstants.AdminRole && s != PolicyConstants.PlebRole)
|
||||
.ToList();
|
||||
|
||||
// Find what needs to be added and what needs to be removed
|
||||
var rolesToRemove = existingRoles.Except(updateRbsDto.Roles);
|
||||
var result = await _userManager.AddToRolesAsync(user, updateRbsDto.Roles);
|
||||
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
await _unitOfWork.RollbackAsync();
|
||||
return BadRequest("Something went wrong, unable to update user's roles");
|
||||
}
|
||||
if ((await _userManager.RemoveFromRolesAsync(user, rolesToRemove)).Succeeded)
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
|
||||
await _unitOfWork.RollbackAsync();
|
||||
return BadRequest("Something went wrong, unable to update user's roles");
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the API Key assigned with a user
|
||||
|
|
18
API/Data/MigrateChangePasswordRoles.cs
Normal file
18
API/Data/MigrateChangePasswordRoles.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
using API.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace API.Data;
|
||||
|
||||
public static class MigrateChangePasswordRoles
|
||||
{
|
||||
public static async Task Migrate(IUnitOfWork unitOfWork, UserManager<AppUser> userManager)
|
||||
{
|
||||
foreach (var user in await unitOfWork.UserRepository.GetAllUsers())
|
||||
{
|
||||
await userManager.RemoveFromRoleAsync(user, "ChangePassword");
|
||||
await userManager.AddToRoleAsync(user, PolicyConstants.ChangePasswordRole);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@ public interface IUserRepository
|
|||
Task<AppUser> GetUserWithReadingListsByUsernameAsync(string username);
|
||||
Task<IList<AppUserBookmark>> GetAllBookmarksByIds(IList<int> bookmarkIds);
|
||||
Task<AppUser> GetUserByEmailAsync(string email);
|
||||
Task<IEnumerable<AppUser>> GetAllUsers();
|
||||
}
|
||||
|
||||
public class UserRepository : IUserRepository
|
||||
|
@ -214,6 +215,11 @@ public class UserRepository : IUserRepository
|
|||
return await _context.AppUser.SingleOrDefaultAsync(u => u.Email.ToLower().Equals(email.ToLower()));
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<AppUser>> GetAllUsers()
|
||||
{
|
||||
return await _context.AppUser.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<AppUser>> GetAdminUsersAsync()
|
||||
{
|
||||
return await _userManager.GetUsersInRoleAsync(PolicyConstants.AdminRole);
|
||||
|
|
|
@ -67,6 +67,7 @@ namespace API.Extensions
|
|||
{
|
||||
opt.AddPolicy("RequireAdminRole", policy => policy.RequireRole(PolicyConstants.AdminRole));
|
||||
opt.AddPolicy("RequireDownloadRole", policy => policy.RequireRole(PolicyConstants.DownloadRole, PolicyConstants.AdminRole));
|
||||
opt.AddPolicy("RequireChangePasswordRole", policy => policy.RequireRole(PolicyConstants.ChangePasswordRole, PolicyConstants.AdminRole));
|
||||
});
|
||||
|
||||
return services;
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Net;
|
|||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
using API.Middleware;
|
||||
using API.Services;
|
||||
|
@ -20,6 +21,7 @@ using Microsoft.AspNetCore.Builder;
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.ResponseCompression;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
@ -140,27 +142,16 @@ namespace API
|
|||
Task.Run(async () =>
|
||||
{
|
||||
// Apply all migrations on startup
|
||||
// If we have pending migrations, make a backup first
|
||||
//var isDocker = new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker;
|
||||
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
|
||||
var context = serviceProvider.GetRequiredService<DataContext>();
|
||||
// var pendingMigrations = await context.Database.GetPendingMigrationsAsync();
|
||||
// if (pendingMigrations.Any())
|
||||
// {
|
||||
// logger.LogInformation("Performing backup as migrations are needed");
|
||||
// await backupService.BackupDatabase();
|
||||
// }
|
||||
//
|
||||
// await context.Database.MigrateAsync();
|
||||
// var roleManager = serviceProvider.GetRequiredService<RoleManager<AppRole>>();
|
||||
//
|
||||
// await Seed.SeedRoles(roleManager);
|
||||
// await Seed.SeedSettings(context, directoryService);
|
||||
// await Seed.SeedUserApiKeys(context);
|
||||
var userManager = serviceProvider.GetRequiredService<UserManager<AppUser>>();
|
||||
|
||||
|
||||
await MigrateBookmarks.Migrate(directoryService, unitOfWork,
|
||||
logger, cacheService);
|
||||
|
||||
await MigrateChangePasswordRoles.Migrate(unitOfWork, userManager);
|
||||
|
||||
var requiresCoverImageMigration = !Directory.Exists(directoryService.CoverImageDirectory);
|
||||
try
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue