mirror of https://github.com/Squidex/squidex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
8.0 KiB
214 lines
8.0 KiB
// ==========================================================================
|
|
// Squidex Headless CMS
|
|
// ==========================================================================
|
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|
// All rights reserved. Licensed under the MIT license.
|
|
// ==========================================================================
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Options;
|
|
using Squidex.Config;
|
|
using Squidex.Domain.Users;
|
|
using Squidex.Infrastructure.Assets;
|
|
using Squidex.Infrastructure.Reflection;
|
|
using Squidex.Shared.Identity;
|
|
using Squidex.Shared.Users;
|
|
|
|
namespace Squidex.Areas.IdentityServer.Controllers.Profile
|
|
{
|
|
[Authorize]
|
|
[ApiExplorerSettings(IgnoreApi = true)]
|
|
public sealed class ProfileController : IdentityServerController
|
|
{
|
|
private readonly SignInManager<IdentityUser> signInManager;
|
|
private readonly UserManager<IdentityUser> userManager;
|
|
private readonly IUserPictureStore userPictureStore;
|
|
private readonly IAssetThumbnailGenerator assetThumbnailGenerator;
|
|
private readonly IOptions<MyIdentityOptions> identityOptions;
|
|
|
|
public ProfileController(
|
|
SignInManager<IdentityUser> signInManager,
|
|
UserManager<IdentityUser> userManager,
|
|
IUserPictureStore userPictureStore,
|
|
IAssetThumbnailGenerator assetThumbnailGenerator,
|
|
IOptions<MyIdentityOptions> identityOptions)
|
|
{
|
|
this.signInManager = signInManager;
|
|
this.identityOptions = identityOptions;
|
|
this.userManager = userManager;
|
|
this.userPictureStore = userPictureStore;
|
|
this.assetThumbnailGenerator = assetThumbnailGenerator;
|
|
}
|
|
|
|
[HttpGet]
|
|
[Route("/account/profile/")]
|
|
public async Task<IActionResult> Profile(string successMessage = null)
|
|
{
|
|
var user = await userManager.GetUserWithClaimsAsync(User);
|
|
|
|
return View(await GetProfileVM(user, successMessage: successMessage));
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("/account/profile/login-add/")]
|
|
public async Task<IActionResult> AddLogin(string provider)
|
|
{
|
|
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
|
|
|
|
var properties =
|
|
signInManager.ConfigureExternalAuthenticationProperties(provider,
|
|
Url.Action(nameof(AddLoginCallback)), userManager.GetUserId(User));
|
|
|
|
return Challenge(properties, provider);
|
|
}
|
|
|
|
[HttpGet]
|
|
[Route("/account/profile/login-add-callback/")]
|
|
public Task<IActionResult> AddLoginCallback()
|
|
{
|
|
return MakeChangeAsync(user => AddLoginAsync(user),
|
|
"Login added successfully.");
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("/account/profile/update/")]
|
|
public Task<IActionResult> UpdateProfile(ChangeProfileModel model)
|
|
{
|
|
return MakeChangeAsync(user => userManager.UpdateSafeAsync(user.Identity, model.ToValues()),
|
|
"Account updated successfully.");
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("/account/profile/login-remove/")]
|
|
public Task<IActionResult> RemoveLogin(RemoveLoginModel model)
|
|
{
|
|
return MakeChangeAsync(user => userManager.RemoveLoginAsync(user.Identity, model.LoginProvider, model.ProviderKey),
|
|
"Login provider removed successfully.");
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("/account/profile/password-set/")]
|
|
public Task<IActionResult> SetPassword(SetPasswordModel model)
|
|
{
|
|
return MakeChangeAsync(user => userManager.AddPasswordAsync(user.Identity, model.Password),
|
|
"Password set successfully.");
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("/account/profile/password-change/")]
|
|
public Task<IActionResult> ChangePassword(ChangePasswordModel model)
|
|
{
|
|
return MakeChangeAsync(user => userManager.ChangePasswordAsync(user.Identity, model.OldPassword, model.Password),
|
|
"Password changed successfully.");
|
|
}
|
|
|
|
[HttpPost]
|
|
[Route("/account/profile/upload-picture/")]
|
|
public Task<IActionResult> UploadPicture(List<IFormFile> file)
|
|
{
|
|
return MakeChangeAsync(user => UpdatePictureAsync(file, user),
|
|
"Picture uploaded successfully.");
|
|
}
|
|
|
|
private async Task<IdentityResult> AddLoginAsync(UserWithClaims user)
|
|
{
|
|
var externalLogin = await signInManager.GetExternalLoginInfoWithDisplayNameAsync(userManager.GetUserId(User));
|
|
|
|
return await userManager.AddLoginAsync(user.Identity, externalLogin);
|
|
}
|
|
|
|
private async Task<IdentityResult> UpdatePictureAsync(List<IFormFile> file, UserWithClaims user)
|
|
{
|
|
if (file.Count != 1)
|
|
{
|
|
return IdentityResult.Failed(new IdentityError { Description = "Please upload a single file." });
|
|
}
|
|
|
|
var thumbnailStream = new MemoryStream();
|
|
try
|
|
{
|
|
await assetThumbnailGenerator.CreateThumbnailAsync(file[0].OpenReadStream(), thumbnailStream, 128, 128, "Crop");
|
|
|
|
thumbnailStream.Position = 0;
|
|
}
|
|
catch
|
|
{
|
|
return IdentityResult.Failed(new IdentityError { Description = "Picture is not a valid image." });
|
|
}
|
|
|
|
await userPictureStore.UploadAsync(user.Id, thumbnailStream);
|
|
|
|
return await userManager.UpdateSafeAsync(user.Identity, new UserValues { PictureUrl = SquidexClaimTypes.PictureUrlStore });
|
|
}
|
|
|
|
private async Task<IActionResult> MakeChangeAsync(Func<UserWithClaims, Task<IdentityResult>> action, string successMessage, ChangeProfileModel model = null)
|
|
{
|
|
var user = await userManager.GetUserWithClaimsAsync(User);
|
|
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return View(nameof(Profile), await GetProfileVM(user, model));
|
|
}
|
|
|
|
string errorMessage;
|
|
try
|
|
{
|
|
var result = await action(user);
|
|
|
|
if (result.Succeeded)
|
|
{
|
|
await signInManager.SignInAsync(user.Identity, true);
|
|
|
|
return RedirectToAction(nameof(Profile), new { successMessage });
|
|
}
|
|
|
|
errorMessage = string.Join(". ", result.Errors.Select(x => x.Description));
|
|
}
|
|
catch
|
|
{
|
|
errorMessage = "An unexpected exception occurred.";
|
|
}
|
|
|
|
return View(nameof(Profile), await GetProfileVM(user, model, errorMessage));
|
|
}
|
|
|
|
private async Task<ProfileVM> GetProfileVM(UserWithClaims user, ChangeProfileModel model = null, string errorMessage = null, string successMessage = null)
|
|
{
|
|
var taskForProviders = signInManager.GetExternalProvidersAsync();
|
|
var taskForPassword = userManager.HasPasswordAsync(user.Identity);
|
|
var taskForLogins = userManager.GetLoginsAsync(user.Identity);
|
|
|
|
await Task.WhenAll(taskForProviders, taskForPassword, taskForLogins);
|
|
|
|
var result = new ProfileVM
|
|
{
|
|
Id = user.Id,
|
|
Email = user.Email,
|
|
ErrorMessage = errorMessage,
|
|
ExternalLogins = taskForLogins.Result,
|
|
ExternalProviders = taskForProviders.Result,
|
|
DisplayName = user.DisplayName(),
|
|
IsHidden = user.IsHidden(),
|
|
HasPassword = taskForPassword.Result,
|
|
HasPasswordAuth = identityOptions.Value.AllowPasswordAuth,
|
|
SuccessMessage = successMessage
|
|
};
|
|
|
|
if (model != null)
|
|
{
|
|
SimpleMapper.Map(model, result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|