Giter Site home page Giter Site logo

Comments (8)

maticzav avatar maticzav commented on May 5, 2024 1

I have given a bit more thought to your problem. I am not sure this is your case, but here are two things that you can do.

1.

Because shield accepts a JSON object as an argument defining your schema, you could use Object.assign or ES6 merge syntax to merge your permissions into one.

// Query.allAuthStuffPermissions.ts
export const permissions = {
  login: noAuth,
  somethingElse: auth
}

// Query.somethingElseEquallyImportant.ts
export const permissions = {
  fruits: complexRule,
  vegies: simpleRule
}

// index.ts
import { permissions as permissions1 } from 'Query.allAuthStuffPermissions.ts'
import { permissions as permissions2 } from 'Query.somethingElseEquallyImportant.ts'

export default shield({
  Query: {
    ...permissions1,
    ...permissions2,
  }
})

2.

You can logically combine your permissions using logic operators. Available operators are and, or and not. You can read more about them here https://github.com/maticzav/graphql-shield#and-or-not

from graphql-shield.

miketdonahue avatar miketdonahue commented on May 5, 2024 1

@leonelpena and others who come across this issue... To accomplish what the OP and myself are looking to do, you can use a library called "assign-deep". https://www.npmjs.com/package/assign-deep.

It merges the permissions together correctly allowing the following:

// file1.js
export default {
  Query: {
    getSecurityQuestions: allow,
  },
  SecurityQuestion: {
    shortName: allow,
  },
};
// file2.js
export default {
  Query: {
    getUser: allow,
  },
};
// permissionsArray is an array of the permissions above like...
// [{Query: {getSecurityQuestions: allow}}, {Query: {getUser: allow}}]

import assign from 'assign-deep';

const permissions = shield(assign(...permissionsArray), {
  debug: config.get('graphql.debug'),
  fallbackRule: deny,
  fallbackError: ApiError('UNAUTHORIZED'),
});

This produces the correct output that graphql-shield needs to operate.

from graphql-shield.

maticzav avatar maticzav commented on May 5, 2024

Hey @geminiyellow 👋

I love your suggestion!

Could you give a more concrete example of your case, and maybe explain the issues you are facing now with the current API?

This way we can discuss the API first and later cover a broader problem.

from graphql-shield.

maticzav avatar maticzav commented on May 5, 2024

I will close this issue as it seems like it has been solved.

from graphql-shield.

leonelpena avatar leonelpena commented on May 5, 2024

I tried the same approached of merging permissions and I thought the mergeResolvers function provided by https://github.com/okgrow/merge-graphql-schemas could do the work. I read its source code and thought as both structure (permissions vs. resolvers) are almost the same, maybe it could work. So I tried the following:

I have a folder with middlewares which consist of: index.js, rules.js and permissions folder.

rules.js:

export const isAuthenticated = rule()(async (parent, args, ctx, info) => {
  console.log("isAuthenticated...");
  return getUserFromCtx(ctx) !== null;
});

export const isAdmin = rule()(async (parent, args, ctx, info) => {
  console.log("isAdmin...");
  return hasRole(getUserFromCtx(ctx).roles, "ADMIN");
});

Inside permissions folder there are two files: user1.js and user2.js. I made this just for experimental purposes.

user1.js:

import { isAdmin } from "../rules";

const user1Permissions = {
  Query: {
    user: isAdmin,
  },
};

export default user1Permissions;

user2.js:

import { and, or, not } from "graphql-shield";
import { isAuthenticated, isAdmin } from "../rules";

const user2Permissions = {
  Query: {
    allUsers: and(isAuthenticated, isAdmin),
    currentUser: isAuthenticated,
  },
};

export default user2Permissions;

index.js

import path from 'path';
import {
  fileLoader,
  mergeResolvers
} from 'merge-graphql-schemas';
import { shield } from "graphql-shield";

const permissions = shield(mergeResolvers(
  fileLoader(path.join(__dirname, './permissions'), { extensions: ['.js'] })
));

const middlewares = [
  permissions
];

export default middlewares;

My idea is to split the permissions and placed them into the subfolders that are organized by entities which contains the types and resolvers, this way:

+-- entities
|    +-- User
|    |   +-- user.type.gpl
|    |   +-- user.resolver.js
|    |   +-- user.permissions.js

I also have logged the follow:

Doing:

console.log(JSON.stringify(fileLoader(path.join(__dirname, './permissions'), { extensions: ['.js'] })));

I get:

[{"Query":{"user":{"name":"0.5497311718774505","cache":"contextual"}}},{"Query":{"allUsers":{"rules":[{"name":"0.12993142526487356","cache":"contextual"},{"name":"0.5497311718774505","cache":"contextual"}]},"currentUser":{"name":"0.12993142526487356","cache":"contextual"}}}]

Doing:

console.log(JSON.stringify(mergeResolvers(
  fileLoader(path.join(__dirname, './permissions'), { extensions: ['.js'] })
)));

I get:

{"Query":{"user":{"name":"0.5497311718774505","cache":"contextual"},"allUsers":{"rules":[{"name":"0.12993142526487356","cache":"contextual"},{"name":"0.5497311718774505","cache":"contextual"}]},"currentUser":{"name":"0.12993142526487356","cache":"contextual"}}}

Doing:

const permissions = shield(mergeResolvers(
  fileLoader(path.join(__dirname, './permissions'), { extensions: ['.js'] })
));
console.log(permissions);

I get: MiddlewareGenerator { generator: [Function: generator] }

However when I query the endpoint there is no validation for user, both isAdmin and isAuthenticated are not executed. There is no console.log being executed ("isAuthenticated...", "isAdmin...") from rules.js.

It should be noted that without doing all of these the permissions layer works fine.

So, do you know if this could work? and why it is not working?

from graphql-shield.

miketdonahue avatar miketdonahue commented on May 5, 2024

@maticzav - This above post is exactly why we need a merge function. It is all about modularity and re-composability. I want to break apart my permissions entirely and merge them back together in a central location.

from graphql-shield.

blazestudios23 avatar blazestudios23 commented on May 5, 2024

Thanks. I have several large massive data sets and it makes no sense to create a thousand line file when each set should have their own permissions and I am connected to each one separately.

I'm using Nexus and graphql-binding depending on the endpoint. It would be able to lock down whole sets of my data with just one check.

for example if I have

fil1.ts
file2.ts
file3.ts

each represents the permissions for a different GraphQL endpoint. I want to be able to merge them all and set one rule that could lock down all of file2.

I feel that when libraries are tested they use the simplest data set possible with only 3 tables. When you have 10 databases or more each with 30 or more tables you don't want to have write rules for every single query/mutation and molecularity is very important. If anything changes, a like a new query added I have to go and change the huge files, when it would make more sense for me to be able to just lock down all mutations on one endpoint with one rule.

from graphql-shield.

bigman73 avatar bigman73 commented on May 5, 2024

FWIW, I use loaddash.merge, it merges recurisevly and correctly unlike the ... operator which causes the last permission rules to override the others in case of a conflict (e.g. Query)

      const permissionRules = merge(
        {},
        settings.servicePermissionRules ?? {},
        builtInPermissionRules
      )

from graphql-shield.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.