Skip to content

Commit

Permalink
Authorization (#145)
Browse files Browse the repository at this point in the history
* Authorization - fully implemented registry part (#133)

This includes:
Create/update policy resource
Attachments support for policy resource (with support for writing the attachment to both s3 and fs repositories)
Opa policy type implementation, including compiling rego code to wasm and adding that to the policy as an attachment

* Authorization gateway - basic features (#138)

implemented full flow with basic features
Implement local policy attachment caching for all resource repositories

* Add policy definitions and attachments to request context, change pol… (#141)

* Add policy definitions and attachments to request context, change policy executor to use them from context instead of directly from repo

* PR comments

* allow jwt in param injection (policy authorization can use it through args) (#144)

* Support for policy query (#143)

* Policy directive - accept only a single policy (#146)

* change policy directive to accept only a single policy

* Refactored PolicyExecutor API to only expose static methods

Co-authored-by: Tomer Eskenazi <tomeresk@gmail.com>
  • Loading branch information
AleF83 and tomeresk authored Jul 2, 2020
1 parent 0958914 commit 2a02f20
Show file tree
Hide file tree
Showing 56 changed files with 10,942 additions and 7,796 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ lerna-debug.log*

node_modules/
*.tsbuildinfo
.yarn-integrity
.yarn-integrity
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"debug.node.autoAttach": "on"
}
90 changes: 48 additions & 42 deletions docs/authorization_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
Create a new object policy that describes an access logic that can be attached to a graphql field.
Some examples:

### Policy that always allows access:
### Policy that always allows access

```yaml
kind: Policy
Expand All @@ -28,7 +28,7 @@ Spec:
code: {'result': 'allow'}
```
### Policy that allows access only if the issuer is "abc.com":
### Policy that allows access only if the issuer is "abc.com"
```yaml
kind: Policy
Expand All @@ -37,10 +37,13 @@ metadata:
name: abc-user
Spec:
type: js-expression
code: { "result": (data.jwt.issuer === "abc.com") ? "allow" : "deny" }
code: { "result": (input.args.issuer === "abc.com") ? "allow" : "deny" }
args:
issuer: String!
```
`jwt` is available on the data object to validate fields from the web token
The `args` are available to use on the input object.
`jwt` is available for parameter injection in args using `issuer: "{jwt.issuer}"`
### Policy that allows access only if the subject matches the provided userId argument:
Expand All @@ -51,16 +54,15 @@ metadata:
name: my-user
Spec:
type: js-expression
code: { "result": (data.jwt.sub === data.args.userId) ? "allow" : "deny"}
code: { "result": (input.args.sub === input.args.userId) ? "allow" : "deny"}
args:
userId: ID!
sub: ID!
```
The `args` are available to use on the data object

_Note the js-expression type is an example of a possible type and not planned to be implemented at this time._
### Policy that uses a graphql query to fetch additional data and allows based on the results and an argument:
### Policy that uses a graphql query to fetch additional data and allows based on the results and an argument
```yaml
kind: Policy
Expand All @@ -72,29 +74,29 @@ Spec:
code: |
allow = false
allow = {
data.args.userId == data.queries.familyQuery.family.members[_].id
input.args.userId == input.query.user.family.members[_].id
}
args:
userId: ID!
queries:
- type: graphql
paramName: familyQuery
graphql:
query: |
{
user({jwt.sub}) {
family {
members {
id
}
}
}
sub: ID!
query:
gql: |
query ($sub: String!) {
user(sub: $sub) {
family {
members {
id
}
}
}
}
variables:
sub: '{args.sub}'
```
Queries support the standard Stitch parameter injection syntax, but only support the `jwt` and `args` objects for injection

### Same as the previous policy, but evaluates the query while opa is running:
### Same as the previous policy, but evaluates the query while opa is running

```yaml
kind: Policy
Expand All @@ -104,18 +106,19 @@ metadata:
Spec:
type: opa
code: |
query = sprintf(“graphql { user(%s) {family { members { id} } } }”, data.jwt.sub)
query = sprintf(“graphql { user(%s) {family { members { id} } } }”, input.args.sub)
allow = false
allow = {
data.args.userId == data.query.family.members[_].id
input.args.userId == input.query.family.members[_].id
}
args:
userId: ID!
sub: ID!
```

This example will always evaluate the graphql query, but generally this approach should be used when conditional side effect evaluation is needed

### Policy that uses a policy query and allows based on the results:
### Policy that uses another policy as query and allows based on the results

```yaml
kind: Policy
Expand All @@ -127,29 +130,31 @@ Spec:
code: |
allow = false
allow = {
data.queries.myUserPolicy == true
input.query.myUserPolicy == true
}
args:
userId: ID!
queries:
- type: policy
paramName: myUserPolicy
policy:
policyName: my-user
args:
userId: '{args.userId}'
query:
gql: |
query(userId: ID!) {
policy.my_user(userId: $userId) {
allow
}
}
variables:
userId: '{args.userId}'
```

`args` for query policy can use parameter injection from the `jwt` and `args` objects, similarly to the graphql query

### Attach policy to fields:

```
```gql
type User {
ID: ID!
Picture: String @policy-some-ns-public
Friends: [User] @policy-some-ns-abc-user
Email: String @policy-some-ns-my-user(userId: "{source.UserId}")
Email: String @policy-some-ns-my-user(userId: "{source.UserId}", sub: "{jwt.sub}")
NickName: @policy-some-ns-my-user-family(userId: "{source.UserId}")
}
```
Expand All @@ -160,7 +165,7 @@ Adding args support to policies can ease memoization and remove hidden dependenc

Initially, we will support a single `@policy` directive that gets an array of policy names and their args:

```
```gql
type User {
Picture: String @policy(policies: [
{ namespace: "some-ns", name: “my-user”, args: { userId: “{source.UserId}” } },
Expand All @@ -176,13 +181,13 @@ Later on, for convenience and type safety, we will add an alternative syntax or
1. Client requests a graphql query that includes a field that has a `@policy` directive.
2. Server activates the policy resolver and reads the arguments with parameter injection.
3. The resolver passes the args to a policy executor.
The policy executor invokes the right binding (see policy implementation contract) and manages the queries for the policy.
The policy executor invokes the right binding (see policy implementation contract) and manages the query for the policy.
4. The policy executor should return true/false if the user has access or a string describing the failure.
5. If the user has access, return the field value. Otherwise, return an error.

## Policy implementation contract

The naive js contract can be implemented with a generator, that has a result of true/false and can yield queries.
The naive js contract can be implemented with a generator, that has a result of true/false and can yield query.
All queries and policy evaluation itself are memoized for a request context for same query/policy arguments.

```typescript
Expand Down Expand Up @@ -267,18 +272,19 @@ Spec:
code: |
allow = false
allow {
data.jwt.claims[data.args.claims[i]] == data.args.values[i]
input.args.jwtClaims[input.args.claims[i]] == input.args.values[i]
}
args:
claims: [String]
values: [String]
jwtClaims: JSONObject
```

Usage:

```
```gql
type User {
ID: ID!
Picture: String @policy-some-ns-has-claims(claims:["issuer", "sub"], values: ["soluto.com", "{source.UserId}"]
Picture: String @policy-some-ns-has-claims(claims:["issuer", "sub"], values: ["soluto.com", "{source.UserId}"], jwtClaims: "{jwt.claims}")
}
```
Loading

0 comments on commit 2a02f20

Please sign in to comment.