Skip to content
This repository has been archived by the owner on Jan 24, 2020. It is now read-only.

Commit

Permalink
simplified bootstrap setup + updated server project with a base contr…
Browse files Browse the repository at this point in the history
…oller class
  • Loading branch information
mrellipse committed Jul 15, 2018
1 parent fad1fb6 commit ac47475
Show file tree
Hide file tree
Showing 110 changed files with 187 additions and 6,474 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,6 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
* Wille Ristimäki for [vue.js-starter-template](https://github.com/villeristi/vue.js-starter-template)
* Nate Barbettini for [SimpleTokenProvider](https://github.com/nbarbettini/SimpleTokenProvider)
* Monterail for [Vuelidate](https://monterail.github.io/vuelidate/)
* Simon de la Salle [Toucan](http://simondelasalle.com/blog/)

*[EF]: Entity Framework
19 changes: 10 additions & 9 deletions src/server/Constant/Validation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ namespace Toucan.Server
{
public static partial class Constants
{
public const string EmailAddressInUse = "EmailAddressInUse";
public const string FailedToResolveUser = "FailedToResolveUser";
public const string FailedToVerifyUser = "FailedToVerifyUser";
public const string InvalidNonce = "login.nonce.invalid"; //
public const string ExpiredNonce = "login.nonce.expired"; //
public const string InProgressNonce = "login.nonce.inProgress"; //
public const string InvalidAccessToken = "InvalidAccessToken";
public const string SecureContent = "secureContent";
public const string UnsupportedCulture = "UnsupportedCulture";
public const string EmailAddressInUse = "validation.login.EmailAddressInUse";
public const string ExpiredNonce = "login.nonce.expired";
public const string FailedToVerifyUser = "validation.login.FailedToVerifyUser";
public const string InvalidNonce = "login.nonce.invalid";
public const string InvalidUsernameOrPassword = "validation.login.InvalidUsernameOrPassword";
public const string InProgressNonce = "login.nonce.inProgress";
public const string InvalidAccessToken = "validation.login.InvalidAccessToken";
public const string SecureContent = "dict.secureContent";
public const string UnknownCulture = "validation.unknownCulture";
public const string UnknownUser = "validation.unknownUser";
}
}
12 changes: 5 additions & 7 deletions src/server/Controllers/Admin/ManageUserController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,14 @@ namespace Toucan.Server.Controllers.Admin
[Route("api/manage/user/[action]")]
[ServiceFilter(typeof(Filters.ApiResultFilter))]
[ServiceFilter(typeof(Filters.ApiExceptionFilter))]
public class ManageUserController : Controller
public class ManageUserController : ControllerBase
{
private readonly IManageUserService manageUserService;

public ILocalizationService localizationService { get; }

public ManageUserController(IManageUserService manageUserService, ILocalizationService localizationService)
public ManageUserController(IManageUserService manageUserService,IDomainContextResolver resolver, ILocalizationService localization) : base(resolver, localization)
{
this.manageUserService = manageUserService;
this.localizationService = localizationService;
}

[HttpGet]
Expand All @@ -38,8 +36,8 @@ public async Task<object> GetUser(string id)
user = await this.manageUserService.ResolveUserBy(id);

var availableRoles = await this.manageUserService.GetAvailableRoles();
var availableCultures = await this.localizationService.GetSupportedCultures();
var availableTimeZones = await this.localizationService.GetSupportedTimeZones();
var availableCultures = await this.Localization.GetSupportedCultures();
var availableTimeZones = await this.Localization.GetSupportedTimeZones();

return new
{
Expand All @@ -66,7 +64,7 @@ public async Task<object> UpdateUser([FromBody]User user)
public async Task<object> UpdateUserStatus([FromBody] UpdateUserStatusOptions options)
{
if (options == null || string.IsNullOrEmpty(options.UserName))
throw new ServiceException(Constants.FailedToResolveUser);
this.ThrowLocalizedServiceException(Constants.UnknownUser);

return await this.manageUserService.UpdateUserStatus(options.UserName, options.Enabled);
}
Expand Down
20 changes: 10 additions & 10 deletions src/server/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Toucan.Server.Controllers
[ServiceFilter(typeof(Filters.ApiResultFilter))]
[ServiceFilter(typeof(Filters.ApiExceptionFilter))]
[ServiceFilter(typeof(Filters.IdentityMappingFilter))]
public class AuthController : Controller
public class AuthController : ControllerBase
{
private readonly IAntiforgery antiForgeryService;
private readonly ILocalAuthenticationService authService;
Expand All @@ -26,7 +26,7 @@ public class AuthController : Controller
private readonly ISignupService signupService;
private readonly ITokenProviderService<Token> tokenService;

public AuthController(IAntiforgery antiForgeryService, ILocalAuthenticationService authService, CultureService cultureService, IOptions<Toucan.Server.Config> serverConfig, ISignupService signupService, ITokenProviderService<Token> tokenService)
public AuthController(IAntiforgery antiForgeryService, ILocalAuthenticationService authService, CultureService cultureService, IOptions<Toucan.Server.Config> serverConfig, ISignupService signupService, ITokenProviderService<Token> tokenService, IDomainContextResolver resolver, ILocalizationService localization) : base(resolver, localization)
{
this.antiForgeryService = antiForgeryService;
this.authService = authService;
Expand All @@ -43,15 +43,15 @@ public async Task<object> IssueVerificationCode(string providerKey = null)
IUser user = this.ApplicationUser();

if (user == null)
throw new ServiceException(Constants.FailedToVerifyUser);
this.ThrowLocalizedServiceException(Constants.UnknownUser);

if (string.IsNullOrWhiteSpace(providerKey))
providerKey = HttpVerificationProvider.ProviderKey;

string code = await this.signupService.SendVerificationCode(user, providerKey);

if (code == null)
throw new ServiceException(Constants.FailedToVerifyUser);
this.ThrowLocalizedServiceException(Constants.FailedToVerifyUser);

return code;
}
Expand All @@ -78,12 +78,12 @@ public async Task<Object> Logout()
public async Task<object> Signup([FromBody]LocalSignupOptions options)
{
if (!await this.authService.ValidateUser(options.Username))
throw new ServiceException(Constants.EmailAddressInUse);
this.ThrowLocalizedServiceException(Constants.EmailAddressInUse);

var identity = await this.signupService.SignupUser(options);

if (identity == null)
throw new ServiceException(Constants.FailedToResolveUser);
this.ThrowLocalizedServiceException(Constants.UnknownUser);

this.SetAntiforgeryCookies();

Expand All @@ -97,7 +97,7 @@ public async Task<object> Token([FromBody] Model.TokenRequest credentials)
var identity = await this.authService.ResolveUser(credentials.Username, credentials.password);

if (identity == null)
throw new ServiceException(Constants.FailedToResolveUser);
this.ThrowLocalizedServiceException(Constants.UnknownUser);

this.SetAntiforgeryCookies();

Expand All @@ -116,7 +116,7 @@ public async Task<object> ValidateUser(string username)
bool available = await this.authService.ValidateUser(username);

if (!available)
throw new ServiceException(Constants.EmailAddressInUse);
this.ThrowLocalizedServiceException(Constants.EmailAddressInUse);

return "";
}
Expand All @@ -128,12 +128,12 @@ public async Task<object> RedeemVerificationCode(string code)
IUser user = this.ApplicationUser();

if (user == null)
throw new ServiceException(Constants.FailedToVerifyUser);
this.ThrowLocalizedServiceException(Constants.UnknownUser);

var identity = await this.signupService.RedeemVerificationCode(user, code);

if (identity == null)
throw new ServiceException(Constants.FailedToVerifyUser);
this.ThrowLocalizedServiceException(Constants.FailedToVerifyUser);

return await this.tokenService.IssueToken(identity, identity.Name);
}
Expand Down
18 changes: 5 additions & 13 deletions src/server/Controllers/ContentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,25 @@
namespace Toucan.Server.Controllers
{
[Route("api/[controller]/[action]")]
public class ContentController : Controller
public class ContentController : ControllerBase
{
private ILocalizationService localization;
private readonly IDomainContextResolver resolver;

public ContentController(IDomainContextResolver resolver, ILocalizationService localization)
public ContentController(IDomainContextResolver resolver, ILocalizationService localization) : base(resolver, localization)
{
this.localization = localization;
this.resolver = resolver;
}

[HttpGet()]
[ServiceFilter(typeof(Filters.ApiExceptionFilter))]
public async Task<object> RikerIpsum([FromQuery]DateTime clientTime)
{
IDomainContext context = this.resolver.Resolve();
ILocalizationDictionary dict = this.localization.CreateDictionary(context);

DateTime roundTripTime = TimeZoneInfo.ConvertTimeFromUtc(clientTime, context.SourceTimeZone);
DateTime roundTripTime = TimeZoneInfo.ConvertTimeFromUtc(clientTime, this.DomainContext.SourceTimeZone);

var payload = new Model.Payload<object>()
{
Data = !context.User.Enabled ? dict["home.body.0"].Value : dict["home.body.1"].Value,
Data = !this.DomainContext.User.Enabled ? this.Dictionary["home.body.0"].Value : this.Dictionary["home.body.1"].Value,
Message = new PayloadMessage()
{
MessageType = PayloadMessageType.Info,
Text = string.Format(dict["home.text"].Value, roundTripTime.ToString("hh:mm tt"))
Text = string.Format(this.Dictionary["home.text"].Value, roundTripTime.ToString("hh:mm tt"))
}
};

Expand Down
54 changes: 54 additions & 0 deletions src/server/Controllers/ControllerBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Toucan.Contract;
using Toucan.Server.Model;
using Toucan.Service;

namespace Toucan.Server.Controllers
{
public abstract class ControllerBase : Controller
{
protected ILocalizationService Localization { get; private set; }
protected IDomainContextResolver Resolver;
private ILocalizationDictionary dictionary;
private IDomainContext domainContext;

public ControllerBase(IDomainContextResolver resolver, ILocalizationService localization)
{
this.Localization = localization;
this.Resolver = resolver;
}

protected ILocalizationDictionary Dictionary
{
get
{
if (this.dictionary == null)
this.dictionary = this.Localization.CreateDictionary(this.DomainContext);

return this.dictionary;
}
}

protected IDomainContext DomainContext
{
get
{
if (this.domainContext == null)
this.domainContext = this.Resolver.Resolve();

return this.domainContext;
}
}

protected void ThrowLocalizedServiceException(string key)
{
throw new ServiceException(this.Dictionary[key].Value);
}
}
}
21 changes: 7 additions & 14 deletions src/server/Controllers/ExternalAuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ namespace Toucan.Server.Controllers
[Route("auth/external/[action]")]
[ServiceFilter(typeof(Filters.ApiResultFilter))]
[ServiceFilter(typeof(Filters.ApiExceptionFilter))]
public class ExternalAuthControllerController : Controller
public class ExternalAuthControllerController : ControllerBase
{
private const string IssuedNoncesKey = "IssuedNonces";
private readonly IExternalAuthenticationService externalAuthService;
private readonly ILocalizationService localization;
private readonly IDomainContextResolver resolver;
private readonly ITokenProviderService<Token> tokenService;
private readonly IMemoryCache cache;

Expand All @@ -30,12 +28,10 @@ private List<Nonce> IssuedNonces
}
}

public ExternalAuthControllerController(IExternalAuthenticationService externalAuthService, IMemoryCache cache, ITokenProviderService<Token> tokenService, IDomainContextResolver resolver, ILocalizationService localization)
public ExternalAuthControllerController(IExternalAuthenticationService externalAuthService, IMemoryCache cache, ITokenProviderService<Token> tokenService, IDomainContextResolver resolver, ILocalizationService localization) : base(resolver, localization)
{
this.cache = cache;
this.externalAuthService = externalAuthService;
this.localization = localization;
this.resolver = resolver;
this.tokenService = tokenService;
}

Expand All @@ -55,22 +51,19 @@ public async Task<object> IssueNonce()
[IgnoreAntiforgeryToken(Order = 1000)]
public async Task<object> RedeemToken([FromBody]ExternalLogin options)
{
IDomainContext context = this.resolver.Resolve();
ILocalizationDictionary dict = this.localization.CreateDictionary(context);

// check for server-generated nonce, and make sure it was issued recently
if (!IssuedNonces.Any(o => o.Hash == options.Nonce))
throw new ServiceException(dict[Constants.InvalidNonce].Value);
this.ThrowLocalizedServiceException(Constants.InvalidNonce);

Nonce nonce = IssuedNonces.FirstOrDefault(o => o.Hash == options.Nonce);

if (nonce.Processing)
throw new ServiceException(dict[Constants.InProgressNonce].Value);
this.ThrowLocalizedServiceException(Constants.InProgressNonce);

if (nonce.Created.AddMinutes(30) < DateTime.UtcNow)
{
IssuedNonces.Remove(nonce);
throw new ServiceException(dict[Constants.ExpiredNonce].Value);
this.ThrowLocalizedServiceException(Constants.ExpiredNonce);
}

nonce.Update(true);
Expand All @@ -79,7 +72,7 @@ public async Task<object> RedeemToken([FromBody]ExternalLogin options)
var identity = await this.externalAuthService.RedeemToken(options);

if (identity == null)
throw new ServiceException(Constants.FailedToResolveUser);
this.ThrowLocalizedServiceException(Constants.UnknownUser);

// remove the original nonce, and revoke the external access token, as they are longer required
IssuedNonces.Remove(nonce);
Expand All @@ -95,7 +88,7 @@ public async Task<bool> ValidateToken([FromBody]ExternalToken token)
bool available = await this.externalAuthService.ValidateToken(token.ProviderId, token.AccessToken);

if (!available)
throw new ServiceException(Constants.InvalidAccessToken);
this.ThrowLocalizedServiceException(Constants.InvalidAccessToken);

return true;
}
Expand Down
19 changes: 8 additions & 11 deletions src/server/Controllers/Profile/ProfileController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,32 @@ namespace Toucan.Server.Controllers.Admin
[ServiceFilter(typeof(Filters.ApiResultFilter))]
[ServiceFilter(typeof(Filters.ApiExceptionFilter))]
[ServiceFilter(typeof(Filters.IdentityMappingFilter))]
public class ProfileController : Controller
public class ProfileController : ControllerBase
{
private readonly CultureService cultureService;
private readonly ILocalizationService localization;
private readonly IManageProfileService profileService;

public ILocalizationService localizationService { get; }

public ProfileController(CultureService cultureService, ILocalizationService localization, IManageProfileService profileService)
public ProfileController(CultureService cultureService, IManageProfileService profileService, IDomainContextResolver resolver, ILocalizationService localization) : base(resolver, localization)
{
this.cultureService = cultureService;
this.localization = localization;
this.profileService = profileService;
}

[HttpPut]
[IgnoreAntiforgeryToken(Order=1000)]
[IgnoreAntiforgeryToken(Order = 1000)]
[Route("[action]")]
public async Task<object> UpdateUserCulture([FromBody] UpdateUserCultureOptions user)
{
if (user == null || string.IsNullOrWhiteSpace(user.CultureName))
throw new ServiceException(Constants.UnsupportedCulture);
this.ThrowLocalizedServiceException(Constants.UnknownUser);

if (!this.localization.IsSupportedCulture(user.CultureName))
throw new ServiceException(Constants.UnsupportedCulture);
if (!this.Localization.IsSupportedCulture(user.CultureName))
this.ThrowLocalizedServiceException(Constants.UnknownCulture);

string cultureName = user.CultureName;
string timeZoneId = user.TimeZoneId;

if (this.ApplicationUser() != null && this.ApplicationUser().UserId == user.UserId)
{
var dbUser = await this.profileService.UpdateUserCulture(user.UserId, user.CultureName, user.TimeZoneId);
Expand All @@ -57,7 +54,7 @@ public async Task<object> UpdateUserCulture([FromBody] UpdateUserCultureOptions
}
}

var resources = await localization.ResolveCulture(cultureName);
var resources = await this.Localization.ResolveCulture(cultureName);

this.cultureService.RefreshCookie(this.HttpContext, user.CultureName, user.TimeZoneId);

Expand Down
Loading

0 comments on commit ac47475

Please sign in to comment.