objection-graphql
objection-graphql copied to clipboard
How to implement access control?
Hi there!
I am trying to implement a GraphQL server with Objection.js to handle database queries, and of course this library to build the GraphQL schema. And for security measures, some access control needs to be implemented, as it is undesirable to have let's say a user querying another user's data in Users
table. Assuming there are common (normal user privilege) and admin user roles in the table, the access controls should be implemented is as follows:
- Unauthenticated users cannot query from the table
- Authenticated (common) users can only query their own info
- Authenticated (admin) users can query all user info
How can we achieve such access control with this library?
Thanks in advance!
AHH! I'm terribly sorry, but I only just saw #56 and the access control example folder. But, I still don't understand what exactly needs to be done here to achieve such access control described above. But from what I understand, the objectives (according to #56) are:
- Pass
context
intographqlHTTP
'srootValue
argument withonQuery
function - Create a custom model template class by extending
Model
and implement new abstract methods where it will be overridden by actual models (in this case,User
model) - Override the
QueryBuilder
of the custom model to use the new abstract methods which are used to control the query and result
Is this correct, or is there a better solution to access control?
I finally managed to figure something out myself:
// app.js - Main file, graphql route
app.use('/graphql', isLoggedIn, (req, res, next) => graphql({
schema,
context: loggedUser,
graphiql: true,
rootValue: {
onQuery: async (qb) => {
await qb.mergeContext({ loggedUser, isGraphQLQuery: true });
},
},
})(req, res, next));
// user.js - Model file
const { Model } = require('objection');
class User extends Model {
static get tableName() {
return 'user';
}
static get jsonSchema() {
return {
type: 'object',
required: ['name'],
properties: {
id: { type: 'integer' },
name: { type: 'string' },
},
};
}
static query(...args) {
const query = super.query(...args);
return query.onBuild((qb) => {
const ctx = qb.context();
const { loggedUser: user, isGraphQLQuery } = ctx;
if (user.id !== 1 && isGraphQLQuery) return qb.where('id', user.id);
return qb;
});
}
}
module.exports = User;
What this basically does is if the authenticated user's id is not 1 (let's say this one is an admin), then the user cannot query for the other user info.
Though, I really feel this is a really hacky solution as this adds another AND user.id = ?
to the WHERE
clause resulting in something like this when querying {user(id: 2)}
when logged in as user with id 3
:
SELECT id, name FROM user WHERE id = 2 AND user.id = 3
So, would anybody share an improvement to this solution or even provide a better solution?
Thanks!