Skip to content

Commit

Permalink
fixup! 📝(project) add a ralph lrs tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
wilbrdt committed Dec 20, 2023
1 parent e48d6fc commit 8685444
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 134 deletions.
4 changes: 2 additions & 2 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ RALPH_BACKENDS__DATA__ES__TEST_FORWARDING_INDEX=test-index-foo-2
# ES lrs backend

# Same options as for the ES data backend, however they are prefixed with
# RALPH_BACKENDS__LRS__ES__ instead. Example:
# RALPH_BACKENDS__LRS__ES__HOSTS=http://elasticsearch:9200
# RALPH_BACKENDS__LRS__ES__ instead.
RALPH_BACKENDS__LRS__ES__HOSTS=http://elasticsearch:9200

# MONGO data backend

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,15 @@ If the database status is satisfying, you are now ready to send xAPI statements
to the LRS:

```bash
gunzip data/statements.json.gz | \
gunzip -c data/statements.json.gz | \
head -n 100 | \
jq -s . | \
curl -Lk \
--user ralph:secret \
-X POST \
-H "Content-Type: application/json" \
http://localhost:8100/xAPI/statements/ -d @-
-d @- \
http://localhost:8100/xAPI/statements/
```

The command above fetches one hundred (100) example xAPI statements from our
Expand Down
43 changes: 0 additions & 43 deletions docker-compose-test.yml

This file was deleted.

4 changes: 2 additions & 2 deletions docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,13 @@ By default, all authenticated users have full read and write access to the serve

### Filtering results by authority (multitenancy)

In Ralph LRS, all incoming statements are assigned an `authority` (or ownership) derived from the user that makes the call. You may restrict read access to users "own" statements (thus enabling multitenancy) by setting the following environment variable:
In Ralph LRS, all incoming statements are assigned an `authority` (or ownership) derived from the user that makes the request. You may restrict read access to users "own" statements (thus enabling multitenancy) by setting the following environment variable:

```bash
RALPH_LRS_RESTRICT_BY_AUTHORITY = True # Default: False
```

**WARNING**: Two accounts with different credentials may share the same `authority` meaning they can access the same statements. It is the administrators responsability to ensure that `authority` is properly assigned.
**WARNING**: Two accounts with different credentials may share the same `authority`, meaning they can access the same statements. It is the administrator's responsibility to ensure that `authority` is properly assigned.

NB: If not using "scopes", or for users with limited "scopes", using this option will make the use of option `?mine=True` implicit when fetching statement.

Expand Down
10 changes: 5 additions & 5 deletions docs/tutorials/development_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ You should know that we would be glad to help you contribute to Ralph! Here's ou


!!! info
In this tutorial, and even more generally in others tutorials, we tend to use Elasticsearch backend. Note that you can do the same with [another LRS backend](../backends) implemented in Ralph.
In this tutorial, and even more generally in others tutorials, we tend to use Elasticsearch backend. Note that you can do the same with [another LRS backend](../backends/index.md) implemented in Ralph.

To start playing with `ralph`, you should first `bootstrap` using:

