Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #2144 - install issue, Fix #2146 - move file issue, require verification of external login account linkage #2149

Merged
merged 1 commit into from
Apr 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions Oqtane.Client/Modules/Admin/Login/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -117,22 +117,47 @@
_username = PageState.QueryString["name"];
}

if (PageState.QueryString.ContainsKey("token"))
if (PageState.QueryString.ContainsKey("token") && !string.IsNullOrEmpty(_username))
{
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);

if (user != null)
if (PageState.QueryString.ContainsKey("key"))
{
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
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
{
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Message.Account.NotVerfied"], MessageType.Warning);
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
if (user != null)
{
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
}
else
{
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Message.Account.NotVerified"], MessageType.Warning);
}
}
}
else
{
if (PageState.QueryString.ContainsKey("status"))
{
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
}
}
}
Expand Down
28 changes: 26 additions & 2 deletions Oqtane.Client/Resources/Modules/Admin/Login/Index.resx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Expand Down Expand Up @@ -123,9 +123,15 @@
<data name="Success.Account.Verified" xml:space="preserve">
<value>User Account Verified Successfully. You Can Now Login With Your Username And Password Below.</value>
</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>
</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">
<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>
Expand Down Expand Up @@ -201,4 +207,22 @@
<data name="Error.ResetPassword" xml:space="preserve">
<value>Error Resetting Password</value>
</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>
10 changes: 2 additions & 8 deletions Oqtane.Client/Services/AliasService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,12 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class AliasService : ServiceBase, IAliasService
{

private readonly SiteState _siteState;

/// <summary>
/// Constructor - should only be used by Dependency Injection
/// </summary>
public AliasService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
public AliasService(HttpClient http, SiteState siteState) : base(http, siteState) { }

private string ApiUrl => CreateApiUrl("Alias", _siteState.Alias);
private string ApiUrl => CreateApiUrl("Alias");

/// <inheritdoc />
public async Task<List<Alias>> GetAliasesAsync()
Expand Down
10 changes: 2 additions & 8 deletions Oqtane.Client/Services/DatabaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,9 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class DatabaseService : ServiceBase, IDatabaseService
{
public DatabaseService(HttpClient http, SiteState siteState) : base(http, siteState) { }

private readonly SiteState _siteState;

public DatabaseService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}

private string Apiurl => CreateApiUrl("Database", _siteState.Alias);
private string Apiurl => CreateApiUrl("Database");

public async Task<List<Database>> GetDatabasesAsync()
{
Expand Down
6 changes: 2 additions & 4 deletions Oqtane.Client/Services/FileService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,14 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class FileService : ServiceBase, IFileService
{
private readonly SiteState _siteState;
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;
}

private string Apiurl => CreateApiUrl("File", _siteState.Alias);
private string Apiurl => CreateApiUrl("File");

public async Task<List<File>> GetFilesAsync(int folderId)
{
Expand Down
9 changes: 2 additions & 7 deletions Oqtane.Client/Services/FolderService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,9 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
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)
{
_siteState = siteState;
}

private string ApiUrl => CreateApiUrl("Folder", _siteState.Alias);
private string ApiUrl => CreateApiUrl("Folder");

public async Task<List<Folder>> GetFoldersAsync(int siteId)
{
Expand Down
2 changes: 1 addition & 1 deletion Oqtane.Client/Services/InstallationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class InstallationService : ServiceBase, IInstallationService
private readonly NavigationManager _navigationManager;
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;
_siteState = siteState;
Expand Down
13 changes: 13 additions & 0 deletions Oqtane.Client/Services/Interfaces/IUserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,18 @@ public interface IUserService
/// </summary>
/// <returns></returns>
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);


}
}
9 changes: 2 additions & 7 deletions Oqtane.Client/Services/JobLogService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,9 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
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)
{
_siteState = siteState;
}

private string Apiurl => CreateApiUrl("JobLog", _siteState.Alias);
private string Apiurl => CreateApiUrl("JobLog");

public async Task<List<JobLog>> GetJobLogsAsync()
{
Expand Down
9 changes: 2 additions & 7 deletions Oqtane.Client/Services/JobService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,9 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
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)
{
_siteState = siteState;
}

private string Apiurl => CreateApiUrl("Job", _siteState.Alias);
private string Apiurl => CreateApiUrl("Job");

public async Task<List<Job>> GetJobsAsync()
{
Expand Down
12 changes: 3 additions & 9 deletions Oqtane.Client/Services/LanguageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,10 @@ namespace Oqtane.Services
{
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class LanguageService : ServiceBase, ILanguageService
{

private readonly SiteState _siteState;
{
public LanguageService(HttpClient http, SiteState siteState) : base(http, siteState) { }

public LanguageService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}

private string Apiurl => CreateApiUrl("Language", _siteState.Alias);
private string Apiurl => CreateApiUrl("Language");

public async Task<List<Language>> GetLanguagesAsync(int siteId)
{
Expand Down
9 changes: 2 additions & 7 deletions Oqtane.Client/Services/LocalizationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,9 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
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)
{
_siteState = siteState;
}

private string Apiurl => CreateApiUrl("Localization", _siteState.Alias);
private string Apiurl => CreateApiUrl("Localization");

public async Task<IEnumerable<Culture>> GetCulturesAsync() => await GetJsonAsync<IEnumerable<Culture>>(Apiurl);
}
Expand Down
6 changes: 2 additions & 4 deletions Oqtane.Client/Services/LogService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,16 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class LogService : ServiceBase, ILogService
{

private readonly SiteState _siteState;
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;
_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)
{
Expand Down
11 changes: 2 additions & 9 deletions Oqtane.Client/Services/ModuleDefinitionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,9 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class ModuleDefinitionService : ServiceBase, IModuleDefinitionService
{
private readonly HttpClient _http;
private readonly SiteState _siteState;
public ModuleDefinitionService(HttpClient http, SiteState siteState) : base(http, siteState) { }

public ModuleDefinitionService(HttpClient http, SiteState siteState) : base(http)
{
_http = http;
_siteState = siteState;
}

private string Apiurl => CreateApiUrl("ModuleDefinition", _siteState.Alias);
private string Apiurl => CreateApiUrl("ModuleDefinition");

public async Task<List<ModuleDefinition>> GetModuleDefinitionsAsync(int siteId)
{
Expand Down
10 changes: 2 additions & 8 deletions Oqtane.Client/Services/ModuleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,9 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class ModuleService : ServiceBase, IModuleService
{

private readonly SiteState _siteState;
public ModuleService(HttpClient http, SiteState siteState) : base(http, siteState) { }

public ModuleService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}

private string Apiurl => CreateApiUrl("Module", _siteState.Alias);
private string Apiurl => CreateApiUrl("Module");

public async Task<List<Module>> GetModulesAsync(int siteId)
{
Expand Down
9 changes: 2 additions & 7 deletions Oqtane.Client/Services/NotificationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,9 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
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)
{
_siteState = siteState;
}

private string Apiurl => CreateApiUrl("Notification", _siteState.Alias);
private string Apiurl => CreateApiUrl("Notification");

public async Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId)
{
Expand Down
8 changes: 2 additions & 6 deletions Oqtane.Client/Services/PackageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
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)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl("Package", _siteState.Alias);
private string Apiurl => CreateApiUrl("Package");

public async Task<List<Package>> GetPackagesAsync(string type)
{
Expand Down
10 changes: 2 additions & 8 deletions Oqtane.Client/Services/PageModuleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,9 @@ namespace Oqtane.Services
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class PageModuleService : ServiceBase, IPageModuleService
{

private readonly SiteState _siteState;
public PageModuleService(HttpClient http, SiteState siteState) : base(http, siteState) { }

public PageModuleService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}

private string Apiurl => CreateApiUrl("PageModule", _siteState.Alias);
private string Apiurl => CreateApiUrl("PageModule");

public async Task<PageModule> GetPageModuleAsync(int pageModuleId)
{
Expand Down
Loading