330 lines
16 KiB
Plaintext
330 lines
16 KiB
Plaintext
@namespace Oqtane.Modules.Admin.Users
|
|
@inherits ModuleBase
|
|
@inject IUserRoleService UserRoleService
|
|
@inject IUserService UserService
|
|
@inject ISettingService SettingService
|
|
@inject ISiteService SiteService
|
|
@inject IStringLocalizer<Index> Localizer
|
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
|
|
|
@if (userroles == null)
|
|
{
|
|
<p>
|
|
<em>@SharedLocalizer["Loading"]</em>
|
|
</p>
|
|
}
|
|
else
|
|
{
|
|
<TabStrip>
|
|
<TabPanel Name="Users" Heading="Users" ResourceKey="Users">
|
|
<div class="container">
|
|
<div class="row mb-1 align-items-center">
|
|
<div class="col-sm-4">
|
|
<ActionLink Action="Add" Text="Add User" ResourceKey="AddUser" />
|
|
</div>
|
|
<div class="col-sm-4">
|
|
<input class="form-control" @bind="@_search" />
|
|
</div>
|
|
<div class="col-sm-4">
|
|
<button type="button" class="btn btn-secondary" @onclick="OnSearch">@SharedLocalizer["Search"]</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Pager Items="@userroles">
|
|
<Header>
|
|
<th style="width: 1px;"> </th>
|
|
<th style="width: 1px;"> </th>
|
|
<th style="width: 1px;"> </th>
|
|
<th>@SharedLocalizer["Name"]</th>
|
|
<th>@SharedLocalizer["Username"]</th>
|
|
</Header>
|
|
<Row>
|
|
<td>
|
|
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="EditUser" />
|
|
</td>
|
|
<td>
|
|
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" />
|
|
</td>
|
|
<td>
|
|
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
|
</td>
|
|
<td>@context.User.DisplayName</td>
|
|
<td>@context.User.Username</td>
|
|
</Row>
|
|
</Pager>
|
|
</TabPanel>
|
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
|
<div class="container">
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="allowregistration" HelpText="Do You Want To Allow Visitors To Be Able To Register For A User Account On This Site?" ResourceKey="AllowRegistration">Allow User Registration? </Label>
|
|
<div class="col-sm-9">
|
|
<select id="allowregistration" class="form-select" @bind="@_allowregistration" required>
|
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
<option value="False">@SharedLocalizer["No"]</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<br />
|
|
<Section Name="Password" Heading="Password Settings" ResourceKey="PasswordSettings">
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="minimumlength" HelpText="The Minimum Length For A Password" ResourceKey="RequiredLength">Minimum Length:</Label>
|
|
<div class="col-sm-9">
|
|
<input id="minimumlength" class="form-control" @bind="@_minimumlength" required />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="uniquecharacters" HelpText="The Minimum Number Of Unique Characters Which A Password Must Contain" ResourceKey="UniqueCharacters">Unique Characters:</Label>
|
|
<div class="col-sm-9">
|
|
<input id="uniquecharacters" class="form-control" @bind="@_uniquecharacters" required />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="requiredigit" HelpText="Indicate If Passwords Must Contain A Digit" ResourceKey="RequireDigit">Require Digit?</Label>
|
|
<div class="col-sm-9">
|
|
<select id="requiredigit" class="form-select" @bind="@_requiredigit" required>
|
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
|
<option value="false">@SharedLocalizer["No"]</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="requireupper" HelpText="Indicate If Passwords Must Contain An Upper Case Character" ResourceKey="RequireUpper">Require Uppercase?</Label>
|
|
<div class="col-sm-9">
|
|
<select id="requireupper" class="form-select" @bind="@_requireupper" required>
|
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
|
<option value="false">@SharedLocalizer["No"]</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="requirelower" HelpText="Indicate If Passwords Must Contain A Lower Case Character" ResourceKey="RequireLower">Require Lowercase?</Label>
|
|
<div class="col-sm-9">
|
|
<select id="requirelower" class="form-select" @bind="@_requirelower" required>
|
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
|
<option value="false">@SharedLocalizer["No"]</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="requirepunctuation" HelpText="Indicate if Passwords Must Contain A Non-alphanumeric Character (ie. Punctuation)" ResourceKey="RequirePunctuation">Require Punctuation?</Label>
|
|
<div class="col-sm-9">
|
|
<select id="requirepunctuation" class="form-select" @bind="@_requirepunctuation" required>
|
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
|
<option value="false">@SharedLocalizer["No"]</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</Section>
|
|
<Section Name="Lockout" Heading="Lockout Settings" ResourceKey="LockoutSettings">
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="maximum" HelpText="The Maximum Number Of Sign In Attempts Before A User Is Locked Out" ResourceKey="MaximumFailures">Maximum Failures:</Label>
|
|
<div class="col-sm-9">
|
|
<input id="maximum" class="form-control" @bind="@_maximumfailures" required />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="lockoutduration" HelpText="The Number Of Minutes A User Should Be Locked Out" ResourceKey="LockoutDuration">Lockout Duration:</Label>
|
|
<div class="col-sm-9">
|
|
<input id="lockoutduration" class="form-control" @bind="@_lockoutduration" required />
|
|
</div>
|
|
</div>
|
|
</Section>
|
|
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="provider" HelpText="The OpenID Connect Provider Name. This Name Will Be Displayed On The Login Page" ResourceKey="Provider">Provider:</Label>
|
|
<div class="col-sm-9">
|
|
<input id="provider" class="form-control" @bind="@_provider" />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="authority" HelpText="The Authority Or Issuer URL Associated With The OpenID Connect Provider. " ResourceKey="Authority">Authority:</Label>
|
|
<div class="col-sm-9">
|
|
<input id="authority" class="form-control" @bind="@_authority" />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="clientid" HelpText="The OpenID Connect Client ID" ResourceKey="ClientID">Client ID:</Label>
|
|
<div class="col-sm-9">
|
|
<input id="clientid" class="form-control" @bind="@_clientid" />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="clientsecret" HelpText="The OpenID Connect Client Secret" ResourceKey="ClientSecret">Client Secret:</Label>
|
|
<div class="col-sm-9">
|
|
<input id="clientsecret" class="form-control" @bind="@_clientsecret" />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="metadata" HelpText="The Discovery Endpoint For Obtaining Metadata. Only Specify If The OpenID Connect Provider Does Not Use The Standard Approach (ie. /.well-known/openid-configuration)" ResourceKey="Metadata">Metadata Address:</Label>
|
|
<div class="col-sm-9">
|
|
<input id="metadata" class="form-control" @bind="@_metadata" />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="logouturl" HelpText="The Url For Logging Out The User From The OpenID Connect Provider. Only Specify If The OpenID Connect Provider Supports This Feature And You Do Not Want The User To Remain Signed In To The OpenID Connect Provider After Logging Out From The Site." ResourceKey="LogoutUrl">Logout Url:</Label>
|
|
<div class="col-sm-9">
|
|
<input id="logouturl" class="form-control" @bind="@_logouturl" />
|
|
</div>
|
|
</div>
|
|
<div class="row mb-1 align-items-center">
|
|
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do You Want To Allow Users To Sign In Using A Username And Password That Is Managed Locally On This Site? Note That You Should Only Disable This Option If You Have Already Sucessfully Configured An External Login Provider, Or Else You May Lock Yourself Out Of This Site." ResourceKey="AllowSiteLogin">Allow Site Login? </Label>
|
|
<div class="col-sm-9">
|
|
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin" required>
|
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
<option value="False">@SharedLocalizer["No"]</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</Section>
|
|
</div>
|
|
<br />
|
|
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
|
</TabPanel>
|
|
</TabStrip>
|
|
}
|
|
|
|
@code {
|
|
private List<UserRole> allroles;
|
|
private List<UserRole> userroles;
|
|
private string _search;
|
|
|
|
private string _allowregistration;
|
|
private string _minimumlength;
|
|
private string _uniquecharacters;
|
|
private string _requiredigit;
|
|
private string _requireupper;
|
|
private string _requirelower;
|
|
private string _requirepunctuation;
|
|
private string _maximumfailures;
|
|
private string _lockoutduration;
|
|
private string _provider;
|
|
private string _authority;
|
|
private string _clientid;
|
|
private string _clientsecret;
|
|
private string _metadata;
|
|
private string _logouturl;
|
|
private string _allowsitelogin;
|
|
|
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
|
await LoadSettingsAsync();
|
|
userroles = Search(_search);
|
|
|
|
_allowregistration = PageState.Site.AllowRegistration.ToString();
|
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
|
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
|
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
|
|
_requiredigit = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireDigit", "true");
|
|
_requireupper = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireUppercase", "true");
|
|
_requirelower = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireLowercase", "true");
|
|
_requirepunctuation = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", "true");
|
|
_maximumfailures = SettingService.GetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", "5");
|
|
_lockoutduration = TimeSpan.Parse(SettingService.GetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", "00:05:00")).TotalMinutes.ToString();
|
|
_provider = SettingService.GetSetting(settings, "OpenIdConnectOptions:Provider", "");
|
|
_authority = SettingService.GetSetting(settings, "OpenIdConnectOptions:Authority", "");
|
|
_clientid = SettingService.GetSetting(settings, "OpenIdConnectOptions:ClientId", "");
|
|
_clientsecret = SettingService.GetSetting(settings, "OpenIdConnectOptions:ClientSecret", "");
|
|
_metadata = SettingService.GetSetting(settings, "OpenIdConnectOptions:MetadataAddress", "");
|
|
_logouturl = SettingService.GetSetting(settings, "OpenIdConnectOptions:LogoutUrl", "");
|
|
_allowsitelogin = SettingService.GetSetting(settings, "AllowSiteLogin", "True");
|
|
}
|
|
|
|
private List<UserRole> Search(string search)
|
|
{
|
|
var results = allroles.Where(item => item.Role.Name == RoleNames.Registered || (item.Role.Name == RoleNames.Host && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)));
|
|
|
|
if (!string.IsNullOrEmpty(_search))
|
|
{
|
|
results = results.Where(item =>
|
|
(
|
|
item.User.Username.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
|
item.User.Email.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
|
item.User.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase)
|
|
)
|
|
);
|
|
}
|
|
return results.ToList();
|
|
}
|
|
|
|
private async Task OnSearch()
|
|
{
|
|
userroles = Search(_search);
|
|
await UpdateSettingsAsync();
|
|
}
|
|
|
|
private async Task DeleteUser(UserRole UserRole)
|
|
{
|
|
try
|
|
{
|
|
var user = await UserService.GetUserAsync(UserRole.UserId, PageState.Site.SiteId);
|
|
if (user != null)
|
|
{
|
|
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
|
|
await logger.LogInformation("User Deleted {User}", UserRole.User);
|
|
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
|
userroles = Search(_search);
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await logger.LogError(ex, "Error Deleting User {User} {Error}", UserRole.User, ex.Message);
|
|
AddModuleMessage(ex.Message, MessageType.Error);
|
|
}
|
|
}
|
|
|
|
private string settingSearch = "AU-search";
|
|
|
|
private async Task LoadSettingsAsync()
|
|
{
|
|
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
|
_search = SettingService.GetSetting(settings, settingSearch, "");
|
|
}
|
|
|
|
private async Task UpdateSettingsAsync()
|
|
{
|
|
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
|
SettingService.SetSetting(settings, settingSearch, _search);
|
|
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
|
}
|
|
|
|
private async Task SaveSiteSettings()
|
|
{
|
|
try
|
|
{
|
|
var site = PageState.Site;
|
|
site.AllowRegistration = bool.Parse(_allowregistration);
|
|
await SiteService.UpdateSiteAsync(site);
|
|
|
|
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredLength", _minimumlength, true);
|
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", _uniquecharacters, true);
|
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireDigit", _requiredigit, true);
|
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireUppercase", _requireupper, true);
|
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireLowercase", _requirelower, true);
|
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", _requirepunctuation, true);
|
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", _maximumfailures, true);
|
|
settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", TimeSpan.FromMinutes(Convert.ToInt64(_lockoutduration)).ToString(), true);
|
|
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:Provider", _provider, false);
|
|
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:Authority", _authority, true);
|
|
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:ClientId", _clientid, true);
|
|
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:ClientSecret", _clientsecret, true);
|
|
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:MetadataAddress", _metadata, true);
|
|
settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:LogoutUrl", _logouturl, true);
|
|
settings = SettingService.SetSetting(settings, "AllowSiteLogin", _allowsitelogin, false);
|
|
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
|
await SettingService.ClearSiteSettingsCacheAsync(site.SiteId);
|
|
|
|
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
|
|
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
|
}
|
|
}
|
|
}
|