Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User management for local development #1455

Merged
merged 4 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 62 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ export NODE_OPTIONS=--dns-result-order=ipv4first

By default, while developing, the caching won't work. If you want to have caching, change the value `CACHE_TYPE` in `.env` to any other
value besides 'empty'.
To check the cache locally, run `yarn cache:cli`.
With `GET <key>` you get the cache value of the key that is defined in [serlo.ts](https://github.com/serlo/api.serlo.org/blob/staging/packages/server/src/model/serlo.ts).
See in `package.json` for other scripts regarding cache.
To check the cache locally, run `yarn cache:cli`.

Some other useful commands:
`redis:empty` removes the whole cache
`redis:list` lists all cached keys

### Run tests

Expand Down Expand Up @@ -112,3 +114,60 @@ We have `~` as an absolute path alias for `./src` in place, e.g. `~/internals` r
## Changelog

Via filtering PRs by [`base:production`](https://github.com/serlo/api.serlo.org/pulls?q=is%3Apr+base%3Aproduction+) you can access the changelog of production.

## Developing with Ory Kratos

We use Ory Kratos for our user management.
Usually you won't need it, but if you do, run first:

`yarn start:kratos`

In the folder `./kratos` you find all important configuration files.
Emails sent by Kratos are going to be found at `http://localhost:4436`.

For more info about it see its [documentation](https://www.ory.sh/docs/kratos).

### Integrating Keycloak

First of all add `nbp` as host
`sudo bash -c "echo '127.0.0.1 nbp'" >> /etc/hosts`

_why do I need it? Kratos makes a request to the url of the oauth2 provider, but since it is running inside a container, it cannot easily use the host port. nbp is a dns that is discoverable for the kratos container, so the host can also use it._

Run `yarn start:nbp`.

Keycloak UI is available on `nbp:11111` (username: admin, pw: admin).
There you have to configure Serlo as a client.

> Client -> Create Client
>
> ```
> id: serlo
> home and root url: http://localhost:3000
> redirect uri: http://localhost:4433/self-service/methods/oidc/callback/nbp
> ```

Get the credentials and go to `kratos/config.yml`:

```yaml
selfservice:
methods:
oidc:
enabled: true
config:
providers:
- id: nbp
provider: generic
client_id: serlo
client_secret: <put secret here>
```

Run the local frontend (not forgetting to change environment in its `.env` to local) to test.

### Email templates

Kratos has to be rebuilt every time you change an email template. Use the following workflow:

1. Edit templates. See at `kratos/config.yml` where they are.
2. Run `yarn kratos:rebuild`
3. Test the verification or the recovery email at `localhost:4436`. Repeat the process.
50 changes: 50 additions & 0 deletions docker-compose.kratos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
services:
kratos-migrate:
depends_on:
- postgres
image: oryd/kratos:v1.0.0
volumes:
- ./kratos:/etc/config/kratos:z
command: -c /etc/config/kratos/config.yml migrate sql -e --yes
restart: on-failure
kratos:
depends_on:
- kratos-migrate
image: oryd/kratos:v1.0.0
ports:
- '4433:4433' # public
- '4434:4434' # admin
restart: unless-stopped
environment:
- LOG_LEVEL=trace
command: serve -c /etc/config/kratos/config.yml --dev --watch-courier
volumes:
- ./kratos:/etc/config/kratos:z
extra_hosts:
- 'host.docker.internal:host-gateway'
postgres:
image: postgres:9.6
ports:
- '5432:5432'
volumes:
- ./kratos/postgres:/docker-entrypoint-initdb.d:z
environment:
- POSTGRES_USER=serlo
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=kratos
mailslurper:
# TODO: use image: marcopas/docker-mailslurper:latest or another maintained project
image: oryd/mailslurper:latest-smtps
ports:
- '4436:4436'
- '4437:4437'
nbp:
image: quay.io/keycloak/keycloak:21.0.0
ports:
- 11111:11111
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=admin
command: ['start-dev', '--http-port=11111']
extra_hosts:
- 'host.docker.internal:host-gateway'
18 changes: 18 additions & 0 deletions kratos/add_to_newsletter.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
local getInterestsKey = function(interest)
if interest == 'parent' then 'dec9a97288'
else if interest == 'teacher' then '05a5ab768a'
else if interest == 'pupil' then 'bbffc7a064'
else if interest == 'student' then 'ebff3b63f6'
else if interest == 'other' then 'd251aad97e';

function(ctx) {
email_address: if 'subscribedNewsletter' in ctx.identity.traits && ctx.identity.traits.subscribedNewsletter == true then
ctx.identity.traits.email
else
error 'User did not subscribed to newsletter. Aborting!',
merge_fields: {
UNAME: ctx.identity.traits.username,
},
status: 'subscribed',
[if 'interest' in ctx.identity.traits then 'interests' else null]: { [getInterestsKey(ctx.identity.traits.interest)]: true },
}
183 changes: 183 additions & 0 deletions kratos/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
version: v1.0.0

dev: true

dsn: postgres://serlo:secret@postgres:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4

serve:
public:
base_url: http://localhost:4433/
admin:
base_url: http://localhost:4434/

selfservice:
default_browser_return_url: http://localhost:3000/

methods:
password:
enabled: true
config:
haveibeenpwned_enabled: false
min_password_length: 6
identifier_similarity_check_enabled: false
link:
enabled: true
oidc:
enabled: true
config:
providers:
- id: nbp
provider: generic
client_id: serlo
client_secret: H8t6WKWtGwFjqNfuAjqxrwCfsdznMAfj
issuer_url: http://nbp:11111/realms/master
mapper_url: file:///etc/config/kratos/user_mapper.jsonnet

flows:
error:
ui_url: http://localhost:3000/auth/error
logout:
after:
default_browser_return_url: http://localhost:3000/auth/login
login:
ui_url: http://localhost:3000/auth/login
lifespan: 10m
after:
password:
hooks:
- hook: require_verified_address
- hook: web_hook
config:
url: http://host.docker.internal:3001/kratos/updateLastLogin
method: POST
body: file:///etc/config/kratos/identity_id.jsonnet
response:
ignore: true
oidc:
default_browser_return_url: http://localhost:3000/auth/login

registration:
ui_url: http://localhost:3000/auth/registration
after:
hooks:
- hook: web_hook
config:
url: http://host.docker.internal:3001/kratos/register
method: POST
can_interrupt: true
body: file:///etc/config/kratos/identity_id.jsonnet
auth:
type: api_key
config:
name: x-kratos-key
value: api.serlo.org-kratos-secret
in: header
- hook: web_hook
config:
url: https://<YOUR_DC>.api.mailchimp.com/3.0/lists/<YOUR_LIST_ID>/members
method: POST
body: file:///etc/config/kratos/add_to_newsletter.jsonnet
can_interrupt: false
response:
ignore: true
auth:
type: basic_auth
config:
user: serlo
password: PUT_HERE_YOUR_MAILCHIMP_API_KEY

oidc:
hooks:
- hook: web_hook
config:
url: http://host.docker.internal:3001/kratos/register
method: POST
body: file:///etc/config/kratos/identity_id.jsonnet
auth:
type: api_key
config:
name: x-kratos-key
value: api.serlo.org-kratos-secret
in: header
- hook: web_hook
config:
url: https://<YOUR_DC>.api.mailchimp.com/3.0/lists/<YOUR_LIST_ID>/members
method: POST
body: file:///etc/config/kratos/add_to_newsletter.jsonnet
can_interrupt: false
response:
ignore: true
auth:
type: basic_auth
config:
user: serlo
password: PUT_HERE_YOUR_MAILCHIMP_API_KEY
- hook: session
default_browser_return_url: http://localhost:3000/auth/login
verification:
enabled: true
use: link
ui_url: http://localhost:3000/auth/verification
recovery:
enabled: true
use: link
ui_url: http://localhost:3000/auth/recovery

settings:
ui_url: http://localhost:3000/auth/settings
log:
level: debug
format: text
leak_sensitive_values: true

secrets:
cookie:
- PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
cipher:
- 32-LONG-SECRET-NOT-SECURE-AT-ALL

ciphers:
algorithm: xchacha20-poly1305

hashers:
algorithm: bcrypt
bcrypt:
cost: 8

identity:
default_schema_id: default
schemas:
- id: default
url: file:///etc/config/kratos/identity.schema.json

courier:
smtp:
connection_uri: smtps://test:secret@mailslurper:1025/?skip_ssl_verify=true
template_override_path: /etc/config/kratos/email-templates/
templates:
verification:
valid:
email:
subject: http://host.docker.internal:3000/api/.ory/mail-templates/verification/valid/email.subject.gotmpl
body:
html: http://host.docker.internal:3000/api/.ory/mail-templates/verification/valid/email.body.gotmpl
plaintext: http://host.docker.internal:3000/api/.ory/mail-templates/verification/valid/email.body.plaintext.gotmpl
invalid:
email:
subject: http://host.docker.internal:3000/api/.ory/mail-templates/verification/invalid/email.subject.gotmpl
body:
html: http://host.docker.internal:3000/api/.ory/mail-templates/verification/invalid/email.body.gotmpl
plaintext: http://host.docker.internal:3000/api/.ory/mail-templates/verification/invalid/email.body.plaintext.gotmpl
recovery:
valid:
email:
subject: http://host.docker.internal:3000/api/.ory/mail-templates/recovery/valid/email.subject.gotmpl
body:
html: http://host.docker.internal:3000/api/.ory/mail-templates/recovery/valid/email.body.gotmpl
plaintext: http://host.docker.internal:3000/api/.ory/mail-templates/recovery/valid/email.body.plaintext.gotmpl
invalid:
email:
subject: http://host.docker.internal:3000/api/.ory/mail-templates/recovery/invalid/email.subject.gotmpl
body:
html: http://host.docker.internal:3000/api/.ory/mail-templates/recovery/invalid/email.body.gotmpl
plaintext: http://host.docker.internal:3000/api/.ory/mail-templates/recovery/invalid/email.body.plaintext.gotmpl
Loading