Skip to content

Commit

Permalink
update readme with instructions on how to enable events (#46)
Browse files Browse the repository at this point in the history
Co-authored-by: Garth <[email protected]>
  • Loading branch information
pnzrr and xgp authored Dec 21, 2023
1 parent 0fbb24e commit 6f81c3b
Showing 1 changed file with 41 additions and 28 deletions.
69 changes: 41 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
> :rocket: **Try it for free** in the new Phase Two [keycloak managed service](https://phasetwo.io/?utm_source=github&utm_medium=readme&utm_campaign=keycloak-events
). See the [announcement and demo video](https://phasetwo.io/blog/self-service/) for more information.
> :rocket: **Try it for free** in the new Phase Two [keycloak managed service](https://phasetwo.io/?utm_source=github&utm_medium=readme&utm_campaign=keycloak-events). See the [announcement and demo video](https://phasetwo.io/blog/self-service/) for more information.
# keycloak-events

Expand Down Expand Up @@ -27,12 +26,16 @@ The maven build can be triggered by running `mvn clean install`. It uses the sha
## Use

The `EventListenerProvider` implementations in this library rely on two utilities packaged within.

1. The first is that configuration is loaded from Realm attributes. This means that you can update the configuration for these implementations at runtime by either writing directly to the `realm_attributes` table or by calling the [Realm Update](https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_updaterealm) method. Also, In order to make using these easier, there is included a `RealmAttributesResource` that allows you to CRUD the attributes separately from updating the whole realm. It's available at the `/auth/realms/<realm>/attributes` endpoint. Attribute keys are `_providerConfig.<provider_id>.<optional:N>`, and the configurations are stored in the value field as JSON. Currently only single depth JSON objects are supported.
2. The optional `N` value in the key is relevant to the second important utility. The `EventListenerProviderFactory` implementations are all subclasses of `MultiEventListenerProviderFactory`, which enables multiple `EventListenerProvider` instances of the same type to run with different configurations. This is a facility that is not currently available in Keycloak, although some tickets and features in the future Admin UI indicate that it is coming soon.

Most events require being enabled through the Admin UI. Go to (Configure) Realm Settings > Events (tab) > Event listeners. In the Event listeners dropdown select `ext-event-********` for the appropriate event being enabled.

### Script

The script event listener allows you to run JS event listeners in the same fashion as the `ScriptBasedAuthenticator`. The following script bindings are present for the `onEvent` and `onAdminEvent` methods.

- `event`: the Event or AdminEvent
- `realm`: the RealmModel
- `user`: the UserModel (for `onEvent` only)
Expand All @@ -46,18 +49,27 @@ The script event listener is configured by setting a Realm attribute in the Real
The configuration requires 3 mandatory values:
| Name | Required | Default | Description |
| -----| -------- | ------- | ----------- |
| `scriptName` | Y | | The name of the script |
| `scriptDescription` | Y | | A description of what the script does |
| `scriptCode` | Y | | The JS code |
| `scriptName` | Y | | The name of the script |
| `scriptDescription` | Y | | A description of what the script does |
| `scriptCode` | Y | | The JS code |

A trivial example:

```js
function onEvent(event) {
LOG.info(event.type + " in realm " + realm.name + " for user " + user.username);
LOG.info(
event.type + " in realm " + realm.name + " for user " + user.username
);
}

function onAdminEvent(event, representation) {
LOG.info(event.operationType + " on " + event.resourceType + " in realm " + realm.name);
LOG.info(
event.operationType +
" on " +
event.resourceType +
" in realm " +
realm.name
);
}
```

Expand All @@ -68,8 +80,8 @@ Send the events to a specified URI. May sign the request using keyed-HMAC. Optio
Configuration values:
| Name | Required | Default | Description |
| -----| -------- | ------- | ----------- |
| `targetUri` | Y | | The URI to send the event payload |
| `sharedSecret` | N | | The shared secret value to use for HMAC signing. If present, the signature according to RFC2104 will be passed as `X-Keycloak-Signature` header |
| `targetUri` | Y | | The URI to send the event payload |
| `sharedSecret` | N | | The shared secret value to use for HMAC signing. If present, the signature according to RFC2104 will be passed as `X-Keycloak-Signature` header |
| `hmacAlgorithm` | N | HmacSHA256 | The HMAC algortihm used for signing. Defaults to HmacSHA256. Can be set to HmacSHA1 for backwards compatibility |
| `retry` | N | true | Should it use exponential backoff to retry on non 2xx response |
| `backoffInitialInterval` | N | 500 | Initial interval value in milliseconds |
Expand All @@ -95,6 +107,7 @@ This provides a base class for the ever-requested "do something when a user is a
Please note that this has not been tested with users added via Identity/Federated Providers, and it may not catch the appropriate events for those. There is future work to verify that this does/not work in those cases, and potentially implement a wrapper to the `UserStorageProvider` which would correctly intercept those events.

For example:

```java
public class MyUserAddRemove extends UserEventListenerProviderFactory {

Expand Down Expand Up @@ -123,36 +136,37 @@ public class MyUserAddRemove extends UserEventListenerProviderFactory {

### Webhooks

This provides the entities and REST endpoints required to allow webhook subscriptions to events. The events have been slightly modified so that there are no longer 2 types of events, but are now distinguished by a type prefix. Definition on the event format and types is available in the [Phase Two](https://phasetwo.io/) documentation under [Audit Logs](https://phasetwo.io/docs/audit-logs/).
This provides the entities and REST endpoints required to allow webhook subscriptions to events. The events have been slightly modified so that there are no longer 2 types of events, but are now distinguished by a type prefix. Definition on the event format and types is available in the [Phase Two](https://phasetwo.io/) documentation under [Audit Logs](https://phasetwo.io/docs/audit-logs/).

Webhooks are sent using the same mechanics as the `HttpSenderEventListenerProvider`, and there is an automatic exponential backoff if there is not a 2xx response. The sending tasks are scheduled in a thread pool and executed after the Keycloak transaction has been committed.

Webhooks are sent using the same mechanics as the `HttpSenderEventListenerProvider`, and there is an automatic exponential backoff if there is not a 2xx response. The sending tasks are scheduled in a thread pool and executed after the Keycloak transaction has been committed.
Enable webhook events in the Admin UI by going to (Configure) Realm Settings > Events (tab) > Event Listeners, in the Event listeners dropdown select `ext-event-webhook` and save.

#### Managing webhook subscriptions

Webhooks are managed with a custom REST resource with the following methods. Use of these methods requires the authenticated user to have the `view-events` and `manage-events` permissions.

| Path | Method | Payload | Returns | Description |
| ---- | ------ | ------- | ------- | ----------- |
| `/auth/realms/:realm/webhooks` | `GET` | | List of webhook objects | Get webhooks |
| `/auth/realms/:realm/webhooks` | `POST` | Webhook object | `201` | Create webhook |
| `/auth/realms/:realm/webhooks/:id` | `GET` | | Webhook object | Get webhook |
| `/auth/realms/:realm/webhooks/:id` | `PUT` | Webhook object | `204` | Update webhook |
| `/auth/realms/:realm/webhooks/:id` | `DELETE` | Webhook object | `204` | Delete webhook |
| Path | Method | Payload | Returns | Description |
| ---------------------------------- | -------- | -------------- | ----------------------- | -------------- |
| `/auth/realms/:realm/webhooks` | `GET` | | List of webhook objects | Get webhooks |
| `/auth/realms/:realm/webhooks` | `POST` | Webhook object | `201` | Create webhook |
| `/auth/realms/:realm/webhooks/:id` | `GET` | | Webhook object | Get webhook |
| `/auth/realms/:realm/webhooks/:id` | `PUT` | Webhook object | `204` | Update webhook |
| `/auth/realms/:realm/webhooks/:id` | `DELETE` | Webhook object | `204` | Delete webhook |

The webhook object has this format:

```json
{
"id": "475cd2fd-3ca8-4c22-b5c8-c8b8927dcc10",
"enabled": "true",
"url": "https://example.com/some/webhook",
"secret": "ofj09saP4",
"eventTypes": [
"*"
],
"eventTypes": ["*"],
"createdBy": "ff730b72-a421-4f6e-9e4e-7fc7f53bac88",
"createdAt": "2021-04-21T18:25:43-05:00"
}
```
```

For creating and updating of webhooks, `id`, `createdBy` and `createdAt` are ignored. `secret` is not sent when fetching webhooks.

Expand All @@ -177,14 +191,14 @@ POST /auth/realms/master/webhooks

#### Sending app events

There is also a custom REST resource that allows publishing of arbitrary events. These are subsequently sent to the registered webhooks. In order to publish events, there is a new role `publish-events` which callers must have.
There is also a custom REST resource that allows publishing of arbitrary events. These are subsequently sent to the registered webhooks. In order to publish events, there is a new role `publish-events` which callers must have.

| Path | Method | Payload | Returns | Description |
| ---- | ------ | ------- | ------- | ----------- |
| Path | Method | Payload | Returns | Description |
| ---------------------------- | ------ | ------------ | ---------------------------------------------------------------------------------------------------------------------- | ------------- |
| `/auth/realms/:realm/events` | `POST` | Event object | `202 = Event received`<br/>`400 = Malformed event`<br/>`403 = API rate limit exceeded`<br/>`409 = Reserved event type` | Publish event |


### For system owners
#### For system owners
=======

#### Scripts
It is possible to disable the scripts run by the `ScriptEventListenerProvider` by setting `SCRIPTS_DISABLED=true`. This may be desirable in shared environments where it is not ideal to allow user code to run in the Keycloak process. Note that this will just cause the scripts to fail silently.
Expand All @@ -195,4 +209,3 @@ There is a special catch-all webhook that can be used by system owners to always
---

All documentation, source code and other files in this repository are Copyright 2023 Phase Two, Inc.

0 comments on commit 6f81c3b

Please sign in to comment.