auth improvements related to multi-tenancy
This commit is contained in:
@ -9,7 +9,13 @@
|
||||
{
|
||||
<ModuleMessage Message="@_message" Type="@_type" />
|
||||
}
|
||||
<AuthorizeView>
|
||||
<AuthorizeView Roles="@RoleNames.Registered">
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<ModuleMessage Message="@Localizer["You Are Already Signed In"]" Type="MessageType.Info" />
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<div class="container Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
|
||||
@ -112,11 +118,10 @@
|
||||
if (user.IsAuthenticated)
|
||||
{
|
||||
await logger.LogInformation("Login Successful For Username {Username}", _username);
|
||||
// complete the login on the server so that the cookies are set correctly on SignalR
|
||||
// complete the login on the server so that the cookies are set correctly
|
||||
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
|
||||
var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
||||
string url = "/pages/login/";
|
||||
if (!string.IsNullOrEmpty(PageState.Alias.Path)) url = "/" + PageState.Alias.Path + url;
|
||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
||||
await interop.SubmitForm(url, fields);
|
||||
}
|
||||
else
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
@if (PageState.Site.AllowRegistration)
|
||||
{
|
||||
<AuthorizeView>
|
||||
<AuthorizeView Roles="@RoleNames.Registered">
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
|
@ -301,15 +301,15 @@ else
|
||||
connectionString = databaseConfigControl.GetConnectionString();
|
||||
}
|
||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||
|
||||
|
||||
if (connectionString != "")
|
||||
{
|
||||
config.TenantName = _tenantName;
|
||||
config.DatabaseType = database.DBType;
|
||||
config.ConnectionString = connectionString;
|
||||
config.HostPassword = _hostpassword;
|
||||
config.HostEmail = user.Email;
|
||||
config.HostPassword = _hostpassword;
|
||||
config.HostName = user.DisplayName;
|
||||
config.TenantName = _tenantName;
|
||||
config.IsNewTenant = true;
|
||||
}
|
||||
else
|
||||
@ -333,6 +333,7 @@ else
|
||||
if (tenant != null)
|
||||
{
|
||||
config.TenantName = tenant.Name;
|
||||
config.DatabaseType = tenant.DBType;
|
||||
config.ConnectionString = tenant.DBConnectionString;
|
||||
config.IsNewTenant = false;
|
||||
}
|
||||
|
@ -1,29 +1,27 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Security;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Providers
|
||||
{
|
||||
public class IdentityAuthenticationStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
private readonly NavigationManager _navigationManager;
|
||||
private readonly SiteState _siteState;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public IdentityAuthenticationStateProvider(NavigationManager navigationManager, SiteState siteState, IServiceProvider serviceProvider)
|
||||
private readonly NavigationManager _navigationManager;
|
||||
|
||||
public IdentityAuthenticationStateProvider(IServiceProvider serviceProvider, NavigationManager navigationManager)
|
||||
{
|
||||
_navigationManager = navigationManager;
|
||||
_siteState = siteState;
|
||||
_serviceProvider = serviceProvider;
|
||||
_navigationManager = navigationManager;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
@ -32,17 +30,14 @@ namespace Oqtane.Providers
|
||||
|
||||
// get HttpClient lazily from IServiceProvider as you cannot use standard dependency injection due to the AuthenticationStateProvider being initialized prior to NavigationManager(https://github.com/aspnet/AspNetCore/issues/11867 )
|
||||
var http = _serviceProvider.GetRequiredService<HttpClient>();
|
||||
string apiurl = "/api/User/authenticate";
|
||||
User user = await http.GetFromJsonAsync<User>(apiurl);
|
||||
// get alias as SiteState has not been initialized ( cannot use AliasService as it is not yet registered )
|
||||
var path = new Uri(_navigationManager.Uri).LocalPath.Substring(1);
|
||||
var alias = await http.GetFromJsonAsync<Alias>($"/api/Alias/name/?path={WebUtility.UrlEncode(path)}&sync={DateTime.UtcNow.ToString("yyyyMMddHHmmssfff")}");
|
||||
// get user
|
||||
User user = await http.GetFromJsonAsync<User>(Utilities.TenantUrl(alias, "/api/User/authenticate"));
|
||||
if (user.IsAuthenticated)
|
||||
{
|
||||
identity = new ClaimsIdentity("Identity.Application");
|
||||
identity.AddClaim(new Claim(ClaimTypes.Name, user.Username));
|
||||
identity.AddClaim(new Claim(ClaimTypes.PrimarySid, user.UserId.ToString()));
|
||||
foreach (string role in user.Roles.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Role, role));
|
||||
}
|
||||
identity = UserSecurity.CreateClaimsIdentity(alias, user);
|
||||
}
|
||||
|
||||
return new AuthenticationState(new ClaimsPrincipal(identity));
|
||||
|
@ -19,36 +19,37 @@ namespace Oqtane.Services
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl("Alias", _siteState.Alias);
|
||||
private string ApiUrl => CreateApiUrl("Alias", _siteState.Alias);
|
||||
|
||||
public async Task<List<Alias>> GetAliasesAsync()
|
||||
{
|
||||
List<Alias> aliases = await GetJsonAsync<List<Alias>>(Apiurl);
|
||||
List<Alias> aliases = await GetJsonAsync<List<Alias>>(ApiUrl);
|
||||
return aliases.OrderBy(item => item.Name).ToList();
|
||||
}
|
||||
|
||||
public async Task<Alias> GetAliasAsync(int aliasId)
|
||||
{
|
||||
return await GetJsonAsync<Alias>($"{Apiurl}/{aliasId}");
|
||||
return await GetJsonAsync<Alias>($"{ApiUrl}/{aliasId}");
|
||||
}
|
||||
|
||||
public async Task<Alias> GetAliasAsync(string path, DateTime lastSyncDate)
|
||||
{
|
||||
return await GetJsonAsync<Alias>($"{Apiurl}/name/?path={WebUtility.UrlEncode(path)}&sync={lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
|
||||
// tenant agnostic as SiteState does not exist
|
||||
return await GetJsonAsync<Alias>($"{CreateApiUrl("Alias", null)}/name/?path={WebUtility.UrlEncode(path)}&sync={lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
|
||||
}
|
||||
|
||||
public async Task<Alias> AddAliasAsync(Alias alias)
|
||||
{
|
||||
return await PostJsonAsync<Alias>(Apiurl, alias);
|
||||
return await PostJsonAsync<Alias>(ApiUrl, alias);
|
||||
}
|
||||
|
||||
public async Task<Alias> UpdateAliasAsync(Alias alias)
|
||||
{
|
||||
return await PutJsonAsync<Alias>($"{Apiurl}/{alias.AliasId}", alias);
|
||||
return await PutJsonAsync<Alias>($"{ApiUrl}/{alias.AliasId}", alias);
|
||||
}
|
||||
public async Task DeleteAliasAsync(int aliasId)
|
||||
{
|
||||
await DeleteAsync($"{Apiurl}/{aliasId}");
|
||||
await DeleteAsync($"{ApiUrl}/{aliasId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace Oqtane.Services
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string ApiUrl => CreateApiUrl("Installation", _siteState.Alias);
|
||||
private string ApiUrl => CreateApiUrl("Installation", null); // tenant agnostic as SiteState does not exist
|
||||
|
||||
public async Task<Installation> IsInstalled()
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
@ -81,7 +81,6 @@ namespace Oqtane.Services
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//TODO replace with logging
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
|
||||
@ -169,8 +168,6 @@ namespace Oqtane.Services
|
||||
if (response.IsSuccessStatusCode) return true;
|
||||
if (response.StatusCode != HttpStatusCode.NoContent && response.StatusCode != HttpStatusCode.NotFound)
|
||||
{
|
||||
//TODO: Log errors here
|
||||
|
||||
Console.WriteLine($"Request: {response.RequestMessage.RequestUri}");
|
||||
Console.WriteLine($"Response status: {response.StatusCode} {response.ReasonPhrase}");
|
||||
}
|
||||
@ -182,7 +179,6 @@ namespace Oqtane.Services
|
||||
{
|
||||
var mediaType = content?.Headers.ContentType?.MediaType;
|
||||
return mediaType != null && mediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase);
|
||||
//TODO Missing content JSON validation
|
||||
}
|
||||
|
||||
[Obsolete("This method is obsolete. Use CreateApiUrl(Alias alias, string serviceName) instead.", false)]
|
||||
|
@ -203,7 +203,7 @@
|
||||
<LanguageSwitcher />
|
||||
}
|
||||
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null))
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
||||
{
|
||||
if (PageState.EditMode)
|
||||
{
|
||||
|
@ -3,7 +3,7 @@
|
||||
@inject IStringLocalizer<Login> Localizer
|
||||
|
||||
<span class="app-login">
|
||||
<AuthorizeView>
|
||||
<AuthorizeView Roles="@RoleNames.Registered">
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
|
@ -39,8 +39,7 @@ namespace Oqtane.Themes.Controls
|
||||
var interop = new Interop(jsRuntime);
|
||||
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
|
||||
var fields = new { __RequestVerificationToken = antiforgerytoken, returnurl = !authorizedtoviewpage ? PageState.Alias.Path : PageState.Alias.Path + "/" + PageState.Page.Path };
|
||||
string url = "/pages/logout/";
|
||||
if (!string.IsNullOrEmpty(PageState.Alias.Path)) url = "/" + PageState.Alias.Path + url;
|
||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/logout/");
|
||||
await interop.SubmitForm(url, fields);
|
||||
}
|
||||
else
|
||||
|
@ -5,7 +5,7 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<span class="app-profile">
|
||||
<AuthorizeView>
|
||||
<AuthorizeView Roles="@RoleNames.Registered">
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
|
@ -12,6 +12,7 @@
|
||||
@inject IUserService UserService
|
||||
@inject IModuleService ModuleService
|
||||
@inject ILogService LogService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@implements IHandleAfterRender
|
||||
|
||||
@DynamicComponent
|
||||
@ -107,7 +108,7 @@
|
||||
SiteState.Alias = alias; // set state for services
|
||||
lastsyncdate = alias.SyncDate;
|
||||
|
||||
// process any sync events
|
||||
// process any sync events
|
||||
if (reload != Reload.Site && alias.SyncEvents.Any())
|
||||
{
|
||||
// if running on WebAssembly reload the client application if the server application was restarted
|
||||
@ -330,15 +331,13 @@
|
||||
await Refresh();
|
||||
}
|
||||
|
||||
Task IHandleAfterRender.OnAfterRenderAsync()
|
||||
async Task IHandleAfterRender.OnAfterRenderAsync()
|
||||
{
|
||||
if (!_navigationInterceptionEnabled)
|
||||
{
|
||||
_navigationInterceptionEnabled = true;
|
||||
return NavigationInterception.EnableNavigationInterceptionAsync();
|
||||
await NavigationInterception.EnableNavigationInterceptionAsync();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> ParseQueryString(string query)
|
||||
@ -556,4 +555,5 @@
|
||||
=> RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))
|
||||
? Oqtane.Shared.Runtime.WebAssembly
|
||||
: Oqtane.Shared.Runtime.Server;
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user