Skip to content

Commit

Permalink
refactor!: Subject has been made immutable (#1487)
Browse files Browse the repository at this point in the history
  • Loading branch information
dadrus authored May 22, 2024
1 parent a710a64 commit 6c4957f
Show file tree
Hide file tree
Showing 25 changed files with 313 additions and 102 deletions.
16 changes: 8 additions & 8 deletions docs/content/docs/mechanisms/authorizers.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ type: deny

== Local (CEL)

This authorizer allows definition of authorization requirements based on information available about the authenticated subject, as well as the actual request by using https://github.com/google/cel-spec[CEL] based authorization expressions. Each expression is expected to return `true` to signal success. Otherwise, the authorization fails, resulting in the execution of the error handler mechanisms.
This authorizer allows definition of authorization requirements based on information available about the authenticated subject, the existing pipeline results, as well as the actual request by using https://github.com/google/cel-spec[CEL] based authorization expressions. Each expression is expected to return `true` to signal success. Otherwise, the authorization fails, resulting in the execution of the error handler mechanisms.

To enable the usage of this authorizer, you have to set the `type` property to `cel`.

Configuration using the `config` property is mandatory. Following properties are available:

* *`expressions`*: _link:{{< relref "/docs/configuration/types.adoc#_authorization_expression">}}[Authorization Expression] array_ (mandatory, overridable)
+
List of authorization expressions, which define the actual authorization logic. Each expression has access to the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`] and the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_request" >}}[`Request`] objects.
List of authorization expressions, which define the actual authorization logic. Each expression has access to the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`], link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_outputs" >}}[`Outputs`], and the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_request" >}}[`Request`] objects.

.Authorization based on subject properties
====
Expand Down Expand Up @@ -103,19 +103,19 @@ The usage of this type of configuration makes sense in a pipeline, which combine

This authorizer allows communication with other systems, like https://www.openpolicyagent.org/[Open Policy Agent], https://www.ory.sh/docs/keto/[Ory Keto], etc. for the actual authorization purpose. If the used endpoint answers with a not 2xx HTTP response code, this authorizer assumes, the authorization has failed, resulting in the execution of the error handler mechanisms. Otherwise, if no expressions for the verification of the response are defined, the authorizer assumes, the request has been authorized. If expressions are defined and do not fail, the authorization succeeds.

If your authorization system provides a payload in the response, heimdall inspects the `Content-Type` header to prepare the payload for further usage, e.g. for payload verification expressions, or for a link:{{< relref "#_local_cel" >}}[Local (CEL)] authorizer. If the content type does either end with `json` or is `application/x-www-form-urlencoded`, the payload is decoded, so key based access to the corresponding attributes is possible, otherwise it is made available as well, but as a simple string. In all cases this value is available for the authorization expressions as well as in the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[Subject's] `Attributes` property under a key named by the `id` of the authorizer (See also the example below).
If your authorization system provides a payload in the response, heimdall inspects the `Content-Type` header to prepare the payload for further usage, e.g. for payload verification expressions, or for a link:{{< relref "#_local_cel" >}}[Local (CEL)] authorizer. If the content type does either end with `json` or is `application/x-www-form-urlencoded`, the payload is decoded, so key based access to the corresponding attributes is possible, otherwise it is made available as well, but as a simple string. In all cases this value is available for the authorization expressions, as well as in the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_outputs" >}}[`Outputs`] property under a key named by the `id` of the authorizer (See also the example below).

To enable the usage of this authorizer, you have to set the `type` property to `remote`.

Configuration using the `config` property is mandatory. Following properties are available:

* *`endpoint`*: _link:{{< relref "/docs/configuration/types.adoc#_endpoint">}}[Endpoint]_ (mandatory, not overridable)
+
The API endpoint of your authorization system. At least the `url` must be configured. This mechanism allows templating of the url and makes the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`] object, as well as the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`] (see also below) objects available to it. By default, this authorizer will use HTTP `POST` to send the rendered payload to this endpoint. You can override this behavior by configuring `method` as well. Depending on the API requirements of your authorization system, you might need to configure further properties, like headers, etc.
The API endpoint of your authorization system. At least the `url` must be configured. This mechanism allows templating of the url and makes the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`] object, the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_outputs" >}}[`Outputs`] object, as well as the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`] (see also below) objects available to it. By default, this authorizer will use HTTP `POST` to send the rendered payload to this endpoint. You can override this behavior by configuring `method` as well. Depending on the API requirements of your authorization system, you might need to configure further properties, like headers, etc.

