refactoring, enhancements, and some fixes
This commit is contained in:
parent
82c05a841f
commit
bc720555c4
|
@ -1,4 +1,6 @@
|
|||
@inject IInstallationService InstallationService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject SiteState SiteState
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
|
@ -20,21 +22,28 @@
|
|||
{
|
||||
<div class="app-alert">
|
||||
@_installation.Message
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
private Installation _installation;
|
||||
private bool _initialized;
|
||||
private bool _initialized = false;
|
||||
private Installation _installation = new Installation { Success = false, Message = "" };
|
||||
|
||||
private PageState PageState { get; set; }
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
_installation = await InstallationService.IsInstalled();
|
||||
_initialized = true;
|
||||
if (firstRender && !_initialized)
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
SiteState.AntiForgeryToken = await interop.GetElementByName(Constants.RequestVerificationToken);
|
||||
_installation = await InstallationService.IsInstalled();
|
||||
SiteState.Alias = _installation.Alias;
|
||||
_initialized = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeState(PageState pageState)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject SiteState SiteState
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
|
||||
@if (_message != string.Empty)
|
||||
|
@ -57,7 +58,7 @@
|
|||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
||||
};
|
||||
|
||||
|
@ -108,7 +109,6 @@
|
|||
{
|
||||
if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
|
||||
{
|
||||
// server-side Blazor
|
||||
var user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = _username;
|
||||
|
@ -118,9 +118,8 @@
|
|||
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
|
||||
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
|
||||
var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
||||
// server-side Blazor needs to post to the Login page so that the cookies are set correctly
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
||||
await interop.SubmitForm(url, fields);
|
||||
}
|
||||
|
|
|
@ -21,14 +21,6 @@
|
|||
<input id="name" class="form-control" @bind="@_name" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they can be separated by commas." ResourceKey="Aliases">Aliases: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="allowRegister" HelpText="Do you want the users to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration? </Label>
|
||||
|
@ -210,6 +202,18 @@
|
|||
</Section>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<Section Name="Alias" Heading="Alias Management" ResourceKey="Alias">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they can be separated by commas." ResourceKey="Aliases">Aliases: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</Section>
|
||||
<Section Name="TenantInformation" Heading="Tenant Information" ResourceKey="TenantInformation">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
|
@ -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
|
||||
|
|
|
@ -15,12 +15,14 @@ else
|
|||
|
||||
<Pager Items="@_sites">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["Name"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><NavLink class="btn btn-primary" href="@(_scheme + context.Name +"/admin/site")">@Localizer["Edit"]</NavLink></td>
|
||||
<td><a href="@(_scheme + context.Name +"?reload")">@context.Name</a></td>
|
||||
<td><button type="button" class="btn btn-primary" @onclick="@(async () => Edit(context.Name))">@Localizer["Edit"]</button></td>
|
||||
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => Browse(context.Name))">@Localizer["Browse"]</button></td>
|
||||
<td>@context.Name</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Models.HtmlText> GetHtmlTextAsync(int moduleId)
|
||||
{
|
||||
var htmltext = await GetJsonAsync<List<Models.HtmlText>>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", new Dictionary<string, int>() { { EntityNames.Module, moduleId } }));
|
||||
return htmltext.FirstOrDefault();
|
||||
AddAuthorizationPolicyHeader(EntityNames.Module, moduleId);
|
||||
return await GetJsonAsync<Models.HtmlText>($"{ApiUrl}/{moduleId}");
|
||||
}
|
||||
|
||||
public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
|
||||
{
|
||||
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", new Dictionary<string, int>() { { 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<string, int>() { { 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<string, int>() { { EntityNames.Module, moduleId } }));
|
||||
AddAntiForgeryToken();
|
||||
AddAuthorizationPolicyHeader(EntityNames.Module, moduleId);
|
||||
await DeleteAsync($"{ApiUrl}/{moduleId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ISystemService, SystemService>();
|
||||
builder.Services.AddScoped<ILocalizationService, LocalizationService>();
|
||||
builder.Services.AddScoped<ILanguageService, LanguageService>();
|
||||
builder.Services.AddScoped<IDatabaseService, DatabaseService>();
|
||||
builder.Services.AddScoped<ISyncService, SyncService>();
|
||||
|
||||
await LoadClientAssemblies(httpClient);
|
||||
|
||||
|
|
|
@ -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<HttpClient>();
|
||||
// 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"));
|
||||
var siteState = _serviceProvider.GetRequiredService<SiteState>();
|
||||
User user = await http.GetFromJsonAsync<User>(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));
|
||||
|
|
|
@ -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<Alias>($"{ApiUrl}/{aliasId}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Alias> GetAliasAsync(string path, DateTime lastSyncDate)
|
||||
{
|
||||
// tenant agnostic as SiteState does not exist
|
||||
return await GetJsonAsync<Alias>($"{CreateApiUrl("Alias", null)}/name/?path={WebUtility.UrlEncode(path)}&sync={lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Alias> AddAliasAsync(Alias alias)
|
||||
{
|
||||
|
|
|
@ -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<Installation> IsInstalled()
|
||||
{
|
||||
return await GetJsonAsync<Installation>($"{ApiUrl}/installed");
|
||||
var path = new Uri(_navigationManager.Uri).LocalPath.Substring(1);
|
||||
return await GetJsonAsync<Installation>($"{ApiUrl}/installed/?path={WebUtility.UrlEncode(path)}");
|
||||
}
|
||||
|
||||
public async Task<Installation> Install(InstallConfig config)
|
||||
|
|
|
@ -23,14 +23,6 @@ namespace Oqtane.Services
|
|||
/// <returns></returns>
|
||||
Task<Alias> GetAliasAsync(int aliasId);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the Alias object of a URL.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL - todoc - is this only the root, or can it be a longer path?</param>
|
||||
/// <param name="lastSyncDate">todoc - unclear what this is for</param>
|
||||
/// <returns></returns>
|
||||
Task<Alias> GetAliasAsync(string url, DateTime lastSyncDate);
|
||||
|
||||
/// <summary>
|
||||
/// Save another <see cref="Oqtane.Models.Alias"/> in the DB. It must already contain all the information incl. <see cref="Oqtane.Models.Tenant"/> it belongs to.
|
||||
/// </summary>
|
||||
|
|
19
Oqtane.Client/Services/Interfaces/ISyncService.cs
Normal file
19
Oqtane.Client/Services/Interfaces/ISyncService.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using Oqtane.Models;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service to retrieve <see cref="Sync"/> information.
|
||||
/// </summary>
|
||||
public interface ISyncService
|
||||
{
|
||||
/// <summary>
|
||||
/// Get sync events
|
||||
/// </summary>
|
||||
/// <param name="lastSyncDate"></param>
|
||||
/// <returns></returns>
|
||||
Task<Sync> GetSyncAsync(DateTime lastSyncDate);
|
||||
}
|
||||
}
|
|
@ -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<string, int>() { { entityName, entityId } });
|
||||
}
|
||||
|
||||
public string CreateAuthorizationPolicyUrl(string url, Dictionary<string, int> args)
|
||||
public string CreateAuthorizationPolicyUrl(string url, Dictionary<string, int> authEntityId)
|
||||
{
|
||||
string qs = "";
|
||||
foreach (KeyValuePair<string, int> kvp in args)
|
||||
foreach (KeyValuePair<string, int> 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<string, int>() { { entityName, entityId } });
|
||||
}
|
||||
|
||||
public void AddAuthorizationPolicyHeader(Dictionary<string, int> authEntityId)
|
||||
{
|
||||
foreach (KeyValuePair<string, int> 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)]
|
||||
|
|
33
Oqtane.Client/Services/SyncService.cs
Normal file
33
Oqtane.Client/Services/SyncService.cs
Normal file
|
@ -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
|
||||
{
|
||||
/// <inheritdoc cref="ISyncService" />
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class SyncService : ServiceBase, ISyncService
|
||||
{
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor - should only be used by Dependency Injection
|
||||
/// </summary>
|
||||
public SyncService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string ApiUrl => CreateApiUrl("Sync", _siteState.Alias);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Sync> GetSyncAsync(DateTime lastSyncDate)
|
||||
{
|
||||
return await GetJsonAsync<Sync>($"{ApiUrl}/{lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<string> 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<string> 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<string> 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<string> 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<string> 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Page> 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, "", ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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/<controller>
|
||||
[HttpGet]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
[Authorize(Roles = RoleNames.Host)]
|
||||
public IEnumerable<Alias> Get()
|
||||
{
|
||||
return _aliases.GetAliases();
|
||||
|
@ -40,37 +33,15 @@ namespace Oqtane.Controllers
|
|||
|
||||
// GET api/<controller>/5
|
||||
[HttpGet("{id}")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
[Authorize(Roles = RoleNames.Host)]
|
||||
public Alias Get(int id)
|
||||
{
|
||||
return _aliases.GetAlias(id);
|
||||
}
|
||||
|
||||
// GET api/<controller>/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/<controller>
|
||||
[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/<controller>/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/<controller>/5
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
[Authorize(Roles = RoleNames.Host)]
|
||||
public void Delete(int id)
|
||||
{
|
||||
_aliases.DeleteAlias(id);
|
||||
|
|
|
@ -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/<controller>
|
||||
|
@ -52,11 +58,17 @@ namespace Oqtane.Controllers
|
|||
return installation;
|
||||
}
|
||||
|
||||
// GET api/<controller>/installed
|
||||
// GET api/<controller>/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")]
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
34
Oqtane.Server/Controllers/SyncController.cs
Normal file
34
Oqtane.Server/Controllers/SyncController.cs
Normal file
|
@ -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/<controller>/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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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/<controller>/5
|
||||
[HttpGet("{id}")]
|
||||
[Authorize(Policy = PolicyNames.ViewModule)]
|
||||
public List<Models.HtmlText> Get(int id)
|
||||
public Models.HtmlText Get(int id)
|
||||
{
|
||||
var list = new List<Models.HtmlText>();
|
||||
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/<controller>
|
||||
[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/<controller>/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/<controller>/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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -78,12 +78,12 @@ namespace Oqtane
|
|||
var navigationManager = s.GetRequiredService<NavigationManager>();
|
||||
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<IHttpContextAccessor>();
|
||||
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<ILocalizationService, LocalizationService>();
|
||||
services.AddScoped<ILanguageService, LanguageService>();
|
||||
services.AddScoped<IDatabaseService, DatabaseService>();
|
||||
services.AddScoped<ISyncService, SyncService>();
|
||||
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
|
@ -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<IInstallationManager, InstallationManager>();
|
||||
|
|
|
@ -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
|
|||
/// <inheritdoc />
|
||||
public DateTime ModifiedOn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// todoc - unclear what this is for
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public DateTime SyncDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// todoc - unclear what this is for
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public List<SyncEvent> SyncEvents { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path contains the url-part after the first slash.
|
||||
/// * If the Name is `oqtane.me` the Path is empty
|
||||
|
|
|
@ -16,5 +16,10 @@ namespace Oqtane.Models
|
|||
/// Message or error in case something failed.
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// current alias value from server
|
||||
/// </summary>
|
||||
public Alias Alias { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<SyncEvent> SyncEvents { get; set; }
|
||||
}
|
||||
|
||||
public class SyncEvent
|
||||
{
|
||||
public int TenantId { get; set; }
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user