diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index 67393b65..59d329dd 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -442,7 +442,7 @@ _homepageid = site.HomePageId.Value.ToString(); } _isdeleted = site.IsDeleted.ToString(); - _sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/pages/sitemap.xml"; + _sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/sitemap.xml"; _siteguid = site.SiteGuid; _version = site.Version; diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index fd1be041..51877aa0 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -162,7 +162,8 @@ if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId) { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - if (authState.User.Identity.IsAuthenticated) + // verify user is authenticated for current site + if (authState.User.Identity.IsAuthenticated && authState.User.Claims.Any(item => item.Type == "sitekey" && item.Value == SiteState.Alias.SiteKey)) { user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId); if (user != null) diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index f906494b..9432e2ac 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -88,8 +88,8 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { - // force user to provide email address (email may be missing if using external login) - if (PageState.User != null && string.IsNullOrEmpty(PageState.User.Email) && PageState.Route.PagePath != "profile") + // force authenticated user to provide email address (email may be missing if using external login) + if (PageState.User != null && PageState.User.IsAuthenticated && string.IsNullOrEmpty(PageState.User.Email) && PageState.Route.PagePath != "profile") { NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, "profile", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery))); return; diff --git a/Oqtane.Server/Controllers/NotificationController.cs b/Oqtane.Server/Controllers/NotificationController.cs index c4e051da..95fb8a07 100644 --- a/Oqtane.Server/Controllers/NotificationController.cs +++ b/Oqtane.Server/Controllers/NotificationController.cs @@ -161,6 +161,12 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && notification.SiteId == _alias.SiteId && IsAuthorized(notification.FromUserId)) { + if (!User.IsInRole(RoleNames.Admin)) + { + // content must be HTML encoded for non-admins to prevent HTML injection + notification.Subject = WebUtility.HtmlEncode(notification.Subject); + notification.Body = WebUtility.HtmlEncode(notification.Body); + } notification = _notifications.AddNotification(notification); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Create); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {NotificationId}", notification.NotificationId); @@ -181,6 +187,12 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && notification.SiteId == _alias.SiteId && notification.NotificationId == id && _notifications.GetNotification(notification.NotificationId, false) != null && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId))) { + if (!User.IsInRole(RoleNames.Admin)) + { + // content must be HTML encoded for non-admins to prevent HTML injection + notification.Subject = WebUtility.HtmlEncode(notification.Subject); + notification.Body = WebUtility.HtmlEncode(notification.Body); + } notification = _notifications.UpdateNotification(notification); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Update); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {NotificationId}", notification.NotificationId); diff --git a/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs b/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs index f6d7649c..3addfb01 100644 --- a/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs +++ b/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs @@ -58,21 +58,21 @@ namespace Oqtane.Infrastructure { if (path.StartsWith("/" + alias.Path) && (Constants.ReservedRoutes.Any(item => path.Contains("/" + item + "/")))) { - context.Request.Path = path.Replace("/" + alias.Path, ""); + context.Request.Path = path.Substring(alias.Path.Length + 1); } } - // handle sitemap.xml root request (does not support subfolder aliases) - if (context.Request.Path.StartsWithSegments("/sitemap.xml")) + // handle sitemap.xml request + if (context.Request.Path.ToString().Contains("/sitemap.xml") && !context.Request.Path.ToString().Contains("/pages")) { - context.Request.Path = "/pages" + context.Request.Path; + context.Request.Path = "/pages/sitemap.xml"; } // handle robots.txt root request (does not support subfolder aliases) - if (context.Request.Path.StartsWithSegments("/robots.txt")) + if (context.Request.Path.StartsWithSegments("/robots.txt") && string.IsNullOrEmpty(alias.Path)) { - // allow all and specify site map - var robots = $"User-agent: *\n\nSitemap: {context.Request.Scheme}://{alias.Name}/pages/sitemap.xml"; + // allow all user agents and specify site map + var robots = $"User-agent: *\n\nSitemap: {context.Request.Scheme}://{alias.Name}/sitemap.xml"; context.Response.ContentType = "text/plain"; await context.Response.WriteAsync(robots); return; diff --git a/Oqtane.Server/Pages/Sitemap.cshtml.cs b/Oqtane.Server/Pages/Sitemap.cshtml.cs index 0e0d09f4..461bc3d6 100644 --- a/Oqtane.Server/Pages/Sitemap.cshtml.cs +++ b/Oqtane.Server/Pages/Sitemap.cshtml.cs @@ -53,7 +53,8 @@ namespace Oqtane.Pages { if (_userPermissions.IsAuthorized(null, PermissionNames.View, page.PermissionList) && page.IsNavigation) { - sitemap.Add(new Sitemap { Url = _alias.Protocol + _alias.Name + Utilities.NavigateUrl(_alias.Path, page.Path, ""), ModifiedOn = DateTime.UtcNow }); + var rooturl = _alias.Protocol + (string.IsNullOrEmpty(_alias.Path) ? _alias.Name : _alias.Name.Substring(0, _alias.Name.IndexOf("/"))); + sitemap.Add(new Sitemap { Url = rooturl + Utilities.NavigateUrl(_alias.Path, page.Path, ""), ModifiedOn = DateTime.UtcNow }); foreach (var pageModule in pageModules.Where(item => item.PageId == page.PageId)) { @@ -72,7 +73,7 @@ namespace Oqtane.Pages var urls = ((ISitemap)moduleobject).GetUrls(_alias.Path, page.Path, pageModule.Module); foreach (var url in urls) { - sitemap.Add(new Sitemap { Url = _alias.Protocol + _alias.Name + url.Url, ModifiedOn = DateTime.UtcNow }); + sitemap.Add(new Sitemap { Url = rooturl + url.Url, ModifiedOn = DateTime.UtcNow }); } } catch (Exception ex) diff --git a/Oqtane.Server/Security/PrincipalValidator.cs b/Oqtane.Server/Security/PrincipalValidator.cs index 5a15a2b2..fcd3b4aa 100644 --- a/Oqtane.Server/Security/PrincipalValidator.cs +++ b/Oqtane.Server/Security/PrincipalValidator.cs @@ -28,7 +28,7 @@ namespace Oqtane.Security var claims = context.Principal.Claims; // check if principal has roles and matches current site - if (!claims.Any(item => item.Type == ClaimTypes.Role) || claims.FirstOrDefault(item => item.Type == "sitekey")?.Value != alias.SiteKey) + if (!claims.Any(item => item.Type == ClaimTypes.Role) || claims.Any(item => item.Type == "sitekey" && item.Value == alias.SiteKey)) { var userRepository = context.HttpContext.RequestServices.GetService(typeof(IUserRepository)) as IUserRepository; var userRoleRepository = context.HttpContext.RequestServices.GetService(typeof(IUserRoleRepository)) as IUserRoleRepository;