Skip to content

Commit 3157601

Browse files
committed
Merge branch 'v15/dev' into contrib
2 parents 09b3bd1 + 3829b09 commit 3157601

File tree

471 files changed

+18007
-2709
lines changed

Some content is hidden

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

471 files changed

+18007
-2709
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<NeutralLanguage>en-US</NeutralLanguage>
1515
<Nullable>enable</Nullable>
1616
<WarningsAsErrors>nullable</WarningsAsErrors>
17-
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
17+
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
1818
<ImplicitUsings>enable</ImplicitUsings>
1919
<GenerateDocumentationFile>true</GenerateDocumentationFile>
2020
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.0" />
4646
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
4747
<PackageVersion Include="Dazinator.Extensions.FileProviders" Version="2.0.0" />
48-
<PackageVersion Include="Examine" Version="3.5.0" />
48+
<PackageVersion Include="Examine" Version="3.6.0" />
4949
<PackageVersion Include="Examine.Core" Version="3.5.0" />
5050
<PackageVersion Include="HtmlAgilityPack" Version="1.11.71" />
5151
<PackageVersion Include="JsonPatch.Net" Version="3.1.1" />

src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderAuthExtensions.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ private static void ConfigureOpenIddict(IUmbracoBuilder builder)
4646
Paths.BackOfficeApi.LogoutEndpoint.TrimStart(Constants.CharArrays.ForwardSlash))
4747
.SetRevocationEndpointUris(
4848
Paths.MemberApi.RevokeEndpoint.TrimStart(Constants.CharArrays.ForwardSlash),
49-
Paths.BackOfficeApi.RevokeEndpoint.TrimStart(Constants.CharArrays.ForwardSlash));
49+
Paths.BackOfficeApi.RevokeEndpoint.TrimStart(Constants.CharArrays.ForwardSlash))
50+
.SetUserInfoEndpointUris(
51+
Paths.MemberApi.UserinfoEndpoint.TrimStart(Constants.CharArrays.ForwardSlash));
5052

5153
// Enable authorization code flow with PKCE
5254
options
@@ -62,7 +64,8 @@ private static void ConfigureOpenIddict(IUmbracoBuilder builder)
6264
.UseAspNetCore()
6365
.EnableAuthorizationEndpointPassthrough()
6466
.EnableTokenEndpointPassthrough()
65-
.EnableEndSessionEndpointPassthrough();
67+
.EnableEndSessionEndpointPassthrough()
68+
.EnableUserInfoEndpointPassthrough();
6669

6770
// Enable reference tokens
6871
// - see https://documentation.openiddict.com/configuration/token-storage.html

src/Umbraco.Cms.Api.Common/Security/Paths.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public static class MemberApi
3131

3232
public static readonly string RevokeEndpoint = EndpointPath($"{EndpointTemplate}/revoke");
3333

34+
public static readonly string UserinfoEndpoint = EndpointPath($"{EndpointTemplate}/userinfo");
35+
3436
// NOTE: we're NOT using /api/v1.0/ here because it will clash with the Delivery API docs
3537
private static string EndpointPath(string relativePath) => $"/umbraco/delivery/api/v1/{relativePath}";
3638
}

