dRBACs.Client 3.1.1
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.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 a0disattiva il backoff. AncheRbac:CacheTtlMinutesa0disattiva 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.