Expand Down Expand Up @@ -279,22 +279,22 @@ curl -sLk \
-o jsonpath='{.spec.rules[0].host}')/whoami"
```

And why not send test statements:
Let's also send some test statements:

```bash
gunzip data/statements.json.gz | \
gunzip -c data/statements.json.gz | \
head -n 100 | \
jq -s . | \
curl -sLk \
--user foo:bar \
-X POST \
-H "Content-Type: application/json" \
-d @- \
"https://$(\
kubectl -n development-ralph \
get \
ingress/ralph-app-current \
-o jsonpath='{.spec.rules[0].host}')/xAPI/statements/" \
-d @-
-o jsonpath='{.spec.rules[0].host}')/xAPI/statements/"
```

!!! tip "Install `jq`"
Expand Down
30 changes: 15 additions & 15 deletions docs/tutorials/lrs/authentication/basic.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# HTTP Basic Authentication

The default method for securing Ralph API server is with HTTP basic authentication. For this, we need to create a user in Ralph LRS.
The default method for securing the Ralph API server is HTTP Basic Authentication.
For this, we need to create a user in Ralph LRS.

## Creating user credentials

Expand All @@ -10,31 +11,30 @@ To create a new user credentials, Ralph CLI provides a dedicated command:

```bash
ralph auth \
--write-to-disk \
--username janedoe \
--password supersecret \
--scope janedoe_scope \
--agent-ifi-mbox mailto:[email protected] \
# or --agent-ifi-mbox-sha1sum ebd31e95054c018b10727ccffd2ef2ec3a016ee9 \
# or --agent-ifi-openid "http://jane.openid.example.org/" \
# or --agent-ifi-account exampleAccountname http://www.exampleHomePage.com \
-w
--scope statements/write \
--scope statements/read \
--agent-ifi-mbox mailto:[email protected]
```

=== "Docker Compose"

```bash
docker compose run --rm lrs \
bin/ralph auth \
ralph auth \
--write-to-disk \
--username janedoe \
--password supersecret \
--scope janedoe_scope \
--agent-ifi-mbox mailto:[email protected] \
# or --agent-ifi-mbox-sha1sum ebd31e95054c018b10727ccffd2ef2ec3a016ee9 \
# or --agent-ifi-openid "http://jane.openid.example.org/" \
# or --agent-ifi-account exampleAccountname http://www.exampleHomePage.com \
-w
--scope statements/write \
--scope statements/read \
--agent-ifi-mbox mailto:[email protected]
```

!!! tip
You can either display the helper with `ralph auth --help` or check the CLI tutorial [here](../../cli.md)

This command updates your credentials file with the new `janedoe` user.
Here is the file that has been created by the `ralph auth` command:

Expand Down Expand Up @@ -104,7 +104,7 @@ and running the Ralph LRS with:
docker compose up -d lrs
```

we can try to make a request to the `whoami` endpoint again, but this time sending our username and password through Basic Auth:
we can request the `whoami` endpoint again, but this time sending our username and password through Basic Auth:

=== "curl"

Expand Down
169 changes: 128 additions & 41 deletions docs/tutorials/lrs/authentication/oidc.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,69 +17,156 @@ It is also strongly recommended to set the optional `RALPH_RUNSERVER_AUTH_OIDC_A

OpenID Connect support is currently developed and tested against [Keycloak](https://www.keycloak.org/) but may work with other identity providers that implement the specification.

## An example with Keycloak

The [Learning analytics playground](https://github.com/openfun/learning-analytics-playground/) repository contains a Docker Compose file and configuration for a demo instance of Keycloak with a `ralph` client.

## Making a GET request
First, we should stop the Ralph LRS server (if it's still running):
```bash
docker compose down
```

We can clone the `learning-analytics-playground` repository:
```bash
git clone [email protected]:openfun/learning-analytics-playground
```

Let's first create the network named `ralph`:
```bash
docker network create ralph
```

Then we can bootstrap the project:
```bash
cd learning-analytics-playground/
make bootstrap
```

After a couple of minutes, the playground containers should be up and running.



Create another docker compose file, let's call it `docker-compose.oidc.yml`, with the following content:
```yaml title="docker-compose.oidc.yml" hl_lines="9-10 26-27 29-31"
version: "3.9"

services:

lrs:
image: fundocker/ralph:latest
environment:
RALPH_APP_DIR: /app/.ralph
RALPH_RUNSERVER_AUTH_BACKEND: oidc
RALPH_RUNSERVER_AUTH_OIDC_ISSUER_URI: http://learning-analytics-playground_keycloak_1:8080/auth/realms/fun-mooc
RALPH_RUNSERVER_BACKEND: fs
ports:
- "8100:8100"
command:
- "uvicorn"
- "ralph.api:app"
- "--proxy-headers"
- "--workers"
- "1"
- "--host"
- "0.0.0.0"
- "--port"
- "8100"
volumes:
- .ralph:/app/.ralph
networks:
- ralph

