dRBACs.Client 3.1.2

dRBACs.Client

Libreria client per l'integrazione del sistema RBAC (Role-Based Access Control) nei progetti di ASLRoma1.

Installazione

Per utilizzare questa libreria, aggiungi il riferimento al progetto o installa il package NuGet (quando disponibile).

Compatibilità

Il pacchetto è multi-target sulle sole release LTS:

TFM Note
net6.0 LTS scaduta (nov 2024), inclusa per i progetti non ancora migrati
net8.0 LTS
net10.0 LTS

NuGet seleziona automaticamente l'assembly giusto e non serve alcuna configurazione nel progetto consumer. Un progetto su una release STS funziona comunque: risolve all'LTS compatibile più vicina — per esempio un progetto net9.0 usa lib/net8.0.

Il codice sorgente è identico per tutti i target — nessuna #if di compilazione condizionale, quindi il comportamento non cambia da un framework all'altro.

Aggiornamento a v3.1.2

Configure<RbacOptions> chiamata dopo AddRbacClient (o dopo AddIamAuthentication, che la chiama internamente) non aveva effetto sull'indirizzo usato dal client: il base url veniva letto subito da Rbac:BaseUrl e catturato nella closure di AddHttpClient. Un'app che tiene la configurazione in variabili d'ambiente piatte finiva così per chiamare il default https://localhost:7171.

Sintomo: a log l'indirizzo giusto, e a runtime Risoluzione ruoli RBAC non riuscita ...: Connection refused (localhost:7171).

Ora il base url si risolve da IOptions<RbacOptions> alla creazione del client, quindi entrambe le forme funzionano:

// sezione di configurazione
builder.Services.AddRbacClient(builder.Configuration);   // legge Rbac:BaseUrl

// oppure valore da variabile d'ambiente piatta
builder.Services.Configure<RbacOptions>(o => o.BaseUrl = Environment.GetEnvironmentVariable("RBAC_BASE_URL")!);

Un BaseUrl esplicitamente vuoto ora fallisce con un messaggio chiaro invece di ripiegare in silenzio su localhost.

Aggiornamento a v3.1.1

Correzione obbligatoria per chi usa AddIamAuthentication o comunque la risoluzione ruoli: in v3.1.0 RemoteRolesClaimsTransformation entrava in ricorsione infinita alla prima richiesta autenticata.

Il token per il servizio RBAC si legge con HttpContext.GetTokenAsync("access_token"), che internamente chiama AuthenticateAsync, che rinvoca la claims transformation: senza una guardia il giro non finisce mai. Il risultato è una StackOverflowException — non intercettabile — quindi il processo terminava subito dopo il login senza lasciare nulla nei log, e il container ripartiva.

Sintomo tipico: il login sull'IAM va a buon fine, il browser viene rediretto all'applicazione e l'applicazione si riavvia. Nessun aggiornamento di configurazione è necessario: basta passare a 3.1.1.

Migrazione a v3.0.0

IRbacClient restituisce RbacResult<T> invece di liste e bool. Il motivo: prima un servizio irraggiungibile e un utente senza ruoli restituivano entrambi una lista vuota, e la claims transformation metteva quell'esito in cache — un blip di rete lasciava l'utente senza permessi per l'intera durata della cache, e una revoca era indistinguibile da un guasto.

// prima (v2.x)
List<RbacRoleDto> roles = await client.GetUserRolesAsync(cf);
// una lista vuota poteva voler dire "nessun ruolo" oppure "servizio giù": impossibile saperlo

// ora (v3.0.0)
RbacResult<IReadOnlyList<RbacRoleDto>> result = await client.GetUserRolesAsync(cf);
if (!result.IsSuccess)
{
    // result.Error, result.StatusCode
    return;
}
IReadOnlyList<RbacRoleDto> roles = result.Value!;

Altri cambiamenti, senza impatto sul codice consumer:

  • il token di accesso viaggia sulla singola richiesta e non più su HttpClient.DefaultRequestHeaders;
  • la claims transformation non muta più il principal ricevuto e non emette il claim RBAC_TRANSFORMED, che se persistito in un cookie avrebbe congelato i ruoli fino al logout;
  • nuova opzione Rbac:FailureRetrySeconds (default 15): attesa prima di riprovare dopo un guasto. Impostarla a 0 disattiva il backoff. Anche Rbac:CacheTtlMinutes a 0 disattiva la cache dei ruoli.

Migrazione da AslRoma1.Rbac.Client (v1.x → v2.0.0)

Il pacchetto è stato rinominato. Per aggiornare un progetto consumer:

Prima (v1.x) Ora (v2.0.0)
PackageReference Include="AslRoma1.Rbac.Client" PackageReference Include="dRBACs.Client"
using AslRoma1.Rbac.Client.Extensions; using dRBACs.Client.Extensions;
using AslRoma1.Rbac.Client.Security; using dRBACs.Client.Security;
using AslRoma1.Rbac.Client.Services; using dRBACs.Client.Services;
AddAslRoma1RbacClient(configuration) AddRbacClient(configuration)

Nessuna chiave di configurazione è cambiata: la sezione Rbac resta identica.

Configurazione

Aggiungi la configurazione RBAC nel file appsettings.json:

{
  "Rbac": {
    "BaseUrl": "https://url-del-servizio-rbac",
    "CacheTtlMinutes": 10,
    "FailureRetrySeconds": 15
  }
}

