forked from Github-Mirrors/canaille
make demo entirely runnable with docker-compose
This commit is contained in:
parent
f6680ee1fd
commit
239fce706f
12 changed files with 396 additions and 17 deletions
|
@ -19,13 +19,13 @@ Development environment
|
|||
.. code-block:: console
|
||||
|
||||
cd demo
|
||||
./run.sh
|
||||
./run.sh # or `docker-compose up` to run it with docker
|
||||
|
||||
Then you have access to:
|
||||
|
||||
- A canaille server at http://127.0.0.1:5000
|
||||
- A dummy client at http://127.0.0.1:5001
|
||||
- Another dummy client at http://127.0.0.1:5002
|
||||
- A canaille server at http://localhost:5000
|
||||
- A dummy client at http://localhost:5001
|
||||
- Another dummy client at http://localhost:5002
|
||||
|
||||
The canaille server has some default users:
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ It aims to be very light, simple to install and simple to maintain. Its main fea
|
|||
|
||||
```bash
|
||||
cd demo
|
||||
./run.sh
|
||||
./run.sh # or `docker-compose up` to run it with docker
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
|
16
demo/Dockerfile-canaille
Normal file
16
demo/Dockerfile-canaille
Normal file
|
@ -0,0 +1,16 @@
|
|||
FROM python:slim
|
||||
|
||||
RUN \
|
||||
apt update && \
|
||||
apt -y upgrade && \
|
||||
apt install -y \
|
||||
gcc \
|
||||
libsasl2-dev \
|
||||
libldap2-dev \
|
||||
libssl-dev
|
||||
|
||||
COPY setup.cfg setup.py /opt/canaille/
|
||||
RUN pip install --editable /opt/canaille
|
||||
|
||||
WORKDIR /opt/canaille
|
||||
ENTRYPOINT ["flask", "run", "--host=0.0.0.0"]
|
13
demo/Dockerfile-client
Normal file
13
demo/Dockerfile-client
Normal file
|
@ -0,0 +1,13 @@
|
|||
FROM python:slim
|
||||
|
||||
RUN \
|
||||
apt update && \
|
||||
apt -y upgrade
|
||||
|
||||
RUN pip install \
|
||||
flask \
|
||||
"authlib<1.0.0" \
|
||||
requests
|
||||
|
||||
WORKDIR /opt/client
|
||||
ENTRYPOINT ["flask", "run", "--host=0.0.0.0"]
|
|
@ -1,8 +1,10 @@
|
|||
# Demo and development
|
||||
|
||||
To check out how canaille looks like, or to start contributions, just run it with `./run.sh`!
|
||||
To check out how canaille looks like, or to start contributions, just run the demo:
|
||||
- with `docker-compose up` to install and run it in preconfigured docker containers
|
||||
- or with `./run.sh` to install it natively in a virtual environment and run it locally!
|
||||
|
||||
# Prerequisites
|
||||
# Prerequisites for native demo installation
|
||||
|
||||
You need to have `OpenLDAP` somewhere in your system.
|
||||
|
||||
|
@ -10,7 +12,7 @@ You can either:
|
|||
- install it with your distro packages *(for instance `sudo apt install slapd ldap-utils` with Ubuntu)*.
|
||||
it is not required to launch the system ldap service.
|
||||
- have `docker` plus `docker-compose` installed on your system, the `./run.sh` script will download and
|
||||
run a OpenLDAP image.
|
||||
run an OpenLDAP image.
|
||||
|
||||
canaille depends on [python-ldap](https://github.com/python-ldap/python-ldap), and this package needs
|
||||
some headers to be installed on your system to be built. For instance on Ubuntu you can install this:
|
||||
|
@ -32,15 +34,15 @@ sudo aa-complain /usr/sbin/slapd
|
|||
|
||||
Then you have access to:
|
||||
|
||||
- A canaille server at http://127.0.0.1:5000
|
||||
- A dummy client at http://127.0.0.1:5001
|
||||
- Another dummy client at http://127.0.0.1:5002
|
||||
- A canaille server at http://localhost:5000
|
||||
- A dummy client at http://localhost:5001
|
||||
- Another dummy client at http://localhost:5002
|
||||
|
||||
The canaille server has some default users:
|
||||
|
||||
- A regular user which login and password are **user**;
|
||||
- A moderator user which login and password are **moderator**;
|
||||
- An admin user which admin and password are **admin**.
|
||||
- A new user which admin and password are **new**. This user has no password yet,
|
||||
and his first attempt to log-in will result in sending a password initialization
|
||||
email.
|
||||
- An admin user which login and password are **admin**.
|
||||
- A new user which login is **james**. This user has no password yet,
|
||||
and his first attempt to log-in would result in sending a password initialization
|
||||
email (if a smtp server is configurated).
|
||||
|
|
180
demo/conf-docker/canaille.toml
Normal file
180
demo/conf-docker/canaille.toml
Normal file
|
@ -0,0 +1,180 @@
|
|||
# All the Flask configuration values can be used:
|
||||
# https://flask.palletsprojects.com/en/1.1.x/config/#builtin-configuration-values
|
||||
|
||||
# The flask secret key for cookies. You MUST change this.
|
||||
SECRET_KEY = "change me before you go in production"
|
||||
|
||||
# Your organization name.
|
||||
NAME = "Canaille"
|
||||
|
||||
# The interface on which canaille will be served
|
||||
# SERVER_NAME = "auth.mydomain.tld"
|
||||
# PREFERRED_URL_SCHEME = "https"
|
||||
|
||||
# You can display a logo to be recognized on login screens
|
||||
LOGO = "/static/img/canaille-head.png"
|
||||
|
||||
# Your favicon. If unset the LOGO will be used.
|
||||
FAVICON = "/static/img/canaille-c.png"
|
||||
|
||||
# The name of a theme in the 'theme' directory, or an absolute path
|
||||
# to a theme. Defaults to 'default'. Theming is done with
|
||||
# https://github.com/tktech/flask-themer
|
||||
# THEME = "default"
|
||||
|
||||
# If unset, language is detected
|
||||
# LANGUAGE = "en"
|
||||
|
||||
# Path to the RFC8414 metadata file. You should update those files
|
||||
# with your production URLs.
|
||||
OAUTH2_METADATA_FILE = "conf/oauth-authorization-server.json"
|
||||
OIDC_METADATA_FILE = "conf/openid-configuration.json"
|
||||
|
||||
# If you have a sentry instance, you can set its dsn here:
|
||||
# SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
|
||||
|
||||
# If HIDE_INVALID_LOGINS is set to true, when a user tries to sign in with
|
||||
# an invalid login, a message is shown saying that the login does not
|
||||
# exist. If HIDE_INVALID_LOGINS is set to false (the default) a message is
|
||||
# shown saying that the password is wrong, but does not give a clue
|
||||
# wether the login exists or not.
|
||||
# HIDE_INVALID_LOGINS = false
|
||||
|
||||
# The validity duration of registration invitations, in seconds.
|
||||
# Defaults to 2 days
|
||||
# INVITATION_EXPIRATION = 172800
|
||||
|
||||
[LOGGING]
|
||||
# LEVEL can be one value among:
|
||||
# DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||
# Defaults to WARNING
|
||||
# LEVEL = "WARNING"
|
||||
LEVEL = "DEBUG"
|
||||
|
||||
# The path of the log file. If not set (the default) logs are
|
||||
# written in the standard error output.
|
||||
# PATH = ""
|
||||
|
||||
[LDAP]
|
||||
URI = "ldap://ldap:389"
|
||||
ROOT_DN = "dc=mydomain,dc=tld"
|
||||
BIND_DN = "cn=admin,dc=mydomain,dc=tld"
|
||||
BIND_PW = "admin"
|
||||
TIMEOUT = 10
|
||||
|
||||
# Where to search for users?
|
||||
USER_BASE = "ou=users,dc=mydomain,dc=tld"
|
||||
|
||||
# The object class to use for creating new users
|
||||
# USER_CLASS = "inetOrgPerson"
|
||||
|
||||
# The attribute to identify an object in the User dn.
|
||||
USER_ID_ATTRIBUTE = "uid"
|
||||
|
||||
# Filter to match users on sign in. Supports a variable
|
||||
# {login} that can be used to compare against several fields:
|
||||
# USER_FILTER = "(|(uid={login})(mail={login}))"
|
||||
|
||||
# Where to search for groups?
|
||||
GROUP_BASE = "ou=groups,dc=mydomain,dc=tld"
|
||||
|
||||
# The object class to use for creating new groups
|
||||
# GROUP_CLASS = "groupOfNames"
|
||||
|
||||
# The attribute to identify an object in the User dn.
|
||||
# GROUP_ID_ATTRIBUTE = "cn"
|
||||
|
||||
# The attribute to use to identify a group
|
||||
# GROUP_NAME_ATTRIBUTE = "cn"
|
||||
|
||||
# A filter to check if a user belongs to a group
|
||||
# A 'user' variable is available.
|
||||
# GROUP_USER_FILTER = "member={user.dn}"
|
||||
|
||||
# You can define access controls that define what users can do on canaille
|
||||
# An access control consists in a FILTER to match users, a list of PERMISSIONS
|
||||
# matched users will be able to perform, and fields users will be able
|
||||
# to READ and WRITE. Users matching several filters will cumulate permissions.
|
||||
#
|
||||
# A 'FILTER' parameter that is a LDAP filter used to determine if a user
|
||||
# belongs to an access control. If absent, all the users will match this
|
||||
# access control. If your LDAP server has the 'memberof' overlay, you can
|
||||
# filter against group membership.
|
||||
# Here are some examples
|
||||
# FILTER = 'uid=admin'
|
||||
# FILTER = 'memberof=cn=admins,ou=groups,dc=mydomain,dc=tld'
|
||||
#
|
||||
# The 'PERMISSIONS' parameter that is an list of items the users in the access
|
||||
# control will be able to manage. 'PERMISSIONS' is optionnal. Values can be:
|
||||
# - "use_oidc" to allow OpenID Connect authentication
|
||||
# - "manage_oidc" to allow OpenID Connect client managements
|
||||
# - "manage_users" to allow other users management
|
||||
# - "manage_groups" to allow group edition and creation
|
||||
# - "delete_account" allows a user to delete his own account. If used with
|
||||
# manage_users, the user can delete any account
|
||||
# - "impersonate_users" to allow a user to take the identity of another user
|
||||
#
|
||||
# The 'READ' and 'WRITE' attributes are the LDAP attributes of the user
|
||||
# object that users will be able to read and/or write.
|
||||
[ACL.DEFAULT]
|
||||
PERMISSIONS = ["use_oidc"]
|
||||
READ = ["uid", "groups"]
|
||||
WRITE = ["jpegPhoto", "givenName", "sn", "userPassword", "telephoneNumber", "mail", "labeledURI"]
|
||||
|
||||
[ACL.ADMIN]
|
||||
FILTER = "memberof=cn=admins,ou=groups,dc=mydomain,dc=tld"
|
||||
PERMISSIONS = [
|
||||
"manage_users",
|
||||
"manage_groups",
|
||||
"manage_oidc",
|
||||
"delete_account",
|
||||
"impersonate_users",
|
||||
]
|
||||
WRITE = ["groups"]
|
||||
|
||||
[ACL.HALF_ADMIN]
|
||||
FILTER = "memberof=cn=moderators,ou=groups,dc=mydomain,dc=tld"
|
||||
PERMISSIONS = ["manage_users", "manage_groups", "delete_account"]
|
||||
WRITE = ["groups"]
|
||||
|
||||
# The jwt configuration. You can generate a RSA keypair with:
|
||||
# openssl genrsa -out private.pem 4096
|
||||
# openssl rsa -in private.pem -pubout -outform PEM -out public.pem
|
||||
[JWT]
|
||||
# The path to the private key.
|
||||
PRIVATE_KEY = "conf/private.pem"
|
||||
# The path to the public key.
|
||||
PUBLIC_KEY = "conf/public.pem"
|
||||
# The key type parameter
|
||||
# KTY = "RSA"
|
||||
# The key algorithm
|
||||
# ALG = "RS256"
|
||||
# The time the JWT will be valid, in seconds
|
||||
# EXP = 3600
|
||||
|
||||
[JWT.MAPPING]
|
||||
# Mapping between JWT fields and LDAP attributes from your
|
||||
# User objectClass.
|
||||
# {attribute} will be replaced by the user ldap attribute value.
|
||||
# Default values fits inetOrgPerson.
|
||||
SUB = "{{ user.uid[0] }}"
|
||||
NAME = "{{ user.cn[0] }}"
|
||||
PHONE_NUMBER = "{{ user.telephoneNumber[0] }}"
|
||||
EMAIL = "{{ user.mail[0] }}"
|
||||
GIVEN_NAME = "{{ user.givenName[0] }}"
|
||||
FAMILY_NAME = "{{ user.sn[0] }}"
|
||||
PREFERRED_USERNAME = "{{ user.displayName[0] }}"
|
||||
LOCALE = "{{ user.preferredLanguage[0] }}"
|
||||
ADDRESS = "{{ user.postalAddress[0] }}"
|
||||
PICTURE = "{% if user.jpegPhoto %}{{ url_for('account.photo', uid=user.uid[0], field='jpegPhoto', _external=True) }}{% endif %}"
|
||||
WEBSITE = "{{ user.labeledURI[0] }}"
|
||||
|
||||
# The SMTP server options. If not set, mail related features such as
|
||||
# user invitations, and password reset emails, will be disabled.
|
||||
[SMTP]
|
||||
# HOST = "localhost"
|
||||
# PORT = 25
|
||||
# TLS = false
|
||||
# LOGIN = ""
|
||||
# PASSWORD = ""
|
||||
FROM_ADDR = "admin@mydomain.tld"
|
7
demo/conf-docker/client1.cfg
Normal file
7
demo/conf-docker/client1.cfg
Normal file
|
@ -0,0 +1,7 @@
|
|||
SECRET_KEY="46bf9fb5-88d5-489b-9312-899588377ff0"
|
||||
NAME = "Client 1"
|
||||
SESSION_COOKIE_NAME="client1-session"
|
||||
|
||||
OAUTH_CLIENT_ID="1JGkkzCbeHpGtlqgI5EENByf"
|
||||
OAUTH_CLIENT_SECRET="2xYPSReTQRmGG1yppMVZQ0ASXwFejPyirvuPbKhNa6TmKC5x"
|
||||
OAUTH_AUTH_SERVER="http://canaille:5000"
|
7
demo/conf-docker/client2.cfg
Normal file
7
demo/conf-docker/client2.cfg
Normal file
|
@ -0,0 +1,7 @@
|
|||
SECRET_KEY="8e953ecc-13be-497b-806f-c65faa1e328f"
|
||||
NAME = "Client 2"
|
||||
SESSION_COOKIE_NAME="client2-session"
|
||||
|
||||
OAUTH_CLIENT_ID="gn4yFN7GDykL7QP8v8gS9YfV"
|
||||
OAUTH_CLIENT_SECRET="ouFJE5WpICt6hxTyf8icXPeeklMektMY4gV0Rmf3aY60VElA"
|
||||
OAUTH_AUTH_SERVER="http://canaille:5000"
|
31
demo/conf-docker/oauth-authorization-server.json
Normal file
31
demo/conf-docker/oauth-authorization-server.json
Normal file
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"issuer":
|
||||
"http://localhost:5000",
|
||||
"authorization_endpoint":
|
||||
"http://localhost:5000/oauth/authorize",
|
||||
"token_endpoint":
|
||||
"http://localhost:5000/oauth/token",
|
||||
"token_endpoint_auth_methods_supported":
|
||||
["client_secret_basic", "private_key_jwt",
|
||||
"client_secret_post", "none"],
|
||||
"token_endpoint_auth_signing_alg_values_supported":
|
||||
["RS256", "ES256"],
|
||||
"userinfo_endpoint":
|
||||
"http://localhost:5000/oauth/userinfo",
|
||||
"jwks_uri":
|
||||
"http://localhost:5000/oauth/jwks.json",
|
||||
"registration_endpoint":
|
||||
"http://localhost:5000/oauth/register",
|
||||
"introspection_endpoint":
|
||||
"https://mydomain.tld/oauth/introspect",
|
||||
"scopes_supported":
|
||||
["openid", "profile", "email", "address",
|
||||
"phone", "groups"],
|
||||
"response_types_supported":
|
||||
["code", "token", "id_token", "code token",
|
||||
"code id_token", "token id_token"],
|
||||
"service_documentation":
|
||||
"http://localhost:5000/documentation.html",
|
||||
"ui_locales_supported":
|
||||
["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"]
|
||||
}
|
66
demo/conf-docker/openid-configuration.json
Normal file
66
demo/conf-docker/openid-configuration.json
Normal file
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"issuer":
|
||||
"http://localhost:5000",
|
||||
"authorization_endpoint":
|
||||
"http://localhost:5000/oauth/authorize",
|
||||
"token_endpoint":
|
||||
"http://canaille:5000/oauth/token",
|
||||
"token_endpoint_auth_methods_supported":
|
||||
["client_secret_basic", "private_key_jwt",
|
||||
"client_secret_post", "none"],
|
||||
"token_endpoint_auth_signing_alg_values_supported":
|
||||
["RS256"],
|
||||
"userinfo_endpoint":
|
||||
"http://canaille:5000/oauth/userinfo",
|
||||
"check_session_iframe":
|
||||
"http://canaille:5000/oauth/check_session",
|
||||
"end_session_endpoint":
|
||||
"http://canaille:5000/oauth/end_session",
|
||||
"jwks_uri":
|
||||
"http://canaille:5000/oauth/jwks.json",
|
||||
"registration_endpoint":
|
||||
"http://canaille:5000/oauth/register",
|
||||
"introspection_endpoint":
|
||||
"https://canaille:5000/oauth/introspect",
|
||||
"scopes_supported":
|
||||
["openid", "profile", "email", "address",
|
||||
"phone", "groups"],
|
||||
"response_types_supported":
|
||||
["code", "token", "id_token", "code token",
|
||||
"code id_token", "token id_token"],
|
||||
"acr_values_supported":
|
||||
["urn:mace:incommon:iap:silver",
|
||||
"urn:mace:incommon:iap:bronze"],
|
||||
"subject_types_supported":
|
||||
["public", "pairwise"],
|
||||
"userinfo_signing_alg_values_supported":
|
||||
["RS256", "ES256", "HS256"],
|
||||
"userinfo_encryption_alg_values_supported":
|
||||
["RSA1_5", "A128KW"],
|
||||
"userinfo_encryption_enc_values_supported":
|
||||
["A128CBC-HS256", "A128GCM"],
|
||||
"id_token_signing_alg_values_supported":
|
||||
["RS256", "ES256", "HS256"],
|
||||
"id_token_encryption_alg_values_supported":
|
||||
["RSA1_5", "A128KW"],
|
||||
"id_token_encryption_enc_values_supported":
|
||||
["A128CBC-HS256", "A128GCM"],
|
||||
"request_object_signing_alg_values_supported":
|
||||
["none", "RS256", "ES256"],
|
||||
"display_values_supported":
|
||||
["page", "popup"],
|
||||
"claim_types_supported":
|
||||
["normal", "distributed"],
|
||||
"claims_supported":
|
||||
["sub", "iss", "auth_time", "acr",
|
||||
"name", "given_name", "family_name", "nickname",
|
||||
"profile", "picture", "website",
|
||||
"email", "email_verified", "locale", "zoneinfo",
|
||||
"groups"],
|
||||
"claims_parameter_supported":
|
||||
true,
|
||||
"service_documentation":
|
||||
"http://localhost:5000/oauth/service_documentation.html",
|
||||
"ui_locales_supported":
|
||||
["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"]
|
||||
}
|
|
@ -7,9 +7,66 @@ services:
|
|||
environment:
|
||||
- LDAP_DOMAIN=mydomain.tld
|
||||
volumes:
|
||||
- ./ldif/bootstrap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-boostrap.ldif:ro
|
||||
- ./ldif/memberof.ldif:/container/service/slapd/assets/config/bootstrap/ldif/03-memberOf.ldif:ro
|
||||
# memberof overlay is already present in openldap docker image but only for groupOfUniqueNames. We need to overwrite it (until canaille can handle groupOfUniqueNames).
|
||||
# https://github.com/osixia/docker-openldap/blob/master/image/service/slapd/assets/config/bootstrap/ldif/03-memberOf.ldif
|
||||
- ../canaille/ldap_backend/schemas/oauth2-openldap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/40-oauth2.ldif:ro
|
||||
- ./ldif/bootstrap-tree.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-boostrap-tree.ldif:ro
|
||||
- ./ldif/bootstrap-data.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/60-boostrap-data.ldif:ro
|
||||
command: --copy-service --loglevel debug
|
||||
ports:
|
||||
- 5389:389
|
||||
- 5636:636
|
||||
|
||||
canaille:
|
||||
depends_on:
|
||||
- ldap
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: demo/Dockerfile-canaille
|
||||
environment:
|
||||
- AUTHLIB_INSECURE_TRANSPORT=1
|
||||
- FLASK_ENV=development
|
||||
- CONFIG=/opt/canaille/conf/canaille.toml
|
||||
- FLASK_APP=canaille
|
||||
volumes:
|
||||
- ../canaille:/opt/canaille/canaille
|
||||
- ./conf-docker:/opt/canaille/conf
|
||||
ports:
|
||||
- 5000:5000
|
||||
|
||||
client1:
|
||||
depends_on:
|
||||
- canaille
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile-client
|
||||
environment:
|
||||
- FLASK_ENV=development
|
||||
- CONFIG=/opt/client/conf/client1.cfg
|
||||
- FLASK_APP=client
|
||||
volumes:
|
||||
- ./client:/opt/client/client
|
||||
- ./conf-docker:/opt/client/conf
|
||||
- ../canaille/static:/opt/canaille/static
|
||||
command: --port=5001
|
||||
ports:
|
||||
- 5001:5001
|
||||
|
||||
client2:
|
||||
depends_on:
|
||||
- canaille
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile-client
|
||||
environment:
|
||||
- FLASK_ENV=development
|
||||
- CONFIG=/opt/client/conf/client2.cfg
|
||||
- FLASK_APP=client
|
||||
volumes:
|
||||
- ./client:/opt/client/client
|
||||
- ./conf-docker:/opt/client/conf
|
||||
- ../canaille/static:/opt/canaille/static
|
||||
command: --port=5002
|
||||
ports:
|
||||
- 5002:5002
|
||||
|
|
|
@ -4,7 +4,7 @@ if [ "$SLAPD_BINARY" == "NATIVE" ] || ([ "$SLAPD_BINARY" == "" ] && type slapd >
|
|||
env BIN=$BIN:/usr/bin:/usr/sbin env/bin/python ldap-server.py
|
||||
|
||||
elif [ "$SLAPD_BINARY" == "DOCKER" ] || ([ "$SLAPD_BINARY" == "" ] && type docker-compose > /dev/null 2>&1); then
|
||||
docker-compose up
|
||||
docker-compose run --service-ports --rm ldap
|
||||
|
||||
else
|
||||
echo "Cannot start the LDAP server. Please install openldap or docker on your system."
|
||||
|
|
Loading…
Reference in a new issue