Fix #2144 - install issue, Fix #2146 - move file issue, require verification of external login account linkage
This commit is contained in:
parent
250701aff0
commit
391713b84d
|
@ -117,13 +117,30 @@
|
||||||
_username = PageState.QueryString["name"];
|
_username = PageState.QueryString["name"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PageState.QueryString.ContainsKey("token"))
|
if (PageState.QueryString.ContainsKey("token") && !string.IsNullOrEmpty(_username))
|
||||||
{
|
{
|
||||||
var user = new User();
|
var user = new User();
|
||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = _username;
|
user.Username = _username;
|
||||||
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
|
||||||
|
|
||||||
|
if (PageState.QueryString.ContainsKey("key"))
|
||||||
|
{
|
||||||
|
user = await UserService.LinkUserAsync(user, PageState.QueryString["token"], PageState.Site.Settings["ExternalLogin:ProviderType"], PageState.QueryString["key"], PageState.Site.Settings["ExternalLogin:ProviderName"]);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
await logger.LogInformation(LogFunction.Security, "External Login Linkage Successful For Username {Username}", _username);
|
||||||
|
AddModuleMessage(Localizer["Success.Account.Linked"], MessageType.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await logger.LogError(LogFunction.Security, "External Login Linkage Failed For Username {Username}", _username);
|
||||||
|
AddModuleMessage(Localizer["Message.Account.NotLinked"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
_username = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
||||||
|
@ -132,7 +149,15 @@
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
|
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
|
||||||
AddModuleMessage(Localizer["Message.Account.NotVerfied"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Account.NotVerified"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (PageState.QueryString.ContainsKey("status"))
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
@ -123,9 +123,15 @@
|
||||||
<data name="Success.Account.Verified" xml:space="preserve">
|
<data name="Success.Account.Verified" xml:space="preserve">
|
||||||
<value>User Account Verified Successfully. You Can Now Login With Your Username And Password Below.</value>
|
<value>User Account Verified Successfully. You Can Now Login With Your Username And Password Below.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Account.NotVerfied" xml:space="preserve">
|
<data name="Message.Account.NotVerified" xml:space="preserve">
|
||||||
<value>User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions.</value>
|
<value>User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Success.Account.Linked" xml:space="preserve">
|
||||||
|
<value>User Account Linked Successfully. You Can Now Login With Your External Login Below.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Account.NotLinked" xml:space="preserve">
|
||||||
|
<value>External Login Could Not Be Linked. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
<data name="Error.Login.Fail" xml:space="preserve">
|
<data name="Error.Login.Fail" xml:space="preserve">
|
||||||
<value>Login Failed. Please Remember That Passwords Are Case Sensitive. If You Have Attempted To Sign In Multiple Times Unsuccessfully, Your Account Will Be Locked Out For A Period Of Time. Note That User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email If You Are A New User.</value>
|
<value>Login Failed. Please Remember That Passwords Are Case Sensitive. If You Have Attempted To Sign In Multiple Times Unsuccessfully, Your Account Will Be Locked Out For A Period Of Time. Note That User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email If You Are A New User.</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -201,4 +207,22 @@
|
||||||
<data name="Error.ResetPassword" xml:space="preserve">
|
<data name="Error.ResetPassword" xml:space="preserve">
|
||||||
<value>Error Resetting Password</value>
|
<value>Error Resetting Password</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.DuplicateEmail" xml:space="preserve">
|
||||||
|
<value>Multiple User Accounts Already Exist With The Email Address Of Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.InvalidEmail" xml:space="preserve">
|
||||||
|
<value>The External Login Provider Did Not Provide A Valid Email Address For Your Account. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.ProviderKeyMismatch" xml:space="preserve">
|
||||||
|
<value>An Error Occurred Verifying Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.UserDoesNotExist" xml:space="preserve">
|
||||||
|
<value>A User Account Matching The Email Address Of Your External Login Does Not Exist. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.UserNotCreated" xml:space="preserve">
|
||||||
|
<value>A User Account Could Not Be Created For Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.VerificationRequired" xml:space="preserve">
|
||||||
|
<value>In Order To Link Your External Login With Your User Account You Must Verify Your Identity. Please Check Your Email For Further Instructions.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -12,18 +12,12 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class AliasService : ServiceBase, IAliasService
|
public class AliasService : ServiceBase, IAliasService
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor - should only be used by Dependency Injection
|
/// Constructor - should only be used by Dependency Injection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AliasService(HttpClient http, SiteState siteState) : base(http)
|
public AliasService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ApiUrl => CreateApiUrl("Alias", _siteState.Alias);
|
private string ApiUrl => CreateApiUrl("Alias");
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<List<Alias>> GetAliasesAsync()
|
public async Task<List<Alias>> GetAliasesAsync()
|
||||||
|
|
|
@ -11,15 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class DatabaseService : ServiceBase, IDatabaseService
|
public class DatabaseService : ServiceBase, IDatabaseService
|
||||||
{
|
{
|
||||||
|
public DatabaseService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Database");
|
||||||
|
|
||||||
public DatabaseService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Database", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Database>> GetDatabasesAsync()
|
public async Task<List<Database>> GetDatabasesAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,16 +14,14 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class FileService : ServiceBase, IFileService
|
public class FileService : ServiceBase, IFileService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
|
||||||
private readonly IJSRuntime _jsRuntime;
|
private readonly IJSRuntime _jsRuntime;
|
||||||
|
|
||||||
public FileService(HttpClient http, SiteState siteState, IJSRuntime jsRuntime) : base(http)
|
public FileService(HttpClient http, SiteState siteState, IJSRuntime jsRuntime) : base(http, siteState)
|
||||||
{
|
{
|
||||||
_siteState = siteState;
|
|
||||||
_jsRuntime = jsRuntime;
|
_jsRuntime = jsRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("File", _siteState.Alias);
|
private string Apiurl => CreateApiUrl("File");
|
||||||
|
|
||||||
public async Task<List<File>> GetFilesAsync(int folderId)
|
public async Task<List<File>> GetFilesAsync(int folderId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,14 +14,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class FolderService : ServiceBase, IFolderService
|
public class FolderService : ServiceBase, IFolderService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public FolderService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public FolderService(HttpClient http, SiteState siteState) : base(http)
|
private string ApiUrl => CreateApiUrl("Folder");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ApiUrl => CreateApiUrl("Folder", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Folder>> GetFoldersAsync(int siteId)
|
public async Task<List<Folder>> GetFoldersAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Oqtane.Services
|
||||||
private readonly NavigationManager _navigationManager;
|
private readonly NavigationManager _navigationManager;
|
||||||
private readonly SiteState _siteState;
|
private readonly SiteState _siteState;
|
||||||
|
|
||||||
public InstallationService(HttpClient http, NavigationManager navigationManager, SiteState siteState) : base(http)
|
public InstallationService(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http, siteState)
|
||||||
{
|
{
|
||||||
_navigationManager = navigationManager;
|
_navigationManager = navigationManager;
|
||||||
_siteState = siteState;
|
_siteState = siteState;
|
||||||
|
|
|
@ -115,5 +115,18 @@ namespace Oqtane.Services
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<string> GetPersonalAccessTokenAsync();
|
Task<string> GetPersonalAccessTokenAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Link an external login with a local user account
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The <see cref="User"/> we're verifying</param>
|
||||||
|
/// <param name="token">A Hash value in the URL which verifies this user got the e-mail (containing this token)</param>
|
||||||
|
/// <param name="type">External Login provider type</param>
|
||||||
|
/// <param name="key">External Login provider key</param>
|
||||||
|
/// <param name="name">External Login provider display name</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<User> LinkUserAsync(User user, string token, string type, string key, string name);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class JobLogService : ServiceBase, IJobLogService
|
public class JobLogService : ServiceBase, IJobLogService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public JobLogService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public JobLogService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("JobLog");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("JobLog", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<JobLog>> GetJobLogsAsync()
|
public async Task<List<JobLog>> GetJobLogsAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class JobService : ServiceBase, IJobService
|
public class JobService : ServiceBase, IJobService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public JobService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public JobService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Job");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Job", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Job>> GetJobsAsync()
|
public async Task<List<Job>> GetJobsAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,15 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class LanguageService : ServiceBase, ILanguageService
|
public class LanguageService : ServiceBase, ILanguageService
|
||||||
{
|
{
|
||||||
|
public LanguageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Language");
|
||||||
|
|
||||||
public LanguageService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Language", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Language>> GetLanguagesAsync(int siteId)
|
public async Task<List<Language>> GetLanguagesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,14 +10,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class LocalizationService : ServiceBase, ILocalizationService
|
public class LocalizationService : ServiceBase, ILocalizationService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public LocalizationService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public LocalizationService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Localization");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Localization", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<IEnumerable<Culture>> GetCulturesAsync() => await GetJsonAsync<IEnumerable<Culture>>(Apiurl);
|
public async Task<IEnumerable<Culture>> GetCulturesAsync() => await GetJsonAsync<IEnumerable<Culture>>(Apiurl);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,18 +14,16 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class LogService : ServiceBase, ILogService
|
public class LogService : ServiceBase, ILogService
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private readonly SiteState _siteState;
|
||||||
private readonly NavigationManager _navigationManager;
|
private readonly NavigationManager _navigationManager;
|
||||||
|
|
||||||
public LogService(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http)
|
public LogService(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http, siteState)
|
||||||
{
|
{
|
||||||
|
|
||||||
_siteState = siteState;
|
_siteState = siteState;
|
||||||
_navigationManager = navigationManager;
|
_navigationManager = navigationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Log", _siteState.Alias);
|
private string Apiurl => CreateApiUrl("Log");
|
||||||
|
|
||||||
public async Task<List<Log>> GetLogsAsync(int siteId, string level, string function, int rows)
|
public async Task<List<Log>> GetLogsAsync(int siteId, string level, string function, int rows)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,16 +14,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class ModuleDefinitionService : ServiceBase, IModuleDefinitionService
|
public class ModuleDefinitionService : ServiceBase, IModuleDefinitionService
|
||||||
{
|
{
|
||||||
private readonly HttpClient _http;
|
public ModuleDefinitionService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
private readonly SiteState _siteState;
|
|
||||||
|
|
||||||
public ModuleDefinitionService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("ModuleDefinition");
|
||||||
{
|
|
||||||
_http = http;
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("ModuleDefinition", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<ModuleDefinition>> GetModuleDefinitionsAsync(int siteId)
|
public async Task<List<ModuleDefinition>> GetModuleDefinitionsAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,15 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class ModuleService : ServiceBase, IModuleService
|
public class ModuleService : ServiceBase, IModuleService
|
||||||
{
|
{
|
||||||
|
public ModuleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Module");
|
||||||
|
|
||||||
public ModuleService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Module", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Module>> GetModulesAsync(int siteId)
|
public async Task<List<Module>> GetModulesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class NotificationService : ServiceBase, INotificationService
|
public class NotificationService : ServiceBase, INotificationService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public NotificationService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public NotificationService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Notification");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Notification", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId)
|
public async Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,13 +12,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class PackageService : ServiceBase, IPackageService
|
public class PackageService : ServiceBase, IPackageService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public PackageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public PackageService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Package");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
private string Apiurl => CreateApiUrl("Package", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Package>> GetPackagesAsync(string type)
|
public async Task<List<Package>> GetPackagesAsync(string type)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,15 +9,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class PageModuleService : ServiceBase, IPageModuleService
|
public class PageModuleService : ServiceBase, IPageModuleService
|
||||||
{
|
{
|
||||||
|
public PageModuleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("PageModule");
|
||||||
|
|
||||||
public PageModuleService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("PageModule", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<PageModule> GetPageModuleAsync(int pageModuleId)
|
public async Task<PageModule> GetPageModuleAsync(int pageModuleId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,16 +13,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class PageService : ServiceBase, IPageService
|
public class PageService : ServiceBase, IPageService
|
||||||
{
|
{
|
||||||
|
public PageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Page");
|
||||||
|
|
||||||
public PageService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Page", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Page>> GetPagesAsync(int siteId)
|
public async Task<List<Page>> GetPagesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,15 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class ProfileService : ServiceBase, IProfileService
|
public class ProfileService : ServiceBase, IProfileService
|
||||||
{
|
{
|
||||||
|
public ProfileService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Profile");
|
||||||
|
|
||||||
public ProfileService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Profile", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Profile>> GetProfilesAsync(int siteId)
|
public async Task<List<Profile>> GetProfilesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,16 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class RoleService : ServiceBase, IRoleService
|
public class RoleService : ServiceBase, IRoleService
|
||||||
{
|
{
|
||||||
|
public RoleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Role");
|
||||||
|
|
||||||
public RoleService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Role", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Role>> GetRolesAsync(int siteId)
|
public async Task<List<Role>> GetRolesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,15 +12,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SettingService : ServiceBase, ISettingService
|
public class SettingService : ServiceBase, ISettingService
|
||||||
{
|
{
|
||||||
|
public SettingService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Setting");
|
||||||
|
|
||||||
public SettingService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Setting", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<Dictionary<string, string>> GetTenantSettingsAsync()
|
public async Task<Dictionary<string, string>> GetTenantSettingsAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,15 +12,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SiteService : ServiceBase, ISiteService
|
public class SiteService : ServiceBase, ISiteService
|
||||||
{
|
{
|
||||||
|
public SiteService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Site");
|
||||||
|
|
||||||
public SiteService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Site", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Site>> GetSitesAsync()
|
public async Task<List<Site>> GetSitesAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,13 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SiteTemplateService : ServiceBase, ISiteTemplateService
|
public class SiteTemplateService : ServiceBase, ISiteTemplateService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public SiteTemplateService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public SiteTemplateService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("SiteTemplate");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
private string Apiurl => CreateApiUrl("SiteTemplate", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<SiteTemplate>> GetSiteTemplatesAsync()
|
public async Task<List<SiteTemplate>> GetSiteTemplatesAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,14 +9,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SqlService : ServiceBase, ISqlService
|
public class SqlService : ServiceBase, ISqlService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public SqlService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public SqlService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Sql");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Sql", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery)
|
public async Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,18 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SyncService : ServiceBase, ISyncService
|
public class SyncService : ServiceBase, ISyncService
|
||||||
{
|
{
|
||||||
|
public SyncService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string ApiUrl => CreateApiUrl("Sync");
|
||||||
|
|
||||||
/// <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 />
|
/// <inheritdoc />
|
||||||
public async Task<Sync> GetSyncAsync(DateTime lastSyncDate)
|
public async Task<Sync> GetSyncAsync(DateTime lastSyncDate)
|
||||||
|
|
|
@ -9,14 +9,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class SystemService : ServiceBase, ISystemService
|
public class SystemService : ServiceBase, ISystemService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public SystemService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public SystemService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("System");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("System", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<Dictionary<string, object>> GetSystemInfoAsync()
|
public async Task<Dictionary<string, object>> GetSystemInfoAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class TenantService : ServiceBase, ITenantService
|
public class TenantService : ServiceBase, ITenantService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public TenantService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public TenantService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("Tenant");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Tenant", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Tenant>> GetTenantsAsync()
|
public async Task<List<Tenant>> GetTenantsAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,14 +11,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class ThemeService : ServiceBase, IThemeService
|
public class ThemeService : ServiceBase, IThemeService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public ThemeService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public ThemeService(HttpClient http, SiteState siteState) : base(http)
|
private string ApiUrl => CreateApiUrl("Theme");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ApiUrl => CreateApiUrl("Theme", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Theme>> GetThemesAsync()
|
public async Task<List<Theme>> GetThemesAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,16 +12,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class UrlMappingService : ServiceBase, IUrlMappingService
|
public class UrlMappingService : ServiceBase, IUrlMappingService
|
||||||
{
|
{
|
||||||
|
public UrlMappingService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("UrlMapping");
|
||||||
|
|
||||||
public UrlMappingService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("UrlMapping", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<UrlMapping>> GetUrlMappingsAsync(int siteId, bool isMapped)
|
public async Task<List<UrlMapping>> GetUrlMappingsAsync(int siteId, bool isMapped)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,15 +10,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class UserRoleService : ServiceBase, IUserRoleService
|
public class UserRoleService : ServiceBase, IUserRoleService
|
||||||
{
|
{
|
||||||
|
public UserRoleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("UserRole");
|
||||||
|
|
||||||
public UserRoleService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("UserRole", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<UserRole>> GetUserRolesAsync(int siteId)
|
public async Task<List<UserRole>> GetUserRolesAsync(int siteId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,14 +10,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class UserService : ServiceBase, IUserService
|
public class UserService : ServiceBase, IUserService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public UserService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
public UserService(HttpClient http, SiteState siteState) : base(http)
|
private string Apiurl => CreateApiUrl("User");
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("User", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<User> GetUserAsync(int userId, int siteId)
|
public async Task<User> GetUserAsync(int userId, int siteId)
|
||||||
{
|
{
|
||||||
|
@ -89,5 +84,11 @@ namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
return await GetStringAsync($"{Apiurl}/personalaccesstoken");
|
return await GetStringAsync($"{Apiurl}/personalaccesstoken");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<User> LinkUserAsync(User user, string token, string type, string key, string name)
|
||||||
|
{
|
||||||
|
return await PostJsonAsync<User>($"{Apiurl}/link?token={token}&type={type}&key={key}&name={name}", user);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,16 +12,9 @@ namespace Oqtane.Services
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class VisitorService : ServiceBase, IVisitorService
|
public class VisitorService : ServiceBase, IVisitorService
|
||||||
{
|
{
|
||||||
|
public VisitorService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private readonly SiteState _siteState;
|
private string Apiurl => CreateApiUrl("Visitor");
|
||||||
|
|
||||||
public VisitorService(HttpClient http, SiteState siteState) : base(http)
|
|
||||||
{
|
|
||||||
|
|
||||||
_siteState = siteState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Visitor", _siteState.Alias);
|
|
||||||
|
|
||||||
public async Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate)
|
public async Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate)
|
||||||
{
|
{
|
||||||
|
|
|
@ -137,6 +137,7 @@ namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
if (File.Name != file.Name || File.FolderId != file.FolderId)
|
if (File.Name != file.Name || File.FolderId != file.FolderId)
|
||||||
{
|
{
|
||||||
|
file.Folder = _folders.GetFolder(file.FolderId);
|
||||||
string folderpath = _folders.GetFolderPath(file.Folder);
|
string folderpath = _folders.GetFolderPath(file.Folder);
|
||||||
if (!Directory.Exists(folderpath))
|
if (!Directory.Exists(folderpath))
|
||||||
{
|
{
|
||||||
|
|
|
@ -203,7 +203,7 @@ namespace Oqtane.Controllers
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string url = HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name;
|
string url = HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name;
|
||||||
string body = "Dear " + user.DisplayName + ",\n\nA User Account Has Been Succesfully Created For You. Please Use The Following Link To Access The Site:\n\n" + url + "\n\nThank You!";
|
string body = "Dear " + user.DisplayName + ",\n\nA User Account Has Been Successfully Created For You. Please Use The Following Link To Access The Site:\n\n" + url + "\n\nThank You!";
|
||||||
var notification = new Notification(user.SiteId, newUser, "User Account Notification", body);
|
var notification = new Notification(user.SiteId, newUser, "User Account Notification", body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
}
|
}
|
||||||
|
@ -419,7 +419,7 @@ namespace Oqtane.Controllers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username} - Error {Error}", user.Username, result.Errors.ToString());
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||||
user = null;
|
user = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,7 +477,7 @@ namespace Oqtane.Controllers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username} - Error {Error}", user.Username, result.Errors.ToString());
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||||
user = null;
|
user = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,6 +511,38 @@ namespace Oqtane.Controllers
|
||||||
return loginUser;
|
return loginUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// POST api/<controller>/link
|
||||||
|
[HttpPost("link")]
|
||||||
|
public async Task<User> Link([FromBody] User user, string token, string type, string key, string name)
|
||||||
|
{
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||||
|
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
var result = await _identityUserManager.ConfirmEmailAsync(identityuser, token);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
// make LoginProvider multi-tenant aware
|
||||||
|
type += ":" + user.SiteId.ToString();
|
||||||
|
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(type, key, name));
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "External Login Linkage Successful For {Username} And Provider {Provider}", user.Username, type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "External Login Linkage Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||||
|
user = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "External Login Linkage Failed For {Username} And Token {Token}", user.Username, token);
|
||||||
|
user = null;
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
// GET api/<controller>/validate/x
|
// GET api/<controller>/validate/x
|
||||||
[HttpGet("validate/{password}")]
|
[HttpGet("validate/{password}")]
|
||||||
public async Task<bool> Validate(string password)
|
public async Task<bool> Validate(string password)
|
||||||
|
|
|
@ -18,6 +18,7 @@ using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace Oqtane.Extensions
|
namespace Oqtane.Extensions
|
||||||
{
|
{
|
||||||
|
@ -147,7 +148,13 @@ namespace Oqtane.Extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
// login user
|
// login user
|
||||||
await LoginUser(email, context.HttpContext, context.Principal);
|
var status = await LoginUser(email, context.HttpContext, context.Principal);
|
||||||
|
if (status != ExternalLoginStatus.Success)
|
||||||
|
{
|
||||||
|
// redirect to login page and pass status
|
||||||
|
var alias = context.HttpContext.GetAlias();
|
||||||
|
context.Response.Redirect($"{alias.Path}/login?status={status}&returnurl={context.Properties.RedirectUri}", true);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task OnTokenValidated(TokenValidatedContext context)
|
private static async Task OnTokenValidated(TokenValidatedContext context)
|
||||||
|
@ -157,7 +164,14 @@ namespace Oqtane.Extensions
|
||||||
var email = context.Principal.FindFirstValue(emailClaimType);
|
var email = context.Principal.FindFirstValue(emailClaimType);
|
||||||
|
|
||||||
// login user
|
// login user
|
||||||
await LoginUser(email, context.HttpContext, context.Principal);
|
var status = await LoginUser(email, context.HttpContext, context.Principal);
|
||||||
|
if (status != ExternalLoginStatus.Success)
|
||||||
|
{
|
||||||
|
// redirect to login page and pass status
|
||||||
|
var alias = context.HttpContext.GetAlias();
|
||||||
|
context.Response.Redirect($"{alias.Path}/login?status={status}&returnurl={context.Properties.RedirectUri}", true);
|
||||||
|
context.HandleResponse();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task OnAccessDenied(AccessDeniedContext context)
|
private static Task OnAccessDenied(AccessDeniedContext context)
|
||||||
|
@ -166,7 +180,7 @@ namespace Oqtane.Extensions
|
||||||
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External Login Access Denied - User May Have Cancelled Their External Login Attempt");
|
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External Login Access Denied - User May Have Cancelled Their External Login Attempt");
|
||||||
// redirect to login page
|
// redirect to login page
|
||||||
var alias = context.HttpContext.GetAlias();
|
var alias = context.HttpContext.GetAlias();
|
||||||
context.Response.Redirect(alias.Path + "/login?returnurl=" + context.Properties.RedirectUri, true);
|
context.Response.Redirect($"{alias.Path}/login?returnurl={context.Properties.RedirectUri}", true);
|
||||||
context.HandleResponse();
|
context.HandleResponse();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
@ -177,20 +191,22 @@ namespace Oqtane.Extensions
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "External Login Remote Failure - {Error}", context.Failure.Message);
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "External Login Remote Failure - {Error}", context.Failure.Message);
|
||||||
// redirect to login page
|
// redirect to login page
|
||||||
var alias = context.HttpContext.GetAlias();
|
var alias = context.HttpContext.GetAlias();
|
||||||
context.Response.Redirect(alias.Path + "/login", true);
|
context.Response.Redirect($"{alias.Path}/login", true);
|
||||||
context.HandleResponse();
|
context.HandleResponse();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task LoginUser(string email, HttpContext httpContext, ClaimsPrincipal claimsPrincipal)
|
private static async Task<ExternalLoginStatus> LoginUser(string email, HttpContext httpContext, ClaimsPrincipal claimsPrincipal)
|
||||||
{
|
{
|
||||||
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
|
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||||
|
var status = ExternalLoginStatus.Success;
|
||||||
|
|
||||||
if (EmailValid(email, httpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
if (EmailValid(email, httpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
||||||
{
|
{
|
||||||
var _identityUserManager = httpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
|
var _identityUserManager = httpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
|
||||||
var _users = httpContext.RequestServices.GetRequiredService<IUserRepository>();
|
var _users = httpContext.RequestServices.GetRequiredService<IUserRepository>();
|
||||||
var _userRoles = httpContext.RequestServices.GetRequiredService<IUserRoleRepository>();
|
var _userRoles = httpContext.RequestServices.GetRequiredService<IUserRoleRepository>();
|
||||||
|
var alias = httpContext.GetAlias();
|
||||||
var providerType = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderType", "");
|
var providerType = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderType", "");
|
||||||
var providerName = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderName", "");
|
var providerName = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderName", "");
|
||||||
var providerKey = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier);
|
var providerKey = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
@ -215,6 +231,7 @@ namespace Oqtane.Extensions
|
||||||
{
|
{
|
||||||
if (duplicates)
|
if (duplicates)
|
||||||
{
|
{
|
||||||
|
status = ExternalLoginStatus.DuplicateEmail;
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email);
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -230,7 +247,7 @@ namespace Oqtane.Extensions
|
||||||
{
|
{
|
||||||
user = new User
|
user = new User
|
||||||
{
|
{
|
||||||
SiteId = httpContext.GetAlias().SiteId,
|
SiteId = alias.SiteId,
|
||||||
Username = email,
|
Username = email,
|
||||||
DisplayName = email,
|
DisplayName = email,
|
||||||
Email = email,
|
Email = email,
|
||||||
|
@ -242,7 +259,7 @@ namespace Oqtane.Extensions
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||||
string url = httpContext.Request.Scheme + "://" + httpContext.GetAlias().Name;
|
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||||
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
||||||
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
|
@ -254,16 +271,19 @@ namespace Oqtane.Extensions
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
status = ExternalLoginStatus.UserNotCreated;
|
||||||
_logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
|
_logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
status = ExternalLoginStatus.UserNotCreated;
|
||||||
_logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
|
_logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
status = ExternalLoginStatus.UserDoesNotExist;
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Creation Of New Users Is Disabled For This Site. User With Email Address {Email} Will First Need To Be Registered On The Site.", email);
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Creation Of New Users Is Disabled For This Site. User With Email Address {Email} Will First Need To Be Registered On The Site.", email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,7 +291,7 @@ namespace Oqtane.Extensions
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
||||||
var login = logins.FirstOrDefault(item => item.LoginProvider == providerType);
|
var login = logins.FirstOrDefault(item => item.LoginProvider == (providerType + ":" + alias.SiteId.ToString()));
|
||||||
if (login != null)
|
if (login != null)
|
||||||
{
|
{
|
||||||
if (login.ProviderKey == providerKey)
|
if (login.ProviderKey == providerKey)
|
||||||
|
@ -281,15 +301,23 @@ namespace Oqtane.Extensions
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// provider keys do not match
|
// provider keys do not match
|
||||||
|
status = ExternalLoginStatus.ProviderKeyMismatch;
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// add user login
|
// new external login using existing user account - verification required
|
||||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType, providerKey, ""));
|
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||||
user = _users.GetUser(identityuser.UserName);
|
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External User Login Added For {Email} Using Provider {Provider}", email, providerName);
|
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||||
|
url += $"/login?name={identityuser.UserName}&token={WebUtility.UrlEncode(token)}&key={WebUtility.UrlEncode(providerKey)}";
|
||||||
|
string body = $"You Recently Signed In To Our Site With {providerName} Using The Email Address {email}. ";
|
||||||
|
body += "In Order To Complete The Linkage Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||||
|
var notification = new Notification(alias.SiteId, email, email, "External Login Linkage", body);
|
||||||
|
_notifications.AddNotification(notification);
|
||||||
|
status = ExternalLoginStatus.VerificationRequired;
|
||||||
|
_logger.Log(alias.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Verification For Provider {Provider} Sent To {Email}", providerName, email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +326,7 @@ namespace Oqtane.Extensions
|
||||||
{
|
{
|
||||||
var principal = (ClaimsIdentity)claimsPrincipal.Identity;
|
var principal = (ClaimsIdentity)claimsPrincipal.Identity;
|
||||||
UserSecurity.ResetClaimsIdentity(principal);
|
UserSecurity.ResetClaimsIdentity(principal);
|
||||||
var identity = UserSecurity.CreateClaimsIdentity(httpContext.GetAlias(), user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList());
|
var identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList());
|
||||||
principal.AddClaims(identity.Claims);
|
principal.AddClaims(identity.Claims);
|
||||||
|
|
||||||
// update user
|
// update user
|
||||||
|
@ -307,13 +335,10 @@ namespace Oqtane.Extensions
|
||||||
_users.UpdateUser(user);
|
_users.UpdateUser(user);
|
||||||
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
|
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
|
||||||
}
|
}
|
||||||
else // user not valid
|
|
||||||
{
|
|
||||||
await httpContext.SignOutAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else // email invalid
|
else // email invalid
|
||||||
{
|
{
|
||||||
|
status = ExternalLoginStatus.InvalidEmail;
|
||||||
if (!string.IsNullOrEmpty(email))
|
if (!string.IsNullOrEmpty(email))
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The Email Address {Email} Is Invalid Or Does Not Match The Domain Filter Criteria. Login Denied.", email);
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The Email Address {Email} Is Invalid Or Does Not Match The Domain Filter Criteria. Login Denied.", email);
|
||||||
|
@ -330,8 +355,8 @@ namespace Oqtane.Extensions
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email To Uniquely Identify The User.");
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email To Uniquely Identify The User.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await httpContext.SignOutAsync();
|
|
||||||
}
|
}
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool EmailValid(string email, string domainfilter)
|
private static bool EmailValid(string email, string domainfilter)
|
||||||
|
|
|
@ -9,7 +9,6 @@ namespace Oqtane.Pages
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public class LoginModel : PageModel
|
public class LoginModel : PageModel
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly UserManager<IdentityUser> _identityUserManager;
|
private readonly UserManager<IdentityUser> _identityUserManager;
|
||||||
private readonly SignInManager<IdentityUser> _identitySignInManager;
|
private readonly SignInManager<IdentityUser> _identitySignInManager;
|
||||||
|
|
||||||
|
|
|
@ -50,15 +50,15 @@ namespace Oqtane.Security
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// user has no roles - remove principal
|
// user has no roles - remove principal
|
||||||
context.RejectPrincipal();
|
|
||||||
Log(_logger, alias, "Permissions Removed For User {Username} Accessing {Url}", context.Principal.Identity.Name, path);
|
Log(_logger, alias, "Permissions Removed For User {Username} Accessing {Url}", context.Principal.Identity.Name, path);
|
||||||
|
context.RejectPrincipal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// user does not exist - remove principal
|
// user does not exist - remove principal
|
||||||
context.RejectPrincipal();
|
|
||||||
Log(_logger, alias, "Permissions Removed For User {Username} Accessing {Url}", context.Principal.Identity.Name, path);
|
Log(_logger, alias, "Permissions Removed For User {Username} Accessing {Url}", context.Principal.Identity.Name, path);
|
||||||
|
context.RejectPrincipal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,6 @@ namespace Oqtane
|
||||||
options.Cookie.Name = Constants.AntiForgeryTokenCookieName;
|
options.Cookie.Name = Constants.AntiForgeryTokenCookieName;
|
||||||
options.Cookie.SameSite = SameSiteMode.Strict;
|
options.Cookie.SameSite = SameSiteMode.Strict;
|
||||||
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
||||||
//options.Cookie.HttpOnly = false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddIdentityCore<IdentityUser>(options => { })
|
services.AddIdentityCore<IdentityUser>(options => { })
|
||||||
|
@ -114,6 +113,7 @@ namespace Oqtane
|
||||||
{
|
{
|
||||||
options.DefaultAuthenticateScheme = Constants.AuthenticationScheme;
|
options.DefaultAuthenticateScheme = Constants.AuthenticationScheme;
|
||||||
options.DefaultChallengeScheme = Constants.AuthenticationScheme;
|
options.DefaultChallengeScheme = Constants.AuthenticationScheme;
|
||||||
|
options.DefaultSignOutScheme = Constants.AuthenticationScheme;
|
||||||
})
|
})
|
||||||
.AddCookie(Constants.AuthenticationScheme)
|
.AddCookie(Constants.AuthenticationScheme)
|
||||||
.AddOpenIdConnect(AuthenticationProviderTypes.OpenIDConnect, options => { })
|
.AddOpenIdConnect(AuthenticationProviderTypes.OpenIDConnect, options => { })
|
||||||
|
@ -130,7 +130,7 @@ namespace Oqtane
|
||||||
|
|
||||||
services.AddMvc(options =>
|
services.AddMvc(options =>
|
||||||
{
|
{
|
||||||
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
|
//options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
|
||||||
})
|
})
|
||||||
.AddNewtonsoftJson()
|
.AddNewtonsoftJson()
|
||||||
.AddOqtaneApplicationParts() // register any Controllers from custom modules
|
.AddOqtaneApplicationParts() // register any Controllers from custom modules
|
||||||
|
|
13
Oqtane.Shared/Enums/ExternalLoginStatus.cs
Normal file
13
Oqtane.Shared/Enums/ExternalLoginStatus.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
namespace Oqtane.Shared
|
||||||
|
{
|
||||||
|
public enum ExternalLoginStatus
|
||||||
|
{
|
||||||
|
Success,
|
||||||
|
InvalidEmail,
|
||||||
|
DuplicateEmail,
|
||||||
|
UserNotCreated,
|
||||||
|
UserDoesNotExist,
|
||||||
|
ProviderKeyMismatch,
|
||||||
|
VerificationRequired
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user