Skip to content

Commit

Permalink
Merge pull request #9 from PiemP/signed_metadata
Browse files Browse the repository at this point in the history
Endpoint per generazione metadata firmato
  • Loading branch information
danielegiallonardo authored Nov 11, 2022
2 parents 40c162d + 25daee3 commit db804a1
Show file tree
Hide file tree
Showing 35 changed files with 7,343 additions and 30 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,24 @@ jobs:
- name: Test
run: dotnet test --no-build --verbosity normal
working-directory: CIE.AspNetCore.Authentication
- name: Restore dependencies WebApp
run: dotnet restore
working-directory: CIE.AspNetCore.Authentication/CIE.AspNetCore.WebApp
- name: Build WebApp
run: dotnet build --no-restore
working-directory: CIE.AspNetCore.Authentication/CIE.AspNetCore.WebApp
- name: Update apt repo
run: sudo apt update
- name: Install dependencies
run: sudo apt install -y libxml2-dev libxmlsec1-dev libxmlsec1-openssl xmlsec1 python3-pip
- name: Install spid-compliant-certificates cryptography correct version
run: sudo pip install -Iv cryptography==35.0.0
- name: Install spid-sp-test
run: sudo pip install spid-sp-test --upgrade --no-cache
- name: Run CIE.AspNetCore.WebApp
working-directory: CIE.AspNetCore.Authentication/CIE.AspNetCore.WebApp
run: dotnet bin/Debug/net6.0/CIE.AspNetCore.WebApp.dll &
- name: Test Metadata spid-sp-public with spid-sp-test
run: spid_sp_test --metadata-url https://localhost:5001/metadata-cie/metadata1.xml --extra --debug ERROR --profile cie-sp-public
- name: Test Metadata spid-sp-private with spid-sp-test
run: spid_sp_test --metadata-url https://localhost:5001/metadata-cie/metadata3.xml --extra --debug ERROR --profile cie-sp-private
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ bld/

# Visual Studio 2015/2017 cache/options directory
.vs/
# Visual Studio Code options directory
.vscode/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
<PackageTags>cie;aspnetcore;authentication</PackageTags>
<PackageProjectUrl>https://github.com/italia/cie-aspnetcore</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageVersion>1.1.3-prerelease4</PackageVersion>
<Version>1.1.3</Version>
<AssemblyVersion>1.1.3</AssemblyVersion>
<FileVersion>1.1.3</FileVersion>
<InformationalVersion>1.1.3</InformationalVersion>
<PackageVersion>1.2.0-prerelease</PackageVersion>
<Version>1.2.0</Version>
<AssemblyVersion>1.2.0</AssemblyVersion>
<FileVersion>1.2.0</FileVersion>
<InformationalVersion>1.2.0</InformationalVersion>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CIE.AspNetCore.Authentication.Events;
using CIE.AspNetCore.Authentication.Extensions;
using CIE.AspNetCore.Authentication.Helpers;
using CIE.AspNetCore.Authentication.Models;
using CIE.AspNetCore.Authentication.Resources;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
using CIE.AspNetCore.Authentication.Models;
using System;
using System.Security.Claims;
using Microsoft.AspNetCore.Builder;
using CIE.AspNetCore.Authentication.Models.ServiceProviders;

