@@ -210,6 +202,18 @@
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
+
+
+
+
+
+ |
+
+
+ |
+
+
+
@@ -292,16 +296,24 @@
try
{
_themeList = await ThemeService.GetThemesAsync();
- _aliasList = await AliasService.GetAliasesAsync();
Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
if (site != null)
{
_name = site.Name;
- foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
+ _allowregistration = site.AllowRegistration.ToString();
+ _isdeleted = site.IsDeleted.ToString();
+
+ if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
- _urls += alias.Name + ",";
+ _aliasList = await AliasService.GetAliasesAsync();
+ foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
+ {
+ _urls += alias.Name + ",";
+ }
+ _urls = _urls.Substring(0, _urls.Length - 1);
+
}
- _urls = _urls.Substring(0, _urls.Length - 1);
+
if (site.LogoFileId != null)
{
_logofileid = site.LogoFileId.Value;
@@ -317,7 +329,6 @@
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
- _allowregistration = site.AllowRegistration.ToString();
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
_smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
@@ -368,7 +379,6 @@
_modifiedon = site.ModifiedOn;
_deletedby = site.DeletedBy;
_deletedon = site.DeletedOn;
- _isdeleted = site.IsDeleted.ToString();
_initialized = true;
}
@@ -427,34 +437,30 @@
bool refresh = (site.DefaultThemeType != _themetype || site.DefaultContainerType != _containertype);
site.Name = _name;
+ site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
+ site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
+
site.LogoFileId = null;
var logofileid = _logofilemanager.GetFileId();
if (logofileid != -1)
{
site.LogoFileId = logofileid;
}
-
-
var faviconFieldId = _faviconfilemanager.GetFileId();
if (faviconFieldId != -1)
{
site.FaviconFileId = faviconFieldId;
}
-
site.DefaultThemeType = _themetype;
site.DefaultContainerType = _containertype;
site.AdminContainerType = _admincontainertype;
- site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
- site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
site.PwaIsEnabled = (_pwaisenabled == null ? true : Boolean.Parse(_pwaisenabled));
-
var pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
if (pwaappiconfileid != -1)
{
site.PwaAppIconFileId = pwaappiconfileid;
}
-
var pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
if (pwasplashiconfileid != -1)
{
@@ -463,27 +469,6 @@
site = await SiteService.UpdateSiteAsync(site);
- var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
- {
- if (!names.Contains(alias.Name))
- {
- await AliasService.DeleteAliasAsync(alias.AliasId);
- }
- }
-
- foreach (string name in names)
- {
- if (!_aliasList.Exists(item => item.Name == name))
- {
- Alias alias = new Alias();
- alias.Name = name;
- alias.TenantId = site.TenantId;
- alias.SiteId = site.SiteId;
- await AliasService.AddAliasAsync(alias);
- }
- }
-
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
SettingService.SetSetting(settings, "SMTPHost", _smtphost);
SettingService.SetSetting(settings, "SMTPPort", _smtpport);
@@ -493,7 +478,32 @@
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
+ if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
+ {
+ var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
+ {
+ if (!names.Contains(alias.Name))
+ {
+ await AliasService.DeleteAliasAsync(alias.AliasId);
+ }
+ }
+
+ foreach (string name in names)
+ {
+ if (!_aliasList.Exists(item => item.Name == name))
+ {
+ Alias alias = new Alias();
+ alias.Name = name;
+ alias.TenantId = site.TenantId;
+ alias.SiteId = site.SiteId;
+ await AliasService.AddAliasAsync(alias);
+ }
+ }
+ }
+
await logger.LogInformation("Site Settings Saved {Site}", site);
+
if (refresh)
{
NavigationManager.NavigateTo(NavigateUrl()); // refresh to show new theme or container
diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor
index f0927b1a..5728905b 100644
--- a/Oqtane.Client/Modules/Admin/Sites/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor
@@ -15,12 +15,14 @@ else
- @Localizer["Edit"] |
- @context.Name |
+ |
+ |
+ @context.Name |
}
@@ -46,4 +48,15 @@ else
}
}
}
+
+ private void Edit(string name)
+ {
+ NavigationManager.NavigateTo(_scheme + name + "/admin/site", true);
+ }
+
+ private void Browse(string name)
+ {
+ NavigationManager.NavigateTo(_scheme + name, true);
+ }
+
}
diff --git a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs
index a90451cd..344eb8f6 100644
--- a/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs
+++ b/Oqtane.Client/Modules/HtmlText/Services/HtmlTextService.cs
@@ -1,5 +1,3 @@
-using System.Collections.Generic;
-using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Oqtane.Services;
@@ -9,34 +7,35 @@ namespace Oqtane.Modules.HtmlText.Services
{
public class HtmlTextService : ServiceBase, IHtmlTextService, IService
{
- private readonly SiteState _siteState;
+ public HtmlTextService(HttpClient http, SiteState siteState) : base(http, siteState) {}
- public HtmlTextService(HttpClient http, SiteState siteState) : base(http)
- {
- _siteState = siteState;
- }
-
- private string ApiUrl => CreateApiUrl("HtmlText", _siteState.Alias);
+ private string ApiUrl => CreateApiUrl("HtmlText");
public async Task GetHtmlTextAsync(int moduleId)
{
- var htmltext = await GetJsonAsync>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", new Dictionary() { { EntityNames.Module, moduleId } }));
- return htmltext.FirstOrDefault();
+ AddAuthorizationPolicyHeader(EntityNames.Module, moduleId);
+ return await GetJsonAsync($"{ApiUrl}/{moduleId}");
}
public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
{
- await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", new Dictionary() { { EntityNames.Module, htmlText.ModuleId } }), htmlText);
+ AddAntiForgeryToken();
+ AddAuthorizationPolicyHeader(EntityNames.Module, htmlText.ModuleId);
+ await PostJsonAsync($"{ApiUrl}", htmlText);
}
public async Task UpdateHtmlTextAsync(Models.HtmlText htmlText)
{
- await PutJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlText.HtmlTextId}", new Dictionary() { { EntityNames.Module, htmlText.ModuleId } }), htmlText);
+ AddAntiForgeryToken();
+ AddAuthorizationPolicyHeader(EntityNames.Module, htmlText.ModuleId);
+ await PutJsonAsync($"{ApiUrl}/{htmlText.HtmlTextId}", htmlText);
}
public async Task DeleteHtmlTextAsync(int moduleId)
{
- await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", new Dictionary() { { EntityNames.Module, moduleId } }));
+ AddAntiForgeryToken();
+ AddAuthorizationPolicyHeader(EntityNames.Module, moduleId);
+ await DeleteAsync($"{ApiUrl}/{moduleId}");
}
}
}
diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs
index 3ae04bd8..26f7a286 100644
--- a/Oqtane.Client/Program.cs
+++ b/Oqtane.Client/Program.cs
@@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
-using Oqtane.Interfaces;
using Oqtane.Modules;
using Oqtane.Providers;
using Oqtane.Services;
@@ -69,6 +68,8 @@ namespace Oqtane.Client
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
+ builder.Services.AddScoped();
+ builder.Services.AddScoped();
await LoadClientAssemblies(httpClient);
diff --git a/Oqtane.Client/Providers/IdentityAuthenticationStateProvider.cs b/Oqtane.Client/Providers/IdentityAuthenticationStateProvider.cs
index 8bc0dd98..146df8d4 100644
--- a/Oqtane.Client/Providers/IdentityAuthenticationStateProvider.cs
+++ b/Oqtane.Client/Providers/IdentityAuthenticationStateProvider.cs
@@ -30,14 +30,11 @@ 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();
- // 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($"/api/Alias/name/?path={WebUtility.UrlEncode(path)}&sync={DateTime.UtcNow.ToString("yyyyMMddHHmmssfff")}");
- // get user
- User user = await http.GetFromJsonAsync(Utilities.TenantUrl(alias, "/api/User/authenticate"));
+ var siteState = _serviceProvider.GetRequiredService();
+ User user = await http.GetFromJsonAsync(Utilities.TenantUrl(siteState.Alias, "/api/User/authenticate"));
if (user.IsAuthenticated)
{
- identity = UserSecurity.CreateClaimsIdentity(alias, user);
+ identity = UserSecurity.CreateClaimsIdentity(siteState.Alias, user);
}
return new AuthenticationState(new ClaimsPrincipal(identity));
diff --git a/Oqtane.Client/Services/AliasService.cs b/Oqtane.Client/Services/AliasService.cs
index 5a72e4bb..891c5a08 100644
--- a/Oqtane.Client/Services/AliasService.cs
+++ b/Oqtane.Client/Services/AliasService.cs
@@ -3,8 +3,6 @@ using System.Threading.Tasks;
using System.Net.Http;
using System.Linq;
using System.Collections.Generic;
-using System.Net;
-using System;
using Oqtane.Documentation;
using Oqtane.Shared;
@@ -40,13 +38,6 @@ namespace Oqtane.Services
return await GetJsonAsync($"{ApiUrl}/{aliasId}");
}
- ///
- public async Task GetAliasAsync(string path, DateTime lastSyncDate)
- {
- // tenant agnostic as SiteState does not exist
- return await GetJsonAsync($"{CreateApiUrl("Alias", null)}/name/?path={WebUtility.UrlEncode(path)}&sync={lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
- }
-
///
public async Task AddAliasAsync(Alias alias)
{
diff --git a/Oqtane.Client/Services/InstallationService.cs b/Oqtane.Client/Services/InstallationService.cs
index 5cc8f6ec..a37db439 100644
--- a/Oqtane.Client/Services/InstallationService.cs
+++ b/Oqtane.Client/Services/InstallationService.cs
@@ -3,19 +3,28 @@ using System.Threading.Tasks;
using System.Net.Http;
using Oqtane.Documentation;
using Oqtane.Shared;
+using Microsoft.AspNetCore.Components;
+using System;
+using System.Net;
namespace Oqtane.Services
{
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class InstallationService : ServiceBase, IInstallationService
{
- public InstallationService(HttpClient http) : base(http) {}
+ private readonly NavigationManager _navigationManager;
- private string ApiUrl => CreateApiUrl("Installation", null); // tenant agnostic as SiteState does not exist
+ public InstallationService(HttpClient http, NavigationManager navigationManager) : base(http)
+ {
+ _navigationManager = navigationManager;
+ }
+
+ private string ApiUrl => CreateApiUrl("Installation", null, ControllerRoutes.ApiRoute); // tenant agnostic
public async Task IsInstalled()
{
- return await GetJsonAsync($"{ApiUrl}/installed");
+ var path = new Uri(_navigationManager.Uri).LocalPath.Substring(1);
+ return await GetJsonAsync($"{ApiUrl}/installed/?path={WebUtility.UrlEncode(path)}");
}
public async Task Install(InstallConfig config)
diff --git a/Oqtane.Client/Services/Interfaces/IAliasService.cs b/Oqtane.Client/Services/Interfaces/IAliasService.cs
index 1ab43fa9..6b002ec7 100644
--- a/Oqtane.Client/Services/Interfaces/IAliasService.cs
+++ b/Oqtane.Client/Services/Interfaces/IAliasService.cs
@@ -23,14 +23,6 @@ namespace Oqtane.Services
///
Task GetAliasAsync(int aliasId);
- ///
- /// Retrieve the Alias object of a URL.
- ///
- /// The URL - todoc - is this only the root, or can it be a longer path?
- /// todoc - unclear what this is for
- ///
- Task GetAliasAsync(string url, DateTime lastSyncDate);
-
///
/// Save another in the DB. It must already contain all the information incl. it belongs to.
///
diff --git a/Oqtane.Client/Services/Interfaces/ISyncService.cs b/Oqtane.Client/Services/Interfaces/ISyncService.cs
new file mode 100644
index 00000000..65b5c226
--- /dev/null
+++ b/Oqtane.Client/Services/Interfaces/ISyncService.cs
@@ -0,0 +1,19 @@
+using Oqtane.Models;
+using System;
+using System.Threading.Tasks;
+
+namespace Oqtane.Services
+{
+ ///
+ /// Service to retrieve information.
+ ///
+ public interface ISyncService
+ {
+ ///
+ /// Get sync events
+ ///
+ ///
+ ///
+ Task GetSyncAsync(DateTime lastSyncDate);
+ }
+}
diff --git a/Oqtane.Client/Services/ServiceBase.cs b/Oqtane.Client/Services/ServiceBase.cs
index 81d12dc8..805ca2b7 100644
--- a/Oqtane.Client/Services/ServiceBase.cs
+++ b/Oqtane.Client/Services/ServiceBase.cs
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Components;
using Oqtane.Documentation;
using Oqtane.Models;
using Oqtane.Shared;
@@ -15,10 +17,25 @@ namespace Oqtane.Services
public class ServiceBase
{
private readonly HttpClient _http;
+ private readonly SiteState _siteState;
- protected ServiceBase(HttpClient client)
+ protected ServiceBase(HttpClient client, SiteState siteState)
{
_http = client;
+ _siteState = siteState;
+ }
+
+ // should be used with new constructor
+ public string CreateApiUrl(string serviceName)
+ {
+ if (_siteState != null)
+ {
+ return CreateApiUrl(serviceName, _siteState.Alias, ControllerRoutes.ApiRoute);
+ }
+ else // legacy support (before 2.1.0)
+ {
+ return CreateApiUrl(serviceName, null, ControllerRoutes.Default);
+ }
}
public string CreateApiUrl(string serviceName, Alias alias)
@@ -61,10 +78,10 @@ namespace Oqtane.Services
return CreateAuthorizationPolicyUrl(url, new Dictionary() { { entityName, entityId } });
}
- public string CreateAuthorizationPolicyUrl(string url, Dictionary args)
+ public string CreateAuthorizationPolicyUrl(string url, Dictionary authEntityId)
{
string qs = "";
- foreach (KeyValuePair kvp in args)
+ foreach (KeyValuePair kvp in authEntityId)
{
qs += (qs != "") ? "&" : "";
qs += "auth" + kvp.Key.ToLower() + "id=" + kvp.Value.ToString();
@@ -80,6 +97,33 @@ namespace Oqtane.Services
}
}
+ protected void AddRequestHeader(string name, string value)
+ {
+ if (_http.DefaultRequestHeaders.Contains(name))
+ {
+ _http.DefaultRequestHeaders.Remove(name);
+ }
+ _http.DefaultRequestHeaders.Add(name, value);
+ }
+
+ protected void AddAntiForgeryToken()
+ {
+ AddRequestHeader(Constants.AntiForgeryTokenHeaderName, _siteState.AntiForgeryToken);
+ }
+
+ public void AddAuthorizationPolicyHeader(string entityName, int entityId)
+ {
+ AddAuthorizationPolicyHeader(new Dictionary() { { entityName, entityId } });
+ }
+
+ public void AddAuthorizationPolicyHeader(Dictionary authEntityId)
+ {
+ foreach (KeyValuePair kvp in authEntityId)
+ {
+ AddRequestHeader("auth" + kvp.Key.ToLower() + "id", kvp.Value.ToString());
+ }
+ }
+
protected async Task GetAsync(string uri)
{
var response = await _http.GetAsync(uri);
@@ -194,10 +238,11 @@ namespace Oqtane.Services
return mediaType != null && mediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase);
}
- [Obsolete("This method is obsolete. Use CreateApiUrl(string serviceName, Alias alias) in conjunction with ControllerRoutes.ApiRoute in Controllers instead.", false)]
- public string CreateApiUrl(string serviceName)
+ //[Obsolete("This constructor is obsolete. Use ServiceBase(HttpClient client, SiteState siteState) : base(http, siteState) {} instead.", false)]
+ // This constructor is obsolete. Use ServiceBase(HttpClient client, SiteState siteState) : base(http, siteState) {} instead.
+ protected ServiceBase(HttpClient client)
{
- return CreateApiUrl(serviceName, null, ControllerRoutes.Default);
+ _http = client;
}
[Obsolete("This method is obsolete. Use CreateApiUrl(string serviceName, Alias alias) in conjunction with ControllerRoutes.ApiRoute in Controllers instead.", false)]
diff --git a/Oqtane.Client/Services/SyncService.cs b/Oqtane.Client/Services/SyncService.cs
new file mode 100644
index 00000000..45d2cfca
--- /dev/null
+++ b/Oqtane.Client/Services/SyncService.cs
@@ -0,0 +1,33 @@
+using Oqtane.Models;
+using System.Threading.Tasks;
+using System.Net.Http;
+using System;
+using Oqtane.Documentation;
+using Oqtane.Shared;
+
+namespace Oqtane.Services
+{
+ ///
+ [PrivateApi("Don't show in the documentation, as everything should use the Interface")]
+ public class SyncService : ServiceBase, ISyncService
+ {
+
+ private readonly SiteState _siteState;
+
+ ///
+ /// Constructor - should only be used by Dependency Injection
+ ///
+ public SyncService(HttpClient http, SiteState siteState) : base(http)
+ {
+ _siteState = siteState;
+ }
+
+ private string ApiUrl => CreateApiUrl("Sync", _siteState.Alias);
+
+ ///
+ public async Task GetSyncAsync(DateTime lastSyncDate)
+ {
+ return await GetJsonAsync($"{ApiUrl}/{lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
+ }
+ }
+}
diff --git a/Oqtane.Client/Themes/Controls/Container/ModuleActionsBase.cs b/Oqtane.Client/Themes/Controls/Container/ModuleActionsBase.cs
index 3551b897..f8b0b065 100644
--- a/Oqtane.Client/Themes/Controls/Container/ModuleActionsBase.cs
+++ b/Oqtane.Client/Themes/Controls/Container/ModuleActionsBase.cs
@@ -115,7 +115,7 @@ namespace Oqtane.Themes.Controls
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, oldPane);
- return url;
+ return NavigateUrl(url, "reload");
}
private async Task DeleteModule(string url, PageModule pagemodule)
@@ -123,7 +123,7 @@ namespace Oqtane.Themes.Controls
pagemodule.IsDeleted = true;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
- return url;
+ return NavigateUrl(url, "reload");
}
private async Task Settings(string url, PageModule pagemodule)
@@ -174,7 +174,7 @@ namespace Oqtane.Themes.Controls
pagemodule.Order = 0;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
- return s;
+ return NavigateUrl(s, "reload");
}
private async Task MoveBottom(string s, PageModule pagemodule)
@@ -182,7 +182,7 @@ namespace Oqtane.Themes.Controls
pagemodule.Order = int.MaxValue;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
- return s;
+ return NavigateUrl(s, "reload");
}
private async Task MoveUp(string s, PageModule pagemodule)
@@ -190,7 +190,7 @@ namespace Oqtane.Themes.Controls
pagemodule.Order -= 3;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
- return s;
+ return NavigateUrl(s, "reload");
}
private async Task MoveDown(string s, PageModule pagemodule)
@@ -198,7 +198,7 @@ namespace Oqtane.Themes.Controls
pagemodule.Order += 3;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
- return s;
+ return NavigateUrl(s, "reload");
}
public class ActionViewModel
diff --git a/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs b/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs
index af80d518..972f005c 100644
--- a/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs
+++ b/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs
@@ -16,6 +16,7 @@ namespace Oqtane.Themes.Controls
[Inject] public IUserService UserService { get; set; }
[Inject] public IJSRuntime jsRuntime { get; set; }
[Inject] public IServiceProvider ServiceProvider { get; set; }
+ [Inject] public SiteState SiteState { get; set; }
protected void LoginUser()
{
@@ -35,11 +36,10 @@ namespace Oqtane.Themes.Controls
if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
{
- // server-side Blazor
- 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 };
+ // server-side Blazor needs to post to the Logout page
+ var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = !authorizedtoviewpage ? PageState.Alias.Path : PageState.Alias.Path + "/" + PageState.Page.Path };
string url = Utilities.TenantUrl(PageState.Alias, "/pages/logout/");
+ var interop = new Interop(jsRuntime);
await interop.SubmitForm(url, fields);
}
else
diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor
index 0f5870f2..ed5548f4 100644
--- a/Oqtane.Client/UI/SiteRouter.razor
+++ b/Oqtane.Client/UI/SiteRouter.razor
@@ -5,8 +5,7 @@
@inject SiteState SiteState
@inject NavigationManager NavigationManager
@inject INavigationInterception NavigationInterception
-@inject IAliasService AliasService
-@inject ITenantService TenantService
+@inject ISyncService SyncService
@inject ISiteService SiteService
@inject IPageService PageService
@inject IUserService UserService
@@ -69,7 +68,6 @@
[SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")]
private async Task Refresh()
{
- Alias alias = null;
Site site;
List pages;
Page page;
@@ -103,27 +101,25 @@
lastsyncdate = PageState.LastSyncDate;
}
- alias = await AliasService.GetAliasAsync(path, lastsyncdate);
- SiteState.Alias = alias; // set state for services
- lastsyncdate = alias.SyncDate;
-
// process any sync events
- if (reload != Reload.Site && alias.SyncEvents.Any())
+ var sync = await SyncService.GetSyncAsync(lastsyncdate);
+ lastsyncdate = sync.SyncDate;
+ if (reload != Reload.Site && sync.SyncEvents.Any())
{
// if running on WebAssembly reload the client application if the server application was restarted
- if (runtime == Shared.Runtime.WebAssembly && PageState != null && alias.SyncEvents.Exists(item => item.TenantId == -1))
+ if (runtime == Shared.Runtime.WebAssembly && PageState != null && sync.SyncEvents.Exists(item => item.TenantId == -1))
{
NavigationManager.NavigateTo(_absoluteUri + (!_absoluteUri.Contains("?") ? "?" : "&") + "reload", true);
}
- if (alias.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == alias.SiteId))
+ if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
{
reload = Reload.Site;
}
}
- if (reload == Reload.Site || PageState == null || alias.SiteId != PageState.Alias.SiteId)
+ if (reload == Reload.Site || PageState == null || PageState.Alias.SiteId != SiteState.Alias.SiteId)
{
- site = await SiteService.GetSiteAsync(alias.SiteId);
+ site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
reload = Reload.Site;
}
else
@@ -148,9 +144,9 @@
}
// process any sync events for user
- if (reload != Reload.Site && user != null && alias.SyncEvents.Any())
+ if (reload != Reload.Site && user != null && sync.SyncEvents.Any())
{
- if (alias.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId))
+ if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId))
{
reload = Reload.Site;
}
@@ -173,9 +169,9 @@
path += "/";
}
- if (alias.Path != "")
+ if (SiteState.Alias.Path != "")
{
- path = path.Substring(alias.Path.Length + 1);
+ path = path.Substring(SiteState.Alias.Path.Length + 1);
}
// extract admin route elements from path
@@ -281,7 +277,7 @@
_pagestate = new PageState
{
- Alias = alias,
+ Alias = SiteState.Alias,
Site = site,
Pages = pages,
Page = page,
@@ -305,7 +301,7 @@
if (user == null)
{
// redirect to login page
- NavigationManager.NavigateTo(Utilities.NavigateUrl(alias.Path, "login", "?returnurl=" + path));
+ NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "login", "?returnurl=" + path));
}
else
{
@@ -313,7 +309,7 @@
if (path != "")
{
// redirect to home page
- NavigationManager.NavigateTo(Utilities.NavigateUrl(alias.Path, "", ""));
+ NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "", ""));
}
}
}
diff --git a/Oqtane.Server/Controllers/AliasController.cs b/Oqtane.Server/Controllers/AliasController.cs
index 9edea4dd..62dcfc85 100644
--- a/Oqtane.Server/Controllers/AliasController.cs
+++ b/Oqtane.Server/Controllers/AliasController.cs
@@ -3,10 +3,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
using Oqtane.Shared;
-using System.Linq;
-using System;
using System.Net;
-using System.Globalization;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
@@ -18,21 +15,17 @@ namespace Oqtane.Controllers
public class AliasController : Controller
{
private readonly IAliasRepository _aliases;
- private readonly IHttpContextAccessor _accessor;
- private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
- public AliasController(IAliasRepository aliases, IHttpContextAccessor accessor, ISyncManager syncManager, ILogManager logger)
+ public AliasController(IAliasRepository aliases, ILogManager logger)
{
_aliases = aliases;
- _accessor = accessor;
- _syncManager = syncManager;
_logger = logger;
}
// GET: api/
[HttpGet]
- [Authorize(Roles = RoleNames.Admin)]
+ [Authorize(Roles = RoleNames.Host)]
public IEnumerable Get()
{
return _aliases.GetAliases();
@@ -40,37 +33,15 @@ namespace Oqtane.Controllers
// GET api//5
[HttpGet("{id}")]
- [Authorize(Roles = RoleNames.Admin)]
+ [Authorize(Roles = RoleNames.Host)]
public Alias Get(int id)
{
return _aliases.GetAlias(id);
}
-
- // GET api//name/?path=xxx&sync=yyyyMMddHHmmssfff
- [HttpGet("name")]
- public Alias Get(string path, string sync)
- {
- Alias alias = null;
-
- if (_accessor.HttpContext != null)
- {
- path = _accessor.HttpContext.Request.Host.Value + "/" + WebUtility.UrlDecode(path);
- alias = _aliases.GetAlias(path);
- }
-
- // get sync events
- if (alias != null)
- {
- alias.SyncDate = DateTime.UtcNow;
- alias.SyncEvents = _syncManager.GetSyncEvents(alias.TenantId, DateTime.ParseExact(sync, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture));
- }
-
- return alias;
- }
// POST api/
[HttpPost]
- [Authorize(Roles = RoleNames.Admin)]
+ [Authorize(Roles = RoleNames.Host)]
public Alias Post([FromBody] Alias alias)
{
if (ModelState.IsValid)
@@ -78,12 +49,18 @@ namespace Oqtane.Controllers
alias = _aliases.AddAlias(alias);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Alias Added {Alias}", alias);
}
+ else
+ {
+ _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Alias Post Attempt {Alias}", alias);
+ HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
+ alias = null;
+ }
return alias;
}
// PUT api//5
[HttpPut("{id}")]
- [Authorize(Roles = RoleNames.Admin)]
+ [Authorize(Roles = RoleNames.Host)]
public Alias Put(int id, [FromBody] Alias alias)
{
if (ModelState.IsValid)
@@ -91,12 +68,18 @@ namespace Oqtane.Controllers
alias = _aliases.UpdateAlias(alias);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Alias Updated {Alias}", alias);
}
+ else
+ {
+ _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Alias Put Attempt {Alias}", alias);
+ HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
+ alias = null;
+ }
return alias;
}
// DELETE api//5
[HttpDelete("{id}")]
- [Authorize(Roles = RoleNames.Admin)]
+ [Authorize(Roles = RoleNames.Host)]
public void Delete(int id)
{
_aliases.DeleteAlias(id);
diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs
index 143150df..6c76cb24 100644
--- a/Oqtane.Server/Controllers/InstallationController.cs
+++ b/Oqtane.Server/Controllers/InstallationController.cs
@@ -13,6 +13,8 @@ using Oqtane.Shared;
using Oqtane.Themes;
using Microsoft.Extensions.Caching.Memory;
using System.Net;
+using Oqtane.Repository;
+using Microsoft.AspNetCore.Http;
namespace Oqtane.Controllers
{
@@ -24,14 +26,18 @@ namespace Oqtane.Controllers
private readonly IDatabaseManager _databaseManager;
private readonly ILocalizationManager _localizationManager;
private readonly IMemoryCache _cache;
+ private readonly IHttpContextAccessor _accessor;
+ private readonly IAliasRepository _aliases;
- public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache)
+ public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases)
{
_config = config;
_installationManager = installationManager;
_databaseManager = databaseManager;
_localizationManager = localizationManager;
_cache = cache;
+ _accessor = accessor;
+ _aliases = aliases;
}
// POST api/
@@ -52,11 +58,17 @@ namespace Oqtane.Controllers
return installation;
}
- // GET api//installed
+ // GET api//installed/?path=xxx
[HttpGet("installed")]
- public Installation IsInstalled()
+ public Installation IsInstalled(string path)
{
- return _databaseManager.IsInstalled();
+ var installation = _databaseManager.IsInstalled();
+ if (installation.Success)
+ {
+ path = _accessor.HttpContext.Request.Host.Value + "/" + WebUtility.UrlDecode(path);
+ installation.Alias = _aliases.GetAlias(path);
+ }
+ return installation;
}
[HttpGet("upgrade")]
diff --git a/Oqtane.Server/Controllers/ModuleControllerBase.cs b/Oqtane.Server/Controllers/ModuleControllerBase.cs
index c56a836e..14ed583a 100644
--- a/Oqtane.Server/Controllers/ModuleControllerBase.cs
+++ b/Oqtane.Server/Controllers/ModuleControllerBase.cs
@@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Http;
using Oqtane.Infrastructure;
using System.Collections.Generic;
using System;
-using Oqtane.Shared;
namespace Oqtane.Controllers
{
@@ -18,20 +17,41 @@ namespace Oqtane.Controllers
{
_logger = logger;
- // populate policy authorization dictionary
+ // populate policy authorization dictionary from querystring and headers
int value;
foreach (var param in accessor.HttpContext.Request.Query)
+ {
+ if (param.Key.StartsWith("auth") && param.Key.EndsWith("id") && int.TryParse(param.Value, out value))
+ {
+ _authEntityId.Add(param.Key.Substring(4, param.Key.Length - 6), value);
+ }
+ }
+ foreach (var param in accessor.HttpContext.Request.Headers)
{
if (param.Key.StartsWith("auth") && param.Key.EndsWith("id") && int.TryParse(param.Value, out value))
{
_authEntityId.Add(param.Key.Substring(4, param.Key.Length - 6), value);
}
}
+
// legacy support
if (_authEntityId.Count == 0 && accessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
}
+
+ }
+
+ protected int AuthEntityId(string entityname)
+ {
+ if (_authEntityId.ContainsKey(entityname))
+ {
+ return _authEntityId[entityname];
+ }
+ else
+ {
+ return -1;
+ }
}
}
diff --git a/Oqtane.Server/Controllers/SyncController.cs b/Oqtane.Server/Controllers/SyncController.cs
new file mode 100644
index 00000000..6c4a136d
--- /dev/null
+++ b/Oqtane.Server/Controllers/SyncController.cs
@@ -0,0 +1,34 @@
+using Microsoft.AspNetCore.Mvc;
+using Oqtane.Models;
+using Oqtane.Shared;
+using System;
+using System.Globalization;
+using Oqtane.Infrastructure;
+
+namespace Oqtane.Controllers
+{
+ [Route(ControllerRoutes.ApiRoute)]
+ public class SyncController : Controller
+ {
+ private readonly ISyncManager _syncManager;
+ private readonly Alias _alias;
+
+ public SyncController(ISyncManager syncManager, ITenantManager tenantManager)
+ {
+ _syncManager = syncManager;
+ _alias = tenantManager.GetAlias();
+ }
+
+ // GET api//yyyyMMddHHmmssfff
+ [HttpGet("{lastSyncDate}")]
+ public Sync Get(string lastSyncDate)
+ {
+ Sync sync = new Sync
+ {
+ SyncDate = DateTime.UtcNow,
+ SyncEvents = _syncManager.GetSyncEvents(_alias.TenantId, DateTime.ParseExact(lastSyncDate, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture))
+ };
+ return sync;
+ }
+ }
+}
diff --git a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs
index 3de69445..4f0f75e2 100644
--- a/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs
+++ b/Oqtane.Server/Modules/HtmlText/Controllers/HtmlTextController.cs
@@ -3,11 +3,10 @@ using Microsoft.AspNetCore.Authorization;
using Oqtane.Modules.HtmlText.Repository;
using Microsoft.AspNetCore.Http;
using Oqtane.Shared;
-using System;
-using System.Collections.Generic;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Controllers;
+using System.Net;
namespace Oqtane.Modules.HtmlText.Controllers
{
@@ -24,85 +23,75 @@ namespace Oqtane.Modules.HtmlText.Controllers
// GET api//5
[HttpGet("{id}")]
[Authorize(Policy = PolicyNames.ViewModule)]
- public List Get(int id)
+ public Models.HtmlText Get(int id)
{
- var list = new List();
- try
+ if (AuthEntityId(EntityNames.Module) == id)
{
- Models.HtmlText htmlText = null;
- if (_authEntityId[EntityNames.Module] == id)
- {
- htmlText = _htmlText.GetHtmlText(id);
- list.Add(htmlText);
- }
+ return _htmlText.GetHtmlText(id);
}
- catch (Exception ex)
+ else
{
- _logger.Log(LogLevel.Error, this, LogFunction.Read, ex, "Get Error {Error}", ex.Message);
- throw;
+ _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Get Attempt {ModuleId}", id);
+ HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
+ return null;
}
- return list;
}
// POST api/
+ [ValidateAntiForgeryToken]
[HttpPost]
[Authorize(Policy = PolicyNames.EditModule)]
public Models.HtmlText Post([FromBody] Models.HtmlText htmlText)
{
- try
+ if (ModelState.IsValid && AuthEntityId(EntityNames.Module) == htmlText.ModuleId)
{
- if (ModelState.IsValid && htmlText.ModuleId == _authEntityId[EntityNames.Module])
- {
- htmlText = _htmlText.AddHtmlText(htmlText);
- _logger.Log(LogLevel.Information, this, LogFunction.Create, "Html/Text Added {HtmlText}", htmlText);
- }
+ htmlText = _htmlText.AddHtmlText(htmlText);
+ _logger.Log(LogLevel.Information, this, LogFunction.Create, "Html/Text Added {HtmlText}", htmlText);
return htmlText;
}
- catch (Exception ex)
+ else
{
- _logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "Post Error {Error}", ex.Message);
- throw;
+ _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Post Attempt {HtmlText}", htmlText);
+ HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
+ return null;
}
}
// PUT api//5
+ [ValidateAntiForgeryToken]
[HttpPut("{id}")]
[Authorize(Policy = PolicyNames.EditModule)]
public Models.HtmlText Put(int id, [FromBody] Models.HtmlText htmlText)
{
- try
+ if (ModelState.IsValid && AuthEntityId(EntityNames.Module) == htmlText.ModuleId)
{
- if (ModelState.IsValid && htmlText.ModuleId == _authEntityId[EntityNames.Module])
- {
- htmlText = _htmlText.UpdateHtmlText(htmlText);
- _logger.Log(LogLevel.Information, this, LogFunction.Update, "Html/Text Updated {HtmlText}", htmlText);
- }
+ htmlText = _htmlText.UpdateHtmlText(htmlText);
+ _logger.Log(LogLevel.Information, this, LogFunction.Update, "Html/Text Updated {HtmlText}", htmlText);
return htmlText;
}
- catch (Exception ex)
+ else
{
- _logger.Log(LogLevel.Error, this, LogFunction.Update, ex, "Put Error {Error}", ex.Message);
- throw;
+ _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Put Attempt {HtmlText}", htmlText);
+ HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
+ return null;
}
}
// DELETE api//5
+ [ValidateAntiForgeryToken]
[HttpDelete("{id}")]
[Authorize(Policy = PolicyNames.EditModule)]
public void Delete(int id)
{
- try
+ if (AuthEntityId(EntityNames.Module) == id)
{
- if (id == _authEntityId[EntityNames.Module])
- {
- _htmlText.DeleteHtmlText(id);
- _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Html/Text Deleted {HtmlTextId}", id);
- }
+ _htmlText.DeleteHtmlText(id);
+ _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Html/Text Deleted {HtmlTextId}", id);
}
- catch (Exception ex)
+ else
{
- _logger.Log(LogLevel.Error, this, LogFunction.Delete, ex, "Delete Error {Error}", ex.Message);
- throw;
+ _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HtmlText Delete Attempt {ModuleId}", id);
+ HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
diff --git a/Oqtane.Server/Pages/_Host.cshtml.cs b/Oqtane.Server/Pages/_Host.cshtml.cs
index 235d70cc..7537831f 100644
--- a/Oqtane.Server/Pages/_Host.cshtml.cs
+++ b/Oqtane.Server/Pages/_Host.cshtml.cs
@@ -60,12 +60,13 @@ namespace Oqtane.Pages
ProcessThemeControls(assembly);
}
- // if culture not specified and framework is installed
+ // if framework is installed
if (!string.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection")))
{
var alias = _tenantManager.GetAlias();
if (alias != null)
{
+ // if culture not specified
if (HttpContext.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName] == null)
{
// set default language for site if the culture is not supported
diff --git a/Oqtane.Server/Security/PermissionHandler.cs b/Oqtane.Server/Security/PermissionHandler.cs
index bc082967..e26ddcd4 100644
--- a/Oqtane.Server/Security/PermissionHandler.cs
+++ b/Oqtane.Server/Security/PermissionHandler.cs
@@ -22,24 +22,44 @@ namespace Oqtane.Security
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
- // permission is scoped based on auth{entityname}id (ie ?authmoduleid ) which must be passed as a querystring parameter
+ // permission is scoped based on entitynames and ids passed as querystring parameters or headers
var ctx = _httpContextAccessor.HttpContext;
if (ctx != null)
{
+ // get entityid based on a parameter format of auth{entityname}id (ie. authmoduleid )
int entityId = -1;
if (ctx.Request.Query.ContainsKey("auth" + requirement.EntityName.ToLower() + "id"))
{
- entityId = int.Parse(ctx.Request.Query["auth" + requirement.EntityName.ToLower() + "id"]);
- }
- else
- {
- // legacy support
- if (ctx.Request.Query.ContainsKey("entityid"))
+ if (!int.TryParse(ctx.Request.Query["auth" + requirement.EntityName.ToLower() + "id"], out entityId))
{
- entityId = int.Parse(ctx.Request.Query["entityid"]);
+ entityId = -1;
}
}
- if (_userPermissions.IsAuthorized(context.User, requirement.EntityName, entityId, requirement.PermissionName))
+ if (entityId == -1)
+ {
+ if (ctx.Request.Headers.ContainsKey("auth" + requirement.EntityName.ToLower() + "id"))
+ {
+ if (!int.TryParse(ctx.Request.Headers["auth" + requirement.EntityName.ToLower() + "id"], out entityId))
+ {
+ entityId = -1;
+ }
+ }
+ }
+
+ // legacy support
+ if (entityId == -1)
+ {
+ if (ctx.Request.Query.ContainsKey("entityid"))
+ {
+ if (!int.TryParse(ctx.Request.Query["entityid"], out entityId))
+ {
+ entityId = -1;
+ }
+ }
+ }
+
+ // validate permissions
+ if (entityId != -1 && _userPermissions.IsAuthorized(context.User, requirement.EntityName, entityId, requirement.PermissionName))
{
context.Succeed(requirement);
}
diff --git a/Oqtane.Server/Security/PrincipalValidator.cs b/Oqtane.Server/Security/PrincipalValidator.cs
index 0523bd92..170e20ff 100644
--- a/Oqtane.Server/Security/PrincipalValidator.cs
+++ b/Oqtane.Server/Security/PrincipalValidator.cs
@@ -29,7 +29,7 @@ namespace Oqtane.Security
{
// tenant agnostic requests must be ignored
string path = context.Request.Path.ToString().ToLower();
- if (path.StartsWith("/_blazor") || path.StartsWith("/api/installation/") || path.StartsWith("/api/alias/name/"))
+ if (path.StartsWith("/_blazor") || path.StartsWith("/api/installation/"))
{
return Task.CompletedTask;
}
diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs
index 01a8b331..5faa9d06 100644
--- a/Oqtane.Server/Startup.cs
+++ b/Oqtane.Server/Startup.cs
@@ -78,12 +78,12 @@ namespace Oqtane
var navigationManager = s.GetRequiredService();
var client = new HttpClient(new HttpClientHandler { UseCookies = false });
client.BaseAddress = new Uri(navigationManager.Uri);
- // set the auth cookie to allow HttpClient API calls to be authenticated
+
+ // set the cookies to allow HttpClient API calls to be authenticated
var httpContextAccessor = s.GetRequiredService();
- var authToken = httpContextAccessor.HttpContext.Request.Cookies[".AspNetCore." + Constants.AuthenticationScheme];
- if (authToken != null)
+ foreach (var cookie in httpContextAccessor.HttpContext.Request.Cookies)
{
- client.DefaultRequestHeaders.Add("Cookie", ".AspNetCore." + Constants.AuthenticationScheme + "=" + authToken);
+ client.DefaultRequestHeaders.Add("Cookie", cookie.Key + "=" + cookie.Value);
}
return client;
});
@@ -131,6 +131,7 @@ namespace Oqtane
services.AddScoped();
services.AddScoped();
services.AddScoped();
+ services.AddScoped();
services.AddSingleton();
@@ -164,14 +165,30 @@ namespace Oqtane
services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = false;
+ options.Cookie.SameSite = SameSiteMode.Strict;
+ options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Events.OnRedirectToLogin = context =>
{
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return Task.CompletedTask;
};
+ options.Events.OnRedirectToAccessDenied = context =>
+ {
+ context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
+ return Task.CompletedTask;
+ };
options.Events.OnValidatePrincipal = PrincipalValidator.ValidateAsync;
});
+ services.AddAntiforgery(options =>
+ {
+ options.HeaderName = Constants.AntiForgeryTokenHeaderName;
+ options.Cookie.HttpOnly = false;
+ options.Cookie.Name = Constants.AntiForgeryTokenCookieName;
+ options.Cookie.SameSite = SameSiteMode.Strict;
+ options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
+ });
+
// register singleton scoped core services
services.AddSingleton(Configuration);
services.AddSingleton();
diff --git a/Oqtane.Shared/Models/Alias.cs b/Oqtane.Shared/Models/Alias.cs
index 94c340c5..0afad7d2 100644
--- a/Oqtane.Shared/Models/Alias.cs
+++ b/Oqtane.Shared/Models/Alias.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Oqtane.Models
@@ -43,18 +42,6 @@ namespace Oqtane.Models
///
public DateTime ModifiedOn { get; set; }
- ///
- /// todoc - unclear what this is for
- ///
- [NotMapped]
- public DateTime SyncDate { get; set; }
-
- ///
- /// todoc - unclear what this is for
- ///
- [NotMapped]
- public List SyncEvents { get; set; }
-
///
/// The path contains the url-part after the first slash.
/// * If the Name is `oqtane.me` the Path is empty
diff --git a/Oqtane.Shared/Models/Installation.cs b/Oqtane.Shared/Models/Installation.cs
index efa0db31..f3f2f580 100644
--- a/Oqtane.Shared/Models/Installation.cs
+++ b/Oqtane.Shared/Models/Installation.cs
@@ -16,5 +16,10 @@ namespace Oqtane.Models
/// Message or error in case something failed.
///
public string Message { get; set; }
+
+ ///
+ /// current alias value from server
+ ///
+ public Alias Alias { get; set; }
}
}
diff --git a/Oqtane.Shared/Models/SyncEvent.cs b/Oqtane.Shared/Models/Sync.cs
similarity index 57%
rename from Oqtane.Shared/Models/SyncEvent.cs
rename to Oqtane.Shared/Models/Sync.cs
index 1d546d86..9430ab2a 100644
--- a/Oqtane.Shared/Models/SyncEvent.cs
+++ b/Oqtane.Shared/Models/Sync.cs
@@ -1,7 +1,14 @@
-using System;
+using System;
+using System.Collections.Generic;
namespace Oqtane.Models
{
+ public class Sync
+ {
+ public DateTime SyncDate { get; set; }
+ public List SyncEvents { get; set; }
+ }
+
public class SyncEvent
{
public int TenantId { get; set; }
diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs
index 7590fcf6..6b655e16 100644
--- a/Oqtane.Shared/Shared/Constants.cs
+++ b/Oqtane.Shared/Shared/Constants.cs
@@ -76,5 +76,8 @@ namespace Oqtane.Shared {
public static readonly string DefaultCulture = "en";
public static readonly string AuthenticationScheme = "Identity.Application";
+ public static readonly string RequestVerificationToken = "__RequestVerificationToken";
+ public static readonly string AntiForgeryTokenHeaderName = "X-XSRF-TOKEN-HEADER";
+ public static readonly string AntiForgeryTokenCookieName = "X-XSRF-TOKEN-COOKIE";
}
}
diff --git a/Oqtane.Shared/Shared/SiteState.cs b/Oqtane.Shared/Shared/SiteState.cs
index 159d29e6..475a9d59 100644
--- a/Oqtane.Shared/Shared/SiteState.cs
+++ b/Oqtane.Shared/Shared/SiteState.cs
@@ -1,4 +1,4 @@
-using Oqtane.Models;
+using Oqtane.Models;
namespace Oqtane.Shared
{
@@ -6,6 +6,7 @@ namespace Oqtane.Shared
public class SiteState
{
public Alias Alias { get; set; }
+ public string AntiForgeryToken { get; set; } // for use in client services
}
}
|