Skip to content

Commit

Permalink
Merge pull request #46 from canonical/IAM-198-integrate-the-provider-…
Browse files Browse the repository at this point in the history
…side-of-relation-kratos

Add kratos-endpoint-info relation
  • Loading branch information
natalian98 authored Apr 3, 2023
2 parents 327c049 + 6543f98 commit 98b1602
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 52 deletions.
8 changes: 0 additions & 8 deletions .github/workflows/on_pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ name: On Pull Request
# On pull_request, we:
# * always run lint checks
# * always run tests
# * always publish to charmhub at latest/edge/branchname

on:
pull_request:
Expand All @@ -27,10 +26,3 @@ jobs:
needs:
- lint
uses: ./.github/workflows/tests.yaml

# publish runs in parallel with tests, as we always publish in this situation
publish-charm:
name: Publish Charm
uses: ./.github/workflows/publish.yaml
secrets:
CHARMCRAFT_CREDENTIALS: ${{ secrets.CHARMCRAFT_CREDENTIALS }}
26 changes: 26 additions & 0 deletions .github/workflows/publish-lib.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Publish kratos_endpoints library only if it has changed
name: Publish library

on:
push:
branches:
- main
paths:
- "./lib/charms/kratos/v0/kratos_endpoints.py"

jobs:
publish-lib:
name: Publish library
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Install charmcraft
run: sudo snap install charmcraft --channel latest/stable --classic

- name: Publish libs
env:
CHARMCRAFT_AUTH: ${{ secrets.CHARMCRAFT_CREDENTIALS }}
run: |
charmcraft publish-lib charms.kratos.v0.kratos_endpoints
32 changes: 19 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The Kratos Operator may be deployed using the Juju command line as follows:
```bash
juju deploy postgresql-k8s --channel edge --trust
juju deploy kratos
juju relate kratos postgresql-k8s
juju integrate kratos postgresql-k8s
```

