2016-05-20 19 views
10

Come posso verificare se l'utente ha l'autorizzazione per vedere o query qualcosa? Non ho idea di come farlo.Come verificare le autorizzazioni e le altre condizioni nella query GraphQL?

  • In args? Come funzionerebbe?
  • In resolve()? Vedi se l'utente ha il permesso e in qualche modo eliminare/modificare alcuni degli argomenti?

Esempio:

Se l'utente è "visitatore", può vedere solo i messaggi pubblici, "admin" può vedere tutto.

const userRole = 'admin'; // Let's say this could be "admin" or "visitor" 

const Query = new GraphQLObjectType({ 
    name: 'Query', 
    fields:() => { 
     return { 
      posts: { 
       type: new GraphQLList(Post), 
       args: { 
        id: { 
         type: GraphQLString 
        }, 
        title: { 
         type: GraphQLString 
        }, 
        content: { 
         type: GraphQLString 
        }, 
        status: { 
         type: GraphQLInt // 0 means "private", 1 means "public" 
        }, 
       }, 

       // MongoDB/Mongoose magic happens here 
       resolve(root, args) { 
        return PostModel.find(args).exec() 
       } 
      } 
     } 
    } 
}) 

Aggiornamento - modello Mongoose simile a questa:

import mongoose from 'mongoose' 

const postSchema = new mongoose.Schema({ 
    title: { 
     type: String 
    }, 
    content: { 
     type: String 
    }, 
    author: { 
     type: mongoose.Schema.Types.ObjectId, // From user model/collection 
     ref: 'User' 
    }, 
    date: { 
     type: Date, 
     default: Date.now 
    }, 
    status: { 
     type: Number, 
     default: 0 // 0 -> "private", 1 -> "public" 
    }, 
}) 

export default mongoose.model('Post', postSchema) 

risposta

12

È possibile controllare l'autorizzazione di un utente nella funzione di determinazione o il livello di modello. Ecco i passaggi da eseguire:

  1. Autenticare l'utente prima di eseguire la query. Questo dipende dal tuo server e di solito accade al di fuori di graphql, ad esempio guardando il cookie che è stato inviato insieme alla richiesta. Vedere this Medium post per ulteriori dettagli su come farlo utilizzando Passport.js.
  2. Aggiungere l'oggetto utente autenticato o l'id utente al contesto. In Espresso-graphql lo si può fare tramite l'argomento contesto:

    app.use('/graphql', (req, res) => { 
        graphqlHTTP({ schema: Schema, context: { user: req.user } })(req, res); 
    } 
    
  3. utilizzare il contesto all'interno della funzione risolvere in questo modo:

    resolve(parent, args, context){ 
        if(!context.user.isAdmin){ 
        args.isPublic = true; 
        } 
        return PostModel.find(args).exec(); 
    } 
    

Si può fare controlli di autorizzazione direttamente nelle funzioni Resolve , ma se si dispone di un livello del modello, si consiglia vivamente di implementarlo lì passando l'oggetto utente al livello del modello. In questo modo il tuo codice sarà più modulare, più facile da riutilizzare e non dovrai preoccuparti di dimenticare alcuni controlli in un resolver da qualche parte.

per più di fondo su autorizzazione, controllare questo post (scritto anche da me): Auth in GraphQL - part 2

+1

Se si utilizza Mangusta, è più semplice eseguire i controlli nelle funzioni di risoluzione. Non ho molta familiarità con Mongoose, ma ho scritto un tutorial per server GraphQL che usa Mongoose e SQLite qualche tempo fa. Usa apolloServer al posto di express-graphql. Sembra un po 'diverso, ma per risolvere le funzioni e il recupero dei dati tutto è praticamente lo stesso. Puoi trovarlo [qui] (https://medium.com/apollo-stack/tutorial-building-a-graphql-server-cddaa023c035). – helfer

+1

Sì, è possibile restituire null o lanciare un errore e GraphQL smetterà di andare giù per quel ramo della query. Se è necessario verificare le autorizzazioni in base a elementi nel database (ad esempio ruoli o qualcosa), è possibile concatenare le promesse. Prima recupera l'elemento e le autorizzazioni relative all'elemento, quindi quando viene restituito controlla se l'utente ha l'autorizzazione. Se l'utente non dispone dell'autorizzazione, genera un errore o restituisce null. Se l'utente ha il permesso, restituire l'oggetto (o qualcosa del genere). – helfer

8

Un approccio che ci ha aiutato a risolvere l'autorizzazione presso la nostra azienda è quello di pensare a resolver come una composizione di middleware. L'esempio di cui sopra è ottimo ma diventerà indisciplinato su scala in particolare man mano che i meccanismi di autorizzazione diventeranno più avanzati.

Un esempio di un resolver come una composizione di middleware potrebbe essere simile a questo:

type ResolverMiddlewareFn = 
    (fn: GraphQLFieldResolver) => GraphQLFieldResolver; 

Un ResolverMiddlewareFn è una funzione che prende un GraphQLFieldResolver ed e restituisce un GraphQLFieldResolver.

Per comporre le nostre funzioni middleware del risolutore useremo (avete indovinato) la funzione di composizione! Ecco uno example of compose implementato in javascript, ma è anche possibile trovare le funzioni di composizione in ramda e altre librerie funzionali. Compose ci consente di combinare semplici funzioni per rendere più complicate le funzioni.

Tornando al problema delle autorizzazioni GraphQL è possibile osservare un semplice esempio. Supponiamo che vogliamo registrare il resolver, autorizzare l'utente e quindi eseguire la carne e le patate. Compose ci consente di combinare questi tre pezzi in modo tale che possiamo facilmente testarli e riutilizzarli attraverso la nostra applicazione.

const traceResolve = 
    (fn: GraphQLFieldResolver) => 
    async (obj: any, args: any, context: any, info: any) => { 
    const start = new Date().getTime(); 
    const result = await fn(obj, args, context, info); 
    const end = new Date().getTime(); 
    console.log(`Resolver took ${end - start} ms`); 
    return result; 
    }; 

const isAdminAuthorized = 
    (fn: GraphQLFieldResolver) => 
    async (obj: any, args: any, context: any, info: any) => { 
    if (!context.user.isAdmin) { 
     throw new Error('User lacks admin authorization.'); 
    } 
    return await fn(obj, args, context, info); 
    } 

const getPost = (obj: any, args: any, context: any, info: any) => { 
    return PostModel.find(args).exec(); 
} 

const getUser = (obj: any, args: any, context: any, info: any) => { 
    return UserModel.find(args).exec(); 
} 

// You can then define field resolve functions like this: 
postResolver: compose(traceResolve, isAdminAuthorized)(getPost) 

// And then others like this: 
userResolver: compose(traceResolve, isAdminAuthorized)(getUser) 
+0

ottima risposta! grazie Michael –