* *`payload`*: _string_ (optional, overridable)
+
Your link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_templating" >}}[template] with definitions required to communicate to the authorization endpoint. The template can make use of link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`], link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`] and link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_request" >}}[`Request`] objects.
Your link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_templating" >}}[template] with definitions required to communicate to the authorization endpoint. The template can make use of link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`], link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_outputs" >}}[`Outputs`], link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`] and link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_request" >}}[`Request`] objects.

* *`expressions`*: _link:{{< relref "/docs/configuration/types.adoc#_authorization_expression">}}[Authorization Expression] array_ (optional, overridable)
+
Expand All @@ -133,7 +133,7 @@ Allows caching of the authorization endpoint responses. Defaults to 0s, which me

* *`values`* _map of strings_ (optional, overridable)
+
A key value map, which is made accessible to the template rendering engine as link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`] object, to render parts of the URL and/or the payload. The actual values in that map can be templated as well with access to the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`] and link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_request" >}}[`Request`] objects.
A key value map, which is made accessible to the template rendering engine as link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`] object, to render parts of the URL and/or the payload. The actual values in that map can be templated as well with access to the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`], the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_outputs" >}}[`Outputs`], and link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_request" >}}[`Request`] objects.

.Configuration of Remote authorizer to communicate with https://www.openpolicyagent.org/[Open Policy Agent] (OPA)
====
Expand All @@ -155,7 +155,7 @@ config:
user: ${OPA_USER}
password: ${OPA_PASSWORD}
payload: |
{ "input": { "user": {{ quote .Subject.ID }} }, "some_data": {{ quote .Values.whatever }} }
{ "input": { "user": {{ quote .Subject.ID }} }, "some_data": {{ quote .Values.whatever }}, "more_data": {{ quote .Outputs.whatever }} }
values:
namespace: myapi/policy
policy: allow_write
Expand All @@ -167,7 +167,7 @@ config:
message: User does not have required permissions
----
In this case, since an OPA response could look like `{ "result": true }` or `{ "result": false }`, heimdall makes the response also available under `Subject.Attributes["opa"]` as a map, with `"opa"` being the id of the authorizer in this example.
In this case, since an OPA response could look like `{ "result": true }` or `{ "result": false }`, heimdall makes the response also available under `Outputs["opa"]`, with `"opa"` being the id of the authorizer in this example.
A specific rule could then use this authorizer in the following ways:
Expand Down
11 changes: 5 additions & 6 deletions docs/content/docs/mechanisms/contextualizers.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ Some of the contextualizers may support or require additional configuration. The

== Generic

This mechanism allows you to communicate to any API you want to fetch further information about the subject. Typical scenario is getting specific attributes for later authorization purposes which are not known to the authentication system and thus were not made available in link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject's`] `Attributes` property. If the API responses with a 2xx HTTP response code, the payload is made available in the `Attributes` property of the `Subject`, otherwise, if not overridden, an error is thrown and the execution of the authentication & authorization pipeline stops. To avoid overwriting of existing attributes, this object is however not available on the top level, but under a key named by the `id` of the authorizer (See also the example below). If the `Content-Type` of the response is either ending with `json` or is `application/x-www-form-urlencoded`, the payload is decoded and made available as map, otherwise it is treated as string, but, as written above, is made available as well.
This mechanism allows you to communicate to any API you want to fetch further information about the subject. Typical scenario is getting specific attributes for later authorization purposes which are not known to the authentication system and thus were not made available in link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject's`] `Attributes` property. If the API responses with a 2xx HTTP response code, the payload is made available in the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_outputs" >}}[`Outputs`] object, otherwise, if not overridden, an error is thrown and the execution of the authentication & authorization pipeline stops. To avoid overwriting of existing key value pairs, this object is however not available on the top level, but under a key named by the `id` of the contextualizer (See also the example below). If the `Content-Type` of the response is either ending with `json` or is `application/x-www-form-urlencoded`, the payload is decoded and made available as map, otherwise it is treated as string, but, as written above, is made available as well.

To enable the usage of this contextualizer, you have to set the `type` property to `generic`.

Configuration using the `config` property is mandatory. Following properties are available:

* *`endpoint`*: _link:{{< relref "/docs/configuration/types.adoc#_endpoint">}}[Endpoint]_ (mandatory, not overridable)
+
The API of the service providing additional attributes about the authenticated user. At least the `url` must be configured. This mechanism allows templating of the url and makes the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`] object, as well as the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`] (see also below) objects available to it. By default, this contextualizer will use HTTP `POST` to send the rendered payload to this endpoint. You can override this behavior by configuring `method` as well. Depending on the API requirements of the system, this contextualizer should communicate to, you might need to configure further properties, like headers, etc.
The API of the service providing additional attributes about the authenticated user. At least the `url` must be configured. This mechanism allows templating of the url and makes the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`], the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_outputs" >}}[`Outputs`] object, as well as the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`] (see also below) objects available to it. By default, this contextualizer will use HTTP `POST` to send the rendered payload to this endpoint. You can override this behavior by configuring `method` as well. Depending on the API requirements of the system, this contextualizer should communicate to, you might need to configure further properties, like headers, etc.

* *`forward_headers`*: _string array_ (optional, overridable)
+
Expand All @@ -36,7 +36,7 @@ If the API requires any cookies from the request to heimdall, you can forward th

* *`payload`*: _string_ (optional, overridable)
+
Your link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_templating" >}}[template] with definitions required to communicate to the endpoint. The template can make use of link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`], link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`] and link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_request" >}}[`Request`] objects.
Your link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_templating" >}}[template] with definitions required to communicate to the endpoint. The template can make use of link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`], link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`], the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_outputs" >}}[`Outputs`], and link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_request" >}}[`Request`] objects.

