Browse Source

IDentity improvements

pull/1/head
Sebastian 9 years ago
parent
commit
4116ce506e
  1. 6
      src/Squidex.Core/Identity/SquidexClaimTypes.cs
  2. 21
      src/Squidex.Core/Identity/SquidexRoles.cs
  3. 5
      src/Squidex.Read.MongoDb/Users/MongoUserEntity.cs
  4. 7
      src/Squidex.Read.MongoDb/Users/MongoUserRepository.cs
  5. 2
      src/Squidex.Read/Users/Repositories/IUserRepository.cs
  6. 5
      src/Squidex/Config/Identity/IdentityServices.cs
  7. 5
      src/Squidex/Config/Identity/IdentityUsage.cs
  8. 2
      src/Squidex/Config/Identity/MyIdentityOptions.cs
  9. 3
      src/Squidex/Controllers/Api/Apps/AppClientsController.cs
  10. 3
      src/Squidex/Controllers/Api/Apps/AppContributorsController.cs
  11. 3
      src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs
  12. 3
      src/Squidex/Controllers/Api/Schemas/SchemaFieldsController.cs
  13. 3
      src/Squidex/Controllers/Api/Schemas/SchemasController.cs
  14. 3
      src/Squidex/Controllers/ContentApi/ContentsController.cs
  15. 3
      src/Squidex/Controllers/ContentApi/Generator/SchemasSwaggerGenerator.cs
  16. 93
      src/Squidex/Controllers/UI/Account/AccountController.cs
  17. 23
      src/Squidex/Pipeline/AppFilterAttribute.cs
  18. 22
      src/Squidex/Views/Account/LockedOut.cshtml
  19. 1551
      src/Squidex/app/theme/icomoon/selection.json

6
src/Squidex.Infrastructure/Security/ExtendedClaimTypes.cs → src/Squidex.Core/Identity/SquidexClaimTypes.cs

@ -1,14 +1,14 @@
// ========================================================================== // ==========================================================================
// ExtendedClaimTypes.cs // SquidexClaimTypes.cs
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex Group // Copyright (c) Squidex Group
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
namespace Squidex.Infrastructure.Security namespace Squidex.Core.Identity
{ {
public class ExtendedClaimTypes public class SquidexClaimTypes
{ {
public const string SquidexDisplayName = "urn:squidex:name"; public const string SquidexDisplayName = "urn:squidex:name";

21
src/Squidex.Core/Identity/SquidexRoles.cs

@ -0,0 +1,21 @@
// ==========================================================================
// SquidexRoles.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Core.Identity
{
public static class SquidexRoles
{
public const string Administrator = "administrator";
public const string AppOwner = "app-owner";
public const string AppEditor = "app-editor";
public const string AppDeveloper = "app-developer";
}
}

5
src/Squidex.Read.MongoDb/Users/MongoUserEntity.cs

@ -7,6 +7,7 @@
// ========================================================================== // ==========================================================================
using Microsoft.AspNetCore.Identity.MongoDB; using Microsoft.AspNetCore.Identity.MongoDB;
using Squidex.Core.Identity;
using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Security;
using Squidex.Read.Users; using Squidex.Read.Users;
@ -28,12 +29,12 @@ namespace Squidex.Read.MongoDb.Users
public string DisplayName public string DisplayName
{ {
get { return inner.Claims.Find(x => x.Type == ExtendedClaimTypes.SquidexDisplayName)?.Value; } get { return inner.Claims.Find(x => x.Type == SquidexClaimTypes.SquidexDisplayName)?.Value; }
} }
public string PictureUrl public string PictureUrl
{ {
get { return inner.Claims.Find(x => x.Type == ExtendedClaimTypes.SquidexPictureUrl)?.Value; } get { return inner.Claims.Find(x => x.Type == SquidexClaimTypes.SquidexPictureUrl)?.Value; }
} }
public MongoUserEntity(IdentityUser inner) public MongoUserEntity(IdentityUser inner)

7
src/Squidex.Read.MongoDb/Users/MongoUserRepository.cs

@ -41,5 +41,12 @@ namespace Squidex.Read.MongoDb.Users
return user != null ? new MongoUserEntity(user) : null; return user != null ? new MongoUserEntity(user) : null;
} }
public Task<long> CountAsync()
{
var count = userManager.Users.LongCount();
return Task.FromResult(count);
}
} }
} }

2
src/Squidex.Read/Users/Repositories/IUserRepository.cs

@ -16,5 +16,7 @@ namespace Squidex.Read.Users.Repositories
Task<List<IUserEntity>> QueryUsersByQuery(string query); Task<List<IUserEntity>> QueryUsersByQuery(string query);
Task<IUserEntity> FindUserByIdAsync(string id); Task<IUserEntity> FindUserByIdAsync(string id);
Task<long> CountAsync();
} }
} }

5
src/Squidex/Config/Identity/IdentityServices.cs

@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity.MongoDB; using Microsoft.AspNetCore.Identity.MongoDB;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Squidex.Core.Identity;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Security;
using StackExchange.Redis; using StackExchange.Redis;
@ -130,8 +131,8 @@ namespace Squidex.Config.Identity
yield return new IdentityResource(Constants.ProfileScope, yield return new IdentityResource(Constants.ProfileScope,
new[] new[]
{ {
ExtendedClaimTypes.SquidexDisplayName, SquidexClaimTypes.SquidexDisplayName,
ExtendedClaimTypes.SquidexPictureUrl SquidexClaimTypes.SquidexPictureUrl
}); });
} }
} }

5
src/Squidex/Config/Identity/IdentityUsage.cs

@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Identity.MongoDB;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Squidex.Core.Identity;
using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Security;
// ReSharper disable InvertIf // ReSharper disable InvertIf
@ -116,7 +117,7 @@ namespace Squidex.Config.Identity
var displayNameClaim = context.Identity.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Name); var displayNameClaim = context.Identity.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Name);
if (displayNameClaim != null) if (displayNameClaim != null)
{ {
context.Identity.AddClaim(new Claim(ExtendedClaimTypes.SquidexDisplayName, displayNameClaim.Value)); context.Identity.AddClaim(new Claim(SquidexClaimTypes.SquidexDisplayName, displayNameClaim.Value));
} }
return base.CreatingTicket(context); return base.CreatingTicket(context);
@ -147,7 +148,7 @@ namespace Squidex.Config.Identity
if (!string.IsNullOrWhiteSpace(pictureUrl)) if (!string.IsNullOrWhiteSpace(pictureUrl))
{ {
context.Identity.AddClaim(new Claim(ExtendedClaimTypes.SquidexPictureUrl, pictureUrl)); context.Identity.AddClaim(new Claim(SquidexClaimTypes.SquidexPictureUrl, pictureUrl));
} }
} }

2
src/Squidex/Config/Identity/MyIdentityOptions.cs

@ -19,5 +19,7 @@ namespace Squidex.Config.Identity
public string GoogleSecret { get; set; } public string GoogleSecret { get; set; }
public bool RequiresHttps { get; set; } public bool RequiresHttps { get; set; }
public bool LockAutomatically { get; set; }
} }
} }

3
src/Squidex/Controllers/Api/Apps/AppClientsController.cs

@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NSwag.Annotations; using NSwag.Annotations;
using Squidex.Controllers.Api.Apps.Models; using Squidex.Controllers.Api.Apps.Models;
using Squidex.Core.Identity;
using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Pipeline; using Squidex.Pipeline;
@ -24,7 +25,7 @@ namespace Squidex.Controllers.Api.Apps
/// <summary> /// <summary>
/// Manages and configures apps. /// Manages and configures apps.
/// </summary> /// </summary>
[Authorize(Roles = "app-owner")] [Authorize(Roles = SquidexRoles.AppOwner)]
[ApiExceptionFilter] [ApiExceptionFilter]
[ServiceFilter(typeof(AppFilterAttribute))] [ServiceFilter(typeof(AppFilterAttribute))]
[SwaggerTag("Apps")] [SwaggerTag("Apps")]

