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
	 Shaun Walker
					Shaun Walker