namespace CIE.AspNetCore.Authentication
namespace CIE.AspNetCore.Authentication.Extensions
{
public static class CieExtensions
{
Expand All @@ -20,26 +22,72 @@ public static class CieExtensions
/// <param name="builder"></param>
/// <returns></returns>
public static AuthenticationBuilder AddCie(this AuthenticationBuilder builder, IConfiguration configuration)
=> builder.AddCie(CieDefaults.AuthenticationScheme, configuration, _ => { });
=> builder.AddCie(CieDefaults.AuthenticationScheme, o => { o.LoadFromConfiguration(configuration); });

/// <summary>
/// Registers the <see cref="CieHandler"/> using the default authentication scheme, display name, and the given options configuration.
/// </summary>
/// <param name="builder"></param>
/// <param name="configureOptions">A delegate that configures the <see cref="CieOptions"/>.</param>
/// <returns></returns>
public static AuthenticationBuilder AddCie(this AuthenticationBuilder builder, Action<CieOptions> configureOptions)
=> builder.AddCie(CieDefaults.AuthenticationScheme, configureOptions);

/// <summary>
/// Registers the <see cref="CieHandler"/> using the given authentication scheme, default display name, and the given options configuration.
/// </summary>
/// <param name="builder"></param>
/// <param name="authenticationScheme"></param>
/// <param name="configureOptions">A delegate that configures the <see cref="CieOptions"/>.</param>
/// <returns></returns>
public static AuthenticationBuilder AddCie(this AuthenticationBuilder builder, string authenticationScheme, Action<CieOptions> configureOptions)
=> builder.AddCie(authenticationScheme, CieDefaults.DisplayName, configureOptions);

/// <summary>
/// Registers the <see cref="CieHandler"/> using the default authentication scheme, display name, and the given options configuration.
/// </summary>
/// <param name="builder"></param>
/// <param name="configureOptions">A delegate that configures the <see cref="CieOptions"/>.</param>
/// <returns></returns>
/*
public static AuthenticationBuilder AddCie(this AuthenticationBuilder builder, IConfiguration configuration, Action<CieOptions> configureOptions)
=> builder.AddCie(CieDefaults.AuthenticationScheme, configuration, configureOptions);

*/
/// <summary>
/// Registers the <see cref="CieHandler"/> using the given authentication scheme, default display name, and the given options configuration.
/// </summary>
/// <param name="builder"></param>
/// <param name="authenticationScheme"></param>
/// <param name="configureOptions">A delegate that configures the <see cref="CieOptions"/>.</param>
/// <returns></returns>
/*
public static AuthenticationBuilder AddCie(this AuthenticationBuilder builder, string authenticationScheme, IConfiguration configuration, Action<CieOptions> configureOptions)
=> builder.AddCie(authenticationScheme, CieDefaults.DisplayName, configuration, configureOptions);
*/

/// <summary>
/// Registers the <see cref="CieHandler"/> using the given authentication scheme, display name, and options configuration.
/// </summary>
/// <param name="builder"></param>
/// <param name="authenticationScheme"></param>
/// <param name="displayName"></param>
/// <param name="configureOptions">A delegate that configures the <see cref="CieOptions"/>.</param>
/// <returns></returns>
public static AuthenticationBuilder AddCie(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<CieOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<CieOptions>, CiePostConfigureOptions>());
builder.Services.TryAdd(ServiceDescriptor.Singleton<IActionContextAccessor, ActionContextAccessor>());
builder.Services.AddHttpClient("cie");
builder.Services.TryAddScoped(factory =>
{
var actionContext = factory.GetService<IActionContextAccessor>().ActionContext;
var urlHelperFactory = factory.GetService<IUrlHelperFactory>();
return urlHelperFactory.GetUrlHelper(actionContext);
});
builder.Services.AddOptions<CieOptions>().Configure(configureOptions);
builder.Services.TryAddScoped<IServiceProvidersFactory, DefaultServiceProvidersFactory>();
return builder.AddRemoteScheme<CieOptions, CieHandler>(authenticationScheme, displayName, configureOptions);
}

/// <summary>
/// Registers the <see cref="CieHandler"/> using the given authentication scheme, display name, and options configuration.
Expand All @@ -49,6 +97,7 @@ public static AuthenticationBuilder AddCie(this AuthenticationBuilder builder, s
/// <param name="displayName"></param>
/// <param name="configureOptions">A delegate that configures the <see cref="CieOptions"/>.</param>
/// <returns></returns>
/*
public static AuthenticationBuilder AddCie(this AuthenticationBuilder builder, string authenticationScheme, string displayName, IConfiguration configuration, Action<CieOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<CieOptions>, CiePostConfigureOptions>());
Expand All @@ -63,6 +112,19 @@ public static AuthenticationBuilder AddCie(this AuthenticationBuilder builder, s
builder.Services.AddOptions<CieConfiguration>().Configure(o => OptionsHelper.LoadFromConfiguration(o, configuration));
return builder.AddRemoteScheme<CieOptions, CieHandler>(authenticationScheme, displayName, configureOptions);
}
*/

public static AuthenticationBuilder AddServiceProvidersFactory<T>(this AuthenticationBuilder builder)
where T : class, IServiceProvidersFactory
{
builder.Services.AddScoped<IServiceProvidersFactory, T>();
return builder;
}

public static IApplicationBuilder AddCieSPMetadataEndpoints(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CieSPMetadataMiddleware>();
}

/// <summary>
/// Finds the first value.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using CIE.AspNetCore.Authentication.Models;
using CIE.AspNetCore.Authentication.Models.ServiceProviders;
using System.Linq;
using System.Threading.Tasks;

namespace CIE.AspNetCore.Authentication.Extensions
{
internal class CieSPMetadataMiddleware
{
private readonly RequestDelegate _next;

public CieSPMetadataMiddleware(RequestDelegate next)
{
_next = next;
}

public async Task Invoke(HttpContext context, IOptionsSnapshot<CieOptions> options, IServiceProvidersFactory serviceProvidersFactory)
{
var serviceProviders = options.Value.ServiceProviders;

serviceProviders.AddRange(await serviceProvidersFactory.GetServiceProviders());

var serviceProvider = serviceProviders.FirstOrDefault(m =>
context.Request.Path.Equals($"{options.Value.ServiceProvidersMetadataEndpointsBasePath}/{m.FileName}", System.StringComparison.OrdinalIgnoreCase));
if (serviceProvider is not null)
{
var (result, contentType) = serviceProvider.Serialize();
context.Response.ContentType = contentType ?? "application/xml; charset=UTF-8";
await context.Response.WriteAsync(result);
await context.Response.Body.FlushAsync();
return;
}

await _next(context);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System;

namespace CIE.AspNetCore.Authentication
namespace CIE.AspNetCore.Authentication.Extensions
{
internal static class LoggingExtensions
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,8 @@ public static string CreateSignature(this string payload, X509Certificate2 certi
var hash = shaHash.ComputeHash(Encoding.UTF8.GetBytes(payload));
return Convert.ToBase64String(rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1), Base64FormattingOptions.None);
}

public static byte[] ExportPublicKey(this X509Certificate2 cert)
=> cert.Export(X509ContentType.Cert);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,5 +145,44 @@ public static XmlDocument SerializeToXmlDoc(this object o)

return doc;
}

public static XmlElement SerializeInternalExtensionToXmlElement(object o, string namespacePrefix, string xmlNamespace)
{
XmlDocument doc = SerializeExtensionToXmlElementInternal(o, namespacePrefix, xmlNamespace);

return doc.DocumentElement.FirstChild as XmlElement;
}

public static XmlElement SerializeExtensionToXmlElement(object o, string namespacePrefix, string xmlNamespace)
{
XmlDocument doc = SerializeExtensionToXmlElementInternal(o, namespacePrefix, xmlNamespace);

return doc.DocumentElement;
}

private static XmlDocument SerializeExtensionToXmlElementInternal(object o, string namespacePrefix, string xmlNamespace)
{
XmlDocument doc = new XmlDocument();

using (XmlWriter writer = doc.CreateNavigator().AppendChild())
{
var ns = new XmlSerializerNamespaces();
ns.Add(namespacePrefix, xmlNamespace);
new XmlSerializer(o.GetType()).Serialize(writer, o, ns);
}

return doc;
}

public static XmlElement GetXmlElement(string prefix, string prefixNamespace, string tag, string value = null)
{
XmlDocument doc = new XmlDocument();

XmlElement elem = doc.CreateElement(prefix, tag, prefixNamespace);
if(!string.IsNullOrEmpty(value))
elem.InnerText = value;

return elem;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using CIE.AspNetCore.Authentication.Saml;
using System;
using System.Collections.Generic;

namespace CIE.AspNetCore.Authentication.Models
{
Expand All @@ -24,5 +26,17 @@ private CieClaimTypes(string value)
public static CieClaimTypes FiscalNumber { get { return _types[nameof(FiscalNumber)]; } }
public static CieClaimTypes DateOfBirth { get { return _types[nameof(DateOfBirth)]; } }
public static CieClaimTypes RawFiscalNumber { get { return _types[nameof(RawFiscalNumber)]; } }

internal string GetSamlAttributeName()
{
return Value switch
{
nameof(Name) => SamlConst.name,
nameof(FamilyName) => SamlConst.familyName,
nameof(FiscalNumber) or nameof(RawFiscalNumber) => SamlConst.fiscalNumber,
nameof(DateOfBirth) => SamlConst.dateOfBirth,
_ => throw new Exception("Invalid ClaimType"),
};
}
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
using CIE.AspNetCore.Authentication.Events;
using CIE.AspNetCore.Authentication.Helpers;
using CIE.AspNetCore.Authentication.Models.ServiceProviders;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;

namespace CIE.AspNetCore.Authentication.Models
{
public class CieOptions : RemoteAuthenticationOptions
{
private readonly List<ServiceProvider> _spMetadata = new();

public CieOptions()
{
CallbackPath = "/signin-cie";
// In ADFS the cleanup messages are sent to the same callback path as the initial login.
// In AAD it sends the cleanup message to a random Reply Url and there's no deterministic way to configure it.
// If you manage to get it configured, then you can set RemoteSignOutPath accordingly.
RemoteSignOutPath = "/signout-cie";
ServiceProvidersMetadataEndpointsBasePath = "/metadata-cie";
Events = new CieEvents();
}

Expand Down Expand Up @@ -130,6 +135,22 @@ public override void Validate()
/// </value>
public CieClaimTypes PrincipalNameClaimType { get; set; } = CieClaimTypes.FiscalNumber;

/// <summary>
/// Gets or sets the base path where the configured SP metadata will be exposed.
/// </summary>
/// <value>
/// The SP Metadata Endpoints BasePath.
/// </value>
public PathString ServiceProvidersMetadataEndpointsBasePath { get; set; }

/// <summary>
/// Gets or sets the collection of the exposed SP metadata.
/// </summary>
/// <value>
/// The collection of the exposed SP metadata.
/// </value>
public List<ServiceProvider> ServiceProviders { get { return _spMetadata; } }

public void LoadFromConfiguration(IConfiguration configuration)
{
var conf = OptionsHelper.CreateFromConfiguration(configuration);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace CIE.AspNetCore.Authentication.Models.ServiceProviders
{
public class AssertionConsumerService
{
public ProtocolBinding ProtocolBinding { get; set; }
public string Location { get; set; }
public ushort Index { get; set; } = 0;
public bool IsDefault { get; set; } = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace CIE.AspNetCore.Authentication.Models.ServiceProviders
{
public class AttributeConsumingService
{
public ushort Index { get; set; } = 0;
public string ServiceName { get; set; }
public string ServiceDescription { get; set; }
public CieClaimTypes[] ClaimTypes { get; set; }
}
}
Loading

0 comments on commit db804a1

Please sign in to comment.