3
src/Squidex/Controllers/Api/Apps/AppContributorsController.cs

@ -14,6 +14,7 @@ using NSwag.Annotations;
using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Controllers.Api.Apps.Models; using Squidex.Controllers.Api.Apps.Models;
using Squidex.Core.Identity;
using Squidex.Pipeline; using Squidex.Pipeline;
using Squidex.Read.Apps.Services; using Squidex.Read.Apps.Services;
using Squidex.Write.Apps.Commands; using Squidex.Write.Apps.Commands;
@ -23,7 +24,7 @@ namespace Squidex.Controllers.Api.Apps
/// <summary> /// <summary>
/// Manages and configures apps. /// Manages and configures apps.
/// </summary> /// </summary>
[Authorize(Roles = "app-owner")] [Authorize(Roles = SquidexRoles.AppOwner)]
[ApiExceptionFilter] [ApiExceptionFilter]
[ServiceFilter(typeof(AppFilterAttribute))] [ServiceFilter(typeof(AppFilterAttribute))]
[SwaggerTag("Apps")] [SwaggerTag("Apps")]

3
src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs

@ -14,6 +14,7 @@ using NSwag.Annotations;
using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Controllers.Api.Apps.Models; using Squidex.Controllers.Api.Apps.Models;
using Squidex.Core.Identity;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Pipeline; using Squidex.Pipeline;
using Squidex.Read.Apps.Services; using Squidex.Read.Apps.Services;
@ -24,7 +25,7 @@ namespace Squidex.Controllers.Api.Apps
/// <summary> /// <summary>
/// Manages and configures apps. /// Manages and configures apps.
/// </summary> /// </summary>
[Authorize(Roles = "app-owner")] [Authorize(Roles = SquidexRoles.AppOwner)]
[ApiExceptionFilter] [ApiExceptionFilter]
[ServiceFilter(typeof(AppFilterAttribute))] [ServiceFilter(typeof(AppFilterAttribute))]
[SwaggerTag("Apps")] [SwaggerTag("Apps")]

3
src/Squidex/Controllers/Api/Schemas/SchemaFieldsController.cs

@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc;
using NSwag.Annotations; using NSwag.Annotations;
using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Controllers.Api.Schemas.Models; using Squidex.Controllers.Api.Schemas.Models;
using Squidex.Core.Identity;
using Squidex.Pipeline; using Squidex.Pipeline;
using Squidex.Write.Schemas.Commands; using Squidex.Write.Schemas.Commands;
@ -20,7 +21,7 @@ namespace Squidex.Controllers.Api.Schemas
/// <summary> /// <summary>
/// Manages and retrieves information about schemas. /// Manages and retrieves information about schemas.
/// </summary> /// </summary>
[Authorize(Roles = "app-owner,app-developer")] [Authorize(Roles = SquidexRoles.AppDeveloper)]
[ApiExceptionFilter] [ApiExceptionFilter]
[ServiceFilter(typeof(AppFilterAttribute))] [ServiceFilter(typeof(AppFilterAttribute))]
[SwaggerTag("Schemas")] [SwaggerTag("Schemas")]

3
src/Squidex/Controllers/Api/Schemas/SchemasController.cs

