8 messaggi dal 25 marzo 2016
Buongiorno,
ho la necessità di introdurre la security in un'applicazione sviluppata in MVC.

Sto estendendo la classe CustomRolesProvider per ottenere le roles associate ad un utente. Avrei la necessità di definire la role come un oggetto custom e non come array di stringhe.

es role:

public class myRole 
{
    public string Country { get; set; }
    public List<string> roles { get; set; }
}


Vorrei evitare di concatenare le role con la country.
Esiste un alternativa all'override del metodo GetRolesForUser e all'estensione della RolesProvider?

Grazie
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,
questa soluzione potrebbe aiutarti.
http://stackoverflow.com/questions/1064271/asp-net-mvc-set-custom-iidentity-or-iprincipal#answer-10524305

Si tratta, in pratica, di aggiungere ulteriori informazioni al cookie di autenticazione (il Country e la lista di ruoli, in questo caso). Lo puoi vedere al punto "4. LogIn method" nella pagina che ti ho linkato qui sopra.

Fatto questo, gestisci l'evento PostAuthenticateRequest nel global.asax come vedi al punto 5, ed associa una tua classe personalizzata come Principal per la richiesta corrente. Questa classe conterrà, tra le sue proprietà, anche il tipo complesso che hai postato. Valorizzalo opportunamente in base alle informazioni contenute nel cookie.

ciao,
Moreno

Enjoy learning and just keep making
8 messaggi dal 25 marzo 2016
Ciao e grazie della risposta.

Ho guardato il link e mi sembra abbastanza chiaro; ho solo un dubbio: utilizzo la windows authentication quindi non ho nessun metodo di login il punto 4 dove lo devo mettere?

grazie 1000
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,


utilizzo la windows authentication

Ok, in questo caso non avrai il punto 4. Facciamo così, vediamo se riusciamo a risolverla in maniera ancora più semplice di quella descritta nella discussione che ti ho linkato.

Ricapitolando: tu hai creato un RoleProvider custom e l'hai registrato nel web.config. In questo modo, ogni volta che un utente richiede una pagina, ASP.NET richiamerà il metodo GetRolesForUser per conoscere i suoi ruoli.
Nel corpo del metodo, prima di restituire i ruoli come array di stringhe, potresti anche aggiungere il Country ai Claims dell'identità corrente.
public override string[] GetRolesForUser(string username)
{
    var identity = HttpContext.Current.User.Identity as WindowsIdentity;
    if (identity != null && identity.IsAuthenticated)
    {
        //Questo è il metodo che usi per recuperare il country di questo utente
        string country = MetodoPerRecuperareIlCountry(username);
        //Qui aggiungi il country ai claims dell'identità
        identity.AddClaim(new Claim(ClaimTypes.Country, country));
    }
    //Ottengo e restituisco i ruoli
    string[] roles = MetodoPerRecuperareIRuoli(username);
    return roles;
}

Ma tieni presente che aggiungere il claim del Country da questo metodo potrebbe non essere il punto più opportuno. Per esempio, se stai cachando i ruoli in un cookie (attributo cacheRolesInCookie="true" del nodo roleManager del web.config) il metodo GetRolesForUser non andrà in esecuzione ad ogni richiesta e perciò perderemmo l'opportunità di aggiungere il claim del country.
Allora forse è meglio aggiungere il country dal metodo Application_PostAuthenticateRequest del global.asax, ma questo devi deciderlo tu.

In un modo o nell'altro, questa soluzione ti evita di "concatenare" il country ai ruoli, e te lo fa aggiungere invece ai claims dell'identità corrente, là dove è più opportuno che si trovi.

Veniamo alla seconda parte del problema:

Avrei la necessità di definire la role come un oggetto custom

Ora che i roles e il country sono "separati", è molto facile scrivere un extension method che ti restituisca l'oggetto MyRole che hai descritto.
L'implementazione dell'extension method sarà simile alla seguente:
public static MyRole GetMyRole(this IPrincipal principal)
{
    //Per prima cosa mi assicuro che principal ed identity
    //siano dei tipi previsti
    var rolePrincipal = principal as RolePrincipal;
    if (rolePrincipal == null)
        return null;

    var windowsIdentity = rolePrincipal.Identity as WindowsIdentity;
    if (windowsIdentity == null || !windowsIdentity.IsAuthenticated)
        return null;

    //Restituisco l'istanza della tua classe MyRole
    return new MyRole
    {
        //Estraggo il country dai claims dell'identità
        Country = windowsIdentity.Claims
            .Where(c => c.Type == ClaimTypes.Country)
            .Select(c => c.Value)
            .FirstOrDefault(),
        //e i ruoli dalla principal
        Roles = rolePrincipal.GetRoles().ToList()
    };
}


E poi da un'action o una view ASP.NET MVC lo usi in questo modo:
var myrole = User.GetMyRole();
var country = myrole.Country;
var roles = myrole.Roles;

Lo puoi usare anche da un Authorization attribute personalizzato, se lo scopo è quello di autorizzare le richieste anche in base al country.

