using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AutoMapper;
using gehGassi.Core.Interfaces;
using gehGassi.Core.Services;
using gehGassi.Domain.Common;
using gehGassi.Domain.Users;
using gehGassi.Dto;
using gehGassi.Dto.Common;
using gehGassi.Permissions;
using gehGassi.Web.Auth;
using gehGassi.Web.Auth.Attributes;
using gehGassi.Web.Helper;
using gehGassi.Web.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NetTopologySuite.Geometries;
using NetTopologySuite;
using SixLabors.ImageSharp;
using LocalizationOptions = gehGassi.Web.Helper.LocalizationOptions;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Advanced;
using System.Net;
using System.Net.Http;
using gehGassi.Dto.DogWalkers;
using System.Net.Http.Headers;
using System.Text.Json;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Hosting;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;
using System.Threading;
using Microsoft.Extensions.Caching.Distributed;
using gehGassi.Dto.Reporting;
using Asp.Versioning;
using Microsoft.AspNetCore.Authentication.Cookies;
namespace gehGassi.Web.Controllers.Api
{
///
/// Controller für den API-Zugriff auf gehGassi Authentifizierung und Tokens
///
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HasHeaderAuthorize(ClaimConstants.HasHeader, ClaimConstants.HasHeaderValue)]
[ApiController]
[ApiVersion(1)]
[ApiVersion(2)]
[Route("api/account")]
[Route("api/v{v:apiVersion}/account")]
public class ApiAccountController : ApiBaseController
{
private readonly ILogger _logger;
private readonly ITokenService _tokenService;
private readonly IRefreshTokenService _refreshTokenService;
private readonly IUserService _userService;
private readonly UserManager _userManager;
private readonly IOptions _jwtTokenOptions;
private readonly SignInManager _signInManager;
private readonly IEmailSender _emailSender;
private readonly IStringLocalizer _localizer;
private readonly IOptions _licenseOptions;
private readonly IOptions _authOptions;
private readonly IGeoLocationService _geoLocationService;
private readonly IMessageService _messageService;
private readonly IWebHostEnvironment _environment;
private readonly IDistributedCache _memoryCache;
private readonly IOptions _appleOptions;
private readonly IMangoPayService _mangoPayService;
private readonly IOptions _emailSenderOptions;
private readonly IWalkService _walkService;
private readonly IPublicWalkRequestService _publicWalkRequestService;
private readonly IPublicWalkResponseService _publicWalkResponseService;
private readonly IAppUserReportService _appUserReportService;
///
/// Erstellt eine Instanz
///
/// Instanz eines IMapper
/// Instanz von LocalizationOptions
/// Instanz eines ILogger
/// Instanz eines ITokenService
/// Instanz eines IRefreshTokenService
/// Instanz eines IUserService
/// Instanz eines UserManager
/// Instanz eines IOptions JwtTokenOptions
/// Instanz eines SignInManager
/// Instanz eines IEmailSender
/// Instanz eines IStringLocalizer
/// Instanz von LicenseOptions
/// Instanz eines IAppUserService
/// Instanz von AuthOptions
/// Instanz eines IGeoLocationService
/// Instanz eines IMessageService
/// Instanz eines IWebHostEnvironment
/// Instanz eines IDistributedCache
/// Instanz von AppleOptions
/// Instanz eines IMangoPayService
/// Instanz eines EmailSenderOptions
/// Instanz eines IWalkService
/// Instanz eines IPublicWalkRequestService
/// Instanz eines IPublicWalkResponseService
/// Instanz eines IAppUserReportService
public ApiAccountController(IMapper mapper, IOptions localizationOptions, ILogger logger, ITokenService tokenService, IRefreshTokenService refreshTokenService, IUserService userService,
UserManager userManager, IOptions jwtTokenOptions, SignInManager signInManager,
IEmailSender emailSender, IStringLocalizer localizer, IOptions licenseOptions, IAppUserService appUserService,
IOptions authOptions, IGeoLocationService geoLocationService, IMessageService messageService, IWebHostEnvironment environment, IDistributedCache memoryCache,
IOptions appleOptions, IMangoPayService mangoPayService, IOptions emailSenderOptions, IWalkService walkService,
IPublicWalkRequestService publicWalkRequestService, IPublicWalkResponseService publicWalkResponseService, IAppUserReportService appUserReportService) : base(mapper, localizationOptions, appUserService)
{
_logger = logger;
_tokenService = tokenService;
_refreshTokenService = refreshTokenService;
_userService = userService;
_userManager = userManager;
_jwtTokenOptions = jwtTokenOptions;
_signInManager = signInManager;
_emailSender = emailSender;
_localizer = localizer;
_licenseOptions = licenseOptions;
_authOptions = authOptions;
_geoLocationService = geoLocationService;
_messageService = messageService;
_environment = environment;
_memoryCache = memoryCache;
_appleOptions = appleOptions;
_mangoPayService = mangoPayService;
_emailSenderOptions = emailSenderOptions;
_walkService = walkService;
_publicWalkRequestService = publicWalkRequestService;
_publicWalkResponseService = publicWalkResponseService;
_appUserReportService = appUserReportService;
}
///
/// Prüfen ob eine E-Mail Adresse noch verfügbar ist
///
/// Model mit Benutzername & Passwort
/// HTTP 200 OK wenn erfolgreich
[AllowAnonymous]
[HttpPost]
[Route("IsAvailable")]
public async Task IsAvailable(LoginDto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
var user = await _userService.GetByUsernameAsync(model.UserName);
if (user == null)
{
//Der Benutzername ist noch frei.
//Prüfen ob das Passwort ausreicht
var helpUser = new ApplicationUser();
foreach (var passwordValidator in _userManager.PasswordValidators)
{
var result = await passwordValidator.ValidateAsync(_userManager, helpUser, model.Password);
if (!result.Succeeded)
{
if (result.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return Ok();
}
return BadRequest(CommunicationErrors.Register_Email_Exists);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
///
/// Prüfen ob ein Passwort gültig ist
///
/// Model mit Passwort
/// HTTP 200 OK wenn erfolgreich
[AllowAnonymous]
[HttpPost]
[Route("CheckPassword")]
public async Task CheckPassword(CheckPasswordDto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
var helpUser = new ApplicationUser();
foreach (var passwordValidator in _userManager.PasswordValidators)
{
var result = await passwordValidator.ValidateAsync(_userManager, helpUser, model.Password);
if (!result.Succeeded)
{
if (result.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return Ok();
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
///
/// Registrieren eines App-Users
///
/// Model
/// HTTP 200 OK wenn erfolgreich
[AllowAnonymous]
[HttpPost]
[Route("Register")]
public async Task Register(RegisterDto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
var user = await _userService.GetByUsernameAsync(model.UserName);
if (user == null)
{
user = _userService.Create();
user.Id = Guid.NewGuid().ToString();
user.UserName = model.UserName;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.FullName = $"{model.FirstName} {model.LastName}";
user.RegistrationDate = DateTimeOffset.UtcNow;
user.Photo = string.Empty;
user.Email = model.UserName;
user.EmailConfirmed = !_authOptions.Value.MustConfirmEmail;
user.Permissions = PermissionHelper.GetForRole("AppUser").PackPermissionsIntoString();
var createResult = await _userManager.CreateAsync(user, model.Password);
if (createResult.Succeeded)
{
await _userManager.AddToRoleAsync(user, "AppUser");
user = await _userService.GetAsync(user.Id);
//Nun den AppUser anlegen und dann mit dem Benutzer verknüpfen
var appUser = AppUserService.Create();
appUser.Type = (AppUserType)model.AppUserType;
appUser.Number = AppUserService.GetNextNumber();
appUser.FirstName = model.FirstName;
appUser.LastName = model.LastName;
appUser.Sex = Sex.Undefined;
appUser.BirthDate = model.BirthDate;
appUser.Photo = string.Empty;
appUser.Contact.Email = model.UserName;
appUser.Address.CountryCode = model.CountryCode;
appUser.Address.State = model.State;
appUser.TermsAccepted = true;
appUser.TermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.PrivacyAccepted = true;
appUser.PrivacyAcceptedDate = DateTimeOffset.UtcNow;
appUser.Verified = false;
appUser.VerifiedDate = null;
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = string.Empty;
appUser.MainResidenceCode = string.Empty;
if (appUser.Type == AppUserType.DogWalker)
{
appUser.PaymentTermsAccepted = true;
appUser.PaymentTermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCountryCode;
}
var location = await _geoLocationService.GetLocationAsync($"{model.CountryCode},{model.State}", CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
if (location.Success)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(location.Longitude, location.Latitude));
appUser.Location = geoLocation;
}
AppUserService.Add(appUser);
await AppUserService.CommitAsync("System");
user.AppUserId = appUser.Id;
await _userService.CommitAsync("System");
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync("System");
//Nun für den Walker Mangopay User anlegen
var mangoPayResult = await _mangoPayService.CreateOwnerAsync(appUser.Id, false);
if (mangoPayResult.Success)
{
//Wallets anlegen
var walletCreditsResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Credits);
var walletFeesResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Fees);
}
}
if (_authOptions.Value.MustConfirmEmail)
{
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmailApp", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(user.UserName, callbackUrl, _localizer, _licenseOptions);
}
return Ok();
}
else
{
if (createResult.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
if (createResult.Errors.FirstOrDefault(c => c.Code.StartsWith("IdentityError_Password")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return BadRequest(CommunicationErrors.Register_Failed);
}
return BadRequest(CommunicationErrors.Register_Email_Exists);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
///
/// Registrieren eines App-Users
///
/// Model
/// HTTP 200 OK wenn erfolgreich
[AllowAnonymous]
[HttpPost]
[MapToApiVersion(2)]
[Route("Register")]
public async Task RegisterV2(RegisterV2Dto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
var user = await _userService.GetByUsernameAsync(model.UserName);
if (user == null)
{
user = _userService.Create();
user.Id = Guid.NewGuid().ToString();
user.UserName = model.UserName;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.FullName = $"{model.FirstName} {model.LastName}";
user.RegistrationDate = DateTimeOffset.UtcNow;
user.Photo = string.Empty;
user.Email = model.UserName;
user.EmailConfirmed = !_authOptions.Value.MustConfirmEmail;
user.Permissions = PermissionHelper.GetForRole("AppUser").PackPermissionsIntoString();
var createResult = await _userManager.CreateAsync(user, model.Password);
if (createResult.Succeeded)
{
await _userManager.AddToRoleAsync(user, "AppUser");
user = await _userService.GetAsync(user.Id);
//Nun den AppUser anlegen und dann mit dem Benutzer verknüpfen
var appUser = AppUserService.Create();
appUser.Type = (AppUserType)model.AppUserType;
appUser.Number = AppUserService.GetNextNumber();
appUser.FirstName = model.FirstName;
appUser.LastName = model.LastName;
appUser.Sex = Sex.Undefined;
appUser.BirthDate = model.BirthDate;
appUser.Photo = string.Empty;
appUser.Contact.Email = model.UserName;
appUser.Address.City = model.City;
appUser.Address.Zip = model.Zip;
appUser.Address.CountryCode = model.CountryCode;
appUser.Address.State = model.State;
appUser.TermsAccepted = true;
appUser.TermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.PrivacyAccepted = true;
appUser.PrivacyAcceptedDate = DateTimeOffset.UtcNow;
appUser.Verified = false;
appUser.VerifiedDate = null;
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = string.Empty;
appUser.MainResidenceCode = string.Empty;
if (appUser.Type == AppUserType.DogWalker)
{
appUser.PaymentTermsAccepted = true;
appUser.PaymentTermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCountryCode;
}
var location = await _geoLocationService.GetLocationAsync($"{model.Zip},{model.City},{model.CountryCode},{model.State}", CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
if (location.Success)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(location.Longitude, location.Latitude));
appUser.Location = geoLocation;
}
AppUserService.Add(appUser);
await AppUserService.CommitAsync("System");
user.AppUserId = appUser.Id;
await _userService.CommitAsync("System");
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync("System");
//Nun für den Walker Mangopay User anlegen
var mangoPayResult = await _mangoPayService.CreateOwnerAsync(appUser.Id, false);
if (mangoPayResult.Success)
{
//Wallets anlegen
var walletCreditsResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Credits);
var walletFeesResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Fees);
}
}
if (_authOptions.Value.MustConfirmEmail)
{
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmailApp", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(user.UserName, callbackUrl, _localizer, _licenseOptions);
}
return Ok();
}
else
{
if (createResult.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
if (createResult.Errors.FirstOrDefault(c => c.Code.StartsWith("IdentityError_Password")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return BadRequest(CommunicationErrors.Register_Failed);
}
return BadRequest(CommunicationErrors.Register_Email_Exists);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
///
/// Registrieren eines App-Users mittels external Provider
///
/// Model
/// HTTP 200 OK wenn erfolgreich
[AllowAnonymous]
[HttpPost]
[Route("RegisterExternal")]
public async Task RegisterExternal(RegisterExternalDto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
//Zuerst holen wir nochmal die Infos für den User. Token prüfen... //TODO: Je nach Provider vorgehen. Derzeit nur google. Apple einführen...
var tokenValid = false;
ExternalLoginGoogleResponse userInfo = null;
AppleResponseDto appleResponse = null;
if (model.LoginProvider == "Google")
{
HttpStatusCode tokenResponseCode = HttpStatusCode.OK;
var httpClient = new HttpClient();
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", model.AccessToken);
var tokenResult = await httpClient.GetAsync($"https://www.googleapis.com/oauth2/v3/userinfo");
if (tokenResult.IsSuccessStatusCode)
{
string jsonString = tokenResult.Content.ReadAsStringAsync().Result;
userInfo = JsonSerializer.Deserialize(jsonString);
tokenValid = true;
}
else
{
tokenResponseCode = tokenResult.StatusCode;
}
}
catch (Exception ex)
{
tokenValid = false;
}
}
else if (model.LoginProvider == "Apple")
{
var clientSecret = await GenerateAppleClientSecretAsync();
var parameters = new List>
{
new KeyValuePair("client_id", _appleOptions.Value.ClientId),
new KeyValuePair("client_secret", clientSecret),
new KeyValuePair("refresh_token", model.AccessToken),
new KeyValuePair("grant_type", "refresh_token")
};
var httpClient = new HttpClient();
try
{
var tokenResult = await httpClient.PostAsync($"https://appleid.apple.com/auth/token", new FormUrlEncodedContent(parameters));
if (tokenResult.IsSuccessStatusCode)
{
var successResponse = await tokenResult.Content.ReadAsStringAsync();
appleResponse = JsonSerializer.Deserialize(successResponse);
tokenValid = true;
}
else
{
var errorResponse = await tokenResult.Content.ReadAsStringAsync();
_logger.LogError(errorResponse);
}
}
catch (Exception ex)
{
tokenValid = false;
}
}
if (tokenValid)
{
var user = await _userService.GetByUsernameAsync(model.UserName);
if (user == null)
{
user = _userService.Create();
user.Id = Guid.NewGuid().ToString();
user.UserName = model.UserName;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.FullName = $"{model.FirstName} {model.LastName}";
user.RegistrationDate = DateTimeOffset.UtcNow;
user.Photo = string.Empty;
user.Email = model.UserName;
user.EmailConfirmed = true; //Weil über externen Provider
user.Permissions = PermissionHelper.GetForRole("AppUser").PackPermissionsIntoString();
IdentityResult createResult;
if (!string.IsNullOrEmpty(model.Password))
createResult = await _userManager.CreateAsync(user, model.Password);
else
createResult = await _userManager.CreateAsync(user);
if (createResult.Succeeded)
{
await _userManager.AddToRoleAsync(user, "AppUser");
user = await _userManager.FindByEmailAsync(user.UserName);
//Nun den AppUser anlegen und dann mit dem Benutzer verknüpfen
var appUser = AppUserService.Create();
appUser.Type = (AppUserType)model.AppUserType;
appUser.Number = AppUserService.GetNextNumber();
appUser.FirstName = model.FirstName;
appUser.LastName = model.LastName;
appUser.Sex = Sex.Undefined;
appUser.BirthDate = model.BirthDate;
appUser.Photo = string.Empty;
appUser.Contact.Email = model.UserName;
appUser.Address.CountryCode = model.CountryCode;
appUser.Address.State = model.State;
appUser.TermsAccepted = true;
appUser.TermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.PrivacyAccepted = true;
appUser.PrivacyAcceptedDate = DateTimeOffset.UtcNow;
appUser.Verified = false;
appUser.VerifiedDate = null;
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = string.Empty;
appUser.MainResidenceCode = string.Empty;
if (appUser.Type == AppUserType.DogWalker)
{
appUser.PaymentTermsAccepted = true;
appUser.PaymentTermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCountryCode;
}
var location = await _geoLocationService.GetLocationAsync($"{model.CountryCode},{model.State}", CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
if (location.Success)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(location.Longitude, location.Latitude));
appUser.Location = geoLocation;
}
AppUserService.Add(appUser);
await AppUserService.CommitAsync("System");
user.AppUserId = appUser.Id;
await _userManager.UpdateAsync(user);
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync("System");
//Nun für den Walker Mangopay User anlegen
var mangoPayResult = await _mangoPayService.CreateOwnerAsync(appUser.Id, false);
if (mangoPayResult.Success)
{
//Wallets anlegen
var walletCreditsResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Credits);
var walletFeesResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Fees);
}
}
//Da wir den User gleich einloggen möchten, geben wir alles nötige zurück
var usersClaims = new List()
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.UserData, user.Photo ?? ""),
new Claim(ClaimConstants.AppUserIdClaimType, user.AppUserId ?? ""),
};
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
usersClaims.Add(new Claim(ClaimTypes.Role, role));
}
var token = _tokenService.GenerateAccessToken(usersClaims);
await _refreshTokenService.RemoveExpiredAsync(user.Id, user.UserName);
var refreshToken = _refreshTokenService.Create();
refreshToken.UserId = user.Id;
_refreshTokenService.Add(refreshToken);
await _refreshTokenService.CommitAsync(user.UserName);
var userDto = Mapper.Map(user);
userDto.AccessToken = token;
userDto.AccessTokenExpires = DateTimeOffset.UtcNow.AddMinutes(_jwtTokenOptions.Value.TokenMinutes);
userDto.RefreshToken = refreshToken.Token;
userDto.RefreshTokenExpires = refreshToken.Expires;
userDto.AppUserType = (AppUserTypeDto)appUser.Type;
var roleList = roles.ToList();
userDto.Roles = string.Join(";", roleList);
user.LastLoginDate = DateTimeOffset.UtcNow;
await _userManager.UpdateAsync(user);
if (model.LoginProvider == "Google" && userInfo != null)
{
var createdUser = await _userManager.FindByEmailAsync(user.UserName);
if (createdUser != null)
{
await _userManager.AddLoginAsync(createdUser, new UserLoginInfo("Google", userInfo.Sub, user.FullName));
}
}
else if (model.LoginProvider == "Apple" && appleResponse != null)
{
var createdUser = await _userManager.FindByEmailAsync(user.UserName);
if (createdUser != null)
{
var appleToken = new JwtSecurityToken(appleResponse.IdToken);
if (appleToken != null)
{
var appleUserId = appleToken.Claims.First(c => c.Type == "sub").Value;
await _userManager.AddLoginAsync(createdUser, new UserLoginInfo("Apple", appleUserId, user.FullName));
}
}
}
return Ok(userDto);
}
else
{
if (createResult.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
if (createResult.Errors.FirstOrDefault(c => c.Code.StartsWith("Password")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return BadRequest(CommunicationErrors.Register_Failed);
}
return BadRequest(CommunicationErrors.Register_Email_Exists);
}
return BadRequest(CommunicationErrors.Register_TokenInvalid);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
///
/// Registrieren eines App-Users mittels external Provider
///
/// Model
/// HTTP 200 OK wenn erfolgreich
[AllowAnonymous]
[HttpPost]
[MapToApiVersion(2)]
[Route("RegisterExternal")]
public async Task RegisterExternalV2(RegisterExternalV2Dto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
//Zuerst holen wir nochmal die Infos für den User. Token prüfen... //TODO: Je nach Provider vorgehen. Derzeit nur google. Apple einführen...
var tokenValid = false;
ExternalLoginGoogleResponse userInfo = null;
AppleResponseDto appleResponse = null;
if (model.LoginProvider == "Google")
{
HttpStatusCode tokenResponseCode = HttpStatusCode.OK;
var httpClient = new HttpClient();
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", model.AccessToken);
var tokenResult = await httpClient.GetAsync($"https://www.googleapis.com/oauth2/v3/userinfo");
if (tokenResult.IsSuccessStatusCode)
{
string jsonString = tokenResult.Content.ReadAsStringAsync().Result;
userInfo = JsonSerializer.Deserialize(jsonString);
tokenValid = true;
}
else
{
tokenResponseCode = tokenResult.StatusCode;
}
}
catch (Exception ex)
{
tokenValid = false;
}
}
else if (model.LoginProvider == "Apple")
{
var clientSecret = await GenerateAppleClientSecretAsync();
var parameters = new List>
{
new KeyValuePair("client_id", _appleOptions.Value.ClientId),
new KeyValuePair("client_secret", clientSecret),
new KeyValuePair("refresh_token", model.AccessToken),
new KeyValuePair("grant_type", "refresh_token")
};
var httpClient = new HttpClient();
try
{
var tokenResult = await httpClient.PostAsync($"https://appleid.apple.com/auth/token", new FormUrlEncodedContent(parameters));
if (tokenResult.IsSuccessStatusCode)
{
var successResponse = await tokenResult.Content.ReadAsStringAsync();
appleResponse = JsonSerializer.Deserialize(successResponse);
tokenValid = true;
}
else
{
var errorResponse = await tokenResult.Content.ReadAsStringAsync();
_logger.LogError(errorResponse);
}
}
catch (Exception ex)
{
tokenValid = false;
}
}
if (tokenValid)
{
var user = await _userService.GetByUsernameAsync(model.UserName);
if (user == null)
{
user = _userService.Create();
user.Id = Guid.NewGuid().ToString();
user.UserName = model.UserName;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.FullName = $"{model.FirstName} {model.LastName}";
user.RegistrationDate = DateTimeOffset.UtcNow;
user.Photo = string.Empty;
user.Email = model.UserName;
user.EmailConfirmed = true; //Weil über externen Provider
user.Permissions = PermissionHelper.GetForRole("AppUser").PackPermissionsIntoString();
IdentityResult createResult;
if (!string.IsNullOrEmpty(model.Password))
createResult = await _userManager.CreateAsync(user, model.Password);
else
createResult = await _userManager.CreateAsync(user);
if (createResult.Succeeded)
{
await _userManager.AddToRoleAsync(user, "AppUser");
user = await _userManager.FindByEmailAsync(user.UserName);
//Nun den AppUser anlegen und dann mit dem Benutzer verknüpfen
var appUser = AppUserService.Create();
appUser.Type = (AppUserType)model.AppUserType;
appUser.Number = AppUserService.GetNextNumber();
appUser.FirstName = model.FirstName;
appUser.LastName = model.LastName;
appUser.Sex = Sex.Undefined;
appUser.BirthDate = model.BirthDate;
appUser.Photo = string.Empty;
appUser.Contact.Email = model.UserName;
appUser.Address.City = model.City;
appUser.Address.Zip = model.Zip;
appUser.Address.CountryCode = model.CountryCode;
appUser.Address.State = model.State;
appUser.TermsAccepted = true;
appUser.TermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.PrivacyAccepted = true;
appUser.PrivacyAcceptedDate = DateTimeOffset.UtcNow;
appUser.Verified = false;
appUser.VerifiedDate = null;
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = string.Empty;
appUser.MainResidenceCode = string.Empty;
if (appUser.Type == AppUserType.DogWalker)
{
appUser.PaymentTermsAccepted = true;
appUser.PaymentTermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCountryCode;
}
var location = await _geoLocationService.GetLocationAsync($"{model.Zip},{model.City},{model.CountryCode},{model.State}", CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
if (location.Success)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(location.Longitude, location.Latitude));
appUser.Location = geoLocation;
}
AppUserService.Add(appUser);
await AppUserService.CommitAsync("System");
user.AppUserId = appUser.Id;
await _userManager.UpdateAsync(user);
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync("System");
//Nun für den Walker Mangopay User anlegen
var mangoPayResult = await _mangoPayService.CreateOwnerAsync(appUser.Id, false);
if (mangoPayResult.Success)
{
//Wallets anlegen
var walletCreditsResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Credits);
var walletFeesResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Fees);
}
}
//Da wir den User gleich einloggen möchten, geben wir alles nötige zurück
var usersClaims = new List()
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.UserData, user.Photo ?? ""),
new Claim(ClaimConstants.AppUserIdClaimType, user.AppUserId ?? ""),
};
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
usersClaims.Add(new Claim(ClaimTypes.Role, role));
}
var token = _tokenService.GenerateAccessToken(usersClaims);
await _refreshTokenService.RemoveExpiredAsync(user.Id, user.UserName);
var refreshToken = _refreshTokenService.Create();
refreshToken.UserId = user.Id;
_refreshTokenService.Add(refreshToken);
await _refreshTokenService.CommitAsync(user.UserName);
var userDto = Mapper.Map(user);
userDto.AccessToken = token;
userDto.AccessTokenExpires = DateTimeOffset.UtcNow.AddMinutes(_jwtTokenOptions.Value.TokenMinutes);
userDto.RefreshToken = refreshToken.Token;
userDto.RefreshTokenExpires = refreshToken.Expires;
userDto.AppUserType = (AppUserTypeDto)appUser.Type;
var roleList = roles.ToList();
userDto.Roles = string.Join(";", roleList);
user.LastLoginDate = DateTimeOffset.UtcNow;
await _userManager.UpdateAsync(user);
if (model.LoginProvider == "Google" && userInfo != null)
{
var createdUser = await _userManager.FindByEmailAsync(user.UserName);
if (createdUser != null)
{
await _userManager.AddLoginAsync(createdUser, new UserLoginInfo("Google", userInfo.Sub, user.FullName));
}
}
else if (model.LoginProvider == "Apple" && appleResponse != null)
{
var createdUser = await _userManager.FindByEmailAsync(user.UserName);
if (createdUser != null)
{
var appleToken = new JwtSecurityToken(appleResponse.IdToken);
if (appleToken != null)
{
var appleUserId = appleToken.Claims.First(c => c.Type == "sub").Value;
await _userManager.AddLoginAsync(createdUser, new UserLoginInfo("Apple", appleUserId, user.FullName));
}
}
}
return Ok(userDto);
}
else
{
if (createResult.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
if (createResult.Errors.FirstOrDefault(c => c.Code.StartsWith("Password")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return BadRequest(CommunicationErrors.Register_Failed);
}
return BadRequest(CommunicationErrors.Register_Email_Exists);
}
return BadRequest(CommunicationErrors.Register_TokenInvalid);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
///
/// Anmelden via API. Erstellt ein Access-Token und ein Refresh-Token wenn erfolgreich
///
/// Model mit Benutzername & Passwort
/// Token und Refreshtoken oder BadRequest wenn nicht erfolgreich
[AllowAnonymous]
[HttpPost]
[Route("Login")]
public async Task Login(LoginDto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.UserName);// _userService.GetByUsernameAsync(model.UserName);
if (user != null)
{
if (await _userManager.CheckPasswordAsync(user, model.Password))
{
if (await _userManager.IsLockedOutAsync(user) == false)
{
if (await _userManager.IsEmailConfirmedAsync(user))
{
if (!string.IsNullOrWhiteSpace(user.AppUserId))
{
var appUser = await AppUserService.GetAsync(user.AppUserId);
if (appUser != null)
{
if (appUser.Locked && appUser.LockedUntil < DateTimeOffset.UtcNow)
{
appUser.Locked = false;
appUser.LockedUntil = null;
await AppUserService.CommitAsync("System");
}
if (!appUser.Locked)
{
var usersClaims = new List()
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.UserData, user.Photo ?? ""),
new Claim(ClaimConstants.AppUserIdClaimType, user.AppUserId ?? ""),
};
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
usersClaims.Add(new Claim(ClaimTypes.Role, role));
}
var token = _tokenService.GenerateAccessToken(usersClaims);
await _refreshTokenService.RemoveExpiredAsync(user.Id, user.UserName);
var refreshToken = _refreshTokenService.Create();
refreshToken.UserId = user.Id;
_refreshTokenService.Add(refreshToken);
await _refreshTokenService.CommitAsync(user.UserName);
var userDto = Mapper.Map(user);
userDto.AccessToken = token;
userDto.AccessTokenExpires = DateTimeOffset.UtcNow.AddMinutes(_jwtTokenOptions.Value.TokenMinutes);
userDto.RefreshToken = refreshToken.Token;
userDto.RefreshTokenExpires = refreshToken.Expires;
userDto.AppUserType = (AppUserTypeDto)appUser.Type;
var roleList = roles.ToList();
userDto.Roles = string.Join(";", roleList);
if (!string.IsNullOrWhiteSpace(user.Photo))
{
var photo = await FileService.GetAsync(FileServiceHelper.DocumentContainer, user.Photo);
//if (photo != null)
// userDto.PhotoBase64 = Convert.ToBase64String(photo);
}
user.LastLoginDate = DateTimeOffset.UtcNow;
await _userManager.UpdateAsync(user);
//await _userService.CommitAsync(user.UserName);
return Ok(userDto);
}
else
{
return Unauthorized(CommunicationErrors.Login_LockedOut);
}
}
}
else
{
return BadRequest(CommunicationErrors.Login_Assignment_Missing);
}
}
return BadRequest(CommunicationErrors.Login_Email_NotConfirmed);
}
return Unauthorized(CommunicationErrors.Login_LockedOut);
}
return Unauthorized(CommunicationErrors.Login_Invalid_Credentials);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
///
/// Anmelden mit external Provider von der App.
/// Die App hat erfolgreich ein AccessToken erhalten, daher wissen wir wer der Benutzer ist.
/// Nun einen Login durchführen ohne Prüfung des Passwortes
///
/// Token und Refreshtoken oder BadRequest wenn nicht erfolgreich
[HttpGet]
[Route("LoginExternal")]
public async Task LoginExternal()
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userManager.FindByNameAsync(User.Identity.Name);
if (user != null)
{
if (await _userManager.IsLockedOutAsync(user) == false)
{
if (await _userManager.IsEmailConfirmedAsync(user))
{
if (!string.IsNullOrWhiteSpace(user.AppUserId))
{
var appUser = await AppUserService.GetAsync(user.AppUserId);
if (appUser != null)
{
if (appUser.Locked && appUser.LockedUntil < DateTimeOffset.UtcNow)
{
appUser.Locked = false;
appUser.LockedUntil = null;
await AppUserService.CommitAsync("System");
}
if (!appUser.Locked)
{
var usersClaims = new List()
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.UserData, user.Photo ?? ""),
new Claim(ClaimConstants.AppUserIdClaimType, user.AppUserId ?? ""),
};
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
usersClaims.Add(new Claim(ClaimTypes.Role, role));
}
var token = _tokenService.GenerateAccessToken(usersClaims);
await _refreshTokenService.RemoveExpiredAsync(user.Id, user.UserName);
var refreshToken = _refreshTokenService.Create();
refreshToken.UserId = user.Id;
_refreshTokenService.Add(refreshToken);
await _refreshTokenService.CommitAsync(user.UserName);
var userDto = Mapper.Map(user);
userDto.AccessToken = token;
userDto.AccessTokenExpires = DateTimeOffset.UtcNow.AddMinutes(_jwtTokenOptions.Value.TokenMinutes);
userDto.RefreshToken = refreshToken.Token;
userDto.RefreshTokenExpires = refreshToken.Expires;
userDto.AppUserType = (AppUserTypeDto)appUser.Type;
var roleList = roles.ToList();
userDto.Roles = string.Join(";", roleList);
if (!string.IsNullOrWhiteSpace(user.Photo))
{
var photo = await FileService.GetAsync(FileServiceHelper.DocumentContainer, user.Photo);
//if (photo != null)
// userDto.PhotoBase64 = Convert.ToBase64String(photo);
}
user.LastLoginDate = DateTimeOffset.UtcNow;
await _userManager.UpdateAsync(user);
//await _userService.CommitAsync(user.UserName);
return Ok(userDto);
}
else
{
return Unauthorized(CommunicationErrors.Login_LockedOut);
}
}
}
else
{
return BadRequest(CommunicationErrors.Login_Assignment_Missing);
}
}
return BadRequest(CommunicationErrors.Login_Email_NotConfirmed);
}
return Unauthorized(CommunicationErrors.Login_LockedOut);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Anmelden mit Apple
///
/// Model mit notwendigen Daten von Apple
/// Token und Refreshtoken oder BadRequest wenn nicht erfolgreich
[AllowAnonymous]
[HttpPost]
[Route("LoginApple")]
public async Task LoginApple(LoginAppleDto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
//Daten gegen Apple validieren...
var tokenValid = false;
var emailToUse = model.Email;
AppleResponseDto tokenResponse = null;
var clientSecret = await GenerateAppleClientSecretAsync();
var parameters = new List>
{
new KeyValuePair("client_id", _appleOptions.Value.ClientId),
new KeyValuePair("client_secret", clientSecret),
new KeyValuePair("code", model.AuthCode),
new KeyValuePair("grant_type", "authorization_code")
};
var httpClient = new HttpClient();
try
{
var tokenResult = await httpClient.PostAsync($"https://appleid.apple.com/auth/token", new FormUrlEncodedContent(parameters));
if (tokenResult.IsSuccessStatusCode)
{
var successResponse = await tokenResult.Content.ReadAsStringAsync();
tokenResponse = JsonSerializer.Deserialize(successResponse);
tokenValid = true;
}
else
{
var errorResponse = await tokenResult.Content.ReadAsStringAsync();
_logger.LogError(errorResponse);
}
}
catch (Exception ex)
{
tokenValid = false;
}
if (tokenValid)
{
var user = await _userManager.FindByLoginAsync("Apple", model.UserId);
if (user == null && !string.IsNullOrWhiteSpace(emailToUse))
{
user = await _userManager.FindByEmailAsync(emailToUse);
}
if (user != null)
{
if (await _userManager.IsLockedOutAsync(user) == false)
{
if (await _userManager.IsEmailConfirmedAsync(user))
{
if (!string.IsNullOrWhiteSpace(user.AppUserId))
{
var appUser = await AppUserService.GetAsync(user.AppUserId);
if (appUser != null)
{
if (appUser.Locked && appUser.LockedUntil < DateTimeOffset.UtcNow)
{
appUser.Locked = false;
appUser.LockedUntil = null;
await AppUserService.CommitAsync("System");
}
if (!appUser.Locked)
{
var usersClaims = new List()
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.UserData, user.Photo ?? ""),
new Claim(ClaimConstants.AppUserIdClaimType, user.AppUserId ?? ""),
};
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
usersClaims.Add(new Claim(ClaimTypes.Role, role));
}
var token = _tokenService.GenerateAccessToken(usersClaims);
await _refreshTokenService.RemoveExpiredAsync(user.Id, user.UserName);
var refreshToken = _refreshTokenService.Create();
refreshToken.UserId = user.Id;
_refreshTokenService.Add(refreshToken);
await _refreshTokenService.CommitAsync(user.UserName);
var userDto = Mapper.Map(user);
userDto.AccessToken = token;
userDto.AccessTokenExpires = DateTimeOffset.UtcNow.AddMinutes(_jwtTokenOptions.Value.TokenMinutes);
userDto.RefreshToken = refreshToken.Token;
userDto.RefreshTokenExpires = refreshToken.Expires;
userDto.AppUserType = (AppUserTypeDto)appUser.Type;
var roleList = roles.ToList();
userDto.Roles = string.Join(";", roleList);
if (!string.IsNullOrWhiteSpace(user.Photo))
{
var photo = await FileService.GetAsync(FileServiceHelper.DocumentContainer, user.Photo);
//if (photo != null)
// userDto.PhotoBase64 = Convert.ToBase64String(photo);
}
user.LastLoginDate = DateTimeOffset.UtcNow;
await _userManager.UpdateAsync(user);
//await _userService.CommitAsync(user.UserName);
var externalLogins = await _userManager.GetLoginsAsync(user);
if (externalLogins?.FirstOrDefault(c => c.LoginProvider == "Apple") == null)
{
await _userManager.AddLoginAsync(user, new UserLoginInfo("Apple", model.UserId, user.FullName));
}
return Ok(userDto);
}
else
{
return Unauthorized(CommunicationErrors.Login_LockedOut);
}
}
else
{
return BadRequest(CommunicationErrors.Login_Assignment_Missing);
}
}
else
{
return BadRequest(CommunicationErrors.Login_Assignment_Missing);
}
}
return BadRequest(CommunicationErrors.Login_Email_NotConfirmed);
}
return Unauthorized(CommunicationErrors.Login_LockedOut);
}
return NotFound(JsonSerializer.Serialize(tokenResponse)); //Wir müssen hier tricksen, damit das Refreshtoken zur App übertragen wird
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
///
/// Abmelden eines Benutzers
///
/// HTTP 200 wenn erfolgreich
[HttpGet]
[Route("Logout")]
public async Task Logout()
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
//Löschen aller Refresh-tokens des Benutzers da eine Abmeldung erfolgt ist
await _refreshTokenService.RemoveAllAsync(user.Id, user.UserName);
await _refreshTokenService.CommitAsync(user.UserName);
return Ok();
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Zurücksetzen des Passworts für einen App-Benutzer
///
/// Benutzername / E-Mail Adresse des Benutzers
/// HTTP 200 - immer!
[AllowAnonymous]
[HttpPost]
[Route("ResetPassword")]
public async Task ResetPassword([FromBody] string email)
{
var clientOffset = GetClientDateOffset();
try
{
var user = await _signInManager.UserManager.FindByNameAsync(email);
if (user != null && (await _signInManager.UserManager.IsEmailConfirmedAsync(user)))
{
var roles = await _userManager.GetRolesAsync(user);
if (roles.Contains("AppUser"))
{
var token = await _signInManager.UserManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.ResetPasswordCallbackLinkApp(user.Id, token, Request.Scheme);
await _emailSender.SendPasswordResetAppAsync(email, callbackUrl, _localizer, _licenseOptions);
}
}
}
catch { }
return Ok();
}
///
/// Senden der E-Mail Bestätigung für einen App-Benutzer
///
/// Benutzername / E-Mail Adresse des Benutzers
/// HTTP 200 - immer!
[AllowAnonymous]
[HttpPost]
[Route("SendConfirmation")]
public async Task SendConfirmation([FromBody] string email)
{
var clientOffset = GetClientDateOffset();
try
{
var user = await _signInManager.UserManager.FindByNameAsync(email);
if (user != null && (await _signInManager.UserManager.IsEmailConfirmedAsync(user)) == false)
{
var roles = await _userManager.GetRolesAsync(user);
if (roles.Contains("AppUser"))
{
var code = await _signInManager.UserManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmailApp", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailConfirmationAppAsync(email, callbackUrl, _localizer, _licenseOptions);
}
}
}
catch { }
return Ok();
}
///
/// Mit einem abgelaufenen Token und einem RefreshToken ein neues AccessToken holen
///
/// Model
/// Token und Refreshtoken oder BadRequest wenn nicht erfolgreich
[AllowAnonymous]
[HttpPost]
[Route("Refresh")]
public async Task Refresh(RefreshTokenDto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
var principal = _tokenService.GetPrincipalFromExpiredToken(model.AccessToken);
if (principal != null)
{
var username = principal.Identity.Name; //this is mapped to the Name claim by default
var user = await _userService.GetByUsernameAsync(username);
if (user != null)
{
//Wenn der Benutzer gesperrt ist, dann alle RefreshTokens löschen und Fehler zurückgeben
if (user.LockoutEnd.HasValue && user.LockoutEnd > DateTimeOffset.UtcNow)
{
await _refreshTokenService.RemoveAllAsync(user.Id, user.UserName);
await _refreshTokenService.CommitAsync(user.UserName);
return BadRequest(CommunicationErrors.RefreshToken_Expired);
}
var existingRefreshToken = await _refreshTokenService.GetAsync(user.Id, model.RefreshToken);
if (existingRefreshToken != null)
{
if (existingRefreshToken.Expires > DateTimeOffset.UtcNow)
{
var token = _tokenService.GenerateAccessToken(principal.Claims);
await _refreshTokenService.RemoveExpiredAsync(user.Id, user.UserName);
await _refreshTokenService.CommitAsync(user.UserName);
RefreshToken refreshToken = null;
if (existingRefreshToken.Expires >= DateTimeOffset.UtcNow.AddDays(1))
{
refreshToken = existingRefreshToken;
}
else
{
refreshToken = _refreshTokenService.Create();
refreshToken.UserId = user.Id;
_refreshTokenService.Add(refreshToken);
_refreshTokenService.Remove(existingRefreshToken);
await _refreshTokenService.CommitAsync(user.UserName);
}
var dto = new RefreshTokenResponseDto()
{
AccessToken = token,
AccessTokenExpires = DateTimeOffset.UtcNow.AddMinutes(_jwtTokenOptions.Value.TokenMinutes),
RefreshToken = refreshToken.Token,
RefreshTokenExpires = refreshToken.Expires
};
return Ok(dto);
}
return BadRequest(CommunicationErrors.RefreshToken_Expired);
}
return BadRequest(CommunicationErrors.RefreshToken_NotFound);
}
}
//Principal oder user null, nun anhand des Tokens alleine versuchen
if (!string.IsNullOrWhiteSpace(model.AccessToken))
{
var existingRefreshToken = await _refreshTokenService.GetAsync(model.RefreshToken);
if (existingRefreshToken != null)
{
if (existingRefreshToken.Expires > DateTimeOffset.UtcNow)
{
var user = await _userService.GetAsync(existingRefreshToken.UserId);
if (user != null)
{
//Wenn der Benutzer gesperrt ist, dann alle RefreshTokens löschen und Fehler zurückgeben
if (user.LockoutEnd.HasValue && user.LockoutEnd > DateTimeOffset.UtcNow)
{
await _refreshTokenService.RemoveAllAsync(user.Id, user.UserName);
await _refreshTokenService.CommitAsync(user.UserName);
return BadRequest(CommunicationErrors.RefreshToken_Expired);
}
var usersClaims = new List()
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.UserData, user.Photo ?? ""),
new Claim(ClaimConstants.AppUserIdClaimType, user.AppUserId ?? ""),
};
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
usersClaims.Add(new Claim(ClaimTypes.Role, role));
}
var token = _tokenService.GenerateAccessToken(usersClaims);
await _refreshTokenService.RemoveExpiredAsync(user.Id, user.UserName);
await _refreshTokenService.CommitAsync(user.UserName);
RefreshToken refreshToken = null;
if (existingRefreshToken.Expires >= DateTimeOffset.UtcNow.AddDays(1))
{
refreshToken = existingRefreshToken;
}
else
{
refreshToken = _refreshTokenService.Create();
refreshToken.UserId = user.Id;
_refreshTokenService.Add(refreshToken);
_refreshTokenService.Remove(existingRefreshToken);
await _refreshTokenService.CommitAsync(user.UserName);
}
var dto = new RefreshTokenResponseDto()
{
AccessToken = token,
AccessTokenExpires = DateTimeOffset.UtcNow.AddMinutes(_jwtTokenOptions.Value.TokenMinutes),
RefreshToken = refreshToken.Token,
RefreshTokenExpires = refreshToken.Expires
};
return Ok(dto);
}
return NotFound(CommunicationErrors.RefreshToken_UserNotFound);
}
return BadRequest(CommunicationErrors.RefreshToken_Expired);
}
return BadRequest(CommunicationErrors.RefreshToken_NotFound);
}
return NotFound(CommunicationErrors.AccessToken_Empty);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
///
/// Prüfen ob der angemeldete Benutzer ein Passwort hat oder nicht.
/// Das kann bei externen Providern vorkommen
///
/// HTTP 200 OK
[HttpGet]
[Route("HasPassword")]
public async Task HasPassword()
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userManager.FindByEmailAsync(User.Identity.Name);
if (user != null)
{
var hasPassword = await _userManager.HasPasswordAsync(user);
return Ok(hasPassword);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Hinzufügen eines PAsswortes zu einem Benutzer wenn dieser keines har
///
/// Model mit Passwort
/// HTTP 200 OK
[HttpPost]
[Route("AddPassword")]
public async Task AddPassword(AddPasswordDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(User.Identity.Name);
if (user != null && user.UserName.ToLower() == model.UserName.ToLower())
{
var hasPassword = await _userManager.HasPasswordAsync(user);
if (!hasPassword)
{
var addPasswordResult = await _userManager.AddPasswordAsync(user, model.Password);
if (addPasswordResult.Succeeded)
{
return Ok(true);
}
else
{
if (addPasswordResult.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
if (addPasswordResult.Errors.FirstOrDefault(c => c.Code.StartsWith("IdentityError_Password")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
}
return Ok(false);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Ändern des Passwortes eines Benutzers
///
/// Model mit Passwort
/// HTTP 200 OK
[HttpPost]
[Route("ChangePassword")]
public async Task ChangePassword(ChangePasswordDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(User.Identity.Name);
if (user != null && user.UserName.ToLower() == model.UserName.ToLower())
{
var changeResult = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.Password);
if (changeResult.Succeeded)
{
return Ok(true);
}
else
{
if (changeResult.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
if (changeResult.Errors.FirstOrDefault(c => c.Code.StartsWith("IdentityError_PasswordMismatch")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Mismatch);
}
if (changeResult.Errors.FirstOrDefault(c => c.Code.StartsWith("IdentityError_Password")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return Ok(false);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Löschen eines Accounts, Sperren des Benutzers und Ausloggen
///
/// HTTP 200 wenn erfolgreich
[HttpPost]
[Route("DeleteAccount")]
public async Task DeleteAccount(DeleteAccountDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (user.Id == model.UserId && user.Email == model.UserName)
{
//Sperren des Benutzers
user.LockoutEnd = DateTimeOffset.UtcNow.AddYears(5);
await _userService.CommitAsync(user.UserName);
//Löschen aller Refresh-tokens des Benutzers da eine Abmeldung erfolgt ist
await _refreshTokenService.RemoveAllAsync(user.Id, user.UserName);
await _refreshTokenService.CommitAsync(user.UserName);
var appUser = await AppUserService.GetAsync(user.AppUserId);
if (appUser != null)
{
appUser.Locked = true;
appUser.LockedReason = "Delete account requested";
appUser.LockedUntil = DateTimeOffset.UtcNow.AddYears(5);
await AppUserService.CommitAsync(user.UserName);
}
//Nun Email an gehgassi senden, dass der Benutzer gelöscht werden soll
await _emailSender.SendDeleteAccountEmailAsync(user.Email, user.FirstName, user.LastName, _localizer, _emailSenderOptions);
return Ok(true);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
#region AppUser
///
/// Gibt den App-User zurück
///
///
[HttpGet]
[Route("GetAppUser")]
public async Task GetAppUser()
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (!string.IsNullOrWhiteSpace(user.AppUserId))
{
var appUser = await AppUserService.GetAsync(user.AppUserId);
if (appUser != null)
{
var appUserDto = Mapper.Map(appUser);
var baseAddress = GetBaseAddress();
if (!string.IsNullOrWhiteSpace(appUserDto.Photo))
{
appUserDto.Photo = $"{baseAddress}/file/documents/thumbnails/{200}/{appUserDto.Photo}";
}
return Ok(appUserDto);
}
}
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Gibt den App-User für die Background Sync zurück
///
///
[HttpGet]
[Route("GetAppUserSync")]
public async Task GetAppUserSync(DateTimeOffset? lastUpdate)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (!string.IsNullOrWhiteSpace(user.AppUserId))
{
var appUser = await AppUserService.GetAsync(user.AppUserId);
if (appUser != null && appUser.UpdatedAt > lastUpdate)
{
var appUserDto = Mapper.Map(appUser);
var baseAddress = GetBaseAddress();
if (!string.IsNullOrWhiteSpace(appUserDto.Photo))
{
appUserDto.Photo = $"{baseAddress}/file/documents/thumbnails/{200}/{appUserDto.Photo}";
}
return Ok(appUserDto);
}
return NoContent();
}
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Aktualisieren eines App-Users
///
/// AppUserDto
/// Optional Foto
///
[HttpPost]
[Route("Update")]
public async Task Update([ModelBinder(BinderType = typeof(JsonModelBinder))] AppUserDto model, IFormFile photoUpdateFile)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (ModelState.IsValid)
{
var appUser = await AppUserService.GetAsync(model.Id);
if (appUser != null)
{
if (model.UpdatedAt > appUser.UpdatedAt)
{
var photoChanged = false;
var oldPhoto = appUser.Photo;
Mapper.Map(model, appUser);
if (string.IsNullOrWhiteSpace(model.Photo) || model.Photo.StartsWithHttp())
{
//Altes Foto behalten wenn eines als Link kommt
appUser.Photo = oldPhoto;
}
try
{
if (photoUpdateFile != null && !string.IsNullOrEmpty(photoUpdateFile.FileName))
{
//Wenn es ein altes Foto gibt, dieses löschen
if (!string.IsNullOrWhiteSpace(oldPhoto))
{
await FileService.DeleteAsync(FileServiceHelper.DocumentContainer, oldPhoto);
await RemoveThumbnail(FileServiceHelper.DocumentContainer, oldPhoto, 400);
await RemoveThumbnail(FileServiceHelper.DocumentContainer, oldPhoto, 200);
await RemoveThumbnail(FileServiceHelper.DocumentContainer, oldPhoto, 100);
}
var extension = Path.GetExtension(photoUpdateFile.FileName);
var filenameToUse = FileServiceHelper.GetAppUserPath(appUser.Id) + $"photo-{Guid.NewGuid():N}{extension}";
using var memoryStream = new MemoryStream();
await photoUpdateFile.CopyToAsync(memoryStream);
memoryStream.Position = 0;
using var img = await Image.LoadAsync(memoryStream);
img.Mutate(x => x.Resize(new ResizeOptions() { Mode = ResizeMode.Crop, Size = new Size(800) }));
var format = img.DetectEncoder(photoUpdateFile.FileName);
await using var memStream = new MemoryStream();
await img.SaveAsync(memStream, format);
memStream.Position = 0;
await FileService.StoreAsync(FileServiceHelper.DocumentContainer, filenameToUse, memStream);
appUser.Photo = filenameToUse;
//Thumbnails erstellten
await GenerateThumbnailByWidth(FileServiceHelper.DocumentContainer, filenameToUse, 400);
await GenerateThumbnailByWidth(FileServiceHelper.DocumentContainer, filenameToUse, 200);
await GenerateThumbnailByWidth(FileServiceHelper.DocumentContainer, filenameToUse, 100);
photoChanged = true;
}
}
catch (Exception ex)
{
}
//Wenn beim Update keine Koordinaten übergeben werden, dann basierend auf der Adresse versuchen
if (model.Lat == 0 && model.Lng == 0)
{
var location = await _geoLocationService.GetLocationAsync(appUser.Address, CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
if (location.Success)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(location.Longitude, location.Latitude));
appUser.Location = geoLocation;
}
}
await AppUserService.CommitAsync(User.Identity.Name);
user.FirstName = appUser.FirstName;
user.LastName = appUser.LastName;
user.FullName = $"{appUser.FirstName} {appUser.LastName}";
user.Photo = appUser.Photo;
await _userService.CommitAsync(User.Identity.Name);
if (photoChanged)
{
//Update-Datum der Konversationen des Benutzers neu setzen, damit das Foto neu geholt wird...
await _messageService.SetUpdatedAtAsync(appUser.Id, DateTimeOffset.UtcNow, User.Identity.Name);
}
if (!string.IsNullOrWhiteSpace(appUser.PaymentId) && appUser.PaymentTermsAccepted)
{
//Wenn schon eine MangopayId vorhanden ist, dann auch das Mangopay-Profil aktualisieren
var mangoUpdateResult = await _mangoPayService.UpdateOwnerAsync(appUser.Id);
}
}
return Ok();
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Setzt den Modus eines App-Users auf "Beides"
///
/// 200 OK
[HttpPost]
[Route("SetAppUserType")]
public async Task SetAppUserType(SetAppUserTypeDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (ModelState.IsValid)
{
var appUser = await AppUserService.GetAsync(model.Id);
if (appUser != null)
{
if (model.UpdatedAt > appUser.UpdatedAt)
{
appUser.Type = (AppUserType)model.Type;
appUser.UpdatedAt = model.UpdatedAt;
await AppUserService.CommitAsync(User.Identity.Name);
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync(User.Identity.Name);
}
return Ok();
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Setzt den Modus eines App-Users auf "Beides" - von Owner aufgerufen.
/// Erstellt ebenso einen MangoPay User und legt die Wallets an
///
/// 200 OK
[HttpPost]
[Route("SetAppUserTypeEx")]
public async Task SetAppUserTypeEx(SetAppUserTypeExDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (ModelState.IsValid)
{
var appUser = await AppUserService.GetAsync(model.Id);
if (appUser != null)
{
if (model.UpdatedAt > appUser.UpdatedAt)
{
appUser.Type = (AppUserType)model.Type;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCode;
appUser.PaymentTermsAccepted = model.PaymentTermsAccepted;
appUser.PaymentTermsAcceptedDate = model.PaymentTermsAcceptedDate;
appUser.UpdatedAt = model.UpdatedAt;
await AppUserService.CommitAsync(User.Identity.Name);
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync(User.Identity.Name);
//Nun für den Walker Mangopay User anlegen
var mangoPayResult = await _mangoPayService.CreateOwnerAsync(appUser.Id, false);
if (mangoPayResult.Success)
{
appUser.PaymentId = mangoPayResult.Value;
//Wallets anlegen
var walletCreditsResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Credits);
var walletFeesResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Fees);
}
}
var appUserDto = Mapper.Map(appUser);
var baseAddress = GetBaseAddress();
if (!string.IsNullOrWhiteSpace(appUserDto.Photo))
{
appUserDto.Photo = $"{baseAddress}/file/documents/thumbnails/{200}/{appUserDto.Photo}";
}
return Ok(appUserDto);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Hinzufügen eines PaymentUsers zu einem AppUser
///
/// Model
/// 200 OK
[HttpPost]
[Route("AddPaymentUser")]
public async Task AddPaymentUser(AddPaymentUserDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (ModelState.IsValid)
{
var appUser = await AppUserService.GetAsync(model.Id);
if (appUser != null)
{
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCode;
appUser.PaymentTermsAccepted = model.PaymentTermsAccepted;
appUser.PaymentTermsAcceptedDate = model.PaymentTermsAcceptedDate;
appUser.UpdatedAt = DateTimeOffset.UtcNow;
await AppUserService.CommitAsync(User.Identity.Name);
//Nun für den Benutzer einen Mangopay User anlegen
var mangoPayResult = await _mangoPayService.CreateOwnerAsync(appUser.Id, false);
if (mangoPayResult.Success)
{
appUser.PaymentId = mangoPayResult.Value;
//Wallets anlegen
var walletCreditsResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Credits);
var walletFeesResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Fees);
}
var appUserDto = Mapper.Map(appUser);
var baseAddress = GetBaseAddress();
if (!string.IsNullOrWhiteSpace(appUserDto.Photo))
{
appUserDto.Photo = $"{baseAddress}/file/documents/thumbnails/{200}/{appUserDto.Photo}";
}
return Ok(appUserDto);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
#endregion
#region DogWalker Profil
///
/// Gibt ein DogWalker Profil zurück
///
///
[HttpGet]
[Route("GetWalkerProfile")]
public async Task GetWalkerProfile()
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (!string.IsNullOrWhiteSpace(user.AppUserId))
{
var walkerProfile = await AppUserService.GetWalkerProfileAsync(user.AppUserId);
if (walkerProfile != null)
{
var walkerProfileDto = Mapper.Map(walkerProfile);
return Ok(walkerProfileDto);
}
}
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Gibt ein DogWalker Profil für die Background Sync zurück
///
///
[HttpGet]
[Route("GetWalkerProfileSync")]
public async Task GetWalkerProfileSync(DateTimeOffset? lastUpdate)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (!string.IsNullOrWhiteSpace(user.AppUserId))
{
var walkerProfile = await AppUserService.GetWalkerProfileAsync(user.AppUserId);
if (walkerProfile != null && walkerProfile.UpdatedAt > lastUpdate)
{
var walkerProfileDto = Mapper.Map(walkerProfile);
return Ok(walkerProfileDto);
}
return NoContent();
}
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Aktualisieren eines Dogwalker Profils
///
/// AppUserDto
///
[HttpPost]
[Route("UpdateWalkerProfile")]
public async Task UpdateWalkerProfile(DogWalkerProfileDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (ModelState.IsValid)
{
var walkerProfile = await AppUserService.GetWalkerProfileAsync(model.Id);
if (walkerProfile != null)
{
if (model.UpdatedAt > walkerProfile.UpdatedAt)
{
Mapper.Map(model, walkerProfile);
await AppUserService.CommitAsync(User.Identity.Name);
}
return Ok();
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
#endregion
#region Blocks
///
/// Blockieren eines AppUsers durch einen anderen AppUser
///
/// Model
/// 200 OK
[HttpPost]
[Route("BlockAppUser")]
public async Task BlockAppUser(BlockCreateDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (ModelState.IsValid)
{
var appUser = await AppUserService.GetAsync(model.BlockingAppUserId);
if (appUser != null)
{
//Nur dann zulassen, wenn der Benutzer der angemeldet ist auch der Benutzer ist der die Blockierung vornimmt
if (appUser.Id == user.AppUserId)
{
var block = await AppUserService.BlockAppUserAsync(model.BlockingAppUserId, model.BlockedAppUserId, User.Identity.Name);
await AppUserService.CommitAsync(User.Identity.Name);
//Stornieren der offenen Walks des blockierten Users für den Benutzer der die Blockierung vorgenommen hat
var canceled = await _walkService.CancelWalksForBlockAsync(model.BlockingAppUserId, model.BlockedAppUserId);
//Sehen ob der blockierte Benutzer noch offene Angebote für den blockierenden Benutzer hat und diese Ablehnen
var openRequests = await _publicWalkRequestService.GetByStatusAsync(model.BlockingAppUserId, PublicWalkRequestStatus.Open);
foreach (var publicWalkRequest in openRequests)
{
var respone = await _publicWalkResponseService.GetForWalkerAsync(model.BlockedAppUserId, publicWalkRequest.Id);
if (respone != null)
{
respone.Status = PublicWalkResponseStatus.Declined;
respone.UpdatedAt = DateTimeOffset.UtcNow;
await _publicWalkResponseService.CommitAsync(User.Identity.Name);
}
}
var dto = Mapper.Map(block);
return Ok(dto);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Blockieren eines AppUsers durch einen anderen AppUser aufheben
///
/// Model
/// 200 OK
[HttpPost]
[Route("UnblockAppUser")]
public async Task UnblockAppUser(BlockRemoveDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (ModelState.IsValid)
{
var appUser = await AppUserService.GetAsync(model.BlockingAppUserId);
if (appUser != null)
{
//Nur dann zulassen, wenn der Benutzer der angemeldet ist auch der Benutzer ist der die Blockierung vorgenommen hat
if (appUser.Id == user.AppUserId)
{
var success = await AppUserService.UnblockAppUserAsync(model.BlockingAppUserId, model.BlockedAppUserId);
if(success)
await AppUserService.CommitAsync(User.Identity.Name);
return Ok(success);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
///
/// Abfragen gesperrten Benutzern für einen App-User
///
/// Abfragemodel
/// Liste von gesperrten Benutzern - App-Users
[HttpPost]
[Route("GetBlockedAppUsers")]
public async Task GetBlockedAppUsers(BlockedQueryDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var baseAddress = GetBaseAddress();
var response = new ListResponseDto();
var queryResult = await AppUserService.GetBlockedAppUsersAsync(model);
response.Total = queryResult.total;
var resultList = queryResult.list;
var dtoList = Mapper.Map>(resultList);
foreach (var dtoItem in dtoList)
{
if (!string.IsNullOrWhiteSpace(dtoItem.Photo))
dtoItem.Photo = $"{baseAddress}/file/documents/thumbnails/{400}/{dtoItem.Photo}";
}
response.List = dtoList;
response.Take = model.Take;
response.Skip = model.Skip;
return Ok(response);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
#endregion
#region AppUserReports - Meldungen
///
/// Melden eines AppUsers durch einen anderen AppUser
///
/// Model
/// 200 OK
[HttpPost]
[Route("ReportAppUser")]
public async Task ReportAppUser(AppUserReportDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
if (user != null)
{
if (ModelState.IsValid)
{
var reportingAppUser = await AppUserService.GetAsync(model.ReportingAppUserId);
if (reportingAppUser != null)
{
//Nur dann zulassen, wenn der Benutzer der angemeldet ist auch der Benutzer ist der die Blockierung vornimmt
if (reportingAppUser.Id == user.AppUserId)
{
var reportedAppUser = await AppUserService.GetAsync(model.ReportedAppUserId);
if (reportedAppUser != null)
{
var report = _appUserReportService.Create();
Mapper.Map(model, report);
report.Status = AppUserReportStatus.Open;
_appUserReportService.Add(report);
await _appUserReportService.CommitAsync(User.Identity.Name);
//Jetzt Block behandeln...
if (model.BlockType != AppUserReportBlockTypeDto.NoBlock)
{
DateTimeOffset? blockUntil = null;
if (model.BlockType == AppUserReportBlockTypeDto.BlockForTime)
blockUntil = DateTimeOffset.UtcNow.AddHours(24);
var block = await AppUserService.BlockAppUserAsync(model.ReportingAppUserId, model.ReportedAppUserId, User.Identity.Name, blockUntil, report.Id);
await AppUserService.CommitAsync(User.Identity.Name);
if (model.BlockType == AppUserReportBlockTypeDto.BlockForever)
{
//Stornieren der offenen Walks des blockierten Users für den Benutzer der die Blockierung vorgenommen hat
var canceled = await _walkService.CancelWalksForBlockAsync(model.ReportingAppUserId, model.ReportedAppUserId);
//Sehen ob der blockierte Benutzer noch offene Angebote für den blockierenden Benutzer hat und diese Ablehnen
var openRequests = await _publicWalkRequestService.GetByStatusAsync(model.ReportingAppUserId, PublicWalkRequestStatus.Open);
foreach (var publicWalkRequest in openRequests)
{
var respone = await _publicWalkResponseService.GetForWalkerAsync(model.ReportedAppUserId, publicWalkRequest.Id);
if (respone != null)
{
respone.Status = PublicWalkResponseStatus.Declined;
respone.UpdatedAt = DateTimeOffset.UtcNow;
await _publicWalkResponseService.CommitAsync(User.Identity.Name);
}
}
}
}
return Ok(true);
}
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
#endregion
#region Private
///
/// Generieren des Client-Secrets das Apple für die Prüfung des Benutzers benötigt
///
///
private async Task GenerateAppleClientSecretAsync()
{
var clientSecret = await _memoryCache.GetStringAsync("AppleClientSecret", CancellationToken.None);
if (string.IsNullOrEmpty(clientSecret))
{
var rootPath = _environment.WebRootPath;
var path = Path.Combine(rootPath, $"app_files\\apple\\AuthKey_{_appleOptions.Value.KeyId}.p8");
var lines = System.IO.File.ReadLines(path);
var keyStart = String.Join(String.Empty, lines.Where(x => !x.Contains("PRIVATE KEY")));
var bytes = Convert.FromBase64String(keyStart);
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
//Import the key using a Pkcs8PrivateBlob.
var cngKey = CngKey.Import(bytes, CngKeyBlobFormat.Pkcs8PrivateBlob);
//Create new ECDsaCng object with the imported key.
var ecDsaCng = new ECDsaCng(cngKey);
ecDsaCng.HashAlgorithm = CngAlgorithm.ECDsaP256;
//Create new SigningCredentials instance which will be used for signing the token.
var signingCredentials = new SigningCredentials(new ECDsaSecurityKey(ecDsaCng), SecurityAlgorithms.EcdsaSha256);
var now = DateTime.UtcNow.AddHours(12);
//Create new list with the required claims.
var claims = new List
{
new Claim("iss", _appleOptions.Value.TeamId),
new Claim("iat", EpochTime.GetIntDate(now).ToString(), ClaimValueTypes.Integer64),
new Claim("exp", EpochTime.GetIntDate(now.AddMinutes(5)).ToString(), ClaimValueTypes.Integer64),
new Claim("aud", "https://appleid.apple.com"),
new Claim("sub", _appleOptions.Value.ClientId)
};
//Create the JSON Web Token object.
var token = new JwtSecurityToken(
issuer: _appleOptions.Value.TeamId,
claims: claims,
expires: now.AddMinutes(5),
signingCredentials: signingCredentials);
token.Header.Add("kid", _appleOptions.Value.KeyId);
clientSecret = tokenHandler.WriteToken(token);
//var audience = "https://appleid.apple.com";
//var expiresAt = DateTimeOffset.UtcNow.AddSeconds(15777000).AddMinutes(-15).UtcDateTime; //apple max, but let's not get messed up with clock sync issues
//var subject = new System.Security.Claims.ClaimsIdentity(new[] { new System.Security.Claims.Claim("sub", _appleOptions.Value.ClientId) });
//var lines = System.IO.File.ReadLines(path);
//var keyStart = String.Join(String.Empty, lines.Where(x => !x.Contains("PRIVATE KEY")));
//var bytes = Convert.FromBase64String(keyStart);
//var ecd = ECDsa.Create();
//ecd.ImportPkcs8PrivateKey(bytes, out var _);
//var ecpStart = ecd.ExportParameters(true);
////var param = new { ecpStart.D, ecpStart.Q };
////var json = JsonSerializer.Serialize(param);
//var ecp = new ECParameters
//{
// D = ecpStart.D,
// Q = ecpStart.Q,
// Curve = ECCurve.NamedCurves.nistP256
//};
//var alg = ECDsa.Create(ecp);
//var key = new ECDsaSecurityKey(alg) { KeyId = _appleOptions.Value.KeyId };
//var sig = new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256Signature);
//var tokenHandler = new JwtSecurityTokenHandler();
//var tokenDescriptor = new SecurityTokenDescriptor()
//{
// Audience = audience,
// Expires = expiresAt,
// Issuer = _appleOptions.Value.TeamId,
// Subject = subject,
// SigningCredentials = sig
//};
//clientSecret = tokenHandler.CreateEncodedJwt(tokenDescriptor);
var options = new DistributedCacheEntryOptions();
options.SetAbsoluteExpiration(now.AddMinutes(-15));
await _memoryCache.SetStringAsync("AppleClientSecret", clientSecret, options);
// Tokengültigkeit von 6 Monaten (das ist der Max-Wert laut Apple)
//var expiresAt = DateTime.UtcNow.Add(TimeSpan.FromSeconds(15777000));
//var tokenDescriptor = new SecurityTokenDescriptor()
//{
// Audience = "https://appleid.apple.com",
// Expires = expiresAt,
// Issuer = _appleOptions.Value.TeamId,
// Subject = new ClaimsIdentity(new[] { new Claim("sub", _appleOptions.Value.ClientId) }),
//};
//// Load the .p8 file from disk, removing the
//// `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----`
//// prefix and suffix, and joining `\n` characters between lines.
//var content = await System.IO.File.ReadAllTextAsync(path);
//string[] keyLines = content.Split('\n');
//content = string.Join(string.Empty, keyLines.Skip(1).Take(keyLines.Length - 2));
//byte[] keyBlob = Convert.FromBase64String(content);
//// Create an ECDSA 256 algorithm to sign the token
//using var privateKey = CngKey.Import(keyBlob, CngKeyBlobFormat.Pkcs8PrivateBlob);
//using var algorithm = new ECDsaCng(privateKey);
//{
// algorithm.HashAlgorithm = CngAlgorithm.Sha256;
// var key = new ECDsaSecurityKey(algorithm) { KeyId = _appleOptions.Value.KeyId };
// // Set the signing key for the token
// tokenDescriptor.SigningCredentials = new SigningCredentials(
// key,
// SecurityAlgorithms.EcdsaSha256Signature);
// // Create the token, which acts as the Client Secret
// var tokenHandler = new JwtSecurityTokenHandler();
// clientSecret = tokenHandler.CreateEncodedJwt(tokenDescriptor);
// var options = new DistributedCacheEntryOptions();
// options.SetAbsoluteExpiration(expiresAt.AddMinutes(-15));
// await _memoryCache.SetStringAsync("AppleClientSecret", clientSecret, options);
//}
}
return clientSecret;
}
#endregion
}
}