@ -15,6 +15,7 @@ using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Controllers.Api.Schemas.Models; using Squidex.Controllers.Api.Schemas.Models;
using Squidex.Controllers.Api.Schemas.Models.Converters; using Squidex.Controllers.Api.Schemas.Models.Converters;
using Squidex.Core.Identity;
using Squidex.Core.Schemas; using Squidex.Core.Schemas;
using Squidex.Pipeline; using Squidex.Pipeline;
using Squidex.Read.Schemas.Repositories; using Squidex.Read.Schemas.Repositories;
@ -25,7 +26,7 @@ namespace Squidex.Controllers.Api.Schemas
/// <summary> /// <summary>
/// Manages and retrieves information about schemas. /// Manages and retrieves information about schemas.
/// </summary> /// </summary>
[Authorize(Roles = "app-owner,app-developer")] [Authorize(Roles = SquidexRoles.AppDeveloper)]
[ApiExceptionFilter] [ApiExceptionFilter]
[ServiceFilter(typeof(AppFilterAttribute))] [ServiceFilter(typeof(AppFilterAttribute))]
[SwaggerTag("Schemas")] [SwaggerTag("Schemas")]

3
src/Squidex/Controllers/ContentApi/ContentsController.cs

@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Mvc;
using Squidex.Controllers.Api; using Squidex.Controllers.Api;
using Squidex.Controllers.ContentApi.Models; using Squidex.Controllers.ContentApi.Models;
using Squidex.Core.Contents; using Squidex.Core.Contents;
using Squidex.Core.Identity;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
@ -25,7 +26,7 @@ using Squidex.Write.Contents.Commands;
namespace Squidex.Controllers.ContentApi namespace Squidex.Controllers.ContentApi
{ {
[Authorize(Roles = "app-editor,app-owner,app-developer")] [Authorize(Roles = SquidexRoles.AppEditor)]
[ApiExceptionFilter] [ApiExceptionFilter]
[ServiceFilter(typeof(AppFilterAttribute))] [ServiceFilter(typeof(AppFilterAttribute))]
public class ContentsController : ControllerBase public class ContentsController : ControllerBase

3
src/Squidex/Controllers/ContentApi/Generator/SchemasSwaggerGenerator.cs

@ -19,6 +19,7 @@ using NSwag.AspNetCore;
using NSwag.SwaggerGeneration; using NSwag.SwaggerGeneration;
using Squidex.Config; using Squidex.Config;
using Squidex.Controllers.Api; using Squidex.Controllers.Api;
using Squidex.Core.Identity;
using Squidex.Core.Schemas; using Squidex.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Pipeline.Swagger; using Squidex.Pipeline.Swagger;
@ -144,7 +145,7 @@ When you change the field to be localizable the value will become the value for
{ {
new SwaggerSecurityRequirement new SwaggerSecurityRequirement
{ {
{ "roles", new List<string> { "app-owner", "app-developer", "app-editor" } } { "roles", new List<string> { SquidexRoles.AppOwner, SquidexRoles.AppDeveloper, SquidexRoles.AppEditor } }
} }
}; };

93
src/Squidex/Controllers/UI/Account/AccountController.cs

@ -6,19 +6,25 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using IdentityServer4.Services; using IdentityServer4.Services;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.MongoDB; using Microsoft.AspNetCore.Identity.MongoDB;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NSwag.Annotations; using NSwag.Annotations;
using Squidex.Infrastructure.Security;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Squidex.Config; using Squidex.Config;
using Squidex.Config.Identity;
using Squidex.Core.Identity;
using Squidex.Read.Users.Repositories;
// ReSharper disable InvertIf
// ReSharper disable RedundantIfElseBlock // ReSharper disable RedundantIfElseBlock
// ReSharper disable ConvertIfStatementToReturnStatement // ReSharper disable ConvertIfStatementToReturnStatement
@ -27,21 +33,31 @@ namespace Squidex.Controllers.UI.Account
[SwaggerIgnore] [SwaggerIgnore]
public sealed class AccountController : Controller public sealed class AccountController : Controller
{ {
private static readonly EventId IdentityEventId = new EventId(8000, "IdentityEventId");
private readonly SignInManager<IdentityUser> signInManager; private readonly SignInManager<IdentityUser> signInManager;
private readonly UserManager<IdentityUser> userManager; private readonly UserManager<IdentityUser> userManager;
private readonly IOptions<MyIdentityOptions> identityOptions;
private readonly IOptions<MyUrlsOptions> urlOptions; private readonly IOptions<MyUrlsOptions> urlOptions;
private readonly IUserRepository userRepository;
private readonly ILogger<AccountController> logger;
private readonly IIdentityServerInteractionService interactions; private readonly IIdentityServerInteractionService interactions;
public AccountController( public AccountController(
SignInManager<IdentityUser> signInManager, SignInManager<IdentityUser> signInManager,
UserManager<IdentityUser> userManager, UserManager<IdentityUser> userManager,
IOptions<MyIdentityOptions> identityOptions,
IOptions<MyUrlsOptions> urlOptions, IOptions<MyUrlsOptions> urlOptions,
IUserRepository userRepository,
ILogger<AccountController> logger,
IIdentityServerInteractionService interactions) IIdentityServerInteractionService interactions)
{ {
this.signInManager = signInManager; this.logger = logger;
this.urlOptions = urlOptions; this.urlOptions = urlOptions;
this.userManager = userManager; this.userManager = userManager;
this.userRepository = userRepository;
this.interactions = interactions; this.interactions = interactions;
this.identityOptions = identityOptions;
this.signInManager = signInManager;
} }
[Authorize] [Authorize]
@ -125,15 +141,26 @@ namespace Squidex.Controllers.UI.Account
return RedirectToAction(nameof(Login)); return RedirectToAction(nameof(Login));
} }
var isLoggedIn = await LoginAsync(externalLogin); var result = await signInManager.ExternalLoginSignInAsync(externalLogin.LoginProvider, externalLogin.ProviderKey, true);
if (!result.Succeeded && result.IsLockedOut)
{
return View("LockedOut");
}
var isLoggedIn = result.Succeeded;
if (!isLoggedIn) if (!isLoggedIn)
{ {
var user = CreateUser(externalLogin); var user = CreateUser(externalLogin);
var isFirst = await userRepository.CountAsync() == 0;
isLoggedIn = isLoggedIn =
await AddUserAsync(user) && await AddUserAsync(user) &&
await AddLoginAsync(user, externalLogin) && await AddLoginAsync(user, externalLogin) &&
await MakeAdminAsync(user, isFirst) &&
await LockAsync(user, isFirst) &&
await LoginAsync(externalLogin); await LoginAsync(externalLogin);
} }
@ -158,11 +185,9 @@ namespace Squidex.Controllers.UI.Account
return result.Succeeded; return result.Succeeded;
} }
private async Task<bool> AddUserAsync(IdentityUser user) private Task<bool> AddUserAsync(IdentityUser user)
{ {
var result = await userManager.CreateAsync(user); return MakeIdentityOperation("LoginAsync", () => userManager.CreateAsync(user));
return result.Succeeded;
} }
private async Task<bool> LoginAsync(UserLoginInfo externalLogin) private async Task<bool> LoginAsync(UserLoginInfo externalLogin)
@ -172,19 +197,39 @@ namespace Squidex.Controllers.UI.Account
return result.Succeeded; return result.Succeeded;
} }
private Task<bool> LockAsync(IdentityUser user, bool isFirst)
{
if (isFirst || !identityOptions.Value.LockAutomatically)
{
return Task.FromResult(false);
}
return MakeIdentityOperation("LockAsync", () => userManager.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.AddYears(100)));
}
private Task<bool> MakeAdminAsync(IdentityUser user, bool isFirst)
{
if (isFirst)
{
return Task.FromResult(false);
}
return MakeIdentityOperation("LockAsync", () => userManager.AddToRoleAsync(user, SquidexRoles.Administrator));
}
private static IdentityUser CreateUser(ExternalLoginInfo externalLogin) private static IdentityUser CreateUser(ExternalLoginInfo externalLogin)
{ {
var mail = externalLogin.Principal.FindFirst(ClaimTypes.Email).Value; var mail = externalLogin.Principal.FindFirst(ClaimTypes.Email).Value;
var user = new IdentityUser { Email = mail, UserName = mail }; var user = new IdentityUser { Email = mail, UserName = mail };
var pictureUrl = externalLogin.Principal.Claims.FirstOrDefault(x => x.Type == ExtendedClaimTypes.SquidexPictureUrl); var pictureUrl = externalLogin.Principal.Claims.FirstOrDefault(x => x.Type == SquidexClaimTypes.SquidexPictureUrl);
if (pictureUrl != null) if (pictureUrl != null)
{ {
user.AddClaim(pictureUrl); user.AddClaim(pictureUrl);
} }
var displayName = externalLogin.Principal.Claims.FirstOrDefault(x => x.Type == ExtendedClaimTypes.SquidexDisplayName); var displayName = externalLogin.Principal.Claims.FirstOrDefault(x => x.Type == SquidexClaimTypes.SquidexDisplayName);
if (displayName != null) if (displayName != null)
{ {
user.AddClaim(displayName); user.AddClaim(displayName);
@ -192,5 +237,35 @@ namespace Squidex.Controllers.UI.Account
return user; return user;
} }
private async Task<bool> MakeIdentityOperation(string operationName, Func<Task<IdentityResult>> action)
{
try
{
var result = await action();
if (!result.Succeeded)
{
var errorMessageBuilder = new StringBuilder();
foreach (var error in result.Errors)
{
errorMessageBuilder.Append(error.Code);
errorMessageBuilder.Append(": ");
errorMessageBuilder.AppendLine(error.Description);
}
logger.LogError(IdentityEventId, "Operation '{0}' failed with errors: {1}", operationName, errorMessageBuilder.ToString());
}
return result.Succeeded;
}
catch (Exception e)
{
logger.LogError(IdentityEventId, e, "Operation '{0}' failed with exception", operationName);
return false;
}
}
} }
} }