ciao,
Moreno
Modificato da BrightSoul il 27 marzo 2016 23.12 -

Enjoy learning and just keep making
8 messaggi dal 25 marzo 2016
Si esatto! L'obbiettivo finale è quello di utilizzare un Authorization attribute personalizzato.
Purtroppo i Claims sono per me un concetto nuovo, ho fatto delle ricerche e credo di aver capito cosa sono. Non riesco però ad utilizzarle, ricevo questo errore:

'System.Security.Principal.WindowsIdentity' does not contain a definition for 'AddClaim' and no extension method 'AddClaim'  ....


e non mi propone di importare nessuna reference per il Claim...
Potrebbe essere perché utilizzo il Framework 4 e non il 4.5?

Altra cosa... Mi sono spiegato male nella prima domanda :( un utente potrebbe essere abilitato a più country con roles diverse, quindi alla fine dovrei avere:
 List<myRole> 


Grazie e perdonami se sono poco chiaro.
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,


Potrebbe essere perché utilizzo il Framework 4 e non il 4.5?

Sì, la claim-based identity, che prima era una peculiarità di WIF, è stata introdotta nel framework con la versione 4.5.


un utente potrebbe essere abilitato a più country con roles diverse

ok, a questo punto puoi decidere di intraprendere una tra queste due strade:
  • Restituisci solo i ruoli per il country del contesto corrente (esempio: l'utente sta interagendo con i contenuti per l'Italia, quindi estrai dal db solo i ruoli per l'italia) e in questo caso si applica la stessa soluzione di cui abbiamo discusso nei post precedenti;
  • Restituisci i ruoli per tutti i country, ma a questo punto dobbiamo andare a rompere qualcosa perché le soluzioni viste finora non sono adatte modellare la relazione tra utente, country e ruoli.


Prima di valutare altre soluzioni ti chiedo: perché hai scartato l'ipotesi di concatenare il country al ruolo? Per un motivo di eleganza o per altri motivi pratici?

Il metodo GetRolesForUser potrebbe restituire un array di stringhe contenenti voci come queste:
it.ProductEditor
it.PageEditor
en.PageReviewer
de.Guest

Poi, da un extension method, fai il parsing di questi ruoli e restituisci la lista di MyRole che potrai usare dall'attributo di autorizzazione personalizzato o da qualsiasi altro punto dell'applicazione (es. da una view che riepiloga i privilegi dell'utente).

ciao,
Moreno
Modificato da BrightSoul il 30 marzo 2016 21.51 -

Enjoy learning and just keep making
8 messaggi dal 25 marzo 2016
Ciao,

avevo scartato la concatenazione sia per una questione di eleganza sia per rendere il codice più scalabile. Con futuri sviluppi che andranno fatti (per ora solo solo idee) un utente potrà essere abilitato o al paese o ad un livello più basso, una sorta di regioni, quindi la mia role dovrà diventare:

public class myRole 
{
    public string Country { get; set; }
    public List<string> Region { get; set; }
    public List<string> Roles { get; set; }
}


se eseguo la concatenazione dovrei avere l'array:
cy:it.Region:lombardia.role.ProductEditor
cy:it.Region:piemonte.role.ProductEditor
cy:it.Region:sicilia.role.PageEditor


Inoltre ho paura che l'array diventi parecchio capiente e che superi i 4kb, se non sbaglio è il limite per salvare le role nei cookie.

A questo punto però la concatenazione mi sembra la scelta più adatta... che ne dici?

grazie
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,
al momento la concatenazione è la scelta che ti richiede meno sforzo perché hai già il RoleProvider pronto e quindi ti basta fargli restituire l'array di stringhe.

Però, se pensi che sia un'ipotesi reale che alcuni utenti avranno così tanti ruoli (centinaia?) da raggiungere i 4KB, allora dovresti sin da ora pianificare un altro sistema che non si avvalga del caching nei cookie.

Una soluzione sarebbe quella di mantenere il RoleProvider, il cui metodo GetRolesForUser andrebbe ora in esecuzione ad ogni richiesta.
Se non vuoi concatenare il country al ruolo, puoi anche decidere di fargli restituire solo i ruoli "interessanti", ovvero quelli appartenenti al country/regione corrente, che dovresti essere in grado di stabilire in base al contenuto della richiesta.

L'alternativa, più elaborata, consiste nel rinunciare agli automatismi del RoleProvider per fare tutto "a mano" dal metodo Application_PostAuthenticateRequest del global.asax. In pratica, sostituisci la CurrentPrincipal con una tua implementazione di IPrincipal e popoli una sua proprietà con la lista di MyRole.
In questo caso non servirà crearsi l'attributo di autorizzazione personalizzato perché avrai spostato quella logica nel metodo IsInRole(string role) che l'interfaccia IPrincipal ti chiede di implementare.
In questo caso non devi concatenare i country ai ruoli perché non devi sottostare alla limitazione imposta dal RoleProvider (dato che non lo usi) che ti costringerebbe a restituire array di stringhe.

ciao,
Moreno
Modificato da BrightSoul il 30 marzo 2016 22.16 -

Enjoy learning and just keep making

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.