Skip to content

return always PickedColor type | instance #19347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
103631d
V15 QA Updated the build stage to align with the azure pipelines yml …
nhudinh0309 May 6, 2025
b106305
V15 QA Added acceptance tests for tiptap style select (#19234)
nhudinh0309 May 6, 2025
b01def0
V15 QA added a fix for flaky integration tests run on SQL Server Linu…
andr317c May 8, 2025
2754780
V15 QA complex block grid test (#18347)
andr317c May 8, 2025
822cfe9
Batch item rest requests (#19233)
madsrasmussen May 8, 2025
e9a5daf
Fix #19221 (#19254)
nielslyngsoe May 8, 2025
367a13b
Correct the display of pending package migrations (#19276)
AndyButland May 8, 2025
a463c99
Feature: ability to drag across tabs (#19183)
nielslyngsoe May 8, 2025
713d5ef
setup context tokens to support menu structure workspace (#19172)
nielslyngsoe May 9, 2025
f7bee37
Enforce: allowEditInvariantFromNonDefault configuration in the Backof…
madsrasmussen May 9, 2025
3a39728
hotfix: #19230 (#19285)
nielslyngsoe May 9, 2025
a8f58d8
Simplify descriptions, labels and notification messages for publishin…
madsrasmussen May 9, 2025
1fc8b4f
Allow configuring collection no-items text via manifest or attribute …
nathanwoulfe May 9, 2025
f772c26
Improve active state check for menu and tree item (#19281)
madsrasmussen May 9, 2025
49715d6
Forward port of #19045 (Added custom RichTextRegexValidator to valida…
Migaroez May 9, 2025
0e73c54
implement label for entity actions bundle (#19242)
nielslyngsoe May 9, 2025
3493d9f
align label with other create options (#19210)
madsrasmussen May 9, 2025
23df7f1
Allow selection of unpublished documents in link picker and align dis…
AndyButland May 12, 2025
b13eb8a
V15 Added second level block acceptance tests (#19264)
andr317c May 12, 2025
b0f3009
Authorize copy and move destination for the create granular permissio…
AndyButland May 13, 2025
81ae9f4
fix: turns off autocomplete for the main entity name field
iOvergaard May 13, 2025
f784f62
Deprecate `AuditService.Write()` method (#19306)
lauraneto May 13, 2025
54f50e0
Allow segment access to editors with access to documents, not only se…
AndyButland May 13, 2025
8878bda
Close dropdown after executed action workaround (#19305)
madsrasmussen May 13, 2025
2d5e34b
V16 QA added a fix for a failing language test (#19311)
andr317c May 13, 2025
a84c114
hotfix: #19299 (#19315)
nielslyngsoe May 14, 2025
6fd6319
Ensures cultures set on content are correctly cased (#19290)
AndyButland May 14, 2025
d239849
Include property aliases in compatible composition check (#19277)
madsrasmussen May 14, 2025
41bf741
V16 QA Fixing the failing E2E tests (#19318)
nhudinh0309 May 14, 2025
012959d
Bump version
nikolajlauridsen May 14, 2025
5e212f7
hotfix: remove current user action look and color (#19324)
nielslyngsoe May 15, 2025
8bc3de1
chore: removes unused import to fix lint errors
iOvergaard May 15, 2025
df56f19
Fix Allow Edit Invariant config in split view (#19320)
madsrasmussen May 16, 2025
28fc817
Tiptap Media Picker: ImageSharp HMAC support (#19333)
leekelleher May 16, 2025
e959850
Introduce Variant Context (#19334)
madsrasmussen May 16, 2025
a8c19bf
return always PickedColor type | instance
idseefeld May 16, 2025
1fdeb4f
Add explicit Global Search extension point (#19348)
madsrasmussen May 19, 2025
3f10bd8
Added logging and try/catch around retrieval of references, so we don…
AndyButland May 19, 2025
343bb85
Invalidate external login session on removal of provider (16) (#19289)
AndyButland May 19, 2025
2c3af1b
V16 RC: Add more debug info to System Information (#19343)
iOvergaard May 19, 2025
8f17156
V16 RC: File upload errors should be shown to the user (#19344)
iOvergaard May 19, 2025
dda69a1
Hotfix: do not validate the invariant-variant entry when we vary by c…
nielslyngsoe May 19, 2025
930a29f
Document URLs Data Resolver (#19316)
madsrasmussen May 19, 2025
3d8707e
V16 RC: HtmlImageSourceParser should not care for order of attributes…
iOvergaard May 19, 2025
afed7b0
await value for get methods (#19356)
nielslyngsoe May 19, 2025
57bec51
Add culture awareness to the backoffice search APIs (#19322)
kjac May 19, 2025
2dd2329
Resolve seen issue regarding destroyed validation context (#19359)
nielslyngsoe May 19, 2025
ea8ec4e
V16 QA Updated E2E nightly pipeline (#19355)
nhudinh0309 May 20, 2025
6a03239
Merge remote-tracking branch 'origin/v15/dev' into v16/merge-15-to-16…
andr317c May 20, 2025
fc5f207
Updated test as we do not have notifications for save/publish
andr317c May 20, 2025
66c997a
Skips test as there has been updates to notifications and we will nee…
andr317c May 20, 2025
ec6a38e
Fix for invalid state in JsonBlockValueConverter when an unused layou…
karl-sjogren May 20, 2025
7572ea3
Feature: Content Workspace Icon (#19292)
madsrasmussen May 20, 2025
e5d3927
Merge pull request #19368 from umbraco/v16/merge-15-to-16-release
andr317c May 20, 2025
df11e43
Merge remote-tracking branch 'origin/release/16.0' into v16/merge-16-…
andr317c May 20, 2025
b1e447d
Merge pull request #19371 from umbraco/v16/merge-16-release-to-main
andr317c May 20, 2025
f4515d7
V16/hotfix/set manifests when using create extension api (#19358)
nielslyngsoe May 20, 2025
ed8e941
Fix filter when selecting elements for block types (#19369)
madsrasmussen May 20, 2025
419625a
Optimize the member save as part of the member login process, by-pass…
AndyButland May 20, 2025
225fed5
Handle user id 0 (Unknown/System) when building content version respo…
lauraneto May 20, 2025
2b714d3
bump version
Zeegaan May 21, 2025
45593c6
Merge branch 'release/16.0'
nielslyngsoe May 21, 2025
c971375
Merge branch 'main' into v16/bugfix/19341-ModelsBuilder-generates-dif…
idseefeld May 21, 2025
bd33b19
Merge branch 'v17/dev' into v16/bugfix/19341-ModelsBuilder-generates-…
Matthew-Wise May 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
27 changes: 27 additions & 0 deletions build/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,33 @@ stages:
displayName: Start SQL Server Docker image (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))

- powershell: |
$maxAttempts = 12
$attempt = 0
$status = ""

while (($status -ne 'running') -and ($attempt -lt $maxAttempts)) {
Start-Sleep -Seconds 5
# We use the docker inspect command to check the status of the container. If the container is not running, we wait 5 seconds and try again. And if reaches 12 attempts, we fail the build.
$status = docker inspect -f '{{.State.Status}}' mssql

if ($status -ne 'running') {
Write-Host "Waiting for SQL Server to be ready... Attempt $($attempt + 1)"
$attempt++
}
}

if ($status -eq 'running') {
Write-Host "SQL Server container is running"
docker ps -a
} else {
Write-Host "SQL Server did not become ready in time. Last known status: $status"
docker logs mssql
exit 1
}
displayName: Wait for SQL Server to be ready (Linux)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))

- pwsh: SqlLocalDB start MSSQLLocalDB
displayName: Start SQL Server LocalDB (Windows)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
Expand Down
17 changes: 13 additions & 4 deletions build/nightly-E2E-test-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ schedules:
displayName: Daily midnight build
branches:
include:
- v14/dev
- v15/dev
- release/16.0
- main

parameters:
# Skipped due to DB locks
- name: sqliteAcceptanceTests
displayName: Run SQLite Acceptance Tests
type: boolean
default: false

variables:
nodeVersion: 20
solution: umbraco.sln
Expand Down Expand Up @@ -122,6 +129,8 @@ stages:
# E2E Tests
- job:
displayName: E2E Tests (SQLite)
# currently disabled due to DB locks randomly occuring.
condition: eq(${{parameters.sqliteAcceptanceTests}}, True)
timeoutInMinutes: 180
variables:
# Connection string
Expand Down Expand Up @@ -291,17 +300,17 @@ stages:
testCommand: "npm run testSqlite -- --shard=1/3"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
LinuxPart2Of3:
testCommand: "npm run testSqlite -- --shard=2/3"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
LinuxPart3Of3:
testCommand: "npm run testSqlite -- --shard=3/3"
vmImage: "ubuntu-latest"
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
WindowsPart1Of3:
vmImage: "windows-latest"
testCommand: "npm run testSqlite -- --shard=1/3"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
Expand Down Expand Up @@ -42,12 +42,16 @@ public async Task<IActionResult> Copy(
Guid id,
CopyDocumentRequestModel copyDocumentRequestModel)
{
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
AuthorizationResult sourceAuthorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.WithKeys(ActionCopy.ActionLetter, new[] { copyDocumentRequestModel.Target?.Id, id }),
ContentPermissionResource.WithKeys(ActionCopy.ActionLetter, [id]),
AuthorizationPolicies.ContentPermissionByResource);
AuthorizationResult destinationAuthorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.WithKeys(ActionNew.ActionLetter, [copyDocumentRequestModel.Target?.Id]),
AuthorizationPolicies.ContentPermissionByResource);

if (!authorizationResult.Succeeded)
if (sourceAuthorizationResult.Succeeded is false || destinationAuthorizationResult.Succeeded is false)
{
return Forbidden();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ public async Task<IActionResult> SearchWithTrashed(
CancellationToken cancellationToken,
string query,
bool? trashed = null,
string? culture = null,
int skip = 0,
int take = 100,
Guid? parentId = null,
[FromQuery] IEnumerable<Guid>? allowedDocumentTypes = null)
{
PagedModel<IEntitySlim> searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Document, query, parentId, allowedDocumentTypes, trashed, skip, take);
PagedModel<IEntitySlim> searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Document, query, parentId, allowedDocumentTypes, trashed, culture, skip, take);
var result = new PagedModel<DocumentItemResponseModel>
{
Items = searchResult.Items.OfType<IDocumentEntitySlim>().Select(_documentPresentationFactory.CreateItemResponseModel),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
Expand Down Expand Up @@ -39,6 +39,20 @@ public MoveDocumentController(
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<IActionResult> Move(CancellationToken cancellationToken, Guid id, MoveDocumentRequestModel moveDocumentRequestModel)
{
AuthorizationResult sourceAuthorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.WithKeys(ActionMove.ActionLetter, [id]),
AuthorizationPolicies.ContentPermissionByResource);
AuthorizationResult destinationAuthorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.WithKeys(ActionNew.ActionLetter, [moveDocumentRequestModel.Target?.Id]),
AuthorizationPolicies.ContentPermissionByResource);

if (sourceAuthorizationResult.Succeeded is false || destinationAuthorizationResult.Succeeded is false)
{
return Forbidden();
}

AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.WithKeys(ActionMove.ActionLetter, new[] { moveDocumentRequestModel.Target?.Id, id }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,20 @@
_mediaPresentationFactory = mediaPresentationFactory;
}

[NonAction]
[Obsolete("Scheduled to be removed in v16, use the non obsoleted method instead")]
public Task<IActionResult> SearchFromParentWithAllowedTypes(CancellationToken cancellationToken, string query, int skip = 0, int take = 100, Guid? parentId = null, [FromQuery]IEnumerable<Guid>? allowedMediaTypes = null)
=> SearchFromParentWithAllowedTypes(cancellationToken, query, null, skip, take, parentId);

[HttpGet("search")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(PagedModel<MediaItemResponseModel>), StatusCodes.Status200OK)]
public async Task<IActionResult> SearchFromParentWithAllowedTypes(CancellationToken cancellationToken, string query, bool? trashed = null, int skip = 0, int take = 100, Guid? parentId = null, [FromQuery]IEnumerable<Guid>? allowedMediaTypes = null)
public async Task<IActionResult> SearchFromParentWithAllowedTypes(
CancellationToken cancellationToken,
string query,
bool? trashed = null,
string? culture = null,
int skip = 0,
int take = 100,
Guid? parentId = null,
[FromQuery]IEnumerable<Guid>? allowedMediaTypes = null)
{
PagedModel<IEntitySlim> searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Media, query, parentId, allowedMediaTypes, trashed, skip, take);
PagedModel<IEntitySlim> searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Media, query, parentId, allowedMediaTypes, trashed, culture, skip, take);

Check notice on line 37 in src/Umbraco.Cms.Api.Management/Controllers/Media/Item/SearchMediaItemController.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v17/dev)

✅ No longer an issue: Excess Number of Function Arguments

SearchFromParentWithAllowedTypes is no longer above the threshold for number of arguments. This function has too many arguments, indicating a lack of encapsulation. Avoid adding more arguments.
var result = new PagedModel<MediaItemResponseModel>
{
Items = searchResult.Items.OfType<IMediaEntitySlim>().Select(_mediaPresentationFactory.CreateItemResponseModel),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
namespace Umbraco.Cms.Api.Management.Controllers.Segment;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
public class AllSegmentController : SegmentControllerBase
{
private readonly ISegmentService _segmentService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Api.Management.NotificationHandlers;
using Umbraco.Cms.Api.Management.Security;
using Umbraco.Cms.Api.Management.Services;
using Umbraco.Cms.Api.Management.Telemetry;
Expand All @@ -12,6 +13,7 @@
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Net;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Security;
Expand Down Expand Up @@ -74,6 +76,9 @@ public static IUmbracoBuilder AddBackOfficeIdentity(this IUmbracoBuilder builder

services.AddScoped<IBackOfficeExternalLoginService, BackOfficeExternalLoginService>();

// Register a notification handler to interrogate the registered external login providers at startup.
builder.AddNotificationHandler<UmbracoApplicationStartingNotification, ExternalLoginProviderStartupHandler>();

return builder;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Umbraco.Cms.Api.Management.ViewModels;
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Extensions;
Expand All @@ -19,17 +20,26 @@ public DocumentVersionPresentationFactory(
_userIdKeyResolver = userIdKeyResolver;
}

public async Task<DocumentVersionItemResponseModel> CreateAsync(ContentVersionMeta contentVersion) =>
new(
public async Task<DocumentVersionItemResponseModel> CreateAsync(ContentVersionMeta contentVersion)
{
ReferenceByIdModel userReference = contentVersion.UserId switch
{
Constants.Security.UnknownUserId => new ReferenceByIdModel(),
_ => new ReferenceByIdModel(await _userIdKeyResolver.GetAsync(contentVersion.UserId)),
};

return new DocumentVersionItemResponseModel(
contentVersion.VersionId.ToGuid(), // this is a magic guid since versions do not have keys in the DB
new ReferenceByIdModel(_entityService.GetKey(contentVersion.ContentId, UmbracoObjectTypes.Document).Result),
new ReferenceByIdModel(_entityService.GetKey(contentVersion.ContentTypeId, UmbracoObjectTypes.DocumentType)
.Result),
new ReferenceByIdModel(await _userIdKeyResolver.GetAsync(contentVersion.UserId)),
new ReferenceByIdModel(
_entityService.GetKey(contentVersion.ContentTypeId, UmbracoObjectTypes.DocumentType)
.Result),
userReference,
new DateTimeOffset(contentVersion.VersionDate),
contentVersion.CurrentPublishedVersion,
contentVersion.CurrentDraftVersion,
contentVersion.PreventCleanup);
}

public async Task<IEnumerable<DocumentVersionItemResponseModel>> CreateMultipleAsync(IEnumerable<ContentVersionMeta> contentVersions)
=> await CreateMultipleImplAsync(contentVersions).ToArrayAsync();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Umbraco.Cms.Api.Management.Security;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Sync;

namespace Umbraco.Cms.Api.Management.NotificationHandlers;

/// <summary>
/// Invalidates backoffice sessions and clears external logins for removed providers if the external login
/// provider setup has changed.
/// </summary>
internal sealed class ExternalLoginProviderStartupHandler : INotificationHandler<UmbracoApplicationStartingNotification>
{
private readonly IBackOfficeExternalLoginProviders _backOfficeExternalLoginProviders;
private readonly IRuntimeState _runtimeState;
private readonly IServerRoleAccessor _serverRoleAccessor;

/// <summary>
/// Initializes a new instance of the <see cref="ExternalLoginProviderStartupHandler"/> class.
/// </summary>
public ExternalLoginProviderStartupHandler(
IBackOfficeExternalLoginProviders backOfficeExternalLoginProviders,
IRuntimeState runtimeState,
IServerRoleAccessor serverRoleAccessor)
{
_backOfficeExternalLoginProviders = backOfficeExternalLoginProviders;
_runtimeState = runtimeState;
_serverRoleAccessor = serverRoleAccessor;
}

/// <inheritdoc/>
public void Handle(UmbracoApplicationStartingNotification notification)
{
if (_runtimeState.Level != RuntimeLevel.Run ||
_serverRoleAccessor.CurrentServerRole == ServerRole.Subscriber)
{
return;
}

_backOfficeExternalLoginProviders.InvalidateSessionsIfExternalLoginProvidersChanged();
}
}
14 changes: 14 additions & 0 deletions src/Umbraco.Cms.Api.Management/OpenApi.json
Original file line number Diff line number Diff line change
Expand Up @@ -10159,6 +10159,13 @@
"type": "boolean"
}
},
{
"name": "culture",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "skip",
"in": "query",
Expand Down Expand Up @@ -15918,6 +15925,13 @@
"type": "boolean"
}
},
{
"name": "culture",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "skip",
"in": "query",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using Microsoft.AspNetCore.Authentication;
using Umbraco.Cms.Core.Security;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Services;

namespace Umbraco.Cms.Api.Management.Security;

Expand All @@ -8,13 +12,37 @@ public class BackOfficeExternalLoginProviders : IBackOfficeExternalLoginProvider
{
private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
private readonly Dictionary<string, BackOfficeExternalLoginProvider> _externalLogins;
private readonly IKeyValueService _keyValueService;
private readonly IExternalLoginWithKeyService _externalLoginWithKeyService;
private readonly ILogger<BackOfficeExternalLoginProviders> _logger;

private const string ExternalLoginProvidersKey = "Umbraco.Cms.Web.BackOffice.Security.BackOfficeExternalLoginProviders";

[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
public BackOfficeExternalLoginProviders(
IEnumerable<BackOfficeExternalLoginProvider> externalLogins,
IAuthenticationSchemeProvider authenticationSchemeProvider)
: this(
externalLogins,
authenticationSchemeProvider,
StaticServiceProvider.Instance.GetRequiredService<IKeyValueService>(),
StaticServiceProvider.Instance.GetRequiredService<IExternalLoginWithKeyService>(),
StaticServiceProvider.Instance.GetRequiredService<ILogger<BackOfficeExternalLoginProviders>>())
{
}

public BackOfficeExternalLoginProviders(
IEnumerable<BackOfficeExternalLoginProvider> externalLogins,
IAuthenticationSchemeProvider authenticationSchemeProvider,
IKeyValueService keyValueService,
IExternalLoginWithKeyService externalLoginWithKeyService,
ILogger<BackOfficeExternalLoginProviders> logger)
{
_externalLogins = externalLogins.ToDictionary(x => x.AuthenticationType);
_authenticationSchemeProvider = authenticationSchemeProvider;
_keyValueService = keyValueService;
_externalLoginWithKeyService = externalLoginWithKeyService;
_logger = logger;
}

/// <inheritdoc />
Expand Down Expand Up @@ -60,4 +88,25 @@ public bool HasDenyLocalLogin()
var found = _externalLogins.Values.Where(x => x.Options.DenyLocalLogin).ToList();
return found.Count > 0;
}

/// <inheritdoc />
public void InvalidateSessionsIfExternalLoginProvidersChanged()
{
var previousExternalLoginProvidersValue = _keyValueService.GetValue(ExternalLoginProvidersKey);
var currentExternalLoginProvidersValue = string.Join("|", _externalLogins.Keys.OrderBy(key => key));

if ((previousExternalLoginProvidersValue ?? string.Empty) != currentExternalLoginProvidersValue)
{
_logger.LogWarning(
"The configured external login providers have changed. Existing backoffice sessions using the removed providers will be invalidated and external login data removed.");

_externalLoginWithKeyService.PurgeLoginsForRemovedProviders(_externalLogins.Keys);

_keyValueService.SetValue(ExternalLoginProvidersKey, currentExternalLoginProvidersValue);
}
else if (previousExternalLoginProvidersValue is null)
{
_keyValueService.SetValue(ExternalLoginProvidersKey, string.Empty);
}
}
}
Loading
Loading