23
src/Squidex/Pipeline/AppFilterAttribute.cs

@ -13,10 +13,13 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Squidex.Core.Apps; using Squidex.Core.Apps;
using Squidex.Core.Identity;
using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Security;
using Squidex.Read.Apps; using Squidex.Read.Apps;
using Squidex.Read.Apps.Services; using Squidex.Read.Apps.Services;
// ReSharper disable SwitchStatementMissingSomeCases
namespace Squidex.Pipeline namespace Squidex.Pipeline
{ {
public sealed class AppFilterAttribute : Attribute, IAsyncAuthorizationFilter public sealed class AppFilterAttribute : Attribute, IAsyncAuthorizationFilter
@ -54,13 +57,23 @@ namespace Squidex.Pipeline
return; return;
} }
var roleName = $"app-{permission.ToString().ToLowerInvariant()}";
var defaultIdentity = context.HttpContext.User.Identities.First(); var defaultIdentity = context.HttpContext.User.Identities.First();
defaultIdentity switch (permission.Value)
.AddClaim( {
new Claim(defaultIdentity.RoleClaimType, roleName)); case PermissionLevel.Owner:
defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppOwner));
defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppDeveloper));
defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppEditor));
break;
case PermissionLevel.Editor:
defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppDeveloper));
defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppEditor));
break;
case PermissionLevel.Developer:
defaultIdentity.AddClaim(new Claim(defaultIdentity.RoleClaimType, SquidexRoles.AppEditor));
break;
}
context.HttpContext.Features.Set<IAppFeature>(new AppFeature(app)); context.HttpContext.Features.Set<IAppFeature>(new AppFeature(app));
} }

22
src/Squidex/Views/Account/LockedOut.cshtml

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<title>Squidex - Account locked</title>
<style>
body {
padding: 40px;
}
h1, p {
text-align: center;
}
</style>
</head>
<body>
<h1>Account locked</h1>
<p>
Your account is locked, please contact the administrator.
</p>
</body>
</html>

1551
src/Squidex/app/theme/icomoon/selection.json

File diff suppressed because it is too large
Loading…
Cancel
Save