* Add in-browser postman callback url * Added a jsonconverter for property editor configuration boolean values that are persisted as numbers and serialized as strings... --------- Co-authored-by: Sven Geusens <sge@umbraco.dk>
143 lines
5.8 KiB
C#
143 lines
5.8 KiB
C#
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Options;
|
|
using OpenIddict.Abstractions;
|
|
using Umbraco.Cms.Core;
|
|
using Umbraco.Cms.Core.Models.Configuration;
|
|
using Umbraco.Cms.Core.Services;
|
|
using Umbraco.Cms.Infrastructure.Security;
|
|
|
|
namespace Umbraco.Cms.Api.Management.Security;
|
|
|
|
public class BackOfficeApplicationManager : IBackOfficeApplicationManager
|
|
{
|
|
private readonly IOpenIddictApplicationManager _applicationManager;
|
|
private readonly IWebHostEnvironment _webHostEnvironment;
|
|
private readonly IRuntimeState _runtimeState;
|
|
private readonly Uri? _backOfficeHost;
|
|
private readonly string? _authorizeCallbackPathName;
|
|
|
|
public BackOfficeApplicationManager(
|
|
IOpenIddictApplicationManager applicationManager,
|
|
IWebHostEnvironment webHostEnvironment,
|
|
IOptions<NewBackOfficeSettings> securitySettings,
|
|
IRuntimeState runtimeState)
|
|
{
|
|
_applicationManager = applicationManager;
|
|
_webHostEnvironment = webHostEnvironment;
|
|
_runtimeState = runtimeState;
|
|
_backOfficeHost = securitySettings.Value.BackOfficeHost;
|
|
_authorizeCallbackPathName = securitySettings.Value.AuthorizeCallbackPathName;
|
|
}
|
|
|
|
public async Task EnsureBackOfficeApplicationAsync(Uri backOfficeUrl, CancellationToken cancellationToken = default)
|
|
{
|
|
if (_runtimeState.Level < RuntimeLevel.Run)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (backOfficeUrl.IsAbsoluteUri is false)
|
|
{
|
|
throw new ArgumentException($"Expected an absolute URL, got: {backOfficeUrl}", nameof(backOfficeUrl));
|
|
}
|
|
|
|
await CreateOrUpdate(
|
|
new OpenIddictApplicationDescriptor
|
|
{
|
|
DisplayName = "Umbraco back-office access",
|
|
ClientId = Constants.OauthClientIds.BackOffice,
|
|
RedirectUris =
|
|
{
|
|
CallbackUrlFor(_backOfficeHost ?? backOfficeUrl, _authorizeCallbackPathName ?? "/umbraco")
|
|
},
|
|
Type = OpenIddictConstants.ClientTypes.Public,
|
|
Permissions =
|
|
{
|
|
OpenIddictConstants.Permissions.Endpoints.Authorization,
|
|
OpenIddictConstants.Permissions.Endpoints.Token,
|
|
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
|
|
OpenIddictConstants.Permissions.GrantTypes.RefreshToken,
|
|
OpenIddictConstants.Permissions.ResponseTypes.Code
|
|
}
|
|
},
|
|
cancellationToken);
|
|
|
|
if (_webHostEnvironment.IsProduction())
|
|
{
|
|
await Delete(Constants.OauthClientIds.Swagger, cancellationToken);
|
|
await Delete(Constants.OauthClientIds.Postman, cancellationToken);
|
|
}
|
|
else
|
|
{
|
|
await CreateOrUpdate(
|
|
new OpenIddictApplicationDescriptor
|
|
{
|
|
DisplayName = "Umbraco Swagger access",
|
|
ClientId = Constants.OauthClientIds.Swagger,
|
|
RedirectUris =
|
|
{
|
|
CallbackUrlFor(backOfficeUrl, "/umbraco/swagger/oauth2-redirect.html")
|
|
},
|
|
Type = OpenIddictConstants.ClientTypes.Public,
|
|
Permissions =
|
|
{
|
|
OpenIddictConstants.Permissions.Endpoints.Authorization,
|
|
OpenIddictConstants.Permissions.Endpoints.Token,
|
|
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
|
|
OpenIddictConstants.Permissions.ResponseTypes.Code
|
|
}
|
|
},
|
|
cancellationToken);
|
|
|
|
await CreateOrUpdate(
|
|
new OpenIddictApplicationDescriptor
|
|
{
|
|
DisplayName = "Umbraco Postman access",
|
|
ClientId = Constants.OauthClientIds.Postman,
|
|
RedirectUris =
|
|
{
|
|
new Uri("https://oauth.pstmn.io/v1/callback"), new Uri("https://oauth.pstmn.io/v1/browser-callback")
|
|
},
|
|
Type = OpenIddictConstants.ClientTypes.Public,
|
|
Permissions =
|
|
{
|
|
OpenIddictConstants.Permissions.Endpoints.Authorization,
|
|
OpenIddictConstants.Permissions.Endpoints.Token,
|
|
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
|
|
OpenIddictConstants.Permissions.ResponseTypes.Code
|
|
}
|
|
},
|
|
cancellationToken);
|
|
}
|
|
}
|
|
|
|
private async Task CreateOrUpdate(OpenIddictApplicationDescriptor clientDescriptor, CancellationToken cancellationToken)
|
|
{
|
|
var identifier = clientDescriptor.ClientId ??
|
|
throw new ApplicationException($"ClientId is missing for application: {clientDescriptor.DisplayName ?? "(no name)"}");
|
|
var client = await _applicationManager.FindByClientIdAsync(identifier, cancellationToken);
|
|
if (client is null)
|
|
{
|
|
await _applicationManager.CreateAsync(clientDescriptor, cancellationToken);
|
|
}
|
|
else
|
|
{
|
|
await _applicationManager.UpdateAsync(client, clientDescriptor, cancellationToken);
|
|
}
|
|
}
|
|
|
|
private async Task Delete(string identifier, CancellationToken cancellationToken)
|
|
{
|
|
var client = await _applicationManager.FindByClientIdAsync(identifier, cancellationToken);
|
|
if (client is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
await _applicationManager.DeleteAsync(client, cancellationToken);
|
|
}
|
|
|
|
private static Uri CallbackUrlFor(Uri url, string relativePath) => new Uri( $"{url.GetLeftPart(UriPartial.Authority)}/{relativePath.TrimStart(Core.Constants.CharArrays.ForwardSlash)}");
|
|
}
|