Change to saving the admin promote password in redis
For initial creation of an admin account, when no account with the `admin` role is found on startup, wave creates a password and displays it in the log. Previously, this value was saved temporarely in a file, /app/admin.txt. On some deployments e.g. kubernetes this caused crashes, because this location isnt writable by the "app" user wave is running as. Now, wave saves this value in redis instead.
This commit is contained in:
parent
1126ac87c4
commit
c5cf0b1362
|
@ -5,6 +5,8 @@
|
|||
@using Wave.Components.Account.Shared
|
||||
@using Wave.Data
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.Extensions.Caching.Distributed;
|
||||
@using System.Text;
|
||||
|
||||
@attribute [Authorize]
|
||||
@inject IdentityUserAccessor UserAccessor
|
||||
|
@ -13,6 +15,7 @@
|
|||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject IdentityRedirectManager RedirectManager
|
||||
@inject IStringLocalizer<Admin> Localizer
|
||||
@inject IDistributedCache WaveDistributedCache
|
||||
|
||||
<PageTitle>@(TitlePrefix + Localizer["Title"])</PageTitle>
|
||||
|
||||
|
@ -52,8 +55,8 @@
|
|||
protected override async Task OnInitializedAsync() {
|
||||
User = await UserAccessor.GetRequiredUserAsync(HttpContext);
|
||||
|
||||
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "admin.txt");
|
||||
if (File.Exists(path)) Password = await File.ReadAllTextAsync(path);
|
||||
Password = await WaveDistributedCache.GetStringAsync("admin_promote_key")
|
||||
?? "";
|
||||
}
|
||||
|
||||
private async Task Promote() {
|
||||
|
@ -76,7 +79,7 @@
|
|||
}
|
||||
}
|
||||
await UserManager.AddToRoleAsync(User, "Admin");
|
||||
File.Delete(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "admin.txt"));
|
||||
await WaveDistributedCache.RemoveAsync("admin_promote_key");
|
||||
await SignInManager.RefreshSignInAsync(User);
|
||||
Message = "You have been promoted, this tool is now disabled.";
|
||||
}
|
||||
|
@ -84,4 +87,4 @@
|
|||
private sealed class InputModel {
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StackExchange.Redis;
|
||||
using System.Text;
|
||||
using Tomlyn.Extensions.Configuration;
|
||||
using Wave.Components;
|
||||
using Wave.Components.Account;
|
||||
|
@ -79,7 +81,7 @@
|
|||
.AddPolicy("ArticleDeletePermissions", p => p.RequireRole("Moderator", "Admin"))
|
||||
.AddPolicy("CategoryManagePermissions", p => p.RequireRole("Admin"))
|
||||
.AddPolicy("RoleAssignPermissions", p => p.RequireRole("Admin"))
|
||||
|
||||
|
||||
.AddPolicy("ArticleEditOrReviewPermissions", p => p.RequireRole("Author", "Reviewer", "Admin"));
|
||||
builder.Services.AddAuthentication(options => {
|
||||
options.DefaultScheme = IdentityConstants.ApplicationScheme;
|
||||
|
@ -90,7 +92,7 @@
|
|||
|
||||
#region Identity
|
||||
|
||||
string connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
|
||||
string connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
|
||||
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
|
||||
builder.Services.AddDbContextFactory<ApplicationDbContext>(options =>
|
||||
options.UseNpgsql(connectionString));
|
||||
|
@ -116,7 +118,7 @@
|
|||
|
||||
builder.Services.Configure<Features>(builder.Configuration.GetSection(nameof(Features)));
|
||||
builder.Services.Configure<Customization>(builder.Configuration.GetSection(nameof(Customization)));
|
||||
builder.Services.AddCascadingValue("TitlePrefix",
|
||||
builder.Services.AddCascadingValue("TitlePrefix",
|
||||
sf => (sf.GetService<IOptions<Customization>>()?.Value.AppName ?? "Wave") + " - ");
|
||||
|
||||
var smtpConfig = builder.Configuration.GetSection("Email:Smtp");
|
||||
|
@ -190,10 +192,19 @@
|
|||
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||
if (userManager.GetUsersInRoleAsync("Admin").Result.Any() is false) {
|
||||
string admin = Guid.NewGuid().ToString("N")[..16];
|
||||
IDistributedCache cache = app.Services.GetRequiredService<IDistributedCache>();
|
||||
|
||||
// Check first wheter the password exists already
|
||||
string admin = await cache.GetStringAsync("admin_promote_key");
|
||||
|
||||
// If it does not exist, create a new one and save it to redis
|
||||
if (string.IsNullOrWhiteSpace(admin)){
|
||||
admin = Guid.NewGuid().ToString("N")[..16];
|
||||
await cache.SetAsync("admin_promote_key", Encoding.UTF8.GetBytes(admin), new DistributedCacheEntryOptions{});
|
||||
}
|
||||
|
||||
app.Logger.LogWarning("There is currently no user in your installation with the admin role, " +
|
||||
"go to /Admin and use the following password to self promote your account: {admin}", admin);
|
||||
File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "admin.txt"), admin);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue