Skip to content

Commit b1e447d

Browse files
authored
Merge pull request #19371 from umbraco/v16/merge-16-release-to-main
Merged from release/16.0 to main branch
2 parents 7572ea3 + df11e43 commit b1e447d

File tree

348 files changed

+5003
-1377
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

348 files changed

+5003
-1377
lines changed

build/azure-pipelines.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,33 @@ stages:
432432
displayName: Start SQL Server Docker image (Linux)
433433
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
434434

435+
- powershell: |
436+
$maxAttempts = 12
437+
$attempt = 0
438+
$status = ""
439+
440+
while (($status -ne 'running') -and ($attempt -lt $maxAttempts)) {
441+
Start-Sleep -Seconds 5
442+
# 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.
443+
$status = docker inspect -f '{{.State.Status}}' mssql
444+
445+
if ($status -ne 'running') {
446+
Write-Host "Waiting for SQL Server to be ready... Attempt $($attempt + 1)"
447+
$attempt++
448+
}
449+
}
450+
451+
if ($status -eq 'running') {
452+
Write-Host "SQL Server container is running"
453+
docker ps -a
454+
} else {
455+
Write-Host "SQL Server did not become ready in time. Last known status: $status"
456+
docker logs mssql
457+
exit 1
458+
}
459+
displayName: Wait for SQL Server to be ready (Linux)
460+
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
461+
435462
- pwsh: SqlLocalDB start MSSQLLocalDB
436463
displayName: Start SQL Server LocalDB (Windows)
437464
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))

build/nightly-E2E-test-pipelines.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,17 +300,17 @@ stages:
300300
testCommand: "npm run testSqlite -- --shard=1/3"
301301
vmImage: "ubuntu-latest"
302302
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
303-
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
303+
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
304304
LinuxPart2Of3:
305305
testCommand: "npm run testSqlite -- --shard=2/3"
306306
vmImage: "ubuntu-latest"
307307
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
308-
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
308+
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
309309
LinuxPart3Of3:
310310
testCommand: "npm run testSqlite -- --shard=3/3"
311311
vmImage: "ubuntu-latest"
312312
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
313-
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True"
313+
CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);Encrypt=True;TrustServerCertificate=True"
314314
WindowsPart1Of3:
315315
vmImage: "windows-latest"
316316
testCommand: "npm run testSqlite -- --shard=1/3"

