Metodo dedicato vs metodo con parametro

1

Supponiamo di avere un permissionManager che ci dice se l'utente ha alcuni privilegi per qualche azione o meno. Ne abbiamo parecchio (dozzine, forse più).

Vedo due modi per implementare i controlli per un utente, ad esempio:

  1. Metodo dedicato: permissionManager.canAdminister(user);

  2. Metodo con un parametro: permissionManager.hasPermission(Permissions.ADMINISTER, user);

(dove Permissions è una classe con un gruppo di campi statici per ogni autorizzazione o uno spazio dei nomi con costanti globali)

Ci sono pro e contro in entrambi i modi, vedo questi:

  1. Metodo dedicato

    • Il monitoraggio degli utilizzi e l'aggiunta di ulteriore registrazione / validazione / ecc. al metodo è più semplice (è sufficiente modificare il metodo specifico).

    • Nessuna entità aggiuntiva e variabili globali.

  2. Metodo con un parametro:

    • Più controlli per le autorizzazioni è più semplice (solo un ciclo con convalida sequenziale di ogni autorizzazione).

    • Possibilità di avere una combinazione di permessi (bit o stile ADMINISTER | VIEW ).

Qualche cons / pro aggiuntivo per ogni implementazione?

Cosa è più gestibile e preferibile?

PS. forse spostare la funzionalità a User è ancora meglio, come: user.hasPermission(Permissions.ADMINISTER) ma questo si rompe Principio di responsabilità singola.

    
posta Oleg Kovalov 13.06.2017 - 20:48
fonte

5 risposte

0

A volte, il fatto che tu abbia questa scelta da fare è di per sé un odore. Ti dice che forse ti manca un'astrazione, o che il comportamento non è seduto nel posto giusto. Questo potrebbe o non potrebbe essere vero nel tuo caso particolare, quindi affronterò la tua domanda direttamente.

La scelta preferita dipende da come appariranno i chiamanti. Se il tipo di parametro aggiuntivo verrà passato come variabile, utilizzare il parametro extra. Ad esempio:

if (permissionsManager.hasPermission(requiredPermission, user)) {
    doSomething();
}

Poiché il permesso è una variabile, dovremmo usare qualcosa come un'istruzione case-case per sapere quale metodo chiamare se esistessero metodi separati, e che sarebbe molto meno leggibile.

Allo stesso modo, se ti aspetti che avresti bisogno di testare una combinazione arbitraria di permessi , usa il parametro extra. In questo caso, un metodo separato come hasPermissions potrebbe richiedere qualcosa come EnumSet . Il parametro extra per hasPermission migliorerebbe la leggibilità attraverso la coerenza del test sempre per le autorizzazioni in uno stile simile.

D'altra parte, se passi solo in un singolo valore hard-coded, vai con il metodo separato. In questo caso, afferma più chiaramente la tua intenzione. Ad esempio:

if (permissionsManager.hasPermission(Permission.ADMINISTER, user)) {
    doSomething();
}

non è buono come:

if (permissionsManager.canAdminister(user)) {
    doSomething();
}
    
risposta data 14.06.2017 - 03:30
fonte
1

Nella logica principale vorrei vedere

if ( user.HasPermission(Permission.Delete) )
{
    DeleteSomething();
}

Nel metodo HasPermission avresti qualcosa di meno leggibile, più tecnico, di basso livello come

return PermissionManager.UserHasPermission(this.Id, permission);
    
risposta data 13.06.2017 - 23:26
fonte
1

Mi piace sempre risolvere problemi come questo guardando al codice:

sc = new SecuredCommands( permissions.for(user) );
sc.delete("foo.txt");

Questo sembra ben disaccoppiato e non richiede un mix di responsabilità.

Tutto ciò che ci interessa quando proviamo a emettere il comando è se è permesso. Non dobbiamo sapere chi è l'utente a quel punto. Quindi tutto ciò di cui dipendiamo direttamente sono le autorizzazioni.

Dimostrerò come questo disaccoppiamento aiuta. Guarda come aggiungo il concetto di ruoli senza causare molto dolore:

sc = new SecuredCommands(  permissions.for( rolls.for(user) )  );
sc.delete("foo.txt");

Questo, ovviamente, presuppone che tu non abbia fatto qualcosa di sciocco come dispersione sc di costruzione ovunque.