networks:
ralph:
external: true

```
Again, we need to create the `.ralph` directory:
```bash
mkdir .ralph
```

Then we can start the `lrs` service:
```bash
docker compose -f docker-compose.oidc.yml up -d lrs
```

With the Keycloak instance running, we can get the access token from Keycloak:
Now that both Keycloak and Ralph LRS server are up and running, we should be able to get the access token from Keycloak with the command:

=== "curl"

```bash
curl --request POST 'http://localhost:8080/auth/realms/fun-mooc/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=ralph' \
--data-urlencode 'client_secret=super-secret' \
--data-urlencode 'username=ralph_admin' \
--data-urlencode 'password=funfunfun' \
--data-urlencode 'grant_type=password'
curl -X POST \
-d "grant_type=password" \
-d "client_id=ralph" \
-d "client_secret=bcef3562-730d-4575-9e39-63e185f99bca" \
-d "username=ralph_admin" \
-d "password=funfunfun" \
http://localhost:8080/auth/realms/fun-mooc/protocol/openid-connect/token
```

```bash
{"access_token":"<access token content>","expires_in":300,"refresh_expires_in":1800,"refresh_token":"<refresh token content>","token_type":"Bearer","not-before-policy":0,"session_state":"0889b3a5-d742-45fb-98b3-20e967960e74","scope":"email profile"}
```
=== "HTTPie"

```bash
http --form POST :8090/auth/reals/fun-mooc/protocol/openid-connect/token \
client_id="ralph" \
client_secret="super-secret" \
username="ralph_admin" \
password="funfunfun" \
grant_type="password"
http -f POST \
:8080/auth/realms/fun-mooc/protocol/openid-connect/token \
grant_type=password \
client_id=ralph \
client_secret=bcef3562-730d-4575-9e39-63e185f99bca \
username=ralph_admin \
password=funfunfun
```

which outputs (*tokens truncated for example purpose*):

```json
{
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSTWlLM",
"expires_in":300,
"refresh_expires_in":1800,
"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI4MDc5NjExM",
"token_type":"Bearer",
"not-before-policy":0,
"session_state":"22a36735-e35f-496b-a243-152d32ebff45",
"scope":"profile email"
}
```
```bash
HTTP/1.1 200 OK
...
{
"access_token": "<access token content>",
"expires_in": 300,
"not-before-policy": 0,
"refresh_expires_in": 1800,
"refresh_token": "<refresh token content>",
"scope": "email profile",
"session_state": "1e826fa2-b4b3-42bf-837f-158fe9d5e1e5",
"token_type": "Bearer"
}
```

If we now send the access token to the API server as a Bearer header:
With this access token, we can now make a request to the Ralph LRS server:

=== "curl"

```bash
curl -H 'Authorization: Bearer <access token content>' \
http://localhost:8100/whoami
```

```bash
curl http://localhost:8100/whoami --A "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSTWlLM"
{"agent":{"openid":"http://localhost:8080/auth/realms/fun-mooc/b6e85bd0-ce6e-4b24-9f0e-6e18d8744e54"},"scopes":["email","profile"]}
```

=== "HTTPie"

```bash
http -A bearer -a eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSTWlLM :8100/whoami
http -A bearer -a <access token content> :8100/whoami
```

```bash
HTTP/1.1 200 OK
...
{
"agent": {
"openid": "http://localhost:8080/auth/realms/fun-mooc/b6e85bd0-ce6e-4b24-9f0e-6e18d8744e54"
},
"scopes": [
"email",
"profile"
]
}
```

Ralph LRS returns our authenticated user:

``` console
HTTP/1.1 200 OK
{
"username":"ralph_admin",
"scopes":["all"]
}
```
Congrats, you've managed to authenticate using OpenID Connect! 🎉
Loading

0 comments on commit 8685444

Please sign in to comment.