Merge pull request #4756 from zyhfish/task/fix-4752

Fix #4752: validate the username and email.
This commit is contained in:
Shaun Walker 2024-10-24 09:53:24 -04:00 committed by GitHub
commit 6719d242bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 167 additions and 102 deletions

View File

@ -156,129 +156,130 @@
private List<SiteTemplate> _templates; private List<SiteTemplate> _templates;
private string _template = Constants.DefaultSiteTemplate; private string _template = Constants.DefaultSiteTemplate;
private bool _register = true; private bool _register = true;
private string _message = string.Empty; private string _message = string.Empty;
private string _loadingDisplay = "display: none;"; private string _loadingDisplay = "display: none;";
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
// include CSS // include CSS
var content = $"<link rel=\"stylesheet\" href=\"{Constants.BootstrapStylesheetUrl}\" integrity=\"{Constants.BootstrapStylesheetIntegrity}\" crossorigin=\"anonymous\" type=\"text/css\"/>"; var content = $"<link rel=\"stylesheet\" href=\"{Constants.BootstrapStylesheetUrl}\" integrity=\"{Constants.BootstrapStylesheetIntegrity}\" crossorigin=\"anonymous\" type=\"text/css\"/>";
SiteState.AppendHeadContent(content); SiteState.AppendHeadContent(content);
_togglePassword = SharedLocalizer["ShowPassword"]; _togglePassword = SharedLocalizer["ShowPassword"];
_toggleConfirmPassword = SharedLocalizer["ShowPassword"]; _toggleConfirmPassword = SharedLocalizer["ShowPassword"];
_databases = await DatabaseService.GetDatabasesAsync(); _databases = await DatabaseService.GetDatabasesAsync();
if (_databases.Exists(item => item.IsDefault)) if (_databases.Exists(item => item.IsDefault))
{ {
_databaseName = _databases.Find(item => item.IsDefault).Name; _databaseName = _databases.Find(item => item.IsDefault).Name;
} }
else else
{ {
_databaseName = "LocalDB"; _databaseName = "LocalDB";
} }
LoadDatabaseConfigComponent(); LoadDatabaseConfigComponent();
_templates = await SiteTemplateService.GetSiteTemplatesAsync(); _templates = await SiteTemplateService.GetSiteTemplatesAsync();
} }
private void DatabaseChanged(ChangeEventArgs eventArgs) private void DatabaseChanged(ChangeEventArgs eventArgs)
{ {
try try
{ {
_databaseName = (string)eventArgs.Value; _databaseName = (string)eventArgs.Value;
_showConnectionString = false; _showConnectionString = false;
LoadDatabaseConfigComponent(); LoadDatabaseConfigComponent();
} }
catch catch
{ {
_message = Localizer["Error.DbConfig.Load"]; _message = Localizer["Error.DbConfig.Load"];
} }
} }
private void LoadDatabaseConfigComponent() private void LoadDatabaseConfigComponent()
{ {
var database = _databases.SingleOrDefault(d => d.Name == _databaseName); var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
if (database != null) if (database != null)
{ {
_databaseConfigType = Type.GetType(database.ControlType); _databaseConfigType = Type.GetType(database.ControlType);
DatabaseConfigComponent = builder => DatabaseConfigComponent = builder =>
{ {
builder.OpenComponent(0, _databaseConfigType); builder.OpenComponent(0, _databaseConfigType);
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); }); builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
builder.CloseComponent(); builder.CloseComponent();
}; };
} }
} }
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
if (firstRender) if (firstRender)
{ {
// include JavaScript // include JavaScript
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.IncludeScript("", Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous", "", "head"); await interop.IncludeScript("", Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous", "", "head");
} }
} }
private async Task Install() private async Task Install()
{ {
var connectionString = String.Empty; var connectionString = String.Empty;
if (_showConnectionString) if (_showConnectionString)
{ {
connectionString = _connectionString; connectionString = _connectionString;
} }
else else
{ {
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl) if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
{ {
connectionString = databaseConfigControl.GetConnectionString(); connectionString = databaseConfigControl.GetConnectionString();
} }
} }
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@")) if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
{ {
if (await UserService.ValidatePasswordAsync(_hostPassword)) var result = await UserService.ValidateUserAsync(_hostUsername, _hostEmail, _hostPassword);
{ if (result.Succeeded)
_loadingDisplay = ""; {
StateHasChanged(); _loadingDisplay = "";
StateHasChanged();
Uri uri = new Uri(NavigationManager.Uri); Uri uri = new Uri(NavigationManager.Uri);
var database = _databases.SingleOrDefault(d => d.Name == _databaseName); var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
var config = new InstallConfig var config = new InstallConfig
{ {
DatabaseType = database.DBType, DatabaseType = database.DBType,
ConnectionString = connectionString, ConnectionString = connectionString,
Aliases = uri.Authority, Aliases = uri.Authority,
HostUsername = _hostUsername, HostUsername = _hostUsername,
HostPassword = _hostPassword, HostPassword = _hostPassword,
HostEmail = _hostEmail, HostEmail = _hostEmail,
HostName = _hostUsername, HostName = _hostUsername,
TenantName = TenantNames.Master, TenantName = TenantNames.Master,
IsNewTenant = true, IsNewTenant = true,
SiteName = Constants.DefaultSite, SiteName = Constants.DefaultSite,
Register = _register, Register = _register,
SiteTemplate = _template, SiteTemplate = _template,
RenderMode = RenderModes.Static, RenderMode = RenderModes.Static,
Runtime = Runtimes.Server Runtime = Runtimes.Server
}; };
var installation = await InstallationService.Install(config); var installation = await InstallationService.Install(config);
if (installation.Success) if (installation.Success)
{ {
NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true); NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true);
} }
else else
{ {
_message = installation.Message; _message = installation.Message;
_loadingDisplay = "display: none;"; _loadingDisplay = "display: none;";
} }
} }
else else
{ {
_message = Localizer["Message.Password.Invalid"]; _message = string.Join("<br />", result.Errors.Select(i => !string.IsNullOrEmpty(i.Value) ? i.Value : Localizer[i.Key]));
} }
} }
else else

View File

@ -183,4 +183,7 @@
<data name="Template" xml:space="preserve"> <data name="Template" xml:space="preserve">
<value>Select a site template</value> <value>Select a site template</value>
</data> </data>
<data name="Message.Username.Invalid" xml:space="preserve">
<value>The Username Provided Does Not Meet The System Requirement, It Can Only Contains Letters Or Digits.</value>
</data>
</root> </root>

View File

@ -113,6 +113,15 @@ namespace Oqtane.Services
/// <returns></returns> /// <returns></returns>
Task<User> VerifyTwoFactorAsync(User user, string token); Task<User> VerifyTwoFactorAsync(User user, string token);
/// <summary>
/// Validate identity user info.
/// </summary>
/// <param name="username"></param>
/// <param name="email"></param>
/// <param name="password"></param>
/// <returns></returns>
Task<UserValidateResult> ValidateUserAsync(string username, string email, string password);
/// <summary> /// <summary>
/// Validate a users password against the password policy /// Validate a users password against the password policy
/// </summary> /// </summary>

View File

@ -89,6 +89,11 @@ namespace Oqtane.Services
return await PostJsonAsync<User>($"{Apiurl}/twofactor?token={token}", user); return await PostJsonAsync<User>($"{Apiurl}/twofactor?token={token}", user);
} }
public async Task<UserValidateResult> ValidateUserAsync(string username, string email, string password)
{
return await GetJsonAsync<UserValidateResult>($"{Apiurl}/validateuser?username={WebUtility.UrlEncode(username)}&email={WebUtility.UrlEncode(email)}&password={WebUtility.UrlEncode(password)}");
}
public async Task<bool> ValidatePasswordAsync(string password) public async Task<bool> ValidatePasswordAsync(string password)
{ {
return await GetJsonAsync<bool>($"{Apiurl}/validate/{WebUtility.UrlEncode(password)}"); return await GetJsonAsync<bool>($"{Apiurl}/validate/{WebUtility.UrlEncode(password)}");

View File

@ -347,6 +347,13 @@ namespace Oqtane.Controllers
return user; return user;
} }
// GET api/<controller>/validate/x
[HttpGet("validateuser")]
public async Task<UserValidateResult> ValidateUser(string username, string email, string password)
{
return await _userManager.ValidateUser(username, email, password);
}
// 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)

View File

@ -19,6 +19,7 @@ namespace Oqtane.Managers
Task<User> ResetPassword(User user, string token); Task<User> ResetPassword(User user, string token);
User VerifyTwoFactor(User user, string token); User VerifyTwoFactor(User user, string token);
Task<User> LinkExternalAccount(User user, string token, string type, string key, string name); Task<User> LinkExternalAccount(User user, string token, string type, string key, string name);
Task<UserValidateResult> ValidateUser(string username, string email, string password);
Task<bool> ValidatePassword(string password); Task<bool> ValidatePassword(string password);
Task<Dictionary<string, string>> ImportUsers(int siteId, string filePath, bool notify); Task<Dictionary<string, string>> ImportUsers(int siteId, string filePath, bool notify);
} }

View File

@ -540,6 +540,30 @@ namespace Oqtane.Managers
return user; return user;
} }
public async Task<UserValidateResult> ValidateUser(string username, string email, string password)
{
var validateResult = new UserValidateResult { Succeeded = true };
//validate username
var allowedChars = _identityUserManager.Options.User.AllowedUserNameCharacters;
if (string.IsNullOrWhiteSpace(username) || (!string.IsNullOrEmpty(allowedChars) && username.Any(c => !allowedChars.Contains(c))))
{
validateResult.Succeeded = false;
validateResult.Errors.Add("Message.Username.Invalid", string.Empty);
}
//validate password
var passwordValidator = new PasswordValidator<IdentityUser>();
var passwordResult = await passwordValidator.ValidateAsync(_identityUserManager, null, password);
if (!passwordResult.Succeeded)
{
validateResult.Succeeded = false;
validateResult.Errors.Add("Message.Password.Invalid", string.Empty);
}
return validateResult;
}
public async Task<bool> ValidatePassword(string password) public async Task<bool> ValidatePassword(string password)
{ {
var validator = new PasswordValidator<IdentityUser>(); var validator = new PasswordValidator<IdentityUser>();

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Oqtane.Models
{
public class UserValidateResult
{
public bool Succeeded { get; set; }
public IDictionary<string, string> Errors { get; set; } = new Dictionary<string, string>();
}
}