* *`cache_ttl`*: _link:{{< relref "/docs/configuration/types.adoc#_duration" >}}[Duration]_ (optional, overridable)
+
Expand All @@ -48,7 +48,7 @@ If set to `true`, allows the pipeline to continue with the execution of the next

* *`values`* _map of strings_ (optional, overridable)
+
A key value map, which is made accessible to the template rendering engine as link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`] object to render parts of the URL and/or the payload. The actual values in that map can be templated as well with access to the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`] and link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_request" >}}[`Request`] objects.
A key value map, which is made accessible to the template rendering engine as link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_values" >}}[`Values`] object to render parts of the URL and/or the payload. The actual values in that map can be templated as well with access to the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_subject" >}}[`Subject`], the link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_outputs" >}}[`Outputs`] and link:{{< relref "/docs/mechanisms/evaluation_objects.adoc#_request" >}}[`Request`] objects.

.Contextualizer configuration without payload
====
Expand Down Expand Up @@ -84,7 +84,7 @@ config:
payload: |
{
"user_id": {{ quote .Values.user_id }}
"whatever": {{ quote .Values.whatever }}
"whatever": {{ quote .Outputs.whatever }}
}
----
Expand All @@ -100,7 +100,6 @@ Since the `values` property is not defined but used in the payload, it must be s
config: # overriding with rule specifics
values:
user_id: "{{ .Subject.ID }}"
whatever: "some value"
- # other mechanisms
----
====
28 changes: 22 additions & 6 deletions docs/content/docs/mechanisms/evaluation_objects.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ Objects represent state in the execution of a particular rule. These are the ent

=== Subject

This object contains the information about the authenticated subject and has the following attributes:
This object is created by an authenticator which was able to verify the authentication claim available in the request, and contains the information about the authenticated subject. It has the following properties:

* *`ID`*: _string_
+
The identifier of the subject. This value is set by the authenticator, which was able to authenticate the subject.
The identifier of the subject.

* *`Attributes`*: _map_
+
Contains all attributes, which are known about the subject. The content is initially set by the authenticator, which was able to authenticate the subject. Mechanisms in the following stages can update it, but cannot override any entries.
Contains all attributes, which are known about the subject.

Each object of this type can be thought as a JSON object. Here some examples:

Expand Down Expand Up @@ -179,6 +179,20 @@ Request = {
----
====

=== Outputs

This object represents a pipeline execution specific key value map. It is used by pipeline steps to store or read results of particular step executions. Mechanism id used by a pipeline step is used as a key and the value is the corresponding result.

Example:

[source, javascript]
----
Outputs = {
"id_1": ["a", "b"],
"id_2": { "foo": "bar", "baz": false }
}
----

=== Payload

This object represents the contents of a payload, like the request body or a response body. The contents depend on the MIME-Type of the payload. For `json`, `yaml` or `x-www-form-urlencoded` encoded payload, the object is transformed to a JSON object. Otherwise, it is just a string.
Expand Down Expand Up @@ -233,7 +247,7 @@ Values = {

== Templating

Some mechanisms support templating using https://golang.org/pkg/text/template/[Golang Text Templates]. Templates can act on all objects described above (link:{{< relref "#_subject" >}}[Subject], link:{{< relref "#_request" >}}[Request], link:{{< relref "#_payload" >}}[Payload] and link:{{< relref "#_values" >}}[Values]). Which exactly are supported is mechanism specific.
Some mechanisms support templating using https://golang.org/pkg/text/template/[Golang Text Templates]. Templates can act on all objects described above (link:{{< relref "#_subject" >}}[Subject], link:{{< relref "#_outputs" >}}[Outputs], link:{{< relref "#_request" >}}[Request], link:{{< relref "#_payload" >}}[Payload] and link:{{< relref "#_values" >}}[Values]). Which exactly are supported is mechanism specific.

To ease the usage, all http://masterminds.github.io/sprig/[sprig] functions, except `env` and `expandenv`, as well as the following functions are available:

Expand All @@ -260,7 +274,8 @@ Imagine, we have a `POST` request for the URL `\http://foobar.baz/zab?foo=bar`,
"request_url": {{ quote .Request.URL }},
"foo_value": {{ index .Request.URL.Query.foo 0 | quote }}
"request_method": {{ quote .Request.Method }},
"x_foo_value": {{ .Request.Header "X-Foo" | quote }}
"x_foo_value": {{ .Request.Header "X-Foo" | quote }},
"whatever": {{ .Outputs.whatever | quote }}
}
----
Expand All @@ -276,7 +291,8 @@ This will result in the following JSON object:
"request_url": "http://foobar.baz/zab?foo=bar",
"foo_value": "bar",
"request_method": "POST",
"x_foo_value": "bar"
"x_foo_value": "bar",
"whatever": "some value"
}
----
====
Expand Down
Loading

0 comments on commit 6c4957f

Please sign in to comment.