Oh, e delete dovrebbe essere un metodo se i comandi non cambiano. Se è probabile che vengano aggiunti nuovi comandi successivamente, trasformali in un oggetto.

    
risposta data 13.06.2017 - 21:32
fonte
0

Sono un fan di controllo degli accessi basato sugli attributi perché è estremamente flessibile e non dipende su autorizzazioni o ruoli esplicitamente enumerati. Una forma semplice di questo tipo di controllo degli accessi sarebbe simile a questa:

interface Policy {
    /**
     * Returns true if the given subject is allowed to perform the given 
     * action on the given resource in the given environment
     */
    def isAuthorized(
        subject: User, 
        action: Action, 
        resource: Resource, 
        environment: Environment
    ): Boolean
}

Ecco alcuni esempi di politiche

/**
 * Policy that grants access to everything if user has ADMIN role
 */
class AdministerPolicy extends Policy {
    override def isAuthorized(
        subject: User, 
        action: Action, 
        resource: Resource, 
        environment: Environment
    ): Boolean = subject.roles.contains(ADMIN)
}

/**
 * Policy that grants read-only access to anyone
 */
class ReadPolicy extends Policy {
    override def isAuthorized(
        subject: User, 
        action: Action, 
        resource: Resource, 
        environment: Environment
    ): Boolean = action == READ
}

/**
 * Collection of policies that grants access if one of the child policies
 * grants access
 */
class PolicyCollection(policies: List[Policy]) extends Policy {
    override def isAuthorized(
        subject: User, 
        action: Action, 
        resource: Resource, 
        environment: Environment
    ): Boolean = policies.find(
        p => p.isAuthorized(
            subject, 
            action, 
            resource,  
            environment
        )
    ).map(_ => true).getOrElse(false)
}

Al punto di applicazione della tua autorizzazione, puoi iniettare Policy e invocare isAuthorized prima di eseguire l'azione.

Questa strategia di autorizzazione consente anche di estrarre l'autorizzazione di autorizzazione dal codice dell'applicazione. Il motivo per cui si potrebbe desiderare di farlo è che rende il codice dell'applicazione meno leggibile se disseminato di chiamate di autorizzazione. IMO è meglio mantenere la logica di autorizzazione fuori dalla logica aziendale. Invece l'autorizzazione dovrebbe essere gestita e testata separatamente.

Ad esempio, se stai controllando l'accesso a un servizio web, sarebbe facile implementare un filtro HTTP che accetta il metodo HTTP - > azione e URI - > risorsa e prende la decisione di autorizzazione senza che ogni endpoint effettui esplicitamente chiamate di autorizzazione.

    
risposta data 13.06.2017 - 23:21
fonte
0

Se il tipo di permesso è solo qualcosa che passi come dati, e in realtà non hai chiamate specifiche nel codice che lo accompagnano, e / o le autorizzazioni possono essere definite dagli utenti finali e devono essere flessibili ed estensibile, fornirei l'argomento e userei una chiamata di funzione generalizzata.

Se il tipo di permesso che stai tentando di verificare ha implicazioni specifiche in fase di compilazione (ad esempio, una determinata interfaccia è supportata), allora è adatto un metodo o una funzione dedicata in fase di compilazione.

Ad esempio, se le tue autorizzazioni corrispondono a oggetti (come documenti o pagine web) o azioni enumerabili (come delegati, stored procedure o percorsi di navigazione) e l'elenco di oggetti / azioni cresce nel tempo, ti consigliamo di generalizzare funzione con un argomento.

Ma se il tuo permesso si riferisce a un'implementazione, significa amministrazione funzioni e forse un'interfaccia IAdministration , con tutti i mezzi, verifica il controllo con hardcode. Dopo tutto, anche l'interfaccia è hardcoded, quindi non è come diminuire la flessibilità del sistema.

Potresti anche provare qualcosa di simile:

var user = GetUserSomehow();
var admin = user.GetPolicy().GetAdminInterface();
if (admin!= null) 
    admin.DoSomethingSerious();

Questo tipo di schema ti impedirebbe di chiamare accidentalmente funzioni di amministrazione per un utente che non può averle, ad es. con questo tipo di codice (errato):

var user = GetUserSomehow();
if (user != null)
{
    var admin = GetAdminInterfaceSomehow();
    if (admin != null)
        if (user.CanAdminister)
             log.Debug("Doing something serious");
             admin.DoSomethingSerious(); //Do you see the problem?
}    
    
risposta data 14.06.2017 - 01:51
fonte