up
Some checks failed
Build Test Deploy / build-test (push) Has been cancelled
Build Test Deploy / authority-container (push) Has been cancelled
Build Test Deploy / docs (push) Has been cancelled
Build Test Deploy / deploy (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled

This commit is contained in:
root
2025-10-10 06:53:40 +00:00
parent 3aed135fb5
commit df5984d07e
1081 changed files with 97764 additions and 61389 deletions

View File

@@ -0,0 +1,54 @@
using Microsoft.AspNetCore.Http;
using NetTools;
using System;
using System.Linq;
using System.Net;
using System.Collections.Generic;
using Ablera.Serdica.Authentication.Utilities;
using Ablera.Serdica.Authentication.Models.Oidc;
using Ablera.Serdica.Common.Tools.Utilities;
namespace Ablera.Serdica.Authority.Extensions;
public static class AllowedMaskExtensions
{
// Lazily built the first time AllowedMaskExtensions is referenced.
private static readonly IReadOnlyCollection<IPAddressRange> AssociatedNetworks = ListeningNetworksRetriever.Retrieve();
public static AllowedMask? MergeWith(this AllowedMask? client, AllowedMask? global)
=> (client, global) switch
{
(null, null) => null,
(null, _) => global,
_ => new()
{
SameNetworks = client.SameNetworks ?? global?.SameNetworks,
Networks = client.Networks ?? global?.Networks,
Hosts = client.Hosts ?? global?.Hosts,
Ports = client.Ports ?? global?.Ports
}
};
public static bool MatchesRemote(this AllowedMask allow, HttpContext http)
{
var remoteIp = http.Connection.RemoteIpAddress ?? IPAddress.None;
var host = http.Request.Host.Host;
var port = http.Request.Host.Port ?? 0;
bool ipOk = allow.Networks == null ||
allow.Networks.Any(net => IPAddressRange.Parse(net).Contains(remoteIp));
bool hostOk = allow.Hosts == null ||
allow.Hosts.Any(h => StringComparer.OrdinalIgnoreCase.Equals(h, host));
bool portOk = allow.Ports == null || allow.Ports.Contains(port);
// Same-network rule: only enforced when SameNetwork == true
bool sameNetworkOk =
allow.SameNetworks != true || // Flag not enabled → no restriction
AssociatedNetworks == null || // Could not determine our own network
AssociatedNetworks.Any(network => network.Contains(remoteIp));
return ipOk && hostOk && portOk && sameNetworkOk;
}
}

View File

@@ -0,0 +1,67 @@
using Microsoft.AspNetCore.Identity;
using OpenIddict.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace Ablera.Serdica.Authentication.Extensions
{
public static class ClaimExtensions
{
public static IReadOnlyCollection<Claim> BuildClaims<TKeyType>(
this IdentityUser<TKeyType> identity,
string? userName = null, string? givenName = null, string? surname = null)
where TKeyType : IEquatable<TKeyType> => new[]
{
new Claim(ClaimTypes.NameIdentifier, identity.Id?.ToString() ?? string.Empty),
new Claim(Claims.Subject, identity.Id?.ToString() ?? string.Empty),
new Claim(ClaimTypes.Name, userName ?? identity.UserName ?? string.Empty),
new Claim(ClaimTypes.GivenName, givenName ?? string.Empty),
new Claim(ClaimTypes.Surname, surname ?? string.Empty),
new Claim(ClaimTypes.Email, identity.Email ?? string.Empty)
};
public static IEnumerable<string> DestinationsSelector(this Claim c) => c.Type switch
{
Claims.Name or Claims.PreferredUsername
=> new[] { Destinations.AccessToken, Destinations.IdentityToken },
Claims.Email when c.Subject?.HasScope(Scopes.Email) == true
=> new[] { Destinations.AccessToken, Destinations.IdentityToken },
Claims.Role when c.Subject?.HasScope(Scopes.Roles) == true
=> new[] { Destinations.AccessToken, Destinations.IdentityToken },
_ => new[] { Destinations.AccessToken }
};
public static string? GetUserId(this ClaimsPrincipal user)
=> user.Claims.GetUserId() ?? Guid.Empty.ToString();
public static string? GetUserEmail(this ClaimsPrincipal user)
=> user.Claims
.FirstOrDefault(x => x.Type == ClaimTypes.Email)
?.Value?.ToString();
private static string? GetUserId(this IEnumerable<Claim> claims)
=> claims
.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)
?.Value?.ToString()
?? claims
.FirstOrDefault(x => x.Type == ClaimTypes.Name)
?.Value?.ToString();
public static string? GetClientApplicationId(this ClaimsPrincipal user)
=> user.Claims.GetClientApplicationId();
private static string? GetClientApplicationId(this IEnumerable<Claim> claims)
=> claims
.FirstOrDefault(x => x.Type == Claims.Subject)
?.Value?.ToString()
?? claims
.FirstOrDefault(x => x.Type == Claims.ClientId)
?.Value?.ToString();
}
}

View File

@@ -0,0 +1,16 @@
using System.Security.Claims;
using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace Ablera.Serdica.Authentication.Extensions;
public static class PrincipalBuilder
{
public static ClaimsPrincipal Build(string clientId, string authenticationSchema)
{
var claimsIdentity = new ClaimsIdentity(authenticationSchema);
claimsIdentity.AddClaim(Claims.Subject, clientId, Destinations.AccessToken);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
return claimsPrincipal;
}
}

View File

@@ -0,0 +1,18 @@
using Ablera.Serdica.Authentication.Models;
using Ablera.Serdica.Common.Tools.Extensions;
using Microsoft.AspNetCore.Http;
using System.Text.Json;
using System.Threading.Tasks;
namespace Ablera.Serdica.Authentication.Extensions;
public static class ProxyResultExtension
{
public static async Task ReturnHttpRessponse(this ProxyResult proxyResult, HttpResponse httpResponse)
{
if (httpResponse.HasStarted) return;
httpResponse.StatusCode = (int)proxyResult.StatusCode;
httpResponse.ContentType = "application/json";
await JsonSerializer.SerializeAsync(httpResponse.Body, proxyResult, proxyResult.GetType(), GlobalJsonSerializerOptions.JsonSerializerOptions);
}
}