Skip to content

Commit

Permalink
Operation Field Permissions (#4499)
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Sep 14, 2022
1 parent ee1cb6f commit 077e65c
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 30 deletions.
6 changes: 6 additions & 0 deletions .changeset/angry-seals-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@graphql-mesh/plugin-operation-field-permissions': patch
'@graphql-mesh/types': patch
---

New Operation Field Permissions plugin
13 changes: 6 additions & 7 deletions examples/auth0/.meshrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,7 @@ sources:
field: secret
path: /
method: POST
headers:
'x-auth-sub': '{context._auth0.sub}'
responseByStatusCode:
200:
responseSample: { code: 'MY_SECRET' }
401:
responseSample: { errorMessage: 'You need an Auth0 sub to continue!' }
responseSample: { code: 'MY_SECRET', timestamp: 0 }

additionalTypeDefs: |
"""
Expand Down Expand Up @@ -44,6 +38,11 @@ plugins:
extendContextField: '_auth0'
# No need to prevent unauthorized access
preventUnauthorizedAccess: false
- operationFieldPermissions:
permissions:
- if: 'context._auth0?.sub != null'
allow:
- '*'

serve:
staticFiles: public
22 changes: 7 additions & 15 deletions examples/auth0/privateAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,13 @@ const http = require('http');

http
.createServer((req, res) => {
if (req.headers['x-auth-sub']) {
res.writeHead(200, { 'Content-Type': 'applcation/json' });
res.end(
JSON.stringify({
code: req.headers['x-auth-sub'] + 's code',
})
);
} else {
res.writeHead(401, { 'Content-Type': 'applcation/json' });
res.end(
JSON.stringify({
errorMessage: 'You need an Auth0 sub to continue!',
})
);
}
res.writeHead(200, { 'Content-Type': 'applcation/json' });
res.end(
JSON.stringify({
code: 'I am a secret code',
timestamp: Date.now(),
})
);
})
.listen(3001, 'localhost', () => {
console.info(`Private API listening; http://localost:4001`);
Expand Down
40 changes: 40 additions & 0 deletions packages/plugins/operation-field-permissions/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@graphql-mesh/plugin-operation-field-permissions",
"version": "0.0.0",
"sideEffects": false,
"main": "dist/index.js",
"module": "dist/index.mjs",
"typings": "dist/index.d.ts",
"typescript": {
"definition": "dist/index.d.ts"
},
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./*": {
"require": "./dist/*.js",
"import": "./dist/*.mjs"
}
},
"license": "MIT",
"repository": {
"type": "git",
"url": "Urigo/graphql-mesh",
"directory": "packages/plugins/operation-field-permissions"
},
"peerDependencies": {
"graphql": "*"
},
"dependencies": {
"@graphql-mesh/types": "0.83.5",
"@graphql-mesh/cross-helpers": "0.2.6",
"@envelop/operation-field-permissions": "3.6.0",
"tslib": "^2.4.0"
},
"publishConfig": {
"access": "public",
"directory": "dist"
}
}
29 changes: 29 additions & 0 deletions packages/plugins/operation-field-permissions/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* eslint-disable no-new-func */
import { MeshPlugin, MeshPluginOptions, YamlConfig } from '@graphql-mesh/types';
import { process } from '@graphql-mesh/cross-helpers';
import { useOperationFieldPermissions } from '@envelop/operation-field-permissions';

export default function useMeshOperationFieldPermissions(
options: MeshPluginOptions<YamlConfig.OperationFieldPermissionsConfig>
): MeshPlugin<any> {
return {
onPluginInit({ addPlugin }) {
addPlugin(
useOperationFieldPermissions({
getPermissions(context) {
const allowedFields = new Set<string>();
for (const { if: condition, allow } of options.permissions) {
const ifFn = new Function('context', 'env', 'return ' + condition);
if (ifFn(context, process.env)) {
for (const allowedField of allow) {
allowedFields.add(allowedField);
}
}
}
return allowedFields;
},
})
);
},
};
}
12 changes: 12 additions & 0 deletions packages/plugins/operation-field-permissions/yaml-config.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
extend type Plugin {
operationFieldPermissions: OperationFieldPermissionsConfig
}

type OperationFieldPermissionsConfig @md {
permissions: [OperationFieldPermission]
}

type OperationFieldPermission {
if: String
allow: [String]
}
34 changes: 34 additions & 0 deletions packages/types/src/config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,9 @@
"newrelic": {
"$ref": "#/definitions/NewrelicConfig"
},
"operationFieldPermissions": {
"$ref": "#/definitions/OperationFieldPermissionsConfig"
},
"prometheus": {
"$ref": "#/definitions/PrometheusConfig"
},
Expand Down Expand Up @@ -2140,6 +2143,37 @@
}
}
},
"OperationFieldPermissionsConfig": {
"additionalProperties": false,
"type": "object",
"title": "OperationFieldPermissionsConfig",
"properties": {
"permissions": {
"type": "array",
"items": {
"$ref": "#/definitions/OperationFieldPermission"
},
"additionalItems": false
}
}
},
"OperationFieldPermission": {
"additionalProperties": false,
"type": "object",
"title": "OperationFieldPermission",
"properties": {
"if": {
"type": "string"
},
"allow": {
"type": "array",
"items": {
"type": "string"
},
"additionalItems": false
}
}
},
"PrometheusConfig": {
"additionalProperties": false,
"type": "object",
Expand Down
8 changes: 8 additions & 0 deletions packages/types/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1728,6 +1728,7 @@ export interface Plugin {
liveQuery?: LiveQueryConfig;
mock?: MockingConfig;
newrelic?: NewrelicConfig;
operationFieldPermissions?: OperationFieldPermissionsConfig;
prometheus?: PrometheusConfig;
rateLimit?: RateLimitPluginConfig;
responseCache?: ResponseCacheConfig;
Expand Down Expand Up @@ -1863,6 +1864,13 @@ export interface NewrelicConfig {
*/
extractOperationName?: string;
}
export interface OperationFieldPermissionsConfig {
permissions?: OperationFieldPermission[];
}
export interface OperationFieldPermission {
if?: string;
allow?: string[];
}
export interface PrometheusConfig {
requestCount?: boolean;
requestTotalDuration?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

* `permissions` (type: `Array of Object`):
* `if` (type: `String`)
* `allow` (type: `Array of String`)
20 changes: 13 additions & 7 deletions website/src/pages/docs/guides/auth0.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,17 @@ In the GraphQL schema of this guide we only re-expose the auth0 authentication i
You can use the context object inside your handlers or anywhere else in Mesh;

```yaml
sources:
- name: Test
handler:
graphql:
endpoint: "http://some_graphql.com/graphql"
operationHeaders:
"x-user-sub": "{context._auth0.sub}"
plugins:
- auth0:
domain: '{account_name}.{region}.auth0.com'
audience: 'http://localhost:3000/graphql'
extendContextField: '_auth0'
# No need to prevent unauthorized access
preventUnauthorizedAccess: false
# You can combine this with `@graphql-mesh/plugin-operation-field-permissions`
- operationFieldPermissions:
permissions:
- if: "context._auth0?.sub != null"
allow:
- "*"
```
10 changes: 9 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2072,7 +2072,7 @@
"@envelop/types" "2.4.0"
tslib "2.4.0"

"@envelop/extended-validation@1.9.0":
"@envelop/extended-validation@1.9.0", "@envelop/extended-validation@^1.9.0":
version "1.9.0"
resolved "https://registry.yarnpkg.com/@envelop/extended-validation/-/extended-validation-1.9.0.tgz#a4634452797177e718302a8645a7c8d5381fe09b"
integrity sha512-Kf0xkTKRuP1XEgHa5Pi8+zaCUGYnFyiGPkb24Z/X1zKd8+s2gtuBmaw6GoROgaBoK0XIE+ZYne7BZCheUqfp+A==
Expand All @@ -2098,6 +2098,14 @@
dependencies:
tslib "^2.4.0"

"@envelop/operation-field-permissions@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@envelop/operation-field-permissions/-/operation-field-permissions-3.6.0.tgz#fda6451893d1568baff4edec4bb7ee12a92b644c"
integrity sha512-zAh19EMJ3TLco5OsLW1On3M41zej4lkqKL//gfYGFRtGB2x/Hnuc5/9BIVEDb8l0zmCkD9bqgWHoS3NfEnwB7w==
dependencies:
"@envelop/extended-validation" "^1.9.0"
tslib "^2.4.0"

"@envelop/parser-cache@^4.4.0", "@envelop/parser-cache@^4.6.0":
version "4.7.0"
resolved "https://registry.yarnpkg.com/@envelop/parser-cache/-/parser-cache-4.7.0.tgz#fc438d8ed390c88fa24bf56da3e4da36f088e3fc"
Expand Down

0 comments on commit 077e65c

Please sign in to comment.