using Oqtane.Shared;
using Oqtane.Models;
using System.Net.Http;
using System.Threading.Tasks;
using Oqtane.Documentation;
using System.Net;
using System.Collections.Generic;
using Microsoft.Extensions.Localization;
namespace Oqtane.Services
{
///
/// Manage (get / update) user information
///
public interface IUserService
{
///
/// Get a of a specific site
///
/// ID of a
/// ID of a
///
Task GetUserAsync(int userId, int siteId);
///
/// Get a of a specific site
///
/// Username / login of a
/// ID of a
///
Task GetUserAsync(string username, int siteId);
///
/// Get a of a specific site
///
/// Username / login of a
/// email address of a
/// ID of a
///
Task GetUserAsync(string username, string email, int siteId);
///
/// Save a user to the Database.
/// The object contains all the information incl. what it belongs to.
///
///
///
Task AddUserAsync(User user);
///
/// Update an existing user in the database.
///
///
///
Task UpdateUserAsync(User user);
///
/// Delete / remove a user in the database
///
/// ID-reference to the
/// ID-reference to the
///
Task DeleteUserAsync(int userId, int siteId);
///
/// Will login the specified .
///
/// Note that this will probably not be a real User, but a user object where the `Username` and `Password` have been filled.
///
/// A object which should have at least the and set.
/// Determines if the login cookie should be set (only relevant for Hybrid scenarios)
/// Determines if the login cookie should be persisted for a long time.
///
Task LoginUserAsync(User user, bool setCookie, bool isPersistent);
///
/// Logout a
///
///
///
Task LogoutUserAsync(User user);
///
/// Logout a
///
///
///
Task LogoutUserEverywhereAsync(User user);
///
/// Update e-mail verification status of a user.
///
/// The we're verifying
/// A Hash value in the URL which verifies this user got the e-mail (containing this token)
///
Task VerifyEmailAsync(User user, string token);
///
/// Trigger a forgot-password e-mail for this .
///
///
///
Task ForgotPasswordAsync(User user);
///
/// Reset the password of this
///
///
///
///
Task ResetPasswordAsync(User user, string token);
///
/// Verify the two factor verification code
///
///
///
///
Task VerifyTwoFactorAsync(User user, string token);
///
/// Validate identity user info.
///
///
///
///
///
Task ValidateUserAsync(string username, string email, string password);
///
/// Validate a users password against the password policy
///
///
///
Task ValidatePasswordAsync(string password);
///
/// Get token for current user
///
///
Task GetTokenAsync();
///
/// Get personal access token for current user (administrators only)
///
///
Task GetPersonalAccessTokenAsync();
///
/// Link an external login with a local user account
///
/// The we're verifying
/// A Hash value in the URL which verifies this user got the e-mail (containing this token)
/// External Login provider type
/// External Login provider key
/// External Login provider display name
///
Task LinkUserAsync(User user, string token, string type, string key, string name);
///
/// Get password requirements for site
///
/// ID of a
///
Task GetPasswordRequirementsAsync(int siteId);
///
/// Bulk import of users
///
/// ID of a
/// ID of a
/// Indicates if new users should be notified by email
///
Task> ImportUsersAsync(int siteId, int fileId, bool notify);
}
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class UserService : ServiceBase, IUserService
{
private readonly IStringLocalizer _localizer;
public UserService(IStringLocalizer localizer, HttpClient http, SiteState siteState) : base(http, siteState)
{
_localizer = localizer;
}
private string Apiurl => CreateApiUrl("User");
public async Task GetUserAsync(int userId, int siteId)
{
return await GetJsonAsync($"{Apiurl}/{userId}?siteid={siteId}");
}
public async Task GetUserAsync(string username, int siteId)
{
return await GetJsonAsync($"{Apiurl}/username/{username}?siteid={siteId}");
}
public async Task GetUserAsync(string username, string email, int siteId)
{
return await GetJsonAsync($"{Apiurl}/name/{(!string.IsNullOrEmpty(username) ? username : "-")}/{(!string.IsNullOrEmpty(email) ? email : "-")}/?siteid={siteId}");
}
public async Task AddUserAsync(User user)
{
return await PostJsonAsync(Apiurl, user);
}
public async Task UpdateUserAsync(User user)
{
return await PutJsonAsync($"{Apiurl}/{user.UserId}", user);
}
public async Task DeleteUserAsync(int userId, int siteId)
{
await DeleteAsync($"{Apiurl}/{userId}?siteid={siteId}");
}
public async Task LoginUserAsync(User user, bool setCookie, bool isPersistent)
{
return await PostJsonAsync($"{Apiurl}/login?setcookie={setCookie}&persistent={isPersistent}", user);
}
public async Task LogoutUserAsync(User user)
{
await PostJsonAsync($"{Apiurl}/logout", user);
}
public async Task LogoutUserEverywhereAsync(User user)
{
await PostJsonAsync($"{Apiurl}/logouteverywhere", user);
}
public async Task VerifyEmailAsync(User user, string token)
{
return await PostJsonAsync($"{Apiurl}/verify?token={token}", user);
}
public async Task ForgotPasswordAsync(User user)
{
await PostJsonAsync($"{Apiurl}/forgot", user);
}
public async Task ResetPasswordAsync(User user, string token)
{
return await PostJsonAsync($"{Apiurl}/reset?token={token}", user);
}
public async Task VerifyTwoFactorAsync(User user, string token)
{
return await PostJsonAsync($"{Apiurl}/twofactor?token={token}", user);
}
public async Task ValidateUserAsync(string username, string email, string password)
{
return await GetJsonAsync($"{Apiurl}/validateuser?username={WebUtility.UrlEncode(username)}&email={WebUtility.UrlEncode(email)}&password={WebUtility.UrlEncode(password)}");
}
public async Task ValidatePasswordAsync(string password)
{
return await GetJsonAsync($"{Apiurl}/validate/{WebUtility.UrlEncode(password)}");
}
public async Task GetTokenAsync()
{
return await GetStringAsync($"{Apiurl}/token");
}
public async Task GetPersonalAccessTokenAsync()
{
return await GetStringAsync($"{Apiurl}/personalaccesstoken");
}
public async Task LinkUserAsync(User user, string token, string type, string key, string name)
{
return await PostJsonAsync($"{Apiurl}/link?token={token}&type={type}&key={key}&name={name}", user);
}
public async Task GetPasswordRequirementsAsync(int siteId)
{
var requirements = await GetJsonAsync>($"{Apiurl}/passwordrequirements/{siteId}");
var minimumlength = (requirements.ContainsKey("IdentityOptions:Password:RequiredLength")) ? requirements["IdentityOptions:Password:RequiredLength"] : "6";
var uniquecharacters = (requirements.ContainsKey("IdentityOptions:Password:RequiredUniqueChars")) ? requirements["IdentityOptions:Password:RequiredUniqueChars"] : "1";
var requiredigit = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireDigit")) ? requirements["IdentityOptions:Password:RequireDigit"] : "true");
var requireupper = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireUppercase")) ? requirements["IdentityOptions:Password:RequireUppercase"] : "true");
var requirelower = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireLowercase")) ? requirements["IdentityOptions:Password:RequireLowercase"] : "true");
var requirepunctuation = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireNonAlphanumeric")) ? requirements["IdentityOptions:Password:RequireNonAlphanumeric"] : "true");
// replace the placeholders with the setting values
string digitRequirement = requiredigit ? _localizer["Password.DigitRequirement"] + ", " : "";
string uppercaseRequirement = requireupper ? _localizer["Password.UppercaseRequirement"] + ", " : "";
string lowercaseRequirement = requirelower ? _localizer["Password.LowercaseRequirement"] + ", " : "";
string punctuationRequirement = requirepunctuation ? _localizer["Password.PunctuationRequirement"] + ", " : "";
string passwordValidationCriteriaTemplate = _localizer["Password.ValidationCriteria"];
// format requirements
return string.Format(passwordValidationCriteriaTemplate, minimumlength, uniquecharacters, digitRequirement, uppercaseRequirement, lowercaseRequirement, punctuationRequirement);
}
public async Task> ImportUsersAsync(int siteId, int fileId, bool notify)
{
return await PostJsonAsync>($"{Apiurl}/import?siteid={siteId}&fileid={fileId}¬ify={notify}", null);
}
}
}