diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index 01eb52412..62ad0ebb8 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -78,7 +78,6 @@ namespace API.Controllers /// /// /// - [Authorize(Policy = "RequireAdminRole")] [HttpPost("register")] public async Task> Register(RegisterDto registerDto) { @@ -89,6 +88,17 @@ namespace API.Controllers return BadRequest("Username is taken."); } + // If we are registering an admin account, ensure there are no existing admins or user registering is an admin + if (registerDto.IsAdmin) + { + var firstTimeFlow = !(await _userManager.GetUsersInRoleAsync("Admin")).Any(); + if (!firstTimeFlow && !await _unitOfWork.UserRepository.IsUserAdminAsync( + await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername()))) + { + return BadRequest("You are not permitted to create an admin account"); + } + } + var user = _mapper.Map(registerDto); user.UserPreferences ??= new AppUserPreferences(); user.ApiKey = HashUtil.ApiKey(); @@ -104,6 +114,7 @@ namespace API.Controllers if (!result.Succeeded) return BadRequest(result.Errors); + var role = registerDto.IsAdmin ? PolicyConstants.AdminRole : PolicyConstants.PlebRole; var roleResult = await _userManager.AddToRoleAsync(user, role); @@ -156,7 +167,7 @@ namespace API.Controllers if (user == null) return Unauthorized("Invalid username"); - var isAdmin = await _unitOfWork.UserRepository.IsUserAdmin(user); + var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user); var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync(); if (!settings.EnableAuthentication && !isAdmin) { diff --git a/API/Controllers/CollectionController.cs b/API/Controllers/CollectionController.cs index 2b69499de..9f297273f 100644 --- a/API/Controllers/CollectionController.cs +++ b/API/Controllers/CollectionController.cs @@ -32,7 +32,7 @@ namespace API.Controllers public async Task> GetAllTags() { var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername()); - var isAdmin = await _unitOfWork.UserRepository.IsUserAdmin(user); + var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user); if (isAdmin) { return await _unitOfWork.CollectionTagRepository.GetAllTagDtosAsync(); diff --git a/API/Controllers/OPDSController.cs b/API/Controllers/OPDSController.cs index 68b9c14a2..7de25dcf3 100644 --- a/API/Controllers/OPDSController.cs +++ b/API/Controllers/OPDSController.cs @@ -184,7 +184,7 @@ public class OpdsController : BaseApiController return BadRequest("OPDS is not enabled on this server"); var userId = await GetUser(apiKey); var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId); - var isAdmin = await _unitOfWork.UserRepository.IsUserAdmin(user); + var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user); IList tags = isAdmin ? (await _unitOfWork.CollectionTagRepository.GetAllTagDtosAsync()).ToList() : (await _unitOfWork.CollectionTagRepository.GetAllPromotedTagDtosAsync()).ToList(); @@ -220,7 +220,7 @@ public class OpdsController : BaseApiController return BadRequest("OPDS is not enabled on this server"); var userId = await GetUser(apiKey); var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId); - var isAdmin = await _unitOfWork.UserRepository.IsUserAdmin(user); + var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user); IEnumerable tags; if (isAdmin) diff --git a/API/Controllers/SeriesController.cs b/API/Controllers/SeriesController.cs index bf7de16e7..ec63e3b3e 100644 --- a/API/Controllers/SeriesController.cs +++ b/API/Controllers/SeriesController.cs @@ -154,7 +154,7 @@ namespace API.Controllers public async Task UpdateSeriesRating(UpdateSeriesRatingDto updateSeriesRatingDto) { var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Ratings); - var userRating = await _unitOfWork.UserRepository.GetUserRating(updateSeriesRatingDto.SeriesId, user.Id) ?? + var userRating = await _unitOfWork.UserRepository.GetUserRatingAsync(updateSeriesRatingDto.SeriesId, user.Id) ?? new AppUserRating(); userRating.Rating = updateSeriesRatingDto.UserRating; diff --git a/API/Data/Repositories/UserRepository.cs b/API/Data/Repositories/UserRepository.cs index 2b3bfd478..138ef15b8 100644 --- a/API/Data/Repositories/UserRepository.cs +++ b/API/Data/Repositories/UserRepository.cs @@ -32,8 +32,8 @@ public interface IUserRepository Task> GetMembersAsync(); Task> GetAdminUsersAsync(); Task> GetNonAdminUsersAsync(); - Task IsUserAdmin(AppUser user); - Task GetUserRating(int seriesId, int userId); + Task IsUserAdminAsync(AppUser user); + Task GetUserRatingAsync(int seriesId, int userId); Task GetPreferencesAsync(string username); Task> GetBookmarkDtosForSeries(int userId, int seriesId); Task> GetBookmarkDtosForVolume(int userId, int volumeId); @@ -208,12 +208,12 @@ public class UserRepository : IUserRepository return await _userManager.GetUsersInRoleAsync(PolicyConstants.PlebRole); } - public async Task IsUserAdmin(AppUser user) + public async Task IsUserAdminAsync(AppUser user) { return await _userManager.IsInRoleAsync(user, PolicyConstants.AdminRole); } - public async Task GetUserRating(int seriesId, int userId) + public async Task GetUserRatingAsync(int seriesId, int userId) { return await _context.AppUserRating.Where(r => r.SeriesId == seriesId && r.AppUserId == userId) .SingleOrDefaultAsync(); diff --git a/API/Program.cs b/API/Program.cs index 713e2c404..73b9b5b7f 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -56,51 +56,29 @@ namespace API try { + var logger = services.GetRequiredService>(); var context = services.GetRequiredService(); + var pendingMigrations = await context.Database.GetPendingMigrationsAsync(); + if (pendingMigrations.Any()) + { + logger.LogInformation("Performing backup as migrations are needed. Backup will be kavita.db in temp folder"); + directoryService.CopyFileToDirectory(directoryService.FileSystem.Path.Join(directoryService.ConfigDirectory, "kavita.db"), directoryService.TempDirectory); + } + + await context.Database.MigrateAsync(); + var roleManager = services.GetRequiredService>(); + + await Seed.SeedRoles(roleManager); + await Seed.SeedSettings(context, directoryService); + await Seed.SeedUserApiKeys(context); if (isDocker && new FileInfo("data/appsettings.json").Exists) { - var logger = services.GetRequiredService>(); + //var logger = services.GetRequiredService>(); logger.LogCritical("WARNING! Mount point is incorrect, nothing here will persist. Please change your container mount from /kavita/data to /kavita/config"); return; } - - var requiresCoverImageMigration = !Directory.Exists(directoryService.CoverImageDirectory); - try - { - // If this is a new install, tables wont exist yet - if (requiresCoverImageMigration) - { - MigrateCoverImages.ExtractToImages(context, directoryService, services.GetRequiredService()); - } - } - catch (Exception) - { - requiresCoverImageMigration = false; - } - - if (requiresCoverImageMigration) - { - await MigrateCoverImages.UpdateDatabaseWithImages(context, directoryService); - } - - // // Apply all migrations on startup - // // If we have pending migrations, make a backup first - // var pendingMigrations = await context.Database.GetPendingMigrationsAsync(); - // if (pendingMigrations.Any()) - // { - // var logger = services.GetRequiredService>(); - // logger.LogInformation("Performing backup as migrations are needed"); - // // var backupService = services.GetRequiredService(); - // // await backupService.BackupDatabase(); - // } - // - // await context.Database.MigrateAsync(); - // - // await Seed.SeedRoles(roleManager); - // await Seed.SeedSettings(context, directoryService); - // await Seed.SeedUserApiKeys(context); } catch (Exception ex) { diff --git a/API/Startup.cs b/API/Startup.cs index b6bd5630e..f7eb31aee 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -134,7 +134,7 @@ namespace API // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IBackgroundJobClient backgroundJobs, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime, IServiceProvider serviceProvider, ICacheService cacheService, - IDirectoryService directoryService, IUnitOfWork unitOfWork, IBackupService backupService) + IDirectoryService directoryService, IUnitOfWork unitOfWork, IBackupService backupService, IImageService imageService) { // Apply Migrations @@ -144,26 +144,44 @@ namespace API { // Apply all migrations on startup // If we have pending migrations, make a backup first + var isDocker = new OsInfo(Array.Empty()).IsDocker; var logger = serviceProvider.GetRequiredService>(); var context = serviceProvider.GetRequiredService(); - 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 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>(); + // + // await Seed.SeedRoles(roleManager); + // await Seed.SeedSettings(context, directoryService); + // await Seed.SeedUserApiKeys(context); await MigrateBookmarks.Migrate(directoryService, unitOfWork, logger, cacheService); - var roleManager = serviceProvider.GetRequiredService>(); - - await Seed.SeedRoles(roleManager); - await Seed.SeedSettings(context, directoryService); - await Seed.SeedUserApiKeys(context); + var requiresCoverImageMigration = !Directory.Exists(directoryService.CoverImageDirectory); + try + { + // If this is a new install, tables wont exist yet + if (requiresCoverImageMigration) + { + MigrateCoverImages.ExtractToImages(context, directoryService, imageService); + } + } + catch (Exception) + { + requiresCoverImageMigration = false; + } + if (requiresCoverImageMigration) + { + await MigrateCoverImages.UpdateDatabaseWithImages(context, directoryService); + } }).GetAwaiter() .GetResult(); }