src/Umbraco.Cms.Api.Management/Controllers/Document/CopyDocumentController.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Asp.Versioning;
1+
using Asp.Versioning;
22
using Microsoft.AspNetCore.Authorization;
33
using Microsoft.AspNetCore.Http;
44
using Microsoft.AspNetCore.Mvc;
@@ -42,12 +42,16 @@ public async Task<IActionResult> Copy(
4242
Guid id,
4343
CopyDocumentRequestModel copyDocumentRequestModel)
4444
{
45-
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
45+
AuthorizationResult sourceAuthorizationResult = await _authorizationService.AuthorizeResourceAsync(
4646
User,
47-
ContentPermissionResource.WithKeys(ActionCopy.ActionLetter, new[] { copyDocumentRequestModel.Target?.Id, id }),
47+
ContentPermissionResource.WithKeys(ActionCopy.ActionLetter, [id]),
48+
AuthorizationPolicies.ContentPermissionByResource);
49+
AuthorizationResult destinationAuthorizationResult = await _authorizationService.AuthorizeResourceAsync(
50+
User,
51+
ContentPermissionResource.WithKeys(ActionNew.ActionLetter, [copyDocumentRequestModel.Target?.Id]),
4852
AuthorizationPolicies.ContentPermissionByResource);
4953

50-
if (!authorizationResult.Succeeded)
54+
if (sourceAuthorizationResult.Succeeded is false || destinationAuthorizationResult.Succeeded is false)
5155
{
5256
return Forbidden();
5357
}

src/Umbraco.Cms.Api.Management/Controllers/Document/Item/SearchDocumentItemController.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ public async Task<IActionResult> SearchWithTrashed(
2828
CancellationToken cancellationToken,
2929
string query,
3030
bool? trashed = null,
31+
string? culture = null,
3132
int skip = 0,
3233
int take = 100,
3334
Guid? parentId = null,
3435
[FromQuery] IEnumerable<Guid>? allowedDocumentTypes = null)
3536
{
36-
PagedModel<IEntitySlim> searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Document, query, parentId, allowedDocumentTypes, trashed, skip, take);
37+
PagedModel<IEntitySlim> searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Document, query, parentId, allowedDocumentTypes, trashed, culture, skip, take);
3738
var result = new PagedModel<DocumentItemResponseModel>
3839
{
3940
Items = searchResult.Items.OfType<IDocumentEntitySlim>().Select(_documentPresentationFactory.CreateItemResponseModel),

src/Umbraco.Cms.Api.Management/Controllers/Document/MoveDocumentController.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Asp.Versioning;
1+
using Asp.Versioning;
22
using Microsoft.AspNetCore.Authorization;
33
using Microsoft.AspNetCore.Http;
44
using Microsoft.AspNetCore.Mvc;
@@ -39,6 +39,20 @@ public MoveDocumentController(
3939
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
4040
public async Task<IActionResult> Move(CancellationToken cancellationToken, Guid id, MoveDocumentRequestModel moveDocumentRequestModel)
4141
{
42+
AuthorizationResult sourceAuthorizationResult = await _authorizationService.AuthorizeResourceAsync(
43+
User,
44+
ContentPermissionResource.WithKeys(ActionMove.ActionLetter, [id]),
45+
AuthorizationPolicies.ContentPermissionByResource);
46+
AuthorizationResult destinationAuthorizationResult = await _authorizationService.AuthorizeResourceAsync(
47+
User,
48+
ContentPermissionResource.WithKeys(ActionNew.ActionLetter, [moveDocumentRequestModel.Target?.Id]),
49+
AuthorizationPolicies.ContentPermissionByResource);
50+
51+
if (sourceAuthorizationResult.Succeeded is false || destinationAuthorizationResult.Succeeded is false)
52+
{
53+
return Forbidden();
54+
}
55+
4256
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
4357
User,
4458
ContentPermissionResource.WithKeys(ActionMove.ActionLetter, new[] { moveDocumentRequestModel.Target?.Id, id }),

src/Umbraco.Cms.Api.Management/Controllers/Media/Item/SearchMediaItemController.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,20 @@ public SearchMediaItemController(IIndexedEntitySearchService indexedEntitySearch
2121
_mediaPresentationFactory = mediaPresentationFactory;
2222
}
2323

24-
[NonAction]
25-
[Obsolete("Scheduled to be removed in v16, use the non obsoleted method instead")]
26-
public Task<IActionResult> SearchFromParentWithAllowedTypes(CancellationToken cancellationToken, string query, int skip = 0, int take = 100, Guid? parentId = null, [FromQuery]IEnumerable<Guid>? allowedMediaTypes = null)
27-
=> SearchFromParentWithAllowedTypes(cancellationToken, query, null, skip, take, parentId);
28-
2924
[HttpGet("search")]
3025
[MapToApiVersion("1.0")]
3126
[ProducesResponseType(typeof(PagedModel<MediaItemResponseModel>), StatusCodes.Status200OK)]
32-
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)
27+
public async Task<IActionResult> SearchFromParentWithAllowedTypes(
28+
CancellationToken cancellationToken,
29+
string query,
30+
bool? trashed = null,
31+
string? culture = null,
32+
int skip = 0,
33+
int take = 100,
34+
Guid? parentId = null,
35+
[FromQuery]IEnumerable<Guid>? allowedMediaTypes = null)
3336
{
34-
PagedModel<IEntitySlim> searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Media, query, parentId, allowedMediaTypes, trashed, skip, take);
37+
PagedModel<IEntitySlim> searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Media, query, parentId, allowedMediaTypes, trashed, culture, skip, take);
3538
var result = new PagedModel<MediaItemResponseModel>
3639
{
3740
Items = searchResult.Items.OfType<IMediaEntitySlim>().Select(_mediaPresentationFactory.CreateItemResponseModel),

src/Umbraco.Cms.Api.Management/Controllers/Segment/AllSegmentController.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
namespace Umbraco.Cms.Api.Management.Controllers.Segment;
1515

1616
[ApiVersion("1.0")]
17-
[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
1817
public class AllSegmentController : SegmentControllerBase
1918
{
2019
private readonly ISegmentService _segmentService;

src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Microsoft.Extensions.DependencyInjection.Extensions;
44
using Microsoft.Extensions.Logging;
55
using Microsoft.Extensions.Options;
6+
using Umbraco.Cms.Api.Management.NotificationHandlers;
67
using Umbraco.Cms.Api.Management.Security;
78
using Umbraco.Cms.Api.Management.Services;
89
using Umbraco.Cms.Api.Management.Telemetry;
@@ -12,6 +13,7 @@
1213
using Umbraco.Cms.Core.Events;
1314
using Umbraco.Cms.Core.Mapping;
1415
using Umbraco.Cms.Core.Net;
16+
using Umbraco.Cms.Core.Notifications;
1517
using Umbraco.Cms.Core.Persistence.Repositories;
1618
using Umbraco.Cms.Core.Scoping;
1719
using Umbraco.Cms.Core.Security;
@@ -74,6 +76,9 @@ public static IUmbracoBuilder AddBackOfficeIdentity(this IUmbracoBuilder builder
7476

7577
services.AddScoped<IBackOfficeExternalLoginService, BackOfficeExternalLoginService>();
7678

79+
// Register a notification handler to interrogate the registered external login providers at startup.
80+
builder.AddNotificationHandler<UmbracoApplicationStartingNotification, ExternalLoginProviderStartupHandler>();
81+
7782
return builder;
7883
}
7984

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Umbraco.Cms.Api.Management.Security;
2+
using Umbraco.Cms.Core;
3+
using Umbraco.Cms.Core.Events;
4+
using Umbraco.Cms.Core.Notifications;
5+
using Umbraco.Cms.Core.Services;
6+
using Umbraco.Cms.Core.Sync;
7+
8+
namespace Umbraco.Cms.Api.Management.NotificationHandlers;
9+
10+
/// <summary>
11+
/// Invalidates backoffice sessions and clears external logins for removed providers if the external login
12+
/// provider setup has changed.
13+
/// </summary>
14+
internal sealed class ExternalLoginProviderStartupHandler : INotificationHandler<UmbracoApplicationStartingNotification>
15+
{
16+
private readonly IBackOfficeExternalLoginProviders _backOfficeExternalLoginProviders;
17+
private readonly IRuntimeState _runtimeState;
18+
private readonly IServerRoleAccessor _serverRoleAccessor;
19+
20+
/// <summary>
21+
/// Initializes a new instance of the <see cref="ExternalLoginProviderStartupHandler"/> class.
22+
/// </summary>
23+
public ExternalLoginProviderStartupHandler(
24+
IBackOfficeExternalLoginProviders backOfficeExternalLoginProviders,
25+
IRuntimeState runtimeState,
26+
IServerRoleAccessor serverRoleAccessor)
27+
{
28+
_backOfficeExternalLoginProviders = backOfficeExternalLoginProviders;
29+
_runtimeState = runtimeState;
30+
_serverRoleAccessor = serverRoleAccessor;
31+
}
32+
33+
/// <inheritdoc/>
34+
public void Handle(UmbracoApplicationStartingNotification notification)
35+
{
36+
if (_runtimeState.Level != RuntimeLevel.Run ||
37+
_serverRoleAccessor.CurrentServerRole == ServerRole.Subscriber)
38+
{
39+
return;
40+
}
41+
42+
_backOfficeExternalLoginProviders.InvalidateSessionsIfExternalLoginProvidersChanged();
43+
}
44+
}

src/Umbraco.Cms.Api.Management/OpenApi.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10159,6 +10159,13 @@
1015910159
"type": "boolean"
1016010160
}
1016110161
},
10162+
{
10163+
"name": "culture",
10164+
"in": "query",
10165+
"schema": {
10166+
"type": "string"
10167+
}
10168+
},
1016210169
{
1016310170
"name": "skip",
1016410171
"in": "query",
@@ -15918,6 +15925,13 @@
1591815925
"type": "boolean"
1591915926
}
1592015927
},
15928+
{
15929+
"name": "culture",
15930+
"in": "query",
15931+
"schema": {
15932+
"type": "string"
15933+
}
15934+
},
1592115935
{
1592215936
"name": "skip",
1592315937
"in": "query",

0 commit comments

Comments
 (0)