Compare commits

..

1 Commits

Author SHA1 Message Date
Jonathan Perret 8f9a9ed1f0 temporary enable debug logs in production 2025-03-05 17:32:56 +01:00
57 changed files with 224 additions and 863 deletions
+2 -2
View File
@@ -9,9 +9,9 @@ We primarily use GitHub as an issue tracker. If however you're encountering an i
---
Please make sure you have read our [main Readme](https://github.com/proconnect-gouv/oidc2fer).
Please make sure you have read our [main Readme](https://github.com/numerique-gouv/oidc2fer).
Also make sure it was not already answered in [an open or close issue](https://github.com/proconnect-gouv/oidc2fer/issues).
Also make sure it was not already answered in [an open or close issue](https://github.com/numerique-gouv/oidc2fer/issues).
If your question was not covered, and you feel like it should be, fire away! We'd love to improve our docs! 👌
-67
View File
@@ -1,67 +0,0 @@
name: 🐳 Build docker images
on:
push:
branches:
- 'main'
tags:
- 'v*'
pull_request:
branches:
- 'main'
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
type=sha,prefix=sha-
# set latest tag for default branch
type=raw,value=latest,enable={{is_default_branch}}
- name: Login to Github Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
target: production
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
notify-argocd:
runs-on: ubuntu-latest
if: |
github.event_name != 'pull_request'
needs:
- build-and-push
steps:
- uses: numerique-gouv/action-argocd-webhook-notification@main
id: notify
with:
deployment_repo_path: "${{ github.repository }}"
argocd_webhook_secret: "${{ secrets.ARGOCD_PREPROD_WEBHOOK_SECRET }}"
argocd_url: "${{ vars.ARGOCD_PREPROD_WEBHOOK_URL }}"
+93
View File
@@ -0,0 +1,93 @@
name: Docker Hub Workflow
on:
workflow_dispatch:
push:
branches:
- 'main'
tags:
- 'v*'
pull_request:
branches:
- 'main'
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "oidc2fer,secrets"
-
name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: lasuite/oidc2fer
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: secrets/numerique-gouv/oidc2fer/secrets.enc.env
age-key: ${{ secrets.SOPS_PRIVATE }}
-
name: Login to DockerHub
run: echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_USER" --password-stdin
-
name: Build and push
uses: docker/build-push-action@v5
with:
context: .
target: production
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
notify-argocd:
runs-on: ubuntu-latest
if: |
github.event_name != 'pull_request'
needs:
- build-and-push
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "oidc2fer,secrets"
-
name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: secrets/numerique-gouv/oidc2fer/secrets.enc.env
age-key: ${{ secrets.SOPS_PRIVATE }}
-
name: Call argocd github webhook
run: |
data='{"ref": "'$GITHUB_REF'","repository": {"html_url":"'$GITHUB_SERVER_URL'/'$GITHUB_REPOSITORY'"}}'
sig=$(echo -n ${data} | openssl dgst -sha1 -hmac ''${ARGOCD_WEBHOOK_SECRET}'' | awk '{print "X-Hub-Signature: sha1="$2}')
curl -X POST -H 'X-GitHub-Event:push' -H "Content-Type: application/json" -H "${sig}" --data "${data}" $ARGOCD_WEBHOOK_URL
+22
View File
@@ -0,0 +1,22 @@
name: Helmfile lint
run-name: Helmfile lint
on:
pull_request:
branches:
- 'main'
jobs:
helmfile-lint:
runs-on: ubuntu-latest
container:
image: ghcr.io/helmfile/helmfile:latest
steps:
-
uses: numerique-gouv/action-helmfile-lint@main
with:
app-id: ${{ secrets.APP_ID }}
age-key: ${{ secrets.SOPS_PRIVATE }}
private-key: ${{ secrets.PRIVATE_KEY }}
helmfile-src: "src/helm"
repositories: "oidc2fer,secrets"
@@ -1,4 +1,4 @@
name: 💚 CI Tests
name: OIDC2FER Workflow
on:
push:
@@ -23,7 +23,7 @@ jobs:
run: git log
- name: Enforce absence of print statements in code
run: |
! git diff origin/${{ github.event.pull_request.base.ref }}..HEAD -- . ':(exclude).github/**' | grep "print("
! git diff origin/${{ github.event.pull_request.base.ref }}..HEAD -- . ':(exclude)**/oidc2fer.yml' | grep "print("
- name: Check absence of fixup commits
run: |
! git log | grep 'fixup!'
@@ -43,7 +43,7 @@ jobs:
with:
fetch-depth: 0
- name: Check that the CHANGELOG has been modified in the current branch
run: git log --name-only --pretty="" origin/${{ github.event.pull_request.base.ref }}..HEAD | grep CHANGELOG
run: git whatchanged --name-only --pretty="" origin/${{ github.event.pull_request.base.ref }}..HEAD | grep CHANGELOG
lint-changelog:
runs-on: ubuntu-latest
@@ -101,37 +101,3 @@ jobs:
pip install --user .[dev]
- name: Run tests
run: ~/.local/bin/pytest
helmfile-lint:
runs-on: ubuntu-latest
# skip this job on forks since they won't have access to the required secrets
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request'
container:
image: ghcr.io/helmfile/helmfile:v1.2.0
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: numerique-gouv
repositories: secrets
-
name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
-
name: Helmfile lint
env:
SOPS_AGE_KEY: ${{ secrets.SOPS_PRIVATE }}
run: |
HELMFILE=src/helm/helmfile.yaml.gotmpl
environments=$(< $HELMFILE awk -F'[ :]+' '/^ [^ ]/ {print $2} /^---/ { exit }')
for env in $environments; do
echo "################### $env lint ###################"
helmfile -e $env -f "$HELMFILE" lint || exit 1
echo -e "\n"
done
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "secrets"]
path = secrets
url = ../../numerique-gouv/secrets
url = ../secrets
-27
View File
@@ -6,33 +6,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
- add alternate configuration for connection to CNB (#33)
## [1.0.10] - 2025-11-21
- rename helmfile.yaml for compatibility with helmfile 1.x
## [1.0.9] - 2025-10-03
- map SAML entity IDs to SIRET (#34)
- include ACR claim in ID token only if requested (#32)
## [1.0.8] - 2025-09-10
- fix secrets submodule path following move to `proconnect-gouv` organization
- update other references to `numerique-gouv` organization
- publish Docker images at ghcr.io/proconnect-gouv/oidc2fer
## [1.0.7] - 2025-05-07
- remove unused APT dependencies
- upgrade to gunicorn 23.0.0
## [1.0.6] - 2025-03-11
- upgrade to SATOSA 8.5.1
- allow faculty, staff, employee, researcher, teacher affiliations
## [1.0.5] - 2025-03-06
- add allowlist for URL paths to nginx ingress
- allow customizing log levels per logger
## [1.0.4] - 2024-09-11
- serve static files
+13 -15
View File
@@ -1,11 +1,17 @@
# ---- base image to inherit from ----
FROM python:3.11.12-slim-bookworm AS common
FROM python:3.11-slim-bookworm as common
# Install xmlsec1 dependencies required for xmlsec (for SAML)
# Needs to be kept before the `pip install`
RUN apt-get update && \
apt-get -y upgrade && \
apt-get install -qy --no-install-recommends xmlsec1 && \
apt-get install -y \
pkg-config \
gcc \
xmlsec1 \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl && \
rm -rf /var/lib/apt/lists/*
# We want the most up-to-date stable pip release
@@ -13,7 +19,7 @@ RUN pip install --upgrade pip
ENV PYTHONUNBUFFERED=1
# Give the "root" group the same permissions AS the "root" user on /etc/passwd
# Give the "root" group the same permissions as the "root" user on /etc/passwd
# to allow a user belonging to the root group to add new users; typically the
# docker user (see entrypoint).
RUN chmod g=u /etc/passwd
@@ -35,10 +41,7 @@ COPY docker/files/usr/local/etc/gunicorn/satosa.py /usr/local/etc/gunicorn/satos
CMD ["gunicorn", "-c", "/usr/local/etc/gunicorn/satosa.py"]
# ---- Development image ----
FROM common AS development
# Install curl (for healthchecks)
RUN apt-get update && apt-get install -qy curl
FROM common as development
# Playwright browsers
ENV PLAYWRIGHT_BROWSERS_PATH=/pw-browsers
@@ -54,22 +57,17 @@ WORKDIR /app
# Copy project file to list dependencies
COPY ./src/satosa/pyproject.toml /app/
# Create empty module directory to please pip install --editable
# (without this, the oidc2fer module will not be importable because
# pip --editable scans the directory to create its "links")
RUN mkdir -p /app/oidc2fer
# Install oidc2fer in editable mode along with development dependencies
RUN pip install --editable .[dev]
RUN pip install -e .[dev]
# Copy oidc2fer sources (see .dockerignore)
# Copy oidc2fer application (see .dockerignore)
COPY ./src/satosa /app/
# Switch to unprivileged user
USER ${DOCKER_USER}
# ---- Production image (keep last so it is the default target) ----
FROM common AS production
FROM common as production
# Copy oidc2fer application (see .dockerignore)
COPY ./src/satosa /app/
+1 -8
View File
@@ -57,19 +57,12 @@ number and date.
## Deploying a release to production (DINUM instance)
1. Make sure the release you want to deploy has been built and appears on
https://github.com/proconnect-gouv/oidc2fer/pkgs/container/oidc2fer
https://hub.docker.com/r/lasuite/oidc2fer/tags .
2. Edit `image/tag` at the top of
`src/helm/env.d/outscale-production/values.oidc2fer.yaml.gotmpl`.
3. Commit and push to `main`.
4. Update the `production` tag and push it.
## Environment variables
| variable | usage |
| --- | --- |
| `LOG_LEVEL` | Sets the log level for the root logger, i.e. the default. Defaults to `INFO`. |
| `LOG_LEVELS` | A JSON object that can be used to set log levels for specific loggers, e.g. `{"satosa.backends.saml2": "DEBUG"}`. Defaults to `{}`. |
## Contributing
This project is intended to be community-driven, so please, do not hesitate to
+1 -1
View File
@@ -15,4 +15,4 @@ docker_build(
watch_file('src/helm')
k8s_yaml(local('cd src/helm && helmfile -n oidc2fer -e %s template .' % os.getenv('TILT_ENV', 'dev')))
k8s_yaml(local('cd src/helm && helmfile -n oidc2fer -e dev template .'))
+7 -10
View File
@@ -13,7 +13,7 @@ services:
BASE_URL: https://satosa.traefik.me
GUNICORN_CMD_ARGS: --workers=2
TEST_E2E: 1
#TEST_E2E_PC: 1
#TEST_E2E_AC: 1
OIDC_DB_URI: redis://redis/0
CLIENT_DB_JSON: |
{
@@ -38,27 +38,24 @@ services:
volumes:
- ./src/satosa:/app
- ./docker/files/usr/local/etc/gunicorn/satosa.py:/usr/local/etc/gunicorn/satosa.py
working_dir: /app/config/fer
depends_on:
- redis
healthcheck:
test: ["CMD", "curl", "--fail", "http://localhost:8000/ping"]
interval: 60s
timeout: 5s
retries: 3
start_period: 30s
start_interval: 1s
oidc-test-client:
build: docker/oidc-test-client
depends_on:
- nginx # nginx must be up to override satosa.traefik.me resolution
- app-dev
stop_signal: SIGKILL
volumes:
- ./docker/oidc-test-client:/app
- ./env.d/development/certs/mkcert-root-ca.pem:/usr/local/share/ca-certificates/mkcert-root-ca.crt
# - run update-ca-certificates to make mkcert certificates valid
# - add startup delay because IDP must be available on boot
entrypoint: |
/bin/bash -c '
cat /usr/local/share/ca-certificates/mkcert-root-ca.crt >> $(python -m certifi)
sleep 1
cd /app
exec python app.py
'
@@ -69,7 +66,7 @@ services:
#OIDC_PROVIDER: https://oidc2fer-staging.beta.numerique.gouv.fr
OIDC_CLIENT_ID: oidc-test-client
OIDC_CLIENT_SECRET: oidc-test-secret
OIDC_SCOPES: openid,uid,given_name,usual_name,email,siret
OIDC_SCOPES: openid,uid,given_name,usual_name,email
extra_hosts:
- "oidc2fer.127.0.0.1.nip.io:host-gateway"
-1
View File
@@ -38,7 +38,6 @@ printenv SAML2_BACKEND_CERT > /tmp/backend.crt
printenv SAML2_BACKEND_KEY > /tmp/backend.key
printenv OIDC_FRONTEND_KEY > /tmp/frontend.key
printenv CLIENT_DB_JSON > /tmp/client_db.json
printenv SAML2_IDP_METADATA > /tmp/idp_metadata.xml
# Redis database number must be specified
if [[ ! $OIDC_DB_URI =~ /[0-9]+$ ]]; then
+1 -36
View File
@@ -1,5 +1,4 @@
import datetime
import json
import logging
import os
import sys
@@ -30,8 +29,6 @@ class RequestJSONFormatter(json_log_formatter.JSONFormatter):
url += f"?{record.args['q']}"
return {
"logger": record.name,
"level": record.levelname,
"remote_ip": record.args["h"],
"method": record.args["m"],
"path": url,
@@ -40,7 +37,7 @@ class RequestJSONFormatter(json_log_formatter.JSONFormatter):
"user_agent": record.args["a"],
"referer": record.args["f"],
"duration_in_ms": record.args["M"],
"pid": record.process,
"poppid": record.process,
}
@@ -58,23 +55,11 @@ class DefaultJSONFormatter(json_log_formatter.JSONFormatter):
payload: dict[str, str | int | float] = super().json_record(
message, extra, record
)
payload["logger"] = record.name
payload["level"] = record.levelname
payload["pid"] = record.process
return payload
class NoPingFilter(logging.Filter):
"""
Filters out /ping requests from the access log.
"""
def filter(self, record: logging.LogRecord) -> bool:
return not( record.args.get("m", "") == "GET"
and record.args.get("U", "") == "/ping"
and str(record.args.get("s", "")) == "200" )
bind = ["0.0.0.0:8000"]
name = "satosa"
python_path = "/app"
@@ -91,9 +76,6 @@ accesslog = "-"
errorlog = "-"
loglevel = os.environ.get("LOG_LEVEL", "INFO")
loglevels_json = os.environ.get("LOG_LEVELS", "{}")
loglevels = json.loads(loglevels_json)
logconfig_dict = {
"version": 1,
"disable_existing_loggers": True,
@@ -113,24 +95,7 @@ logconfig_dict = {
"handlers": ["json_request"],
"propagate": False,
"qualname": "gunicorn.access",
"filters": [NoPingFilter()],
},
"satosa.base": {
"level": "INFO",
},
"satosa.state": {
"level": "INFO",
},
"satosa.proxy_server": {
"level": "INFO",
},
"satosa.routing": {
"level": "INFO",
},
"satosa.frontends.ping": {
"level": "INFO",
},
**{k: {"level": v.upper()} for k, v in loglevels.items()},
},
"formatters": {
"json_request": {
+17 -36
View File
@@ -2,11 +2,13 @@ from flask import Flask, jsonify, request, session
from oic.oic import Client
from oic.utils.authn.client import CLIENT_AUTHN_METHOD
from oic import rndstr
from oic.oic.message import Claims, ClaimsRequest, RegistrationResponse
from oic.oic.message import RegistrationResponse
from oic.utils.http_util import Redirect
from oic.oic.message import AuthorizationResponse
from werkzeug.exceptions import InternalServerError
import secrets
import webbrowser
import threading
import time
import logging
import os
@@ -24,27 +26,21 @@ app.config.update(
}
)
client = Client(client_authn_method=CLIENT_AUTHN_METHOD)
def create_client() -> Client:
client = Client(client_authn_method=CLIENT_AUTHN_METHOD)
provider_info = client.provider_config(os.environ["OIDC_PROVIDER"])
client.provider_config(os.environ["OIDC_PROVIDER"])
info = {
"client_id": os.environ["OIDC_CLIENT_ID"],
"client_secret": os.environ["OIDC_CLIENT_SECRET"],
}
client.store_registration_info(RegistrationResponse(**info))
client.redirect_uris = [f"{os.environ['OIDC_ROOT_URL']}/redirect_uri"]
return client
info = {
"client_id": os.environ["OIDC_CLIENT_ID"],
"client_secret": os.environ["OIDC_CLIENT_SECRET"],
"redirect_uris": [f"{os.environ['OIDC_ROOT_URL']}/redirect_uri"],
}
client_reg = RegistrationResponse(**info)
client.store_registration_info(client_reg)
@app.route("/")
def index():
client = create_client()
session["state"] = rndstr()
session["nonce"] = rndstr()
@@ -54,9 +50,8 @@ def index():
"response_type": "code",
"scope": os.environ["OIDC_SCOPES"].split(","),
"nonce": session["nonce"],
"redirect_uri": client.redirect_uris[0],
"redirect_uri": client.registration_response["redirect_uris"][0],
"state": session["state"],
"claims": ClaimsRequest(id_token=Claims(acr=None, amr=None)),
}
)
login_url = auth_req.request(client.authorization_endpoint)
@@ -66,8 +61,6 @@ def index():
@app.route("/redirect_uri")
def oidc_callback():
client = create_client()
response = request.query_string.decode("utf-8")
aresp = client.parse_response(
AuthorizationResponse, info=response, sformat="urlencoded"
@@ -80,6 +73,7 @@ def oidc_callback():
error_response=aresp.to_dict(),
)
code = aresp["code"]
logging.info("got auth code=%s", code)
@@ -98,20 +92,7 @@ def oidc_callback():
logging.info("got userinfo=%s", userinfo)
return jsonify(
access_token_response=access_token_response.to_dict(),
userinfo=userinfo.to_dict(),
)
@app.errorhandler(InternalServerError)
def handle_server_exception(e):
exc = e.original_exception
import traceback
return (
f"""<h1>Internal Server Error</h1>
<pre>{"".join(traceback.format_exception(exc))}</pre>""",
500,
access_token_response=access_token_response.to_dict(), userinfo=userinfo.to_dict()
)
@@ -119,6 +100,6 @@ def handle_server_exception(e):
def health():
return "OK"
if __name__ == "__main__":
app.run(host="0.0.0.0")
+1 -1
View File
@@ -1,2 +1,2 @@
Flask==3.0.3
oic==1.7.0
oic==1.6.1
+1 -2
View File
@@ -1,3 +1,2 @@
# Python
LOG_LEVEL=DEBUG
LOG_LEVELS='{ "satosa.backends.saml2": "DEBUG" }'
PYTHONPATH=/app
+1 -1
Submodule secrets updated: 4e6030feee...b0d485544d
File diff suppressed because one or more lines are too long
@@ -1,44 +0,0 @@
image:
repository: localhost:5001/oidc2fer
pullPolicy: Always
tag: "latest"
satosa:
replicas: 1
workingDir: /app/config/cnb
envVars:
BASE_URL: https://oidc2fer.127.0.0.1.nip.io
GUNICORN_CMD_ARGS: --workers=3
LOG_LEVEL: debug
SAML2_ENTITY_ID: https://oidc2fer.127.0.0.1.nip.io/Saml2/proxy_saml2_backend.xml
SAML2_IDP_METADATA: { secretKeyRef: { name: oidc2fer, key: SAML2_IDP_METADATA } }
STATE_ENCRYPTION_KEY: { secretKeyRef: { name: oidc2fer, key: STATE_ENCRYPTION_KEY } }
SAML2_BACKEND_CERT: { secretKeyRef: { name: oidc2fer, key: SAML2_BACKEND_CERT } }
SAML2_BACKEND_KEY: { secretKeyRef: { name: oidc2fer, key: SAML2_BACKEND_KEY } }
OIDC_FRONTEND_KEY: { secretKeyRef: { name: oidc2fer, key: OIDC_FRONTEND_KEY } }
OIDC_DB_URI: "redis://:pass@redis-master:6379"
CLIENT_DB_JSON: |-
{
"oidc-test-client": {
"response_types": [
"id_token",
"code"
],
"redirect_uris": [
"https://oidc-test-client.traefik.me/redirect_uri",
"https://oidc-test-client.traefik.me:8443/redirect_uri"
],
"client_secret": "oidc-test-secret",
"token_endpoint_auth_method": "client_secret_post"
}
}
ingress:
enabled: true
host: oidc2fer.127.0.0.1.nip.io
annotations:
nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
@@ -1 +0,0 @@
../../../../secrets/numerique-gouv/oidc2fer/env/cnb-staging/secrets.enc.yaml
@@ -1,34 +0,0 @@
image:
repository: ghcr.io/proconnect-gouv/oidc2fer
pullPolicy: Always
tag: "main"
satosa:
replicas: 2
workingDir: /app/config/cnb
envVars:
BASE_URL: https://oidc2cnb-staging.beta.numerique.gouv.fr
GUNICORN_CMD_ARGS: --workers=3
LOG_LEVEL: DEBUG
LOG_LEVELS: '{ "satosa.backends.saml2": "DEBUG" }'
SAML2_IDP_METADATA: { secretKeyRef: { name: oidc2fer, key: SAML2_IDP_METADATA } }
SAML2_ENTITY_ID: https://oidc2cnb-staging.beta.numerique.gouv.fr/Saml2/proxy_saml2_backend.xml
STATE_ENCRYPTION_KEY: { secretKeyRef: { name: oidc2fer, key: STATE_ENCRYPTION_KEY } }
SAML2_BACKEND_CERT: { secretKeyRef: { name: oidc2fer, key: SAML2_BACKEND_CERT } }
SAML2_BACKEND_KEY: { secretKeyRef: { name: oidc2fer, key: SAML2_BACKEND_KEY } }
OIDC_FRONTEND_KEY: { secretKeyRef: { name: oidc2fer, key: OIDC_FRONTEND_KEY } }
CLIENT_DB_JSON: { secretKeyRef: { name: oidc2fer, key: CLIENT_DB_JSON } }
OIDC_DB_URI: { secretKeyRef: { name: redis.redis.libre.sh, key: url } }
ingress:
enabled: true
host: oidc2cnb-staging.beta.numerique.gouv.fr
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt
nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
@@ -5,7 +5,6 @@ image:
satosa:
replicas: 1
workingDir: /app/config/fer
envVars:
BASE_URL: https://oidc2fer.127.0.0.1.nip.io
GUNICORN_CMD_ARGS: --workers=3
@@ -37,10 +36,6 @@ satosa:
"token_endpoint_auth_method": "client_secret_post"
}
}
SIRET_MAP: |-
{
"https://test-idp.federation.renater.fr/idp/shibboleth": "12345678200010"
}
ingress:
enabled: true
@@ -1,7 +1,7 @@
image:
repository: ghcr.io/proconnect-gouv/oidc2fer
repository: lasuite/oidc2fer
pullPolicy: Always
tag: "v1.0.10"
tag: "v1.0.4"
satosa:
replicas: 2
@@ -9,8 +9,7 @@ satosa:
BASE_URL: https://renater.agentconnect.gouv.fr
GUNICORN_CMD_ARGS: --workers=3
LOG_LEVEL: INFO
LOG_LEVELS: '{ "satosa.backends.saml2": "DEBUG" }'
LOG_LEVEL: debug
SAML2_DISCOVERY_URL: https://discovery.renater.fr/agentconnect/
SAML2_METADATA_URL: https://pub.federation.renater.fr/metadata/renater/main/main-idps-renater-metadata.xml
@@ -24,94 +23,6 @@ satosa:
OIDC_DB_URI: { secretKeyRef: { name: redis.redis.libre.sh, key: url } }
# As provided by RENATER on 2025-09-19
SIRET_MAP: |-
{
"https://shibboleth.grenoble-inp.fr/idp/shibboleth": "19381912500017",
"https://identites.ensea.fr/idp/shibboleth": "19951376300011",
"https://idp.ent.dauphine.fr/idp/shibboleth": "19754692200018",
"https://idp.univ-lyon2.fr/idp/shibboleth": "19691775100014",
"https://shibboleth.insa-rouen.fr/idp/shibboleth": "19760165100023",
"https://idp1.univ-fcomte.fr/idp/shibboleth": "93810656400017",
"https://idp.univ-tours.fr/idp/shibboleth": "19370800500478",
"https://apps.univ-lr.fr/idp/shibboleth": "19170032700015",
"https://shib.mines-albi.fr/idp/shibboleth": "18009202500097",
"https://federation.upf.pf/idp/shibboleth": "19987001500013",
"https://federation.utbm.fr/idp/shibboleth": "19900356700013",
"urn:mace:cru.fr:federation:univ-paris1.fr": "19751717000019",
"https://idp.sciencespobordeaux.fr/idp/shibboleth": "19330192600039",
"https://vip.espci.fr/saml2/idp/metadata.php": "20000068500012",
"urn:mace:cru.fr:federation:univ-nantes.fr": "13002974700016",
"https://shibboleth.univ-corse.fr/idp/shibboleth": "19202664900264",
"https://srv-fii.insa-toulouse.fr/idp/shibboleth": "19310152400018",
"urn:mace:cru.fr:federation:univ-rouen.fr": "19761904200017",
"https://ident-shib.ensc-rennes.fr/idp/shibboleth": "19350077400016",
"urn:mace:cru.fr:federation:unilim.fr": "19870669900321",
"https://idp.inha.fr/idp/shibboleth": "19754688000018",
"https://federation.unimes.fr/idp/shibboleth": "93249157400012",
"urn:mace:cru.fr:federation:univ-rennes1.fr": "13003051300019",
"urn:mace:cru.fr:federation:univ-ubs.fr": "19561718800600",
"https://idp.univ-orleans.fr/idp/shibboleth": "19450855200016",
"https://shibboleth.univ-savoie.fr/idp/shibboleth": "19730858800015",
"https://idp.cirad.fr/idp/shibboleth": "33159627000016",
"https://sso.ird.fr/idp/shibboleth": "18000602500159",
"https://idp.ensma.fr/idp/shibboleth": "19860073600021",
"https://idp.renater.fr/idp/shibboleth": "18008947600055",
"https://idp.inp-toulouse.fr/idp/shibboleth": "19311381800127",
"https://idp.unistra.fr/idp/shibboleth": "13000545700010",
"https://idp1.agroparistech.fr/idp/shibboleth": "13000285000134",
"https://janus.cnrs.fr/idp": "18008901303720",
"https://federation.umontpellier.fr/idp/shibboleth": "13002979600013",
"https://idp.sciencespo-lyon.fr/idp/shibboleth": "19690173000024",
"https://shibboleth.univ-grenoble-alpes.fr/idp/shibboleth": "13002608100013",
"https://idp.bnu.fr/idp/shibboleth": "18004406700015",
"https://ruhnu.univ-tlse2.fr/idp/shibboleth": "19311383400017",
"https://idp2.amue.fr/idp/shibboleth": "18004312700091",
"https://idpv3.univ-amu.fr/idp/shibboleth": "13001533200013",
"https://idp-ng.univ-st-etienne.fr/idp/shibboleth": "93850168100010",
"https://identities.univ-jfc.fr/idp/prod": "19811201300018",
"https://idp3.univ-lorraine.fr/idp/shibboleth": "13001550600012",
"https://idp.univ-lemans.fr/idp/shibboleth": "19720916600010",
"https://idp.univ-lille.fr/idp/shibboleth": "13002975400012",
"https://idp.univ-tln.fr/idp/shibboleth": "19830766200017",
"https://idp.uca.fr/idp/shibboleth": "13002806100013",
"https://shibboleth3.utt.fr/idp/shibboleth": "19101060200032",
"https://idp3.ut-capitole.fr/idp/shibboleth": "13003061200019",
"https://idp.imt-atlantique.fr/idp/shibboleth": "18009202500121",
"https://shib.univ-lehavre.fr/idp/shibboleth": "19762762300097",
"https://idp2.univ-paris8.fr/idp/shibboleth": "19931827000014",
"https://shib2.unc.nc/idp/shibboleth": "13000322100012",
"https://upnidp2.parisnanterre.fr/idp/shibboleth": "19921204400010",
"https://idp.ensfea.fr/idp/shibboleth": "19310143300012",
"https://idp.univ-eiffel.fr/idp/shibboleth": "13002612300013",
"https://idp.insa-rennes.fr/idp/shibboleth": "19350097200016",
"https://idp.univ-tlse3.fr/idp/shibboleth": "93827139200012",
"https://idp.universite-paris-saclay.fr/idp": "13002602400054",
"https://shibboleth.ens2m.fr/idp/shibboleth": "19250082500026",
"https://sso-ciation-saclay.centralesupelec.fr/idp/shibboleth": "13002076100016",
"https://federation.ens.psl.eu/idp/shibboleth": "19753459700012",
"https://authentification.inrae.fr/saml/metadata/idp": "18007003901803",
"https://idp.ensam.eu/idp/shibboleth": "19753472000010",
"https://idp1.ens-paris-saclay.fr/idp/shibboleth": "19940607500036",
"https://idp2.emse.fr/idp/shibboleth": "18009202500105",
"https://idp-septa.onisep.fr/idp/shibboleth": "18004302800653",
"https://papangue.vetagro-sup.fr/idp/shibboleth": "13000858400018",
"https://multipass.imt-nord-europe.fr/idp/shibboleth": "18009202500139",
"https://idp4.unicaen.fr/idp/shibboleth": "19141408500016",
"https://sso.bordeaux-inp.fr/idp/shibboleth": "13000635600013",
"https://auth.uttop.fr/saml/metadata": "19650048200019",
"https://idp.univ-cotedazur.fr/idp/shibboleth": "13002566100013",
"https://idp2.brgm.fr/idp/shibboleth": "58205614900120",
"https://idp.crous-reims.fr/idp/shibboleth": "18510200100327",
"https://idp.univ-avignon.fr/idp/shibboleth": "19840685200204",
"https://idp-genci.renater.fr/idp/shibboleth": "49468697500041",
"https://auth.sso.vet-alfort.fr/saml/metadata": "19940608300014",
"https://idp.univ-perp.fr/idp/shibboleth": "19660437500010",
"https://idp.institut-agro.fr/idp": "13002622200013",
"https://prod-idp.inria.fr": "18008904700013",
"https://idp.ec-nantes.fr/idp/shibboleth": "19440100600011"
}
ingress:
enabled: true
host: renater.agentconnect.gouv.fr
@@ -1,17 +1,15 @@
image:
repository: ghcr.io/proconnect-gouv/oidc2fer
repository: lasuite/oidc2fer
pullPolicy: Always
tag: "latest"
tag: "main"
satosa:
replicas: 2
workingDir: /app/config/fer
envVars:
BASE_URL: https://oidc2fer-staging.beta.numerique.gouv.fr
GUNICORN_CMD_ARGS: --workers=3
LOG_LEVEL: DEBUG
LOG_LEVELS: '{ "satosa.backends.saml2": "DEBUG" }'
LOG_LEVEL: debug
SAML2_DISCOVERY_URL: https://discovery.renater.fr/test/
SAML2_METADATA_URL: https://pub.federation.renater.fr/metadata/test/preview/preview-idps-test-metadata.xml
@@ -25,11 +23,6 @@ satosa:
OIDC_DB_URI: { secretKeyRef: { name: redis.redis.libre.sh, key: url } }
SIRET_MAP: |-
{
"https://test-idp.federation.renater.fr/idp/shibboleth": "12345678200010"
}
ingress:
enabled: true
host: oidc2fer-staging.beta.numerique.gouv.fr
@@ -1,30 +1,3 @@
environments:
dev:
values:
- version: 0.0.1
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
cnb-dev:
values:
- version: 0.0.1
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
staging:
values:
- version: 0.0.1
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
cnb-staging:
values:
- version: 0.0.1
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
outscale-production:
values:
- version: 0.0.1
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
---
repositories:
- name: bitnami
url: registry-1.docker.io/bitnamicharts
@@ -32,7 +5,7 @@ repositories:
releases:
- name: redis
installed: {{ or (eq .Environment.Name "dev") (eq .Environment.Name "cnb-dev") | toYaml }}
installed: {{ eq .Environment.Name "dev" | toYaml }}
namespace: {{ .Namespace }}
chart: bitnami/redis
version: 18.19.2
@@ -40,11 +13,9 @@ releases:
- auth:
password: pass
architecture: standalone
image:
repository: bitnamilegacy/redis
- name: extra
installed: {{ and (ne .Environment.Name "dev") (ne .Environment.Name "cnb-dev") | toYaml }}
installed: {{ ne .Environment.Name "dev" | toYaml }}
namespace: {{ .Namespace }}
chart: ./extra
secrets:
@@ -58,3 +29,21 @@ releases:
- env.d/{{ .Environment.Name }}/values.oidc2fer.yaml.gotmpl
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
environments:
dev:
values:
- version: 0.0.1
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
staging:
values:
- version: 0.0.1
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
outscale-production:
values:
- version: 0.0.1
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
+4 -7
View File
@@ -1,6 +1,5 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "oidc2fer.fullname" . -}}
{{- $port := .Values.satosa.service.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
@@ -34,15 +33,13 @@ spec:
- host: {{ .Values.ingress.host | quote }}
http:
paths:
{{- range .Values.ingress.paths }}
- path: {{ .path | quote }}
pathType: {{ .pathType }}
- path: /
pathType: Prefix
backend:
service:
name: {{ $fullName }}-satosa
name: {{ include "oidc2fer.satosa.fullname" . }}
port:
number: {{ $port }}
{{- end }}
number: {{ .Values.satosa.service.port }}
{{- with .Values.ingress.customBackends }}
{{- toYaml . | nindent 10 }}
{{- end }}
@@ -38,9 +38,6 @@ spec:
command:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.satosa.workingDir }}
workingDir: {{ . | quote }}
{{- end }}
{{- with .Values.satosa.args }}
args:
{{- toYaml . | nindent 12 }}
-1
View File
@@ -5,7 +5,6 @@ metadata:
namespace: {{ .Release.Namespace | quote }}
stringData:
STATE_ENCRYPTION_KEY: {{ .Values.STATE_ENCRYPTION_KEY | quote }}
SAML2_IDP_METADATA: {{ .Values.SAML2_IDP_METADATA | toYaml | indent 8 }}
SAML2_BACKEND_CERT: {{ .Values.SAML2_BACKEND_CERT | toYaml | indent 8 }}
SAML2_BACKEND_KEY: {{ .Values.SAML2_BACKEND_KEY | toYaml | indent 8 }}
OIDC_FRONTEND_KEY: {{ .Values.OIDC_FRONTEND_KEY | toYaml | indent 8 }}
+1 -6
View File
@@ -33,9 +33,7 @@ ingress:
enabled: false
className: null
host: oidc2fer.example.com
paths:
- path: '/$|^/\.well-known/openid-configuration$|^/images/|^/Saml2/|^/OIDC/|^/ping$'
pathType: ImplementationSpecific # enables regex matching
path: /
## @param ingress.hosts Additional host to configure for the Ingress
hosts: []
# - chart-example.local
@@ -50,9 +48,6 @@ ingress:
## @param ingress.customBackends Add custom backends to ingress
customBackends: []
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
## @section satosa
satosa:
@@ -1,24 +0,0 @@
attributes:
givenname:
openid:
- given_name
saml:
- cnb_prenom
email:
openid:
- email
saml:
- cnb_email
usual_name:
openid:
- usual_name
saml:
- cnb_nom
acr:
openid:
- acr
uid:
openid:
- uid
saml:
- cnb_id
@@ -1,49 +0,0 @@
module: satosa.backends.saml2.SAMLBackend
name: Saml2
config:
entityid_endpoint: true
mirror_force_authn: false
memorize_idp: false
use_memorized_idp_when_force_authn: false
send_requester_id: false
enable_metadata_reload: false
acs_selection_strategy: prefer_matching_host
sp_config:
name: Passerelle ProConnect vers CNB
description: Passerelle ProConnect vers CNB
key_file: /tmp/backend.key
cert_file: /tmp/backend.crt
organization:
name: DINUM
display_name: DINUM
url: https://beta.gouv.fr/incubateurs/dinum_produits_interministeriels.html
metadata:
local:
- /tmp/idp_metadata.xml
entityid: !ENV SAML2_ENTITY_ID
accepted_time_diff: 60
allow_unknown_attributes: true
service:
sp:
ui_info:
display_name:
- lang: fr
text: Passerelle ProConnect vers CNB
logo:
text: https://beta.gouv.fr/img/incubators/logo_dinum.png
width: '200'
height: '200'
authn_requests_signed: true
want_response_signed: false
want_assertions_signed: false
want_assertions_or_response_signed: true
signing_algorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
allow_unsolicited: true
name_id_format:
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
endpoints:
assertion_consumer_service:
- - <base_url>/<name>/acs/post
- 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
name_id_format_allow_create: true
@@ -1,12 +0,0 @@
module: satosa.micro_services.attribute_processor.AttributeProcessor
name: AttributeProcessor
config:
process:
# Internally SATOSA represents most attributes as lists of strings.
# The SATOSA OIDC frontend automatically flattens standard OIDC attributes
# (like 'given_name' or 'email') to single strings when possible, but
# doesn't do that for custom attributes (like 'usual_name').
- attribute: usual_name
processors:
- module: oidc2fer.attribute_processors.flattening_processor
name: FlatteningProcessor
@@ -1,22 +0,0 @@
module: satosa.micro_services.primary_identifier.PrimaryIdentifier
name: PrimaryIdentifier
config:
# The ordered identifier candidates are searched in order
# to find a candidate primary identifier. The search ends
# when the first candidate is found. The identifier or attribute
# names are the internal SATOSA names for the attributes as
# defined in internal_attributes.yaml.
ordered_identifier_candidates:
- attribute_names: [uid]
# The internal SATOSA attribute into which to place the primary
# identifier value once found from the above configured ordered
# candidates.
primary_identifier: uid
# Whether or not to clear the input attributes after setting the
# primary identifier value.
clear_input_attributes: no
# Whether to replace subject_id with the constructed primary identifier
replace_subject_id: yes
@@ -1,7 +0,0 @@
module: satosa.micro_services.attribute_modifications.AddStaticAttributes
name: AddAttributes
config:
static_attributes:
acr: eidas1
# FIXME temporary hardcoded email for testing
email: ["test@cnb.avocat.fr"] # SATOSA wants this to be an array
-22
View File
@@ -1,22 +0,0 @@
BASE: !ENV BASE_URL
COOKIE_STATE_NAME: SATOSA_STATE
CONTEXT_STATE_DELETE: 'yes'
STATE_ENCRYPTION_KEY: !ENV STATE_ENCRYPTION_KEY
cookies_samesite_compat:
- - SATOSA_STATE
- SATOSA_STATE_LEGACY
INTERNAL_ATTRIBUTES: internal_attributes.yaml
BACKEND_MODULES:
- plugins/backends/saml2_backend.yaml
FRONTEND_MODULES:
- plugins/frontends/openid_connect_frontend.yaml
- plugins/frontends/ping_frontend.yaml
MICRO_SERVICES:
- plugins/microservices/primary_identifier.yaml
- plugins/microservices/static_attributes.yaml
- plugins/microservices/attribute_processor.yaml
LOGGING:
# All the logging configuration is done in the Gunicorn config, this is just
# here to avoid overwriting it with the default SATOSA config
version: 1
incremental: true
-9
View File
@@ -1,9 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="1; url=https://proconnect.gouv.fr/">
</head>
<body>
<h1>Passerelle ProConnect vers CNB. Redirection en cours…</h1>
</body>
</html>
@@ -1,61 +0,0 @@
module: oidc2fer.frontends.jwt_userinfo_openid_connect.JWTUserInfoOpenIDConnectFrontend
name: OIDC
config:
signing_key_path: /tmp/frontend.key
signing_key_id: frontend.key1
# Defines the database connection URI for the databases:
# - authz_code_db
# - access_token_db
# - refresh_token_db
# - sub_db
# - user_db
#
# supported storage backends:
# - In-memory dictionary
# - MongoDB (e.g. mongodb://db.example.com)
# - Redis (e.g. redis://example/0)
# - Stateless (eg. stateless://user:encryptionkey?alg=aes256)
#
# This configuration is optional.
# By default, the in-memory storage is used.
db_uri: !ENV OIDC_DB_URI
# Where to store clients.
#
# If client_db_uri is set, the database connection is used.
# Otherwise, if client_db_path is set, the JSON file is used.
# By default, an in-memory dictionary is used.
# client_db_uri: mongodb://db.example.com
client_db_path: /tmp/client_db.json
# if not specified, it is randomly generated on every startup
sub_hash_salt: randomSALTvalue
sub_mirror_public: yes
provider:
client_registration_supported: no
response_types_supported: ["code", "id_token token"]
subject_types_supported: ["public"]
scopes_supported:
- openid
- email
- uid
- given_name
- usual_name
- siret
# Set code/token lifetimes as short as possible
authorization_code_lifetime: 60
access_token_lifetime: 60
id_token_lifetime: 60
extra_scopes:
uid:
- uid
given_name:
- given_name
usual_name:
- usual_name
siret:
- siret
@@ -1,3 +0,0 @@
module: satosa.frontends.ping.PingFrontend
name: ping
config: null
@@ -1,5 +0,0 @@
module: oidc2fer.attribute_generators.EntityIdToSiretMapper
name: EntityIdToSiretMapper
config:
attribute: siret
mapping_json: !ENV SIRET_MAP
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

@@ -31,6 +31,3 @@ attributes:
uid:
openid:
- uid
siret:
openid:
- siret
@@ -1 +0,0 @@
from .entity_id_to_siret_mapper import EntityIdToSiretMapper
@@ -1,23 +0,0 @@
import json
import logging
from satosa.micro_services.base import ResponseMicroService
logger = logging.getLogger(__name__)
class EntityIdToSiretMapper(ResponseMicroService):
def __init__(self, config, *args, **kwargs):
super().__init__(*args, **kwargs)
self.attribute = config.get("attribute", "siret")
self.mapping = json.loads(config.get("mapping_json", "{}"))
def process(self, context, data):
entity_id = data.auth_info.issuer
if entity_id in self.mapping:
siret = self.mapping[entity_id]
logger.info("Mapping entity ID %s to SIRET %s", entity_id, siret)
data.attributes[self.attribute] = siret
else:
logger.warning("No SIRET mapping found for entity ID %s", entity_id)
return super().process(context, data)
@@ -3,11 +3,11 @@ name: Saml2
config:
disco_srv: !ENV SAML2_DISCOVERY_URL
entityid_endpoint: true
mirror_force_authn: false
memorize_idp: false
use_memorized_idp_when_force_authn: false
send_requester_id: false
enable_metadata_reload: false
mirror_force_authn: 'no'
memorize_idp: 'no'
use_memorized_idp_when_force_authn: 'no'
send_requester_id: 'no'
enable_metadata_reload: 'no'
acs_selection_strategy: prefer_matching_host
sp_config:
name: Passerelle OIDC vers FER
@@ -56,3 +56,8 @@ config:
- given_name
usual_name:
- usual_name
extra_id_token_claims:
oidc-test-client:
- acr
agent-connect:
- acr
@@ -13,8 +13,4 @@ config:
# later in the PrimaryIdentifier processor.
- ".+"
eduPersonAffiliation:
- "^faculty$"
- "^staff$"
- "^employee$"
- "^researcher$"
- "^teacher$"
@@ -17,7 +17,6 @@ MICRO_SERVICES:
- plugins/microservices/primary_identifier.yaml
- plugins/microservices/static_attributes.yaml
- plugins/microservices/attribute_processor.yaml
- plugins/microservices/siret_mapping.yaml
LOGGING:
# All the logging configuration is done in the Gunicorn config, this is just
# here to avoid overwriting it with the default SATOSA config
+6 -13
View File
@@ -23,25 +23,18 @@ license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"SATOSA==8.5.1",
"gunicorn==23.0.0",
"SATOSA==8.4.0",
"gunicorn==22.0.0",
"redis==5.0.4",
"JSON-log-formatter==1.0",
"WhiteNoise==6.7.0",
# Use the most recent pysaml2 that doesn't have the recurrence of
# https://github.com/IdentityPython/pysaml2/issues/819
# (AuthNRequests signed twice)
"pysaml2==7.1.0",
# Pin xmlschema like pysaml2 did in release 7.5.1, see
# https://github.com/IdentityPython/pysaml2/issues/947#issuecomment-1916767026
"xmlschema==2.5.1",
]
[project.urls]
"Bug Tracker" = "https://github.com/proconnect-gouv/oidc2fer/issues/new"
"Changelog" = "https://github.com/proconnect-gouv/oidc2fer/blob/main/CHANGELOG.md"
"Homepage" = "https://github.com/proconnect-gouv/oidc2fer"
"Repository" = "https://github.com/proconnect-gouv/oidc2fer"
"Bug Tracker" = "https://github.com/numerique-gouv/oidc2fer/issues/new"
"Changelog" = "https://github.com/numerique-gouv/oidc2fer/blob/main/CHANGELOG.md"
"Homepage" = "https://github.com/numerique-gouv/oidc2fer"
"Repository" = "https://github.com/numerique-gouv/oidc2fer"
[project.optional-dependencies]
dev = [

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

@@ -1,42 +0,0 @@
import json
from satosa.context import Context
from satosa.internal import AuthenticationInformation, InternalData
from oidc2fer.attribute_generators import EntityIdToSiretMapper
class TestEntityIdToSiretMapper:
def create_mapper(self):
mapper = EntityIdToSiretMapper(
config={
"attribute": "siret",
"mapping_json": json.dumps(
{
"https://idp.example.fr": "12345678200010",
}
),
},
name="siret_mapper",
base_url="https://satosa.example.com",
)
mapper.next = lambda ctx, data: data
return mapper
def test_sets_siret_for_known_entityid(self):
mapper = self.create_mapper()
resp = InternalData(
auth_info=AuthenticationInformation(issuer="https://idp.example.fr")
)
ctx = Context()
mapper.process(ctx, resp)
assert resp.attributes["siret"] == "12345678200010"
def test_does_not_set_siret_for_unknown_entityid(self):
mapper = self.create_mapper()
resp = InternalData(
auth_info=AuthenticationInformation(issuer="https://unknown.example.fr")
)
ctx = Context()
mapper.process(ctx, resp)
assert "siret" not in resp.attributes
+13 -16
View File
@@ -20,7 +20,6 @@ def renater_test_idp(page, login):
def renater_wayf(page):
page.get_by_text("Veuillez sélectionner").click()
page.get_by_role("searchbox").fill("GIP RENATER - IdP de test")
page.get_by_role("option", name="GIP RENATER - IdP de test", exact=True).click()
page.get_by_role("button", name="Sélection").click()
@@ -49,7 +48,6 @@ def oidc_to_renater(
"email": expected_email,
"given_name": expected_given_name,
"usual_name": expected_usual_name,
"siret": "12345678200010",
}.items() <= userinfo.items()
return id_token
@@ -77,58 +75,57 @@ def test_oidc_to_renater_student_not_allowed(page: Page):
expect(page.locator("pre")).to_contain_text('"error":"access_denied"')
def pro_connect_login(page: Page, email):
def agent_connect_login(page: Page, email):
page.goto("https://fsa1v2.integ01.dev-agentconnect.fr/")
page.get_by_role("button", name="Sidentifier avec ProConnect").click()
page.get_by_label("Connexion à AgentConnect").click()
page.get_by_label("Email professionnel").fill(email)
page.get_by_test_id("interaction-connection-button").click()
def pro_connect_to_renater(
def agent_connect_to_renater(
page: Page,
login="enseignant1",
expected_email="georges.grospieds@formation.renater.fr",
expected_given_name="Georges",
expected_usual_name="Grospieds",
):
pro_connect_login(page, email=expected_email)
agent_connect_login(page, email=expected_email)
renater_wayf(page)
renater_test_idp(page, login=login)
expect(page.locator("body")).to_contain_text(expected_email)
text = page.inner_text("#userinfo")
text = page.inner_text("#json")
result = json.loads(text)
assert {
"uid": f"{login}@test-renater.fr",
"email": expected_email,
"given_name": expected_given_name,
"usual_name": expected_usual_name,
"siret": "12345678200010",
}.items() <= result.items()
return result
@pytest.mark.skipif(
"TEST_E2E_PC" not in os.environ, reason="Depends on staging deployment"
"TEST_E2E_AC" not in os.environ, reason="Depends on staging deployment"
)
def test_pro_connect_to_renater_keeps_sub(browser: Browser):
def test_agent_connect_to_renater_keeps_sub(browser: Browser):
with browser.new_context().new_page() as page:
result1 = pro_connect_to_renater(page)
result1 = agent_connect_to_renater(page)
with browser.new_context().new_page() as page:
result2 = pro_connect_to_renater(page)
result2 = agent_connect_to_renater(page)
assert result1["sub"] == result2["sub"]
@pytest.mark.skipif(
"TEST_E2E_PC" not in os.environ, reason="Depends on staging deployment"
"TEST_E2E_AC" not in os.environ, reason="Depends on staging deployment"
)
def test_pro_connect_to_renater_student_not_allowed(page: Page):
pro_connect_login(page, email="jean.dupont@formation.renater.fr")
def test_agent_connect_to_renater_student_not_allowed(page: Page):
agent_connect_login(page, email="jean.dupont@formation.renater.fr")
renater_wayf(page)
renater_test_idp(page, login="etudiant1")
expect(page.locator("body")).to_contain_text("access_denied")
expect(page.locator("body")).to_contain_text("Une erreur technique est survenue.")
@pytest.mark.skipif(