Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,19 @@ private static string CreateJwtToken(byte[] symmetricKey, string issuer, Persist

var subject = new ClaimsIdentity();
subject.AddClaim(new Claim(SessionClaimType, persistedToken.TokenId));
subject.AddClaims(roles.Select(r => new Claim(ClaimTypes.Role, r)));

// Add roles using both the standard schema URI (ClaimTypes.Role) for standards compliance
// and the legacy "role" claim type for backward compatibility with existing consumers
foreach (var role in roles)
{
subject.AddClaim(new Claim(ClaimTypes.Role, role));
subject.AddClaim(new Claim("role", role));
}

// Add deprecation notice for the legacy "role" claim format
subject.AddClaim(new Claim(
"dnn:deprecation:role",
"The role claim is deprecated. Use http://schemas.microsoft.com/ws/2008/06/identity/claims/role instead. The role claim will be removed in DNN v12.0.0."));

var notBefore = DateTime.UtcNow.AddMinutes(-ClockSkew);
var expires = persistedToken.TokenExpiry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,10 @@ public class LoginResultData
/// <summary>Gets or sets any error message.</summary>
[JsonIgnore]
public string Error { get; set; }

/// <summary>Gets deprecation warnings about the JWT token format (included in all responses).</summary>
[JsonProperty("deprecationNotice")]
public string DeprecationNotice =>
"The role claim format in JWT tokens is deprecated. Please use http://schemas.microsoft.com/ws/2008/06/identity/claims/role instead. The legacy role claim will be removed in DNN Platform v12.0.0.";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,33 +123,37 @@ protected void RenderBlocking(StringBuilder htmlString)
/// <param name="htmlString">The HTML string builder to append to.</param>
protected void RenderCrossOriginAttribute(StringBuilder htmlString)
{
if (this.CrossOrigin != CrossOrigin.None)
switch (this.CrossOrigin)
{
if (this.CrossOrigin == CrossOrigin.UseCredentials)
{
case CrossOrigin.UseCredentials:
htmlString.Append($" crossorigin=\"use-credentials\"");
}
else
{
return;
case CrossOrigin.Anonymous:
htmlString.Append($" crossorigin=\"anonymous\"");
}
return;
case CrossOrigin.None:
return;
default:
throw new InvalidOperationException($"Unexpected CrossOrigin value: {this.CrossOrigin}");
}
}

/// <summary>Renders the <c>fetchpriority</c> attribute.</summary>
/// <param name="htmlString">The HTML string builder to append to.</param>
protected void RenderFetchPriority(StringBuilder htmlString)
{
if (this.FetchPriority != FetchPriority.Auto)
switch (this.FetchPriority)
{
if (this.FetchPriority == FetchPriority.High)
{
case FetchPriority.High:
htmlString.Append($" fetchpriority=\"high\"");
}
else if (this.FetchPriority == FetchPriority.Low)
{
return;
case FetchPriority.Low:
htmlString.Append($" fetchpriority=\"low\"");
}
return;
case FetchPriority.Auto:
return;
default:
throw new InvalidOperationException($"Unexpected FetchPriority value: {this.FetchPriority}");
}
}

Expand All @@ -167,35 +171,36 @@ protected void RenderIntegrity(StringBuilder htmlString)
/// <param name="htmlString">The HTML string builder to append to.</param>
protected void RenderReferrerPolicy(StringBuilder htmlString)
{
if (this.ReferrerPolicy != ReferrerPolicy.None)
switch (this.ReferrerPolicy)
{
switch (this.ReferrerPolicy)
{
case ReferrerPolicy.NoReferrer:
htmlString.Append(" referrerpolicy=\"no-referrer\"");
break;
case ReferrerPolicy.NoReferrerWhenDowngrade:
htmlString.Append(" referrerpolicy=\"no-referrer-when-downgrade\"");
break;
case ReferrerPolicy.Origin:
htmlString.Append(" referrerpolicy=\"origin\"");
break;
case ReferrerPolicy.OriginWhenCrossOrigin:
htmlString.Append(" referrerpolicy=\"origin-when-cross-origin\"");
break;
case ReferrerPolicy.SameOrigin:
htmlString.Append(" referrerpolicy=\"same-origin\"");
break;
case ReferrerPolicy.StrictOrigin:
htmlString.Append(" referrerpolicy=\"strict-origin\"");
break;
case ReferrerPolicy.StrictOriginWhenCrossOrigin:
htmlString.Append(" referrerpolicy=\"strict-origin-when-cross-origin\"");
break;
case ReferrerPolicy.UnsafeUrl:
htmlString.Append(" referrerpolicy=\"unsafe-url\"");
break;
}
case ReferrerPolicy.NoReferrer:
htmlString.Append(" referrerpolicy=\"no-referrer\"");
break;
case ReferrerPolicy.NoReferrerWhenDowngrade:
htmlString.Append(" referrerpolicy=\"no-referrer-when-downgrade\"");
break;
case ReferrerPolicy.Origin:
htmlString.Append(" referrerpolicy=\"origin\"");
break;
case ReferrerPolicy.OriginWhenCrossOrigin:
htmlString.Append(" referrerpolicy=\"origin-when-cross-origin\"");
break;
case ReferrerPolicy.SameOrigin:
htmlString.Append(" referrerpolicy=\"same-origin\"");
break;
case ReferrerPolicy.StrictOrigin:
htmlString.Append(" referrerpolicy=\"strict-origin\"");
break;
case ReferrerPolicy.StrictOriginWhenCrossOrigin:
htmlString.Append(" referrerpolicy=\"strict-origin-when-cross-origin\"");
break;
case ReferrerPolicy.UnsafeUrl:
htmlString.Append(" referrerpolicy=\"unsafe-url\"");
break;
case ReferrerPolicy.None:
return;
default:
throw new InvalidOperationException($"Unexpected ReferrerPolicy value: {this.ReferrerPolicy}");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protected ClientResourceExclude(IClientResourceController clientResourceControll
/// </summary>
public ClientDependencyType DependencyType { get; internal set; }

protected override void OnInit(EventArgs e)
protected override void OnLoad(EventArgs e)
{
switch (this.DependencyType)
{
Expand Down
145 changes: 142 additions & 3 deletions DNN Platform/DotNetNuke.Web.Client/Controls/ClientResourceInclude.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@

namespace DotNetNuke.Web.Client.ClientResourceManagement
{
using System;
using System.Collections.Generic;
using System.Web.UI;

using DotNetNuke.Abstractions.ClientResources;
using DotNetNuke.Web.Client.Cdf;
using DotNetNuke.Web.Client.ResourceManager;

/// <summary>Represents an included client resource.</summary>
public abstract class ClientResourceInclude : Control
Expand All @@ -28,9 +32,6 @@ protected ClientResourceInclude()
/// <summary>Gets or sets the priority for the client resource. Resources with lower priority values are included before those with higher values.</summary>
public int Priority { get; set; }

/// <summary>Gets or sets the group for the client resource. Resources in the same group are processed together.</summary>
public int Group { get; set; }

/// <summary>Gets or sets the name of the script (e.g. <c>jQuery</c>, <c>Bootstrap</c>, <c>Angular</c>, etc.).</summary>
public string Name { get; set; }

Expand All @@ -48,5 +49,143 @@ protected ClientResourceInclude()

/// <summary>Gets or sets a value indicating whether to add the HTML tag for this resource to the page output.</summary>
public bool AddTag { get; set; }

/// <summary>Gets or sets the CDN URL of the resource.</summary>
public string CdnUrl { get; set; }

/// <summary>Gets or sets a value indicating whether to render the <c>blocking</c> attribute.</summary>
public bool Blocking { get; set; }

/// <summary>Gets or sets the integrity hash of the resource.</summary>
public string Integrity { get; set; }

/// <summary>Gets or sets the value of the <c>crossorigin</c> attribute.</summary>
public CrossOrigin CrossOrigin { get; set; }

/// <summary>Gets or sets the value of the <c>fetchpriority</c> attribute.</summary>
public FetchPriority FetchPriority { get; set; }

/// <summary>Gets or sets the value of the <c>referrerpolicy</c> attribute.</summary>
public ReferrerPolicy ReferrerPolicy { get; set; }

/// <summary>Gets additional attributes in the HTML markup for the resource.</summary>
public IDictionary<string, string> HtmlAttributes { get; private set; } = new Dictionary<string, string>();

/// <summary>Gets or sets the <see cref="HtmlAttributes"/> for this resource via a string which is parsed.</summary>
/// <remarks>The syntax for the string must be: <c>key1:value1,key2:value2</c> etc.</remarks>
public string HtmlAttributesAsString { get; set; }

/// <summary>Gets or sets the group for the client resource. Resources in the same group are processed together.</summary>
[Obsolete("Deprecated in DotNetNuke 10.2.0. Grouping is no longer supported, there is no replacement within DNN for this functionality. Scheduled removal in v12.0.0.")]
public int Group { get; set; }

/// <summary>Gets or sets a value indicating whether to force this resource to be bundled. No longer supported.</summary>
[Obsolete("Deprecated in DotNetNuke 10.2.0. Bundling is no longer supported, there is no replacement within DNN for this functionality. Scheduled removal in v12.0.0.")]
public bool ForceBundle { get; set; }

/// <summary>Sets common properties on the <paramref name="resource"/> and registers it.</summary>
/// <param name="resource">The resource to register.</param>
protected void RegisterResource(IResource resource)
{
resource = resource
.SetNameAndVersion(this.Name, this.Version, this.ForceVersion)
.SetProvider(this.ForceProvider)
.SetPriority(this.Priority)
.SetCdnUrl(this.CdnUrl)
.SetIntegrity(this.Integrity)
.SetCrossOrigin(this.CrossOrigin)
.SetFetchPriority(this.FetchPriority)
.SetReferrerPolicy(this.ReferrerPolicy);
if (this.Blocking)
{
resource = resource.SetBlocking();
}

var attributes = this.HtmlAttributes;
ParseHtmlAttributesIntoDictionary(this.HtmlAttributesAsString, attributes);

foreach (var attribute in attributes)
{
resource.AddAttribute(attribute.Key, attribute.Value);
}

resource.Register();
}

/// <seealso href="https://github.com/dnnsoftware/ClientDependency/blob/7bf46d9a9b8540e71496fa76ed5d122ec4a16257/src/ClientDependency.Core/HtmlAttributesStringParser.cs" />
private static void ParseHtmlAttributesIntoDictionary(string attributes, IDictionary<string, string> destination)
{
if (string.IsNullOrEmpty(attributes))
{
return;
}

var key = string.Empty;
var val = string.Empty;
var isKey = true;
var isVal = false;
var isValDelimited = false;
for (var i = 0; i < attributes.Length; i++)
{
var c = attributes.ToCharArray()[i];
if (isKey && c == ':')
{
isKey = false;
isVal = true;
continue;
}

if (isKey)
{
key += c;
}

if (!isVal)
{
continue;
}

if (c == '\'')
{
if (!isValDelimited)
{
isValDelimited = true;
continue;
}
else
{
isValDelimited = false;
if (i == attributes.Length - 1)
{
// if it is the end, add/replace the value
destination[key] = val;
}

continue;
}
}

if (c == ',' && !isValDelimited)
{
// we've reached a comma and the value is no longer delimited, this means we create a new key
isKey = true;
isVal = false;

// now we can add/replace the current value to the dictionary
destination[key] = val;
key = string.Empty;
val = string.Empty;
continue;
}

val += c;

if (i == attributes.Length - 1)
{
// if it is the end, add/replace the value
destination[key] = val;
}
}
}
}
}
Loading
Loading