Autenticazione IAM (v3.1.0)

Per autenticarsi contro l'IAM ASLROMA1 (LDAP, SPID, CIE) e risolvere i ruoli in un colpo solo:

using dRBACs.Client.Extensions;

builder.Services.AddIamAuthentication(builder.Configuration);

Restano a carico dell'app app.UseAuthentication() e app.UseAuthorization(): l'ordine dei middleware è una decisione della pipeline, non del pacchetto.

{
  "Iam": {
    "Authority": "https://iam.aslroma1.it",
    "ClientId": "...",
    "ClientSecret": "...",
    "Scopes": "openid profile",
    "ValidationMode": "Compatible"
  }
}

Al termine del login l'utente ha: anagrafica da SPID/CIE, codice fiscale su ClaimTypes.SerialNumber, email e PEC estratte dall'assertion SAML, e i ruoli dal servizio RBAC — quindi IsInRole(...) e [Authorize(Roles = "...")] funzionano senza altro codice.

Modalità di validazione

Modalità Discovery Firma verificata Issuer / nonce
Compatible (default) no, endpoint cablati no rilassati
Discovery sì sì, via JWKS rilassati
Strict sì sì validati

Compatible è il default perché è la configurazione già in produzione, l'unica di cui si sappia con certezza che funziona contro questo IAM. Va però capito che in quella modalità un id_token contraffatto verrebbe accettato: è tollerabile solo perché il token arriva sul canale server-to-server del code flow, protetto da TLS e PKCE. La direzione è passare a Discovery appena la si è vista funzionare sul campo.

Durata della sessione

Il cookie eredita la scadenza del token e non si rinnova (UseTokenLifetime = true, SlidingExpiration = false). L'IAM dichiara solo authorization_code, quindi non esiste refresh token: lasciando i default del framework il cookie durerebbe 14 giorni rinnovandosi a ogni richiesta, e sopravviverebbe per settimane a un token scaduto o revocato. Iam:CookieLifetimeFollowsToken = false disattiva il vincolo, ma è una responsabilità che ci si assume consapevolmente.

Claim applicativi

Per i claim che il pacchetto non può conoscere — il codice struttura di un'app, un profilo amministrativo — si implementa IIamClaimsAugmenter:

builder.Services.AddIamAuthentication(builder.Configuration)
       .WithClaimsAugmenter<CirCodeAugmenter>();

Adattare i default a un'app esistente

I default seguono l'integrazione collaudata (MapInboundClaims = false, nome da email, ruoli da role). Un'app che si aspetta la forma WS-Federation li ribalta:

builder.Services.AddIamAuthentication(builder.Configuration, o =>
{
    o.MapInboundClaims = true;
    o.NameClaimType    = "preferred_username";
});

Altre opzioni: ApiPathPrefixes (default ["/api"], sotto cui si risponde 401/403 invece di redirigere al login), UseRbacRoles (per chi si autorizza diversamente), AccessDeniedPath, e LogUserInfoPayload — che registra il payload grezzo dello userinfo per riallineare i DTO quando lo schema IAM cambia. ⚠️ Quel payload contiene dati personali (codice fiscale, indirizzo, telefono, estremi del documento): va acceso solo il tempo necessario.

Registrazione dei servizi

Nel file Program.cs, utilizza il metodo di estensione per registrare i servizi necessari:

using dRBACs.Client.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Registra i servizi RBAC
builder.Services.AddRbacClient(builder.Configuration);

var app = builder.Build();
// ...

Utilizzo

IUserContext

Puoi iniettare IUserContext per ottenere informazioni sull'utente corrente:

public class MyService
{
    private readonly IUserContext _userContext;

    public MyService(IUserContext userContext)
    {
        _userContext = userContext;
    }

    public void DoSomething()
    {
        var cf = _userContext.CodiceFiscale;
        var isAdmin = _userContext.IsAdmin;
        // ...
    }
}

IRbacClient

Puoi iniettare IRbacClient per interagire direttamente con il servizio RBAC:

public class MyAdminService
{
    private readonly IRbacClient _rbacClient;

    public MyAdminService(IRbacClient rbacClient)
    {
        _rbacClient = rbacClient;
    }

    public async Task<bool> AssignRole(string cf, string roleId)
    {
        var result = await _rbacClient.AddRoleToUserAsync(cf, roleId);
        return result.IsSuccess;
    }
}

No packages depend on dRBACs.Client.

v.3.1.2 - Correzione: il base url impostato con Configure<RbacOptions> dopo AddRbacClient veniva ignorato (il valore era letto subito e catturato nella closure di AddHttpClient), e le chiamate finivano sul default localhost:7171. Ora il base url si risolve da IOptions alla creazione del client. v.3.1.1 - Correzione: RemoteRolesClaimsTransformation entrava in ricorsione infinita (StackOverflow, processo terminato senza log) alla prima richiesta autenticata; aggiunta una guardia di rientranza.

Version Downloads Last updated
4.3.2 56 09/17/2026
4.3.1 1 09/17/2026
4.3.0 1 09/16/2026
4.2.0 23 09/01/2026
4.1.0 15 09/01/2026
4.0.0 5 08/28/2026
3.3.0 15 08/28/2026
3.2.0 3 08/27/2026
3.1.2 3 08/27/2026
3.1.1 3 08/26/2026
3.1.0 1 08/26/2026