To set the smtp connection uri, do:
Expand Down Expand Up @@ -55,12 +55,12 @@ The Kratos Operator offers integration with the [traefik-k8s-operator](https://g

If you have a traefik deployed and configured in your kratos model, to provide ingress to the admin API run:
```console
juju relate traefik-admin kratos:admin-ingress
juju integrate traefik-admin kratos:admin-ingress
```

To provide ingress to the public API run:
```console
juju relate traefik-public kratos:public-ingress
juju integrate traefik-public kratos:public-ingress
```

### External Provider Relation
Expand All @@ -73,34 +73,40 @@ juju config kratos-external-provider-integrator \
client_id={client_id} \
client_secret={client_secret} \
provider={provider}
juju relate kratos-external-provider-integrator kratos
juju integrate kratos-external-provider-integrator kratos
```

Once kratos has registered the provider, you will be able to retrieve the redirect_uri from the integrator by running:
```console
juju run-action {external_provider_integrator_unit_name} get-redirect-uri --wait
juju run {external_provider_integrator_unit_name} get-redirect-uri --wait
```

### Hydra

This charm offers integration with [hydra-operator](https://github.com/canonical/hydra-operator).

## Integration with Hydra and UI
In order to integrate kratos with hydra, it needs to be able to access hydra's admin API endpoint.
To enable that, relate the two charms:
```console
juju integrate kratos hydra
```

For further guidance on integration on hydra side, visit the [hydra-operator](https://github.com/canonical/hydra-operator#readme) repository.

If you have deployed [Login UI charm](https://github.com/canonical/identity-platform-login-ui), you can configure it with kratos by providing its URL.
### Identity Platform Login UI
<!-- TODO: Change this section when identity-platform-login-ui-operator endpoints relation is ready -->

If you have deployed [Login UI charm](https://github.com/canonical/identity-platform-login-ui-operator), you can configure it with kratos by providing its URL.
Note that the UI charm should run behind a proxy.
```console
juju config kratos kratos_ui_url=http://{traefik_public_ip}/{model_name}-{kratos_ui_app_name}
juju config kratos kratos_ui_url=http://{traefik_public_ip}/{model_name}-{identity_platform_login_ui_app_name}
```

In order to integrate kratos with hydra, it needs to be able to access hydra's admin API endpoint.
To enable that, relate the two charms:
Relate the two charms to provide `identity-platform-login-ui-operator` with kratos endpoints:
```console
juju relate kratos hydra
juju integrate kratos identity-platform-login-ui-operator
```

For further guidance on integration on hydra side, visit the [hydra-operator](https://github.com/canonical/hydra-operator#readme) repository.

## OCI Images

The image used by this charm is hosted on [Docker Hub](https://hub.docker.com/r/oryd/kratos) and maintained by Ory.
Expand Down
41 changes: 21 additions & 20 deletions lib/charms/hydra/v0/hydra_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@
"""Interface library for sharing hydra endpoints.
This library provides a Python API for both requesting and providing public and admin endpoints.
## Getting Started
To get started using the library, you need to fetch the library using `charmcraft`.
```shell
cd some-charm
charmcraft fetch-lib charms.hydra.v0.hydra_endpoints
```
To use the library from the requirer side:
In the `metadata.yaml` of the charm, add the following:
```yaml
Expand All @@ -27,7 +25,6 @@
HydraEndpointsRelationError,
HydraEndpointsRequirer,
)
Class SomeCharm(CharmBase):
def __init__(self, *args):
self.hydra_endpoints_relation = HydraEndpointsRequirer(self)
Expand All @@ -39,15 +36,12 @@ def some_event_function():
except HydraEndpointsRelationError as error:
...
```
"""

import logging
from typing import Dict, Optional

from ops.charm import (
CharmBase,
RelationCreatedEvent,
)
from ops.charm import CharmBase, RelationCreatedEvent
from ops.framework import EventBase, EventSource, Object, ObjectEvents
from ops.model import Application

Expand All @@ -59,7 +53,7 @@ def some_event_function():

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 1
LIBPATCH = 2

RELATION_NAME = "endpoint-info"
INTERFACE_NAME = "hydra_endpoints"
Expand Down Expand Up @@ -88,21 +82,21 @@ def __init__(self, charm: CharmBase, relation_name: str = RELATION_NAME):
self._relation_name = relation_name

events = self._charm.on[relation_name]
self.framework.observe(events.relation_created, self._on_provider_endpoint_relation_created)
self.framework.observe(
events.relation_created, self._on_provider_endpoint_relation_created
)

def _on_provider_endpoint_relation_created(self, event: RelationCreatedEvent):
def _on_provider_endpoint_relation_created(self, event: RelationCreatedEvent) -> None:
self.on.ready.emit()

def send_endpoint_relation_data(
self, charm: CharmBase, admin_endpoint: str, public_endpoint: str
) -> None:
def send_endpoint_relation_data(self, admin_endpoint: str, public_endpoint: str) -> None:
"""Updates relation with endpoints info."""
if not self._charm.unit.is_leader():
return

relations = self.model.relations[RELATION_NAME]
for relation in relations:
relation.data[charm].update(
relation.data[self._charm.app].update(
{
"admin_endpoint": admin_endpoint,
"public_endpoint": public_endpoint,
Expand All @@ -111,17 +105,23 @@ def send_endpoint_relation_data(


class HydraEndpointsRelationError(Exception):
"""Base class for the relation exceptions."""

pass


class HydraEndpointsRelationMissingError(HydraEndpointsRelationError):
def __init__(self):
"""Raised when the relation is missing."""

def __init__(self) -> None:
self.message = "Missing endpoint-info relation with hydra"
super().__init__(self.message)


class HydraEndpointsRelationDataMissingError(HydraEndpointsRelationError):
def __init__(self, message):
"""Raised when information is missing from the relation."""

def __init__(self, message: str) -> None:
self.message = message
super().__init__(self.message)

Expand All @@ -134,9 +134,10 @@ def __init__(self, charm: CharmBase, relation_name: str = RELATION_NAME):
self.charm = charm
self.relation_name = relation_name

def get_hydra_endpoints(self) -> dict:
def get_hydra_endpoints(self) -> Optional[Dict]:
"""Get the hydra endpoints."""
if not self.model.unit.is_leader():
return
return None
endpoints = self.model.relations[self.relation_name]
if len(endpoints) == 0:
raise HydraEndpointsRelationMissingError()
Expand All @@ -149,7 +150,7 @@ def get_hydra_endpoints(self) -> dict:

data = endpoints[0].data[remote_app]

if not "admin_endpoint" in data:
if "admin_endpoint" not in data:
raise HydraEndpointsRelationDataMissingError(
"Missing admin endpoint in endpoint-info relation data"
)
Expand Down
Loading

0 comments on commit 98b1602

Please sign in to comment.