diff --git a/Makefile b/Makefile index 846e3210..b8faacfd 100644 --- a/Makefile +++ b/Makefile @@ -370,6 +370,10 @@ start-tilt-keycloak: ## start the kubernetes cluster using kind, without Pro Con DEV_ENV=dev-keycloak tilt up -f ./bin/Tiltfile .PHONY: build-k8s-cluster +start-tilt-authentik: + DEV_ENV=dev-authentik tilt up -f ./bin/Tiltfile +.PHONY: start-tilt-authentik + release: ## helper for release and deployment python scripts/release.py .PHONY: release diff --git a/docker/auth/authentik_blueprint.yaml b/docker/auth/authentik_blueprint.yaml new file mode 100644 index 00000000..fddda454 --- /dev/null +++ b/docker/auth/authentik_blueprint.yaml @@ -0,0 +1,257 @@ +# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json + +# Can be debugged in a worker with: +# ak apply_blueprint /blueprints/mounted/cm-people/people.yaml + +version: 1 +metadata: + name: Initial configuration for People project +entries: + - model: authentik_core.user + identifiers: + username: people + attrs: + name: John Doe + email: people@people.world + password: people + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.marie + attrs: + name: Marie Delamairie + email: marie.varzy@gmail.com + password: password-e2e.marie + path: users + is_active: true + attributes: + siret: 21510339100011 + + - model: authentik_core.user + identifiers: + username: user-e2e-chromium + attrs: + name: E2E Chromium + email: user@chromium.e2e + password: password-e2e-chromium + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: user-e2e-webkit + attrs: + name: E2E Webkit + email: user@webkit.e2e + password: password-e2e-webkit + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: user-e2e-firefox + attrs: + name: E2E Firefox + email: user@firefox.e2e + password: password-e2e-firefox + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-member + attrs: + name: E2E Group Member + email: e2e.team-member@example.com + password: password-e2e.team-member + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-administrator + attrs: + name: E2E Group Administrator + email: e2e.team-administrator@example.com + password: password-e2e.team-administrator + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-owner + attrs: + name: E2E Group Owner + email: e2e.team-owner@example.com + password: password-e2e.team-owner + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.mail-member + attrs: + name: E2E Mailbox Member + email: e2e.mail-member@example.com + password: password-e2e.mail-member + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.mail-administrator + attrs: + name: E2E Mailbox Administrator + email: e2e.mail-administrator@example.com + password: password-e2e.mail-administrator + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.mail-owner + attrs: + name: E2E Mailbox Owner + email: e2e.mail-owner@example.com + password: password-e2e.mail-owner + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-member-mail-member + attrs: + name: E2E Group Member Mailbox Member + email: e2e.team-member-mail-member@example.com + password: password-e2e.team-member-mail-member + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-member-mail-administrator + attrs: + name: E2E Group Member Mailbox Administrator + email: e2e.team-member-mail-administrator@example.com + password: password-e2e.team-member-mail-administrator + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-member-mail-owner + attrs: + name: E2E Group Member Mailbox Owner + email: e2e.team-member-mail-owner@example.com + password: password-e2e.team-member-mail-owner + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-administrator-mail-member + attrs: + name: E2E Group Administrator Mailbox Member + email: e2e.team-administrator-mail-member@example.com + password: password-e2e.team-administrator-mail-member + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-administrator-mail-administrator + attrs: + name: E2E Group Administrator Mailbox Administrator + email: e2e.team-administrator-mail-administrator@example.com + password: password-e2e.team-administrator-mail-administrator + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-administrator-mail-owner + attrs: + name: E2E Group Administrator Mailbox Owner + email: e2e.team-administrator-mail-owner@example.com + password: password-e2e.team-administrator-mail-owner + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-owner-mail-member + attrs: + name: E2E Group Owner Mailbox Member + email: e2e.team-owner-mail-member@example.com + password: password-e2e.team-owner-mail-member + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-owner-mail-administrator + attrs: + name: E2E Group Owner Mailbox Administrator + email: e2e.team-owner-mail-administrator@example.com + password: password-e2e.team-owner-mail-administrator + path: users + is_active: true + + - model: authentik_core.user + identifiers: + username: e2e.team-owner-mail-owner + attrs: + name: E2E Mailbox Owner + email: e2e.team-owner-mail-owner@example.com + password: password-e2e.team-owner-mail-owner + path: users + is_active: true + + - model: authentik_providers_oauth2.scopemapping + id: scope-siret + identifiers: + scope_name: siret + attrs: + name: "ProConnect OAuth Mapping: OpenID 'siret'" + description: Access to your company SIRET number + expression: | + return { + "siret": str(request.user.attributes.get("siret", "")), + } + + # Create the OIDC Provider + - model: authentik_providers_oauth2.oauth2provider + id: people-oidc-provider + identifiers: + name: Provider for people + attrs: + authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]] + authentication_flow: !Find [authentik_flows.flow, [slug, default-authentication-flow]] + invalidation_flow: !Find [authentik_flows.flow, [slug, default-invalidation-flow]] + issuer_mode: global + client_type: confidential + client_id: people + client_secret: ThisIsAnExampleKeyForDevPurposeOnly + access_code_validity: minutes=1 + access_token_validity: minutes=5 + refresh_token_validity: days=30 + include_claims_in_id_token: false + redirect_uris: + - matching_mode: strict + url: https://desk.127.0.0.1.nip.io/api/v1.0/callback/ + property_mappings: + - !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-openid]] + - !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-email]] + - !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-profile]] + - !KeyOf scope-siret + signing_key: !Find [authentik_crypto.certificatekeypair, [name, authentik Self-signed Certificate]] + + # Create the OIDC Application + - model: authentik_core.application + identifiers: + slug: people + attrs: + name: People Application + provider: !KeyOf people-oidc-provider diff --git a/src/backend/core/authentication/backends.py b/src/backend/core/authentication/backends.py index 65ecfec9..6b5f1613 100644 --- a/src/backend/core/authentication/backends.py +++ b/src/backend/core/authentication/backends.py @@ -68,7 +68,10 @@ class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend): proxies=self.get_settings("OIDC_PROXY", None), ) user_response.raise_for_status() - userinfo = self.verify_token(user_response.text) + if settings.OIDC_OP_USER_ENDPOINT_IS_JWT: + userinfo = self.verify_token(user_response.text) + else: + userinfo = user_response.json() return userinfo def get_or_create_user(self, access_token, id_token, payload): diff --git a/src/backend/people/settings.py b/src/backend/people/settings.py index c7c9a7ac..7425677e 100755 --- a/src/backend/people/settings.py +++ b/src/backend/people/settings.py @@ -400,6 +400,11 @@ class Base(Configuration): OIDC_OP_USER_ENDPOINT = values.Value( None, environ_name="OIDC_OP_USER_ENDPOINT", environ_prefix=None ) + OIDC_OP_USER_ENDPOINT_IS_JWT = values.BooleanValue( + default=True, + environ_name="OIDC_OP_USER_ENDPOINT_IS_JWT", + environ_prefix=None, + ) OIDC_OP_LOGOUT_ENDPOINT = values.Value( None, environ_name="OIDC_OP_LOGOUT_ENDPOINT", environ_prefix=None ) diff --git a/src/helm/env.d/dev-authentik/values.desk.yaml.gotmpl b/src/helm/env.d/dev-authentik/values.desk.yaml.gotmpl new file mode 100644 index 00000000..c58f72e2 --- /dev/null +++ b/src/helm/env.d/dev-authentik/values.desk.yaml.gotmpl @@ -0,0 +1,105 @@ +image: + repository: localhost:5001/people-backend + pullPolicy: Always + tag: "latest" + +backend: + replicas: 1 + envVars: + DJANGO_CSRF_TRUSTED_ORIGINS: https://desk.127.0.0.1.nip.io,http://desk.127.0.0.1.nip.io + DJANGO_CONFIGURATION: Local + DJANGO_ALLOWED_HOSTS: "*" + DJANGO_SECRET_KEY: kkdsjfhkjhsfdkjhsd76kjhkjh + DJANGO_SETTINGS_MODULE: people.settings + DJANGO_SUPERUSER_PASSWORD: admin + DJANGO_SUPERUSER_EMAIL: admin@example.com + DJANGO_EMAIL_HOST_PASSWORD: changeme + DJANGO_EMAIL_HOST: "maildev" + DJANGO_EMAIL_PORT: 1025 + DJANGO_EMAIL_USE_SSL: False + DJANGO_SENTRY_DSN: null + OIDC_RS_CLIENT_ID: changeme + OIDC_RS_CLIENT_SECRET: changeme + OIDC_RS_PRIVATE_KEY_STR: "lkj" + OIDC_OP_JWKS_ENDPOINT: https://authentik.127.0.0.1.nip.io/application/o/people/jwks/ + OIDC_OP_AUTHORIZATION_ENDPOINT: https://authentik.127.0.0.1.nip.io/application/o/authorize/ + OIDC_OP_TOKEN_ENDPOINT: https://authentik.127.0.0.1.nip.io/application/o/token/ + OIDC_OP_USER_ENDPOINT: https://authentik.127.0.0.1.nip.io/application/o/userinfo/ + OIDC_OP_USER_ENDPOINT_IS_JWT: False + OIDC_OP_LOGOUT_ENDPOINT: https://authentik.127.0.0.1.nip.io/application/o/people/end-session/ + OIDC_ORGANIZATION_REGISTRATION_ID_FIELD: "siret" + OIDC_RP_CLIENT_ID: people + OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly + OIDC_RP_SIGN_ALGO: RS256 + OIDC_RP_SCOPES: "openid email siret" + OIDC_REDIRECT_ALLOWED_HOSTS: https://desk.127.0.0.1.nip.io + OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}" + OAUTH2_PROVIDER_OIDC_ENABLED: True + OAUTH2_PROVIDER_VALIDATOR_CLASS: "mailbox_oauth2.validators.ProConnectValidator" + ORGANIZATION_REGISTRATION_ID_VALIDATORS: '[{"NAME": "django.core.validators.RegexValidator", "OPTIONS": {"regex": "^[0-9]{14}$"}}]' + LOGIN_REDIRECT_URL: https://desk.127.0.0.1.nip.io + LOGIN_REDIRECT_URL_FAILURE: https://desk.127.0.0.1.nip.io + LOGOUT_REDIRECT_URL: https://desk.127.0.0.1.nip.io + DB_HOST: postgres-postgresql + DB_NAME: people + DB_USER: dinum + DB_PASSWORD: pass + DB_PORT: 5432 + POSTGRES_DB: people + POSTGRES_USER: dinum + POSTGRES_PASSWORD: pass + REDIS_URL: redis://default:pass@redis-master:6379/1 + WEBMAIL_URL: "https://onestendev.yapasdewebmail.fr" + MAIL_PROVISIONING_API_URL: "http://dimail:8000" + MAIL_PROVISIONING_API_CREDENTIALS: changeme + command: + - "gunicorn" + - "-c" + - "/usr/local/etc/gunicorn/people.py" + - "people.wsgi:application" + - "--reload" + + createsuperuser: + command: + - "/bin/sh" + - "-c" + - python manage.py createsuperuser --username ${DJANGO_SUPERUSER_EMAIL} --password ${DJANGO_SUPERUSER_PASSWORD} || echo ok + restartPolicy: Never + + # Extra volume mounts to manage our local custom CA and avoid to set ssl_verify: false + extraVolumeMounts: + - name: certs + mountPath: /usr/local/lib/python3.12/site-packages/certifi/cacert.pem + subPath: cacert.pem + + # Extra volumes to manage our local custom CA and avoid to set ssl_verify: false + extraVolumes: + - name: certs + configMap: + name: certifi + items: + - key: cacert.pem + path: cacert.pem + +frontend: + envVars: + PORT: 8080 + NEXT_PUBLIC_API_ORIGIN: https://desk.127.0.0.1.nip.io + + replicas: 1 + command: + - yarn + - dev + + image: + repository: localhost:5001/people-frontend + pullPolicy: Always + tag: "latest" + +ingress: + enabled: true + host: desk.127.0.0.1.nip.io + +ingressAdmin: + enabled: true + host: desk.127.0.0.1.nip.io diff --git a/src/helm/helmfile.yaml b/src/helm/helmfile.yaml index 25eeae15..cd1197c9 100644 --- a/src/helm/helmfile.yaml +++ b/src/helm/helmfile.yaml @@ -5,13 +5,67 @@ environments: dev-keycloak: values: - version: 0.0.1 + dev-authentik: + values: + - version: 0.0.1 --- repositories: - name: bitnami url: registry-1.docker.io/bitnamicharts oci: true +- name: authentik + url: https://charts.goauthentik.io releases: + - name: authentik + installed: {{ eq .Environment.Name "dev-authentik" | toYaml }} + missingFileHandler: Warn + namespace: {{ .Namespace }} + chart: authentik/authentik + values: + - authentik: + log_level: trace + secret_key: PleaseGenerateASecureKey + postgresql: + password: ThisIsNotASecurePassword + redis: + password: ThisIsNotASecurePassword + + # Basic blueprint configuration - directly mount ConfigMaps + blueprints: + configMaps: + - people + + server: + ingress: + ingressClassName: nginx + enabled: true + hosts: + - authentik.127.0.0.1.nip.io + + postgresql: + enabled: true + auth: + password: ThisIsNotASecurePassword + + redis: + enabled: true + auth: + enabled: true + password: ThisIsNotASecurePassword + architecture: standalone + + # Create the ConfigMap with blueprint data + additionalObjects: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: people + namespace: {{ .Namespace }} + data: + people.yaml: | +{{ readFile "../../docker/auth/authentik_blueprint.yaml" | indent 16 }} + - name: keycloak installed: {{ eq .Environment.Name "dev-keycloak" | toYaml }} missingFileHandler: Warn