Browse Source

Custom admin client.

pull/346/head
Sebastian Stehle 7 years ago
parent
commit
c437d096ee
  1. 5
      src/Squidex.Infrastructure/Security/Extensions.cs
  2. 11
      src/Squidex.Shared/Identity/ClaimsPrincipalExtensions.cs
  3. 4
      src/Squidex.Shared/Identity/SquidexClaimTypes.cs
  4. 6
      src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs
  5. 4
      src/Squidex/Areas/Api/Controllers/Backups/RestoreController.cs
  6. 40
      src/Squidex/Areas/IdentityServer/Config/LazyClientStore.cs
  7. 9
      src/Squidex/Config/Logging.cs
  8. 9
      src/Squidex/Config/MyIdentityOptions.cs
  9. 9
      src/Squidex/Pipeline/Extensions.cs
  10. 10
      src/Squidex/appsettings.json

5
src/Squidex.Infrastructure/Security/Extensions.cs

@ -23,6 +23,11 @@ namespace Squidex.Infrastructure.Security
return principal.Claims.FirstOrDefault(x => x.Type == OpenIdClaims.ClientId)?.Value; return principal.Claims.FirstOrDefault(x => x.Type == OpenIdClaims.ClientId)?.Value;
} }
public static string UserOrClientId(this ClaimsPrincipal principal)
{
return principal.OpenIdSubject() ?? principal.OpenIdClientId();
}
public static string OpenIdPreferredUserName(this ClaimsPrincipal principal) public static string OpenIdPreferredUserName(this ClaimsPrincipal principal)
{ {
return principal.Claims.FirstOrDefault(x => x.Type == OpenIdClaims.PreferredUserName)?.Value; return principal.Claims.FirstOrDefault(x => x.Type == OpenIdClaims.PreferredUserName)?.Value;

11
src/Squidex.Shared/Identity/ClaimsPrincipalExtensions.cs

@ -27,12 +27,19 @@ namespace Squidex.Shared.Identity
public static PermissionSet Permissions(this ClaimsPrincipal principal) public static PermissionSet Permissions(this ClaimsPrincipal principal)
{ {
return new PermissionSet(principal.Claims.Where(x => x.Type == SquidexClaimTypes.Permissions).Select(x => new Permission(x.Value))); return new PermissionSet(principal.Claims
.Where(x =>
x.Type == SquidexClaimTypes.Permissions ||
x.Type == SquidexClaimTypes.PermissionsClient)
.Select(x => new Permission(x.Value)));
} }
public static IEnumerable<Claim> GetSquidexClaims(this ClaimsPrincipal principal) public static IEnumerable<Claim> GetSquidexClaims(this ClaimsPrincipal principal)
{ {
return principal.Claims.Where(c => c.Type.StartsWith(SquidexClaimTypes.Prefix, StringComparison.Ordinal)); return principal.Claims
.Where(x =>
x.Type.StartsWith(SquidexClaimTypes.Prefix, StringComparison.Ordinal) ||
x.Type.StartsWith(SquidexClaimTypes.PrefixClient, StringComparison.Ordinal));
} }
} }
} }

4
src/Squidex.Shared/Identity/SquidexClaimTypes.cs

@ -21,8 +21,12 @@ namespace Squidex.Shared.Identity
public static readonly string Permissions = "urn:squidex:permissions"; public static readonly string Permissions = "urn:squidex:permissions";
public static readonly string PermissionsClient = "client_urn:squidex:permissions";
public static readonly string Prefix = "urn:squidex:"; public static readonly string Prefix = "urn:squidex:";
public static readonly string PrefixClient = "client_urn:squidex:";
public static readonly string PictureUrlStore = "store"; public static readonly string PictureUrlStore = "store";
} }
} }

6
src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs

@ -57,12 +57,12 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ApiCosts(0)] [ApiCosts(0)]
public async Task<IActionResult> GetApps() public async Task<IActionResult> GetApps()
{ {
var userId = HttpContext.User.OpenIdSubject(); var userOrClientId = HttpContext.User.UserOrClientId();
var userPermissions = HttpContext.User.Permissions(); var userPermissions = HttpContext.User.Permissions();
var entities = await appProvider.GetUserApps(userId, userPermissions); var entities = await appProvider.GetUserApps(userOrClientId, userPermissions);
var response = entities.ToArray(a => AppDto.FromApp(a, userId, userPermissions, appPlansProvider)); var response = entities.ToArray(a => AppDto.FromApp(a, userOrClientId, userPermissions, appPlansProvider));
Response.Headers[HeaderNames.ETag] = response.ToManyEtag(); Response.Headers[HeaderNames.ETag] = response.ToManyEtag();

4
src/Squidex/Areas/Api/Controllers/Backups/RestoreController.cs

@ -43,7 +43,7 @@ namespace Squidex.Areas.Api.Controllers.Backups
[ApiPermission(Permissions.AdminRestoreRead)] [ApiPermission(Permissions.AdminRestoreRead)]
public async Task<IActionResult> GetJob() public async Task<IActionResult> GetJob()
{ {
var restoreGrain = grainFactory.GetGrain<IRestoreGrain>(User.OpenIdSubject()); var restoreGrain = grainFactory.GetGrain<IRestoreGrain>(User.UserOrClientId());
var job = await restoreGrain.GetJobAsync(); var job = await restoreGrain.GetJobAsync();
@ -69,7 +69,7 @@ namespace Squidex.Areas.Api.Controllers.Backups
[ApiPermission(Permissions.AdminRestoreCreate)] [ApiPermission(Permissions.AdminRestoreCreate)]
public async Task<IActionResult> PostRestore([FromBody] RestoreRequest request) public async Task<IActionResult> PostRestore([FromBody] RestoreRequest request)
{ {
var restoreGrain = grainFactory.GetGrain<IRestoreGrain>(User.OpenIdSubject()); var restoreGrain = grainFactory.GetGrain<IRestoreGrain>(User.UserOrClientId());
await restoreGrain.RestoreAsync(request.Url, request.Name); await restoreGrain.RestoreAsync(request.Url, request.Name);

40
src/Squidex/Areas/IdentityServer/Config/LazyClientStore.cs

@ -7,6 +7,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using IdentityServer4; using IdentityServer4;
using IdentityServer4.Models; using IdentityServer4.Models;
@ -17,6 +18,8 @@ using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Pipeline; using Squidex.Pipeline;
using Squidex.Shared;
using Squidex.Shared.Identity;
namespace Squidex.Areas.IdentityServer.Config namespace Squidex.Areas.IdentityServer.Config
{ {
@ -25,14 +28,17 @@ namespace Squidex.Areas.IdentityServer.Config
private readonly IAppProvider appProvider; private readonly IAppProvider appProvider;
private readonly Dictionary<string, Client> staticClients = new Dictionary<string, Client>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, Client> staticClients = new Dictionary<string, Client>(StringComparer.OrdinalIgnoreCase);
public LazyClientStore(IOptions<MyUrlsOptions> urlsOptions, IAppProvider appProvider) public LazyClientStore(
IOptions<MyUrlsOptions> urlsOptions,
IOptions<MyIdentityOptions> identityOptions,
IAppProvider appProvider)
{ {
Guard.NotNull(urlsOptions, nameof(urlsOptions)); Guard.NotNull(urlsOptions, nameof(urlsOptions));
Guard.NotNull(appProvider, nameof(appProvider)); Guard.NotNull(appProvider, nameof(appProvider));
this.appProvider = appProvider; this.appProvider = appProvider;
CreateStaticClients(urlsOptions); CreateStaticClients(urlsOptions, identityOptions);
} }
public async Task<Client> FindClientByIdAsync(string clientId) public async Task<Client> FindClientByIdAsync(string clientId)
@ -83,15 +89,15 @@ namespace Squidex.Areas.IdentityServer.Config
}; };
} }
private void CreateStaticClients(IOptions<MyUrlsOptions> urlsOptions) private void CreateStaticClients(IOptions<MyUrlsOptions> urlsOptions, IOptions<MyIdentityOptions> identityOptions)
{ {
foreach (var client in CreateStaticClients(urlsOptions.Value)) foreach (var client in CreateStaticClients(urlsOptions.Value, identityOptions.Value))
{ {
staticClients[client.ClientId] = client; staticClients[client.ClientId] = client;
} }
} }
private static IEnumerable<Client> CreateStaticClients(MyUrlsOptions urlsOptions) private static IEnumerable<Client> CreateStaticClients(MyUrlsOptions urlsOptions, MyIdentityOptions identityOptions)
{ {
var frontendId = Constants.FrontendClient; var frontendId = Constants.FrontendClient;
@ -150,6 +156,30 @@ namespace Squidex.Areas.IdentityServer.Config
}, },
RequireConsent = false RequireConsent = false
}; };
if (identityOptions.IsAdminClientConfigured())
{
var id = identityOptions.AdminClientId;
yield return new Client
{
ClientId = id,
ClientName = id,
ClientSecrets = new List<Secret> { new Secret(identityOptions.AdminClientSecret.Sha256()) },
AccessTokenLifetime = (int)TimeSpan.FromDays(30).TotalSeconds,
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes = new List<string>
{
Constants.ApiScope,
Constants.RoleScope,
Constants.PermissionsScope
},
Claims = new List<Claim>
{
new Claim(SquidexClaimTypes.Permissions, Permissions.Admin)
}
};
}
} }
} }
} }

9
src/Squidex/Config/Logging.cs

@ -5,6 +5,8 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
#define LOG_ALL_IDENTITY_SERVER_NONE
using System; using System;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -40,7 +42,12 @@ namespace Squidex.Config
{ {
return level > LogLevel.Information; return level > LogLevel.Information;
} }
#if LOG_ALL_IDENTITY_SERVER
if (category.StartsWith("IdentityServer4.", StringComparison.OrdinalIgnoreCase))
{
return true;
}
#endif
return level >= LogLevel.Information; return level >= LogLevel.Information;
}); });
} }

9
src/Squidex/Config/MyIdentityOptions.cs

@ -13,6 +13,10 @@ namespace Squidex.Config
public string AdminPassword { get; set; } public string AdminPassword { get; set; }
public string AdminClientId { get; set; }
public string AdminClientSecret { get; set; }
public string GithubClient { get; set; } public string GithubClient { get; set; }
public string GithubSecret { get; set; } public string GithubSecret { get; set; }
@ -48,6 +52,11 @@ namespace Squidex.Config
return !string.IsNullOrWhiteSpace(AdminEmail) && !string.IsNullOrWhiteSpace(AdminPassword); return !string.IsNullOrWhiteSpace(AdminEmail) && !string.IsNullOrWhiteSpace(AdminPassword);
} }
public bool IsAdminClientConfigured()
{
return !string.IsNullOrWhiteSpace(AdminClientId) && !string.IsNullOrWhiteSpace(AdminClientSecret);
}
public bool IsOidcConfigured() public bool IsOidcConfigured()
{ {
return !string.IsNullOrWhiteSpace(OidcAuthority) && !string.IsNullOrWhiteSpace(OidcClient) && !string.IsNullOrWhiteSpace(OidcSecret); return !string.IsNullOrWhiteSpace(OidcAuthority) && !string.IsNullOrWhiteSpace(OidcClient) && !string.IsNullOrWhiteSpace(OidcSecret);

9
src/Squidex/Pipeline/Extensions.cs

@ -29,12 +29,17 @@ namespace Squidex.Pipeline
{ {
var parts = clientId.Split(':', '~'); var parts = clientId.Split(':', '~');
if (parts.Length != 2) if (parts.Length == 1)
{ {
return (null, null); return (null, parts[0]);
} }
if (parts.Length == 2)
{
return (parts[0], parts[1]); return (parts[0], parts[1]);
} }
return (null, null);
}
} }
} }

10
src/Squidex/appsettings.json

@ -263,6 +263,16 @@
* Enable password auth. Set this to false if you want to disable local login, leaving only 3rd party login options. * Enable password auth. Set this to false if you want to disable local login, leaving only 3rd party login options.
*/ */
"allowPasswordAuth": true, "allowPasswordAuth": true,
/*
* Initial admin user.
*/
"adminEmail": "",
"adminPassword": "",
/*
* Client with all admin permissions.
*/
"adminClientId": "",
"adminClientSecret": "",
/* /*
* Settings for Google auth (keep empty to disable). * Settings for Google auth (keep empty to disable).
*/ */

Loading…
Cancel
Save