src/Umbraco.Cms.Api.Delivery/Controllers/Content/ContentApiControllerBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace Umbraco.Cms.Api.Delivery.Controllers.Content;
1515
[ApiExplorerSettings(GroupName = "Content")]
1616
[LocalizeFromAcceptLanguageHeader]
1717
[ValidateStartItem]
18+
[AddVaryHeader]
1819
[OutputCache(PolicyName = Constants.DeliveryApi.OutputCache.ContentCachePolicy)]
1920
public abstract class ContentApiControllerBase : DeliveryApiControllerBase
2021
{
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Authorization;
3+
using Microsoft.AspNetCore.Mvc;
4+
using OpenIddict.Server.AspNetCore;
5+
using Umbraco.Cms.Api.Delivery.Routing;
6+
using Umbraco.Cms.Core.DeliveryApi;
7+
8+
namespace Umbraco.Cms.Api.Delivery.Controllers.Security;
9+
10+
[ApiVersion("1.0")]
11+
[ApiController]
12+
[VersionedDeliveryApiRoute(Common.Security.Paths.MemberApi.EndpointTemplate)]
13+
[ApiExplorerSettings(IgnoreApi = true)]
14+
[Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]
15+
public class CurrentMemberController : DeliveryApiControllerBase
16+
{
17+
private readonly ICurrentMemberClaimsProvider _currentMemberClaimsProvider;
18+
19+
public CurrentMemberController(ICurrentMemberClaimsProvider currentMemberClaimsProvider)
20+
=> _currentMemberClaimsProvider = currentMemberClaimsProvider;
21+
22+
[HttpGet("userinfo")]
23+
public async Task<IActionResult> Userinfo()
24+
{
25+
Dictionary<string, object> claims = await _currentMemberClaimsProvider.GetClaimsAsync();
26+
return Ok(claims);
27+
}
28+
}

src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public static IUmbracoBuilder AddDeliveryApi(this IUmbracoBuilder builder)
6161
builder.Services.AddSingleton<IApiMediaQueryService, ApiMediaQueryService>();
6262
builder.Services.AddTransient<IMemberApplicationManager, MemberApplicationManager>();
6363
builder.Services.AddTransient<IRequestMemberAccessService, RequestMemberAccessService>();
64+
builder.Services.AddTransient<ICurrentMemberClaimsProvider, CurrentMemberClaimsProvider>();
6465
builder.Services.AddScoped<IMemberClientCredentialsManager, MemberClientCredentialsManager>();
6566

6667
builder.Services.ConfigureOptions<ConfigureUmbracoDeliveryApiSwaggerGenOptions>();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.AspNetCore.Mvc.Filters;
2+
3+
namespace Umbraco.Cms.Api.Delivery.Filters;
4+
5+
public sealed class AddVaryHeaderAttribute : ActionFilterAttribute
6+
{
7+
private const string Vary = "Accept-Language, Preview, Start-Item";
8+
9+
public override void OnResultExecuting(ResultExecutingContext context)
10+
=> context.HttpContext.Response.Headers.Vary = context.HttpContext.Response.Headers.Vary.Count > 0
11+
? $"{context.HttpContext.Response.Headers.Vary}, {Vary}"
12+
: Vary;
13+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System.Reflection;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
using Asp.Versioning;
5+
using Microsoft.AspNetCore.Http;
6+
using Umbraco.Cms.Core.DeliveryApi;
7+
8+
namespace Umbraco.Cms.Api.Delivery.Json;
9+
10+
public abstract class DeliveryApiVersionAwareJsonConverterBase<T> : JsonConverter<T>
11+
{
12+
private readonly IHttpContextAccessor _httpContextAccessor;
13+
private readonly JsonConverter<T> _defaultConverter = (JsonConverter<T>)JsonSerializerOptions.Default.GetConverter(typeof(T));
14+
15+
public DeliveryApiVersionAwareJsonConverterBase(IHttpContextAccessor httpContextAccessor)
16+
=> _httpContextAccessor = httpContextAccessor;
17+
18+
/// <inheritdoc />
19+
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
20+
=> _defaultConverter.Read(ref reader, typeToConvert, options);
21+
22+
/// <inheritdoc />
23+
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
24+
{
25+
Type type = typeof(T);
26+
var apiVersion = GetApiVersion();
27+
28+
// Get the properties in the specified order
29+
PropertyInfo[] properties = type.GetProperties().OrderBy(GetPropertyOrder).ToArray();
30+
31+
writer.WriteStartObject();
32+
33+
foreach (PropertyInfo property in properties)
34+
{
35+
// Filter out properties based on the API version
36+
var include = apiVersion is null || ShouldIncludeProperty(property, apiVersion.Value);
37+
38+
if (include is false)
39+
{
40+
continue;
41+
}
42+
43+
var propertyName = property.Name;
44+
writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName);
45+
JsonSerializer.Serialize(writer, property.GetValue(value), options);
46+
}
47+
48+
writer.WriteEndObject();
49+
}
50+
51+
private int? GetApiVersion()
52+
{
53+
HttpContext? httpContext = _httpContextAccessor.HttpContext;
54+
ApiVersion? apiVersion = httpContext?.GetRequestedApiVersion();
55+
56+
return apiVersion?.MajorVersion;
57+
}
58+
59+
private int GetPropertyOrder(PropertyInfo prop)
60+
{
61+
var attribute = prop.GetCustomAttribute<JsonPropertyOrderAttribute>();
62+
return attribute?.Order ?? 0;
63+
}
64+
65+
/// <summary>
66+
/// Determines whether a property should be included based on version bounds.
67+
/// </summary>
68+
/// <param name="propertyInfo">The property info.</param>
69+
/// <param name="version">An integer representing an API version.</param>
70+
/// <returns><c>true</c> if the property should be included; otherwise, <c>false</c>.</returns>
71+
private bool ShouldIncludeProperty(PropertyInfo propertyInfo, int version)
72+
{
73+
var attribute = propertyInfo
74+
.GetCustomAttributes(typeof(IncludeInApiVersionAttribute), false)
75+
.FirstOrDefault();
76+
77+
if (attribute is not IncludeInApiVersionAttribute apiVersionAttribute)
78+
{
79+
return true; // No attribute means include the property
80+
}
81+
82+
// Check if the version is within the specified bounds
83+
var isWithinMinVersion = apiVersionAttribute.MinVersion.HasValue is false || version >= apiVersionAttribute.MinVersion.Value;
84+
var isWithinMaxVersion = apiVersionAttribute.MaxVersion.HasValue is false || version <= apiVersionAttribute.MaxVersion.Value;
85+
86+
return isWithinMinVersion && isWithinMaxVersion;
87+
}
88+
}

src/Umbraco.Cms.Api.Delivery/Querying/Filters/ContentTypeFilter.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Umbraco.Cms.Api.Delivery.Indexing.Filters;
22
using Umbraco.Cms.Core.DeliveryApi;
3-
using Umbraco.Extensions;
43

54
namespace Umbraco.Cms.Api.Delivery.Querying.Filters;
65

@@ -15,15 +14,15 @@ public bool CanHandle(string query)
1514
/// <inheritdoc/>
1615
public FilterOption BuildFilterOption(string filter)
1716
{
18-
var alias = filter.Substring(ContentTypeSpecifier.Length);
17+
var filterValue = filter.Substring(ContentTypeSpecifier.Length);
18+
var negate = filterValue.StartsWith('!');
19+
var aliases = filterValue.TrimStart('!').Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
1920

2021
return new FilterOption
2122
{
2223
FieldName = ContentTypeFilterIndexer.FieldName,
23-
Values = alias.IsNullOrWhiteSpace() == false
24-
? new[] { alias.TrimStart('!') }
25-
: Array.Empty<string>(),
26-
Operator = alias.StartsWith('!')
24+
Values = aliases,
25+
Operator = negate
2726
? FilterOperation.IsNot
2827
: FilterOperation.Is
2928
};

0 commit comments

Comments
 (0)