🚀 Nuova versione beta disponibile! Feedback o problemi? Contattaci

Gestione di Ruoli e Permessi in GraphQL: Best Practices e Implementazione

Codegrind Team•Aug 28 2024

La gestione di ruoli e permessi è cruciale per proteggere le tue API GraphQL e garantire che solo gli utenti autorizzati possano accedere e modificare i dati sensibili. Con una gestione efficace dei ruoli e dei permessi, puoi controllare con precisione chi può eseguire determinate operazioni o accedere a specifiche risorse. In questa guida, esploreremo come implementare ruoli e permessi in GraphQL, utilizzando best practices e strumenti per garantire la sicurezza e l’efficienza delle tue API.

Introduzione ai Ruoli e Permessi

I ruoli e i permessi consentono di definire e applicare regole di accesso granulare all’interno della tua API. I ruoli rappresentano gruppi di utenti con privilegi simili, mentre i permessi definiscono le azioni specifiche che possono essere eseguite o le risorse a cui si può accedere.

Esempio di Ruoli e Permessi

  • Admin: Può eseguire qualsiasi operazione su qualsiasi risorsa.
  • Editor: Può modificare e pubblicare contenuti, ma non gestire utenti.
  • Viewer: Può solo visualizzare i contenuti, senza possibilitĂ  di modifica.

Autenticazione e Autorizzazione

Prima di implementare ruoli e permessi, è necessario disporre di un meccanismo di autenticazione per identificare gli utenti. Una volta autenticato un utente, è possibile applicare l’autorizzazione per determinare se può eseguire una specifica operazione.

Utilizzare un Middleware per l’Autenticazione

In Apollo Server, puoi utilizzare middleware per autenticare gli utenti e associare un ruolo all’utente nel contesto della richiesta.

Esempio di Implementazione

const { ApolloServer, AuthenticationError } = require("apollo-server");

const getUser = (token) => {
  // Simulazione di un metodo per ottenere l'utente dal token
  const users = {
    token1: { id: 1, role: "admin" },
    token2: { id: 2, role: "editor" },
    token3: { id: 3, role: "viewer" },
  };
  return users[token];
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => {
    const token = req.headers.authorization || "";
    const user = getUser(token);

    if (!user) throw new AuthenticationError("Non autenticato");

    return { user };
  },
});

Applicare i Permessi ai Resolvers

Una volta autenticato l’utente e assegnato un ruolo, puoi applicare i permessi direttamente nei resolvers utilizzando condizioni basate sul ruolo dell’utente.

Esempio di Resolvers con Controllo di Permessi

const { ForbiddenError } = require("apollo-server");

const resolvers = {
  Query: {
    user: (parent, args, context) => {
      if (context.user.role !== "admin") {
        throw new ForbiddenError("Accesso negato");
      }
      return getUserById(args.id);
    },
    posts: (parent, args, context) => {
      if (context.user.role === "viewer") {
        throw new ForbiddenError("Accesso negato");
      }
      return getAllPosts();
    },
  },
  Mutation: {
    createPost: (parent, args, context) => {
      if (context.user.role !== "admin" && context.user.role !== "editor") {
        throw new ForbiddenError("Accesso negato");
      }
      return createNewPost(args);
    },
    deletePost: (parent, args, context) => {
      if (context.user.role !== "admin") {
        throw new ForbiddenError("Accesso negato");
      }
      return deletePostById(args.id);
    },
  },
};

In questo esempio, le query e le mutazioni controllano il ruolo dell’utente nel contesto. Se l’utente non ha i permessi necessari, viene lanciato un errore ForbiddenError.

Utilizzare Direttive per l’Autorizzazione

Le direttive personalizzate offrono un modo elegante per applicare i controlli di autorizzazione direttamente nello schema GraphQL.

Creare una Direttiva Personalizzata @auth

Puoi creare una direttiva @auth per gestire l’autorizzazione basata sui ruoli.

Definizione della Direttiva nello Schema

directive @auth(role: String!) on FIELD_DEFINITION

type Query {
  user(id: ID!): User @auth(role: "admin")
  posts: [Post!]! @auth(role: "editor")
}

type Mutation {
  createPost(title: String!, content: String!): Post @auth(role: "editor")
  deletePost(id: ID!): Boolean @auth(role: "admin")
}

Implementazione della Direttiva

const { SchemaDirectiveVisitor } = require("apollo-server");
const { defaultFieldResolver } = require("graphql");

class AuthDirective extends SchemaDirectiveVisitor {
  visitFieldDefinition(field) {
    const { resolve = defaultFieldResolver } = field;
    const { role } = this.args;

    field.resolve = async function (...args) {
      const context = args[2];
      if (context.user.role !== role) {
        throw new ForbiddenError("Non autorizzato");
      }
      return resolve.apply(this, args);
    };
  }
}

const server = new ApolloServer({
  typeDefs,
  resolvers,
  schemaDirectives: {
    auth: AuthDirective,
  },
  context: ({ req }) => {
    const token = req.headers.authorization || "";
    const user = getUser(token);

    if (!user) throw new AuthenticationError("Non autenticato");

    return { user };
  },
});

In questo esempio, la direttiva @auth controlla il ruolo dell’utente prima di consentire l’accesso ai campi protetti.

Gestione Centralizzata dei Permessi

Per mantenere il codice piĂą pulito e manutenibile, considera la possibilitĂ  di centralizzare la gestione dei permessi, creando funzioni helper che verificano i ruoli e i permessi.

Creare Funzioni Helper per i Permessi

const hasRole = (user, role) => {
  if (user.role !== role) {
    throw new ForbiddenError("Non autorizzato");
  }
};

const canEditPost = (user) => {
  if (user.role !== "admin" && user.role !== "editor") {
    throw new ForbiddenError("Non autorizzato");
  }
};

const resolvers = {
  Query: {
    user: (parent, args, context) => {
      hasRole(context.user, "admin");
      return getUserById(args.id);
    },
    posts: (parent, args, context) => {
      canEditPost(context.user);
      return getAllPosts();
    },
  },
  Mutation: {
    createPost: (parent, args, context) => {
      canEditPost(context.user);
      return createNewPost(args);
    },
    deletePost: (parent, args, context) => {
      hasRole(context.user, "admin");
      return deletePostById(args.id);
    },
  },
};

Best Practices per la Gestione di Ruoli e Permessi

  • Minimo Privilegio: Assegna agli utenti il minimo livello di accesso necessario per eseguire le loro funzioni.
  • Separazione dei Compiti: Evita di assegnare tutti i permessi a un solo ruolo. Distribuisci le responsabilitĂ  tra ruoli diversi.
  • Logging e Monitoraggio: Logga e monitora tutte le operazioni sensibili per individuare accessi non autorizzati.
  • Test di Sicurezza: Esegui test di sicurezza regolari per assicurarti che i controlli di autorizzazione siano correttamente implementati.
  • Documentazione dei Ruoli: Mantieni una documentazione chiara sui ruoli e permessi, per facilitare la gestione e la comprensione delle politiche di accesso.

Conclusione

Implementare ruoli e permessi in GraphQL è essenziale per garantire la sicurezza e la corretta gestione delle risorse all’interno della tua API. Utilizzando middleware, direttive personalizzate, e best practices per la gestione dei permessi, puoi creare un sistema flessibile e sicuro che protegge i dati sensibili e garantisce che solo gli utenti autorizzati possano accedere o modificare le informazioni critiche.