diff --git a/Oqtane.Client/UI/PageState.cs b/Oqtane.Client/UI/PageState.cs index 8aac9627..91cf158c 100644 --- a/Oqtane.Client/UI/PageState.cs +++ b/Oqtane.Client/UI/PageState.cs @@ -29,8 +29,6 @@ namespace Oqtane.UI public bool Refresh { get; set; } public bool AllowCookies { get; set; } - public int? StatusCode { get; set; } - public List Pages { get { return Site?.Pages; } @@ -65,8 +63,7 @@ namespace Oqtane.UI IsInternalNavigation = IsInternalNavigation, RenderId = RenderId, Refresh = Refresh, - AllowCookies = AllowCookies, - StatusCode = StatusCode + AllowCookies = AllowCookies }; } } diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index e7b11256..fb7d4fc3 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -158,13 +158,17 @@ // verify user is authenticated for current site var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - if (authState.User.Identity.IsAuthenticated - && authState.User.Claims.Any(item => item.Type == Constants.SiteKeyClaimType && item.Value == SiteState.Alias.SiteKey) - && PageState.StatusCode != (int)HttpStatusCode.NotFound) + if (authState.User.IsAuthenticated() && authState.User.SiteKey() == SiteState.Alias.SiteKey) { - // get user - var userid = int.Parse(authState.User.Claims.First(item => item.Type == ClaimTypes.NameIdentifier).Value); - user = await UserService.GetUserAsync(userid, SiteState.Alias.SiteId); + if (PageState == null || PageState.User == null || PageState.User.UserId != authState.User.UserId()) + { + // get user + user = await UserService.GetUserAsync(authState.User.UserId(), SiteState.Alias.SiteId); + } + else + { + user = PageState.User; + } if (user != null) { user.IsAuthenticated = authState.User.Identity.IsAuthenticated; @@ -339,7 +343,6 @@ IsInternalNavigation = _isInternalNavigation, RenderId = renderid, Refresh = false, - StatusCode = PageState?.StatusCode, AllowCookies = _allowCookies.GetValueOrDefault(true) }; OnStateChange?.Invoke(_pagestate); diff --git a/Oqtane.Client/_Imports.razor b/Oqtane.Client/_Imports.razor index 73af8f3a..86d19622 100644 --- a/Oqtane.Client/_Imports.razor +++ b/Oqtane.Client/_Imports.razor @@ -27,3 +27,4 @@ @using Oqtane.Enums @using Oqtane.Installer @using Oqtane.Interfaces +@using Oqtane.Extensions diff --git a/Oqtane.Server/Components/App.razor b/Oqtane.Server/Components/App.razor index 0082e669..6276eea0 100644 --- a/Oqtane.Server/Components/App.razor +++ b/Oqtane.Server/Components/App.razor @@ -60,7 +60,7 @@ } @((MarkupString)_headResources) - + @if (string.IsNullOrEmpty(_message)) { @if (_renderMode == RenderModes.Static) @@ -97,7 +97,7 @@ private string _renderMode = RenderModes.Interactive; private string _runtime = Runtimes.Server; private bool _prerender = true; - private bool _enhancedNavigation = true; + Dictionary _bodyAttributes { get; set; } = new(); private string _fingerprint = ""; private int _visitorId = -1; private string _antiForgeryToken = ""; @@ -142,7 +142,10 @@ _renderMode = site.RenderMode; _runtime = site.Runtime; _prerender = site.Prerender; - _enhancedNavigation = site.EnhancedNavigation; + if (_renderMode == RenderModes.Static && !site.EnhancedNavigation) + { + _bodyAttributes.Add("data-enhance-nav", "false"); + } _fingerprint = site.Fingerprint; var cookieConsentSettings = SettingService.GetSetting(site.Settings, "CookieConsent", string.Empty); @@ -234,7 +237,7 @@ Site = site, Page = page, Modules = modules, - User = null, + User = site.User, Uri = new Uri(url, UriKind.Absolute), Route = route, QueryString = Utilities.ParseQueryString(route.Query), @@ -251,7 +254,6 @@ IsInternalNavigation = false, RenderId = Guid.NewGuid(), Refresh = true, - StatusCode = Context.Response.StatusCode, AllowCookies = _allowCookies }; } diff --git a/Oqtane.Server/Infrastructure/Options/ISiteNamedOptions.cs b/Oqtane.Server/Infrastructure/Options/ISiteNamedOptions.cs deleted file mode 100644 index 41ed97c4..00000000 --- a/Oqtane.Server/Infrastructure/Options/ISiteNamedOptions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using Oqtane.Models; - -namespace Oqtane.Infrastructure -{ - public interface ISiteNamedOptions - where TOptions : class, new() - { - void Configure(string name, TOptions options, Alias alias, Dictionary sitesettings); - } -} diff --git a/Oqtane.Server/Infrastructure/Options/ISiteOptions.cs b/Oqtane.Server/Infrastructure/Options/ISiteOptions.cs deleted file mode 100644 index c05cd9fa..00000000 --- a/Oqtane.Server/Infrastructure/Options/ISiteOptions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using Oqtane.Models; - -namespace Oqtane.Infrastructure -{ - public interface ISiteOptions - where TOptions : class, new() - { - void Configure(TOptions options, Alias alias, Dictionary sitesettings); - } -} diff --git a/Oqtane.Server/Infrastructure/Options/SiteNamedOptions.cs b/Oqtane.Server/Infrastructure/Options/SiteNamedOptions.cs index 1027cc54..0ebe5a91 100644 --- a/Oqtane.Server/Infrastructure/Options/SiteNamedOptions.cs +++ b/Oqtane.Server/Infrastructure/Options/SiteNamedOptions.cs @@ -4,6 +4,12 @@ using Oqtane.Models; namespace Oqtane.Infrastructure { + public interface ISiteNamedOptions + where TOptions : class, new() + { + void Configure(string name, TOptions options, Alias alias, Dictionary sitesettings); + } + public class SiteNamedOptions : ISiteNamedOptions where TOptions : class, new() { diff --git a/Oqtane.Server/Infrastructure/Options/SiteOptions.cs b/Oqtane.Server/Infrastructure/Options/SiteOptions.cs index f4f55f77..e5d4e7c0 100644 --- a/Oqtane.Server/Infrastructure/Options/SiteOptions.cs +++ b/Oqtane.Server/Infrastructure/Options/SiteOptions.cs @@ -4,6 +4,12 @@ using Oqtane.Models; namespace Oqtane.Infrastructure { + public interface ISiteOptions + where TOptions : class, new() + { + void Configure(TOptions options, Alias alias, Dictionary sitesettings); + } + public class SiteOptions : ISiteOptions where TOptions : class, new() { diff --git a/Oqtane.Server/Services/SiteService.cs b/Oqtane.Server/Services/SiteService.cs index 5e43299b..14b28c2a 100644 --- a/Oqtane.Server/Services/SiteService.cs +++ b/Oqtane.Server/Services/SiteService.cs @@ -13,6 +13,7 @@ using Oqtane.Enums; using Oqtane.Shared; using System.Globalization; using Oqtane.Extensions; +using Oqtane.Managers; namespace Oqtane.Services { @@ -25,6 +26,7 @@ namespace Oqtane.Services private readonly IPageModuleRepository _pageModules; private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly ILanguageRepository _languages; + private readonly IUserManager _userManager; private readonly IUserPermissions _userPermissions; private readonly ISettingRepository _settings; private readonly ITenantManager _tenantManager; @@ -35,7 +37,7 @@ namespace Oqtane.Services private readonly IHttpContextAccessor _accessor; private readonly string _private = "[PRIVATE]"; - public ServerSiteService(ISiteRepository sites, IPageRepository pages, IThemeRepository themes, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, IConfigManager configManager, ILogManager logger, IMemoryCache cache, IHttpContextAccessor accessor) + public ServerSiteService(ISiteRepository sites, IPageRepository pages, IThemeRepository themes, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserManager userManager, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, IConfigManager configManager, ILogManager logger, IMemoryCache cache, IHttpContextAccessor accessor) { _sites = sites; _pages = pages; @@ -43,6 +45,7 @@ namespace Oqtane.Services _pageModules = pageModules; _moduleDefinitions = moduleDefinitions; _languages = languages; + _userManager = userManager; _userPermissions = userPermissions; _settings = settings; _tenantManager = tenantManager; @@ -146,6 +149,12 @@ namespace Oqtane.Services // themes site.Themes = _themes.FilterThemes(_themes.GetThemes(site.SiteId).ToList()); + // user + if (_accessor.HttpContext.User.IsAuthenticated()) + { + site.User = _userManager.GetUser(_accessor.HttpContext.User.UserId(), site.SiteId); + } + // installation date used for fingerprinting static assets site.Fingerprint = Utilities.GenerateSimpleHash(_configManager.GetSetting("InstallationDate", DateTime.UtcNow.ToString("yyyyMMddHHmm"))); diff --git a/Oqtane.Server/Extensions/ClaimsPrincipalExtensions.cs b/Oqtane.Shared/Extensions/ClaimsPrincipalExtensions.cs similarity index 91% rename from Oqtane.Server/Extensions/ClaimsPrincipalExtensions.cs rename to Oqtane.Shared/Extensions/ClaimsPrincipalExtensions.cs index 1749c421..2adfbcde 100644 --- a/Oqtane.Server/Extensions/ClaimsPrincipalExtensions.cs +++ b/Oqtane.Shared/Extensions/ClaimsPrincipalExtensions.cs @@ -8,6 +8,18 @@ namespace Oqtane.Extensions { // extension methods cannot be properties - the methods below must include a () suffix when referenced + public static bool IsAuthenticated(this ClaimsPrincipal claimsPrincipal) + { + if (claimsPrincipal.Identity != null) + { + return claimsPrincipal.Identity.IsAuthenticated; + } + else + { + return false; + } + } + public static string Username(this ClaimsPrincipal claimsPrincipal) { if (claimsPrincipal.HasClaim(item => item.Type == ClaimTypes.Name)) diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index 2b2cb78b..9b444671 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -192,6 +192,12 @@ namespace Oqtane.Models [NotMapped] public List Themes { get; set; } + /// + /// Current user + /// + [NotMapped] + public User User { get; set; } + /// /// fingerprint for framework static assets /// @@ -246,6 +252,7 @@ namespace Oqtane.Models Pages = Pages.ConvertAll(page => page.Clone()), Languages = Languages.ConvertAll(language => language.Clone()), Themes = Themes, + User = User.Clone(), Fingerprint = Fingerprint, TenantId = TenantId }; diff --git a/Oqtane.Shared/Models/User.cs b/Oqtane.Shared/Models/User.cs index 7b6b398b..dd3fa320 100644 --- a/Oqtane.Shared/Models/User.cs +++ b/Oqtane.Shared/Models/User.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Xml.Linq; namespace Oqtane.Models { @@ -128,5 +130,34 @@ namespace Oqtane.Models /// [NotMapped] public Dictionary Settings { get; set; } + + public User Clone() + { + return new User + { + UserId = UserId, + Username = Username, + DisplayName = DisplayName, + Email = Email, + TimeZoneId = TimeZoneId, + PhotoFileId = PhotoFileId, + LastLoginOn = LastLoginOn, + LastIPAddress = LastIPAddress, + TwoFactorRequired = TwoFactorRequired, + TwoFactorCode = TwoFactorCode, + TwoFactorExpiry = TwoFactorExpiry, + SecurityStamp = SecurityStamp, + SiteId = SiteId, + Roles = Roles, + DeletedBy = DeletedBy, + DeletedOn = DeletedOn, + IsDeleted = IsDeleted, + Password = Password, + IsAuthenticated = IsAuthenticated, + EmailConfirmed = EmailConfirmed, + SuppressNotification = SuppressNotification, + Settings = Settings.ToDictionary(setting => setting.Key, setting => setting.Value) + }; + } } }