Merge pull request #2149 from sbwalker/dev
Fix #2144 - install issue, Fix #2146 - move file issue, require verification of external login account linkage
This commit is contained in:
		| @ -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); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <root> | ||||
|   <!--  | ||||
|     Microsoft ResX Schema  | ||||
| @ -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> | ||||
| @ -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> | ||||
| @ -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() | ||||
|  | ||||
| @ -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() | ||||
|         { | ||||
|  | ||||
| @ -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) | ||||
|         { | ||||
|  | ||||
| @ -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) | ||||
|         { | ||||
|  | ||||
| @ -15,7 +15,7 @@ namespace Oqtane.Services | ||||
|         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; | ||||
|  | ||||
| @ -115,5 +115,18 @@ namespace Oqtane.Services | ||||
|         /// </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); | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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() | ||||
|         { | ||||
|  | ||||
| @ -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() | ||||
|         { | ||||
|  | ||||
| @ -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) | ||||
|         { | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
|  | ||||
| @ -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) | ||||
|         { | ||||
|  | ||||
| @ -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) | ||||
|         { | ||||
|  | ||||
| @ -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) | ||||
|         { | ||||
|  | ||||
| @ -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) | ||||
|         { | ||||
|  | ||||
| @ -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) | ||||
|         { | ||||
|  | ||||
| @ -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) | ||||
|         { | ||||
|  | ||||
| @ -13,16 +13,9 @@ namespace Oqtane.Services | ||||
|     [PrivateApi("Don't show in the documentation, as everything should use the Interface")] | ||||
|     public class PageService : ServiceBase, IPageService | ||||
|     { | ||||
|          | ||||
|         private readonly SiteState _siteState; | ||||
|         public PageService(HttpClient http, SiteState siteState) : base(http, siteState) { } | ||||
|  | ||||
|         public PageService(HttpClient http, SiteState siteState) : base(http) | ||||
|         { | ||||
|              | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("Page", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("Page"); | ||||
|  | ||||
|         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")] | ||||
|     public class ProfileService : ServiceBase, IProfileService | ||||
|     { | ||||
|          | ||||
|         private readonly SiteState _siteState; | ||||
|         public ProfileService(HttpClient http, SiteState siteState) : base(http, siteState) { } | ||||
|  | ||||
|         public ProfileService(HttpClient http, SiteState siteState) : base(http) | ||||
|         {             | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("Profile", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("Profile"); | ||||
|  | ||||
|         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")] | ||||
|     public class RoleService : ServiceBase, IRoleService | ||||
|     { | ||||
|          | ||||
|         private readonly SiteState _siteState; | ||||
|         public RoleService(HttpClient http, SiteState siteState) : base(http, siteState) { } | ||||
|  | ||||
|         public RoleService(HttpClient http, SiteState siteState) : base(http) | ||||
|         { | ||||
|              | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("Role", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("Role"); | ||||
|  | ||||
|         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")] | ||||
|     public class SettingService : ServiceBase, ISettingService | ||||
|     { | ||||
|          | ||||
|         private readonly SiteState _siteState; | ||||
|         public SettingService(HttpClient http, SiteState siteState) : base(http, siteState) { } | ||||
|  | ||||
|         public SettingService(HttpClient http, SiteState siteState) : base(http) | ||||
|         {             | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("Setting", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("Setting"); | ||||
|  | ||||
|         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")] | ||||
|     public class SiteService : ServiceBase, ISiteService | ||||
|     { | ||||
|          | ||||
|         private readonly SiteState _siteState; | ||||
|         public SiteService(HttpClient http, SiteState siteState) : base(http, siteState) { } | ||||
|  | ||||
|         public SiteService(HttpClient http, SiteState siteState) : base(http) | ||||
|         {             | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("Site", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("Site"); | ||||
|  | ||||
|         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")] | ||||
|     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) | ||||
|         { | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|         private string Apiurl => CreateApiUrl("SiteTemplate", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("SiteTemplate"); | ||||
|  | ||||
|         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")] | ||||
|     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) | ||||
|         { | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("Sql", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("Sql"); | ||||
|  | ||||
|         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")] | ||||
|     public class SyncService : ServiceBase, ISyncService | ||||
|     { | ||||
|         public SyncService(HttpClient http, SiteState siteState) : base(http, siteState) { } | ||||
|  | ||||
|         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); | ||||
|         private string ApiUrl => CreateApiUrl("Sync"); | ||||
|  | ||||
|         /// <inheritdoc /> | ||||
|         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")] | ||||
|     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) | ||||
|         { | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("System", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("System"); | ||||
|  | ||||
|         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")] | ||||
|     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) | ||||
|         { | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("Tenant", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("Tenant"); | ||||
|  | ||||
|         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")] | ||||
|     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) | ||||
|         { | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string ApiUrl => CreateApiUrl("Theme", _siteState.Alias); | ||||
|         private string ApiUrl => CreateApiUrl("Theme"); | ||||
|  | ||||
|         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")] | ||||
|     public class UrlMappingService : ServiceBase, IUrlMappingService | ||||
|     { | ||||
|          | ||||
|         private readonly SiteState _siteState; | ||||
|         public UrlMappingService(HttpClient http, SiteState siteState) : base(http, siteState) { } | ||||
|  | ||||
|         public UrlMappingService(HttpClient http, SiteState siteState) : base(http) | ||||
|         { | ||||
|              | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("UrlMapping", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("UrlMapping"); | ||||
|  | ||||
|         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")] | ||||
|     public class UserRoleService : ServiceBase, IUserRoleService | ||||
|     { | ||||
|          | ||||
|         private readonly SiteState _siteState; | ||||
|         public UserRoleService(HttpClient http, SiteState siteState) : base(http, siteState) { } | ||||
|  | ||||
|         public UserRoleService(HttpClient http, SiteState siteState) : base(http) | ||||
|         {             | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("UserRole", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("UserRole"); | ||||
|  | ||||
|         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")] | ||||
|     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) | ||||
|         { | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("User", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("User"); | ||||
|  | ||||
|         public async Task<User> GetUserAsync(int userId, int siteId) | ||||
|         { | ||||
| @ -89,5 +84,11 @@ namespace Oqtane.Services | ||||
|         { | ||||
|             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")] | ||||
|     public class VisitorService : ServiceBase, IVisitorService | ||||
|     { | ||||
|          | ||||
|         private readonly SiteState _siteState; | ||||
|         public VisitorService(HttpClient http, SiteState siteState) : base(http, siteState) { } | ||||
|  | ||||
|         public VisitorService(HttpClient http, SiteState siteState) : base(http) | ||||
|         { | ||||
|              | ||||
|             _siteState = siteState; | ||||
|         } | ||||
|  | ||||
|         private string Apiurl => CreateApiUrl("Visitor", _siteState.Alias); | ||||
|         private string Apiurl => CreateApiUrl("Visitor"); | ||||
|  | ||||
|         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) | ||||
|                 { | ||||
|                     file.Folder = _folders.GetFolder(file.FolderId); | ||||
|                     string folderpath = _folders.GetFolderPath(file.Folder); | ||||
|                     if (!Directory.Exists(folderpath)) | ||||
|                     { | ||||
|  | ||||
| @ -203,7 +203,7 @@ namespace Oqtane.Controllers | ||||
|                     else | ||||
|                     { | ||||
|                         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); | ||||
|                         _notifications.AddNotification(notification); | ||||
|                     } | ||||
| @ -419,7 +419,7 @@ namespace Oqtane.Controllers | ||||
|                     } | ||||
|                     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; | ||||
|                     } | ||||
|                 } | ||||
| @ -477,7 +477,7 @@ namespace Oqtane.Controllers | ||||
|                     } | ||||
|                     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; | ||||
|                     } | ||||
|                 } | ||||
| @ -511,6 +511,38 @@ namespace Oqtane.Controllers | ||||
|             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 | ||||
|         [HttpGet("validate/{password}")] | ||||
|         public async Task<bool> Validate(string password) | ||||
|  | ||||
| @ -18,6 +18,7 @@ using System.Net.Http; | ||||
| using System.Net.Http.Headers; | ||||
| using System.Text.RegularExpressions; | ||||
| using Microsoft.AspNetCore.Authentication.Cookies; | ||||
| using System.Net; | ||||
|  | ||||
| namespace Oqtane.Extensions | ||||
| { | ||||
| @ -147,7 +148,13 @@ namespace Oqtane.Extensions | ||||
|             } | ||||
|  | ||||
|             // 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) | ||||
| @ -157,7 +164,14 @@ namespace Oqtane.Extensions | ||||
|             var email = context.Principal.FindFirstValue(emailClaimType); | ||||
|  | ||||
|             // 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) | ||||
| @ -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"); | ||||
|             // redirect to login page | ||||
|             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(); | ||||
|             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); | ||||
|             // redirect to login page | ||||
|             var alias = context.HttpContext.GetAlias(); | ||||
|             context.Response.Redirect(alias.Path + "/login", true); | ||||
|             context.Response.Redirect($"{alias.Path}/login", true); | ||||
|             context.HandleResponse(); | ||||
|             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 status = ExternalLoginStatus.Success; | ||||
|  | ||||
|             if (EmailValid(email, httpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", ""))) | ||||
|             { | ||||
|                 var _identityUserManager = httpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>(); | ||||
|                 var _users = httpContext.RequestServices.GetRequiredService<IUserRepository>(); | ||||
|                 var _userRoles = httpContext.RequestServices.GetRequiredService<IUserRoleRepository>(); | ||||
|                 var alias = httpContext.GetAlias(); | ||||
|                 var providerType = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderType", ""); | ||||
|                 var providerName = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderName", ""); | ||||
|                 var providerKey = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier); | ||||
| @ -215,6 +231,7 @@ namespace Oqtane.Extensions | ||||
|                 { | ||||
|                     if (duplicates) | ||||
|                     { | ||||
|                         status = ExternalLoginStatus.DuplicateEmail; | ||||
|                         _logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email); | ||||
|                     } | ||||
|                     else | ||||
| @ -230,7 +247,7 @@ namespace Oqtane.Extensions | ||||
|                             { | ||||
|                                 user = new User | ||||
|                                 { | ||||
|                                     SiteId = httpContext.GetAlias().SiteId, | ||||
|                                     SiteId = alias.SiteId, | ||||
|                                     Username = email, | ||||
|                                     DisplayName = email, | ||||
|                                     Email = email, | ||||
| @ -242,7 +259,7 @@ namespace Oqtane.Extensions | ||||
|                                 if (user != null) | ||||
|                                 { | ||||
|                                     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!"; | ||||
|                                     var notification = new Notification(user.SiteId, user, "User Account Notification", body); | ||||
|                                     _notifications.AddNotification(notification); | ||||
| @ -254,16 +271,19 @@ namespace Oqtane.Extensions | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     status = ExternalLoginStatus.UserNotCreated; | ||||
|                                     _logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email); | ||||
|                                 } | ||||
|                             } | ||||
|                             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()); | ||||
|                             } | ||||
|                         } | ||||
|                         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); | ||||
|                         } | ||||
|                     } | ||||
| @ -271,7 +291,7 @@ namespace Oqtane.Extensions | ||||
|                 else | ||||
|                 { | ||||
|                     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.ProviderKey == providerKey) | ||||
| @ -281,15 +301,23 @@ namespace Oqtane.Extensions | ||||
|                         else | ||||
|                         { | ||||
|                             // 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); | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         // add user login | ||||
|                         await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType, providerKey, "")); | ||||
|                         user = _users.GetUser(identityuser.UserName); | ||||
|                         _logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External User Login Added For {Email} Using Provider {Provider}", email, providerName); | ||||
|                         // new external login using existing user account - verification required | ||||
|                         var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>(); | ||||
|                         string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser); | ||||
|                         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); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
| @ -297,8 +325,8 @@ namespace Oqtane.Extensions | ||||
|                 if (user != null) | ||||
|                 { | ||||
|                     var principal = (ClaimsIdentity)claimsPrincipal.Identity; | ||||
|                     UserSecurity.ResetClaimsIdentity(principal); | ||||
|                     var identity = UserSecurity.CreateClaimsIdentity(httpContext.GetAlias(), user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList()); | ||||
|                     UserSecurity.ResetClaimsIdentity(principal);                     | ||||
|                     var identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList()); | ||||
|                     principal.AddClaims(identity.Claims); | ||||
|  | ||||
|                     // update user | ||||
| @ -307,13 +335,10 @@ namespace Oqtane.Extensions | ||||
|                     _users.UpdateUser(user); | ||||
|                     _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 | ||||
|             { | ||||
|                 status = ExternalLoginStatus.InvalidEmail; | ||||
|                 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); | ||||
| @ -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."); | ||||
|                     } | ||||
|                 } | ||||
|                 await httpContext.SignOutAsync(); | ||||
|             } | ||||
|             return status; | ||||
|         } | ||||
|  | ||||
|         private static bool EmailValid(string email, string domainfilter) | ||||
|  | ||||
| @ -9,7 +9,6 @@ namespace Oqtane.Pages | ||||
|     [AllowAnonymous] | ||||
|     public class LoginModel : PageModel | ||||
|     { | ||||
|  | ||||
|         private readonly UserManager<IdentityUser> _identityUserManager; | ||||
|         private readonly SignInManager<IdentityUser> _identitySignInManager; | ||||
|  | ||||
|  | ||||
| @ -50,15 +50,15 @@ namespace Oqtane.Security | ||||
|                                 else | ||||
|                                 { | ||||
|                                     // user has no roles - remove principal | ||||
|                                     context.RejectPrincipal(); | ||||
|                                     Log(_logger, alias, "Permissions Removed For User {Username} Accessing {Url}", context.Principal.Identity.Name, path); | ||||
|                                     context.RejectPrincipal(); | ||||
|                                 } | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 // user does not exist - remove principal | ||||
|                                 context.RejectPrincipal(); | ||||
|                                 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.SameSite = SameSiteMode.Strict; | ||||
|                 options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; | ||||
|                 //options.Cookie.HttpOnly = false; | ||||
|             }); | ||||
|  | ||||
|             services.AddIdentityCore<IdentityUser>(options => { }) | ||||
| @ -114,6 +113,7 @@ namespace Oqtane | ||||
|                 { | ||||
|                     options.DefaultAuthenticateScheme = Constants.AuthenticationScheme; | ||||
|                     options.DefaultChallengeScheme = Constants.AuthenticationScheme; | ||||
|                     options.DefaultSignOutScheme = Constants.AuthenticationScheme; | ||||
|                 }) | ||||
|                 .AddCookie(Constants.AuthenticationScheme) | ||||
|                 .AddOpenIdConnect(AuthenticationProviderTypes.OpenIDConnect, options => { }) | ||||
| @ -130,7 +130,7 @@ namespace Oqtane | ||||
|  | ||||
|             services.AddMvc(options => | ||||
|             { | ||||
|                 options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); | ||||
|                 //options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); | ||||
|             }) | ||||
|             .AddNewtonsoftJson() | ||||
|             .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 | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Shaun Walker
					Shaun Walker