Create Users Manually (Email still required) (#1381)
* Implemented a manual button to allow users to setup an account, even after they invited. Updated error toast to put "Error" in the title of the toast. * Updated the exception middleware to always send full context instead of generic "Internal Server Error"
This commit is contained in:
parent
63d74ecf9a
commit
1d806bf622
10 changed files with 1668 additions and 17 deletions
|
@ -348,6 +348,24 @@ namespace API.Controllers
|
|||
return BadRequest("There was an exception when updating the user");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests the Invite Url for the UserId. Will return error if user is already validated.
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="withBaseUrl">Include the "https://ip:port/" in the generated link</param>
|
||||
/// <returns></returns>
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpGet("invite-url")]
|
||||
public async Task<ActionResult<string>> GetInviteUrl(int userId, bool withBaseUrl)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId);
|
||||
if (user.EmailConfirmed)
|
||||
return BadRequest("User is already confirmed");
|
||||
if (string.IsNullOrEmpty(user.ConfirmationToken))
|
||||
return BadRequest("Manual setup is unable to be completed. Please cancel and recreate the invite.");
|
||||
|
||||
return GenerateEmailLink(user.ConfirmationToken, "confirm-email", user.Email, withBaseUrl);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
@ -428,12 +446,10 @@ namespace API.Controllers
|
|||
lib.AppUsers.Add(user);
|
||||
}
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
|
||||
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
if (string.IsNullOrEmpty(token)) return BadRequest("There was an issue sending email");
|
||||
|
||||
|
||||
var emailLink = GenerateEmailLink(token, "confirm-email", dto.Email);
|
||||
_logger.LogCritical("[Invite User]: Email Link for {UserName}: {Link}", user.UserName, emailLink);
|
||||
var host = _environment.IsDevelopment() ? "localhost:4200" : Request.Host.ToString();
|
||||
|
@ -447,6 +463,11 @@ namespace API.Controllers
|
|||
ServerConfirmationLink = emailLink
|
||||
});
|
||||
}
|
||||
|
||||
user.ConfirmationToken = token;
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
return Ok(new InviteUserResponse
|
||||
{
|
||||
EmailLink = emailLink,
|
||||
|
@ -486,6 +507,7 @@ namespace API.Controllers
|
|||
if (!await ConfirmEmailToken(dto.Token, user)) return BadRequest("Invalid Email Token");
|
||||
|
||||
user.UserName = dto.Username;
|
||||
user.ConfirmationToken = null;
|
||||
var errors = await _accountService.ChangeUserPassword(user, dto.Password);
|
||||
if (errors.Any())
|
||||
{
|
||||
|
@ -617,12 +639,11 @@ namespace API.Controllers
|
|||
return Ok(emailLink);
|
||||
}
|
||||
|
||||
private string GenerateEmailLink(string token, string routePart, string email)
|
||||
private string GenerateEmailLink(string token, string routePart, string email, bool withHost = true)
|
||||
{
|
||||
var host = _environment.IsDevelopment() ? "localhost:4200" : Request.Host.ToString();
|
||||
var emailLink =
|
||||
$"{Request.Scheme}://{host}{Request.PathBase}/registration/{routePart}?token={HttpUtility.UrlEncode(token)}&email={HttpUtility.UrlEncode(email)}";
|
||||
return emailLink;
|
||||
if (withHost) return $"{Request.Scheme}://{host}{Request.PathBase}/registration/{routePart}?token={HttpUtility.UrlEncode(token)}&email={HttpUtility.UrlEncode(email)}";
|
||||
return $"registration/{routePart}?token={HttpUtility.UrlEncode(token)}&email={HttpUtility.UrlEncode(email)}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
1579
API/Data/Migrations/20220717145254_UserConfirmationLink.Designer.cs
generated
Normal file
1579
API/Data/Migrations/20220717145254_UserConfirmationLink.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
25
API/Data/Migrations/20220717145254_UserConfirmationLink.cs
Normal file
25
API/Data/Migrations/20220717145254_UserConfirmationLink.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
public partial class UserConfirmationLink : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ConfirmationToken",
|
||||
table: "AspNetUsers",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ConfirmationToken",
|
||||
table: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -60,6 +60,9 @@ namespace API.Data.Migrations
|
|||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConfirmationToken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@ namespace API.Entities
|
|||
/// An API Key to interact with external services, like OPDS
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; }
|
||||
/// <summary>
|
||||
/// The confirmation token for the user (invite). This will be set to null after the user confirms.
|
||||
/// </summary>
|
||||
public string ConfirmationToken { get; set; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
@ -35,9 +35,9 @@ namespace API.Middleware
|
|||
context.Response.ContentType = "application/json";
|
||||
context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
|
||||
|
||||
var response = _env.IsDevelopment()
|
||||
? new ApiException(context.Response.StatusCode, ex.Message, ex.StackTrace)
|
||||
: new ApiException(context.Response.StatusCode, "Internal Server Error", ex.StackTrace);
|
||||
var errorMessage = string.IsNullOrEmpty(ex.Message) ? "Internal Server Error" : ex.Message;
|
||||
|
||||
var response = new ApiException(context.Response.StatusCode, errorMessage, ex.StackTrace);
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue