Compare commits

...

77 Commits

Author SHA1 Message Date
Jonathan Perret 3401da37ff v1.0.9 release 2025-10-03 13:19:29 +02:00
Jonathan Perret 1124893498 Merge pull request #34 from proconnect-gouv/add-siret-mapping 2025-10-03 13:16:09 +02:00
Jonathan Perret 1557f7ba22 🔧(siret) add initial SIRET map to production environment
This mapping was provided by RENATER on 2025-09-19.
2025-10-03 13:10:40 +02:00
Jonathan Perret 6c344fae05 (siret) introduce SIRET mapping from EntityID
The proxy now uses a SIRET_MAP environment variable that must contain
a JSON mapping of SAML entity IDs to SIRET numbers.
These SIRET numbers will be returned to OIDC clients that request the
`siret` scope, in the `siret` claim.
2025-10-02 19:50:40 +02:00
Jonathan Perret cb8352787d Merge pull request #32 from proconnect-gouv/fix-acr-claim 2025-09-17 17:31:38 +02:00
Jonathan Perret 9a33149e9e (e2e) adapt to ProConnect error page wording change
The generic message was replaced by the error code
and description from the IdP.
2025-09-17 16:06:37 +02:00
Jonathan Perret a56ce17b34 ⬆️(oidc-test-client) upgrade oic to 1.7.0
This mainly avoids a conflict with the oidc2fer requirements
when installing both in a local virtualenv for IDE support.
2025-09-17 16:06:36 +02:00
Jonathan Perret a8bf8f61b4 🐛(oidc) stop forcing id_token 'acr' claim
There is a regression in pyop 3.4.2 which causes the configured
extra_id_token_claims to be ignored. Fortunately, ProConnect Core
now requests the required `acr` claim explicitly, so it is no longer
necessary to force it.

The OIDC test client was updated to request the `acr` claim in the
same way that ProConnect does it.
2025-09-17 16:06:35 +02:00
Jonathan Perret 7b1571b0ed (e2e) fix e2e test to more reliably select test IdP
The list of IdPs on the RENATER test federation seems to have
grown enough that the test IdP we want is no longer displayed
by default, so we have to search for it.
2025-09-15 12:15:05 +02:00
Jonathan Perret 3ecbf1a5a0 Fix docker registry for production 2025-09-10 18:23:52 +02:00
Jonathan Perret 445485ecc5 Deploy release 1.0.8 to production 2025-09-10 18:19:49 +02:00
Jonathan Perret 1bceed1373 v1.0.8 release 2025-09-10 18:08:39 +02:00
Jonathan Perret 850636970c 🚀(staging) add missing ghcr.io prefix to image name
Without a registry name, Docker Hub is still the default.
2025-09-10 18:06:02 +02:00
Jonathan Perret 9a1803b3e6 Merge pull request #31 from proconnect-gouv/move-to-proconnect 2025-09-10 17:56:47 +02:00
Jonathan Perret c51c95e007 💚(ci) fix CI jobs post org move
- use the GitHub app on numerique-gouv only to access the secrets repo
- inline helmfile-lint action (it only works on repos in numerique-gouv
  currently)
- replace deprecated `git whatchanged` with `git log`
- publish to ghcr.io instead of Docker Hub
- rename workflows to match other proconnect-gouv repos
2025-09-10 17:40:32 +02:00
Jonathan Perret 2687868465 📝(repo) update remaining references to numerique-gouv
Also updating CHANGELOG.
2025-09-10 17:40:30 +02:00
Jonathan Perret 38707412b3 🔧(secrets) change secrets submodule path
Since the oidcfer repo moved to a different organization the relative
path to the secrets submodule needs to change.
2025-09-10 15:24:56 +02:00
Jonathan Perret 8cd89621b2 Deploy release v1.0.7 to production. 2025-05-07 17:08:42 +02:00
Jonathan Perret 425365d22a v1.0.7 release 2025-05-07 17:07:48 +02:00
Jonathan Perret 63806bde22 Merge pull request #30 from numerique-gouv/fix-cve
Remove unused APT dependencies that trigger a CVE warning
2025-05-07 13:46:13 +02:00
Jonathan Perret 96299e1386 ⬆️ (deps) Bump gunicorn to 23.0.0 2025-05-07 13:43:45 +02:00
Jonathan Perret bf015c9238 🔒️(docker) remove unused APT dependencies that trigger a CVE warning
Specifically CVE-2023-45853 was flagged in our Docker image, but
it is in libfreetype6 which we don't use, and is only pulled in
because of a recommended depdency of `gcc` — which we don't need
in any case.
2025-05-07 13:43:45 +02:00
Jacques ROUSSEL 589d447f19 🐛(ci) fix helmfile linter
Latest helmfile version breaks lots of thing.
2025-03-28 14:46:38 +01:00
Jacques ROUSSEL ddc09cca50 🐛(ci) use github action for argocd webhook notification
In order to refactor this notification between alls projetcs, we
chooseto use a custom github action
2025-03-28 14:46:38 +01:00
Jonathan Perret ae2d4a573b Deploy release v1.0.6 to production. 2025-03-11 16:34:02 +01:00
Jonathan Perret 5be05fe43e v1.0.6 release 2025-03-11 16:31:50 +01:00
Jonathan Perret f90df5c2c6 Merge pull request #27 from numerique-gouv/allow-more-affiliations
Allow more affiliations
2025-03-11 16:02:10 +01:00
Jonathan Perret 6bbcd95069 🔧(config) allow more affiliations
We agreed with RENATER to allow people having one of the following
affiliations: faculty, staff, employee, researcher, teacher.
2025-03-11 15:42:27 +01:00
Jonathan Perret b839c32b50 Merge pull request #26 from numerique-gouv/satosa-8.5.1
⬆️ (deps) upgrade to SATOSA 8.5.1
2025-03-11 15:38:15 +01:00
Jonathan Perret 9b062aa27d ⬆️ (deps) upgrade to SATOSA 8.5.1 2025-03-11 15:23:36 +01:00
Jonathan Perret 4a2ece217b Deploy release v1.0.5 to production. 2025-03-06 19:48:00 +01:00
Jonathan Perret a7577ff85e v1.0.5 release 2025-03-06 19:38:28 +01:00
Jonathan Perret 4e81e1e750 Merge pull request #25 from numerique-gouv/custom-log-levels
🚀(logging) allow customizing log levels per logger
2025-03-06 19:36:05 +01:00
Jonathan Perret c27ebfe6ae 🚀(logging) allow customizing log levels per logger
This lets one enable increased logging at a finer level. For example,
one could enable debug logging for a specific logger, while keeping the
rest of the application at info level.
2025-03-06 19:27:01 +01:00
Jonathan Perret d84f2b32d5 Merge pull request #24 from numerique-gouv/filter-paths
🔒️(nginx) add basic path allowlist to block random attacks
2025-03-05 18:32:09 +01:00
Jonathan Perret 9cb67bfb87 🔒️(nginx) add basic path allowlist
This should cut down on opportunistic attack attempts, which are more
a nuisance than anything right now but it can't hurt to stop them
earlier.
2025-03-05 18:26:45 +01:00
Jonathan Perret ba1774bdbf Merge pull request #23 from numerique-gouv/fix-e2e-test
(e2e) fix e2e test with PC staging
2025-03-05 18:18:45 +01:00
Jonathan Perret cb8ff5d878 (e2e) fix e2e test with PC staging
Adapt to updated mock service.
2025-03-05 17:52:03 +01:00
rouja a6e01c071e Merge pull request #21 from numerique-gouv/add-pdbs
🔧(helm) add pdbs to deployements
2025-02-10 17:24:10 +01:00
Jacques ROUSSEL 5a0f2da202 🔧(helm) add pdbs to deployements
In order to avoid a service interruption during a Kubernetes
(k8s)upgrade, we add a Pod Disruption Budget (PDB) to deployments.
2025-02-10 17:19:08 +01:00
rouja 3b2d50d657 Merge pull request #20 from numerique-gouv/fix-issuer
fix letsencrypt issuer for new preprod
2025-02-07 11:53:59 +01:00
Jacques ROUSSEL 52d7de305c 🔧(helm) change letsencrypt issuer
The name of the cluster issuer on the new preprod is 'letsencrypt,' so I
adjusted it.
2025-02-07 10:49:36 +01:00
Jonathan Perret 64a0d4dd3d Merge pull request #19 from numerique-gouv/improve-helm 2024-10-02 10:46:04 +02:00
Jacques ROUSSEL ffbd161e29 (ci) add helmfile linter
Add a github job to run helmfile linter on PR
2024-09-24 17:11:13 +02:00
Jonathan Perret ba1c14291d Merge pull request #17 from numerique-gouv/add-architecture-doc
📝(architecture) add first architecture doc
2024-09-21 12:21:01 +02:00
Jonathan Perret 4c4ccc88ba 📝(architecture) add first architecture doc
This gives an overview and the detail of the workings of OIDC2FER.
2024-09-18 19:31:33 +02:00
Jonathan Perret c1b33453c3 Deploy release v1.0.4 to production. 2024-09-11 12:43:17 +02:00
Jonathan Perret 6cede0a7f2 v1.0.4 release 2024-09-11 11:00:09 +02:00
Jonathan Perret 3b518b9b3a Merge pull request #15 from numerique-gouv/serve-static-assets
(static) add static frontend to serve assets
2024-09-11 10:49:04 +02:00
Jonathan Perret 039d9a12cc (static) add static file serving
We needed a way to serve a handful of static assets, starting with
the ProConnect logo to be embedded in IdP pages.
2024-09-11 10:44:11 +02:00
Jonathan Perret 6424679b85 Set discovery URL to dedicated WAYF 2024-09-05 19:11:57 +02:00
Jonathan Perret 30c91e2345 Add missing slash to discovery URL
This saves one redirect when accessing the discovery service.
2024-09-04 20:05:06 +02:00
Jonathan Perret ad726a92ce Add missing info about "production" tag to deployment instructions 2024-09-02 17:42:42 +02:00
Jonathan Perret 7cc8fc18a2 Deploy release v1.0.3 to production. 2024-09-02 17:18:00 +02:00
Jonathan Perret 17289b1040 v1.0.3 release 2024-09-02 17:09:56 +02:00
Jonathan Perret 0580ec6d7e Merge pull request #14 from numerique-gouv/fix-saml-signature
Upgrade SAML signature algorithm for interoperability with RENATER Access Check
2024-09-02 16:26:07 +02:00
Jonathan Perret 4b5bd5757c 🔒️(saml) set SAML signing algorithm to use SHA256
It turns out the RENATER AccessCheck IdP rejects AuthnRequests signed
with the default SHA1-based SignatureMethod.
2024-08-29 20:34:36 +02:00
Jonathan Perret 61680de401 Deploy 1.0.2 to outscale-production 2024-07-22 20:55:08 +02:00
Jonathan Perret 0fc4450766 v1.0.2 release 2024-07-22 20:52:50 +02:00
Jonathan Perret db99364ae6 Merge pull request #13 from numerique-gouv/check-eppn-scope
🔒️(saml) check the scope of the eduPersonPrincipalName attribute
2024-07-22 20:49:25 +02:00
Jonathan Perret 25c401c653 🔒️(saml) check the scope of the eduPersonPrincipalName attribute
The metadata obtained from the federation server contains a list of
allowed scopes for each of the registered identity providers.

By checking the value of the eduPersonPrincipalName attribute
returned by an IdP against the allowed scopes for that IdP,
we protect the downstream services against identity theft by
a compromised IdP.
2024-07-22 20:35:06 +02:00
Jonathan Perret 311d490566 Merge pull request #10 from numerique-gouv/check-affiliation
Only allow employees to authenticate
2024-07-22 19:45:25 +02:00
Jonathan Perret ba69d2d4bd 📝(changelog) update CHANGELOG
Document new affiliation check.
2024-07-22 19:00:45 +02:00
Jonathan Perret 82fc42d093 🔒️(saml) only allow employees to authenticate
We check the eduPersonAffiliation attribute for a "employee" value to
reject students.
2024-07-22 17:32:20 +02:00
Jonathan Perret 10fc48b9ad (e2e) make renater test idp interaction deterministic
By default the consent form is only shown if the requested claims have
changed since the last login for that user, and this appears to be
stored server-side, which caused the test to break when I added
eduPersonAffiliation to the requested claims.

We now always request the consent form on login so that the interaction
is deterministic.
2024-07-22 16:02:10 +02:00
Jonathan Perret b8fe38853f 🚀(staging) complete switch to test federation
Switches the staging instance to the test federation.
2024-06-19 14:37:08 +02:00
Jonathan Perret e03dddde67 🚀(staging) use test entity ID
Switches the staging instance to the test federation.
2024-06-19 14:33:34 +02:00
Jonathan Perret 15064a0b45 🚀(config) get SAML entity ID from environment
This lets us decouple the entity ID from the deployment URL.
2024-06-19 14:29:50 +02:00
rouja a2d275366c 💚(ci) remove secret from repository (#12)
- Remove *.enc.*
- Add symlinks
- Adapt CI
2024-06-07 17:20:03 +02:00
Jonathan Perret 5c18904e39 🚀(staging) use qualification federation
The RENATER Qualification federation is more similar to the production
federation than the test federation.
2024-06-04 17:05:50 +02:00
Jonathan Perret 4aae1dbec2 v1.0.1 release 2024-06-04 16:36:22 +02:00
Jonathan Perret 4d19d6ec1b 🚀(logging) enable JSON logs and LOG_LEVEL env var (#11)
All logs (Gunicorn + SATOSA) are now logged as JSON and the level can be
controlled with the LOG_LEVEL environment variable.
2024-06-04 16:28:15 +02:00
Jonathan Perret 7ecc4c404a 🚀(prod) fix letsencrypt issuer for production
Certificate generation fails if we use the wrong issuer.
2024-05-30 16:09:17 +02:00
Jonathan Perret a940151661 🚀(prod) set production URL to renater.agentconnect.gouv.fr
This is the domain we agreed upon with the AgentConnect team.
2024-05-30 12:01:02 +02:00
Jonathan Perret aa9af2a401 🚀(prod) set production URL to renater.agentconnect.gouv.fr
This is the domain we agreed upon with the AgentConnect team.
2024-05-29 17:13:20 +02:00
Jonathan Perret 132e7a442d Merge pull request #9 from numerique-gouv/production-setup
🚀(prod) add production config to chart
2024-05-16 18:21:38 +02:00
Jonathan Perret 08d5a5ef4d 🚀(prod) add production config to chart
This will be picked up by ArgoCD to deploy on the Outscale production
cluster.
2024-05-16 17:43:59 +02:00
43 changed files with 866 additions and 275 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/numerique-gouv/oidc2fer).
Please make sure you have read our [main Readme](https://github.com/proconnect-gouv/oidc2fer).
Also make sure it was not already answered in [an open or close issue](https://github.com/numerique-gouv/oidc2fer/issues).
Also make sure it was not already answered in [an open or close issue](https://github.com/proconnect-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
@@ -0,0 +1,67 @@
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 }}"
@@ -1,4 +1,4 @@
name: OIDC2FER Workflow
name: 💚 CI Tests
on:
push:
@@ -16,14 +16,14 @@ jobs:
if: github.event_name == 'pull_request' # Makes sense only for pull requests
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: show
run: git log
- name: Enforce absence of print statements in code
run: |
! git diff origin/${{ github.event.pull_request.base.ref }}..HEAD -- . ':(exclude)**/oidc2fer.yml' | grep "print("
! git diff origin/${{ github.event.pull_request.base.ref }}..HEAD -- . ':(exclude).github/**' | grep "print("
- name: Check absence of fixup commits
run: |
! git log | grep 'fixup!'
@@ -39,17 +39,17 @@ jobs:
github.event_name == 'pull_request'
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check that the CHANGELOG has been modified in the current branch
run: git whatchanged --name-only --pretty="" origin/${{ github.event.pull_request.base.ref }}..HEAD | grep CHANGELOG
run: git log --name-only --pretty="" origin/${{ github.event.pull_request.base.ref }}..HEAD | grep CHANGELOG
lint-changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Check CHANGELOG max line length
run: |
max_line_length=$(cat CHANGELOG.md | grep -Ev "^\[.*\]: https://github.com" | wc -L)
@@ -65,7 +65,7 @@ jobs:
working-directory: src/satosa
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v3
with:
@@ -89,7 +89,7 @@ jobs:
working-directory: src/satosa
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v3
with:
@@ -101,3 +101,37 @@ jobs:
pip install --user .[dev]
- name: Run tests
run: ~/.local/bin/pytest
helmfile-lint:
runs-on: ubuntu-latest
container:
image: ghcr.io/helmfile/helmfile:v0.171.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
shell: bash
run: |
mkdir -p ~/.config/sops/age/
echo ${{ secrets.SOPS_PRIVATE }} > ~/.config/sops/age/keys.txt
set -e
HELMFILE=src/helm/helmfile.yaml
environments=$(awk '/environments:/ {flag=1; next} flag && NF {print} !NF {flag=0}' "$HELMFILE" | grep -E '^[[:space:]]{2}[a-zA-Z]+' | sed 's/^[[:space:]]*//;s/:.*//')
for env in $environments; do
echo "################### $env lint ###################"
helmfile -e $env -f src/helm/helmfile.yaml lint || exit 1
echo -e "\n"
done
-71
View File
@@ -1,71 +0,0 @@
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:
-
name: Checkout
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: lasuite/oidc2fer
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: .github/workflows/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:
-
name: Checkout
uses: actions/checkout@v4
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: .github/workflows/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
-17
View File
@@ -1,17 +0,0 @@
SOPS_PRIVATE=ENC[AES256_GCM,data:k9v8gJJ1ZTDbN8mR9NegKpCC8Kbv9ZqWDx3nhs6YAC6U2gxI0RDg5KRx0juym+5DPEVG4ck8y+ju/4uthRUZd0JkexEJX0WKFTo=,iv:hucEFAC/FTQsE4A+JmuY1PeJJcBRAWUqEul5wplVsJ8=,tag:uCYu26tM4hlheT8bCInA6A==,type:str]
DOCKER_HUB_PASSWORD=ENC[AES256_GCM,data:gsq8AIbLENiXfOg=,iv:48QhtPLRq0yWpffQPxmkb1FLOkNQ2vp1/o+fnU6QM/o=,tag:s7ADhOZ314EWMc82kRvK+A==,type:str]
DOCKER_HUB_USER=ENC[AES256_GCM,data:/+bIx0jMNg==,iv:Em3MIFQ8B457gwkRGB0JkRknpEQ1maiqW42gh9/DWDY=,tag:Hc8ZHDludj8up7r0tH2FRw==,type:str]
ARGOCD_WEBHOOK_URL=ENC[AES256_GCM,data:JHzooEYiqWdH4MjcNT3lL0fxY6YlfyhP6kM0kV6NPBnTtI46ip7nMXZoChkQT7CoPTof3IjMdJaD,iv:/CRQTZhnSi0UxgVd0crnvlVJfStIbYCG3cMTkejW0tU=,tag:p8QAWnwa+M4m9WSQMr7Obw==,type:str]
ARGOCD_WEBHOOK_SECRET=ENC[AES256_GCM,data:oQyuVQFPmhLIL14hWK0vUxo+r/QEj8hb2uY6bntlEyNqrFg7NOameWFn+dvCsN+C90HT9wD9yhu9aXsV92cJxDI=,iv:MVeeLpJU4taPqoGvJ8bqh6bfLHntRfqic/kIBPY/WV4=,tag:gJXkjUvSjrGjqPqCw6mRyA==,type:str]
sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCQy9CUXhYSTVoMnNGZWda\nUUJuYnd4VVM0YW1tRHhIUjNoQnZ3cEc5ZkdFCmg2a3dkcGNVSGFWd2hBa3pVUU5H\nVlMzSHd1VlBBdVZHTXA4S214UkQvSzQKLS0tIDVjTCtHSnJDZ2pOOG5ONW1zbTY1\neHZpRlZqVlhaVkpsR0VwRHBWZ0d4VjQKQUFraK9M2bFItVmQ7l9Z59SY9YkhGzy3\nON3028ZP4Xfvr+8FaQj0xS/5rnHTFIKLmW7bbvvPWeBW/LFvfxaUcw==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_0__map_recipient=age15fyxdwmg5mvldtqqus87xspuws2u0cpvwheehrtvkexj4tnsqqysw6re2x
sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmWnVKM2hwdXdDYXhVUzh0\nSGxrUWNYa25KcWdvRUxWNHl1eU1UdjN3UVNNCkJ2Y3FZblBMWlpUKzE2bW1HZUJF\nZldzKzhrWVhHd0RKN01Ub3JLYkNlZGMKLS0tIEJDTy9OUVNOTVdKQ1Y4eGpqUnIv\neTRTa1UrU3Q1LzVkZWpPaXpsamRXVGcK3PBKBPIVV58HT6UGOovjzgxqlrhDo/S7\nhIye04YRyGNZamnff0gOA0n9wXrcvTyFYiv5o9CFQtfg082j7ii70w==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_1__map_recipient=age1h7d5y4w370whuawk5jxz8zv320u766mzhvlg2n6gh6hxvqmxg4psfpr03e
sops_age__list_2__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOWndDNDBoMGxuRFhTalJu\nNnZVYXBwTjhsY3BXZmV6ZW1jQkZ2bExIQmowClZCZmU5L3p4SmVXSmV1YnpXT2lm\nSFlpQ0o0cENtYVRvQ05vWE5XTCtNV3MKLS0tIG9oVDIrdElpd01JUEpRK1hmaWZq\ndE4yQks5dkRRbk9ZN2o3UHQxeHhlMXcKXBZxVw/yVb+qM+puQYOqqUHEsNggZ9LX\nkVOelk2UijLUHx9VCoerrJkfI3mUKCsNCvwnrL2bqezHgg+VhPhukw==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_2__map_recipient=age1nma4n6pxaywl39cndnu852vx0xs0vh5stcc3qtasdt89u4dqrussvjk5fj
sops_age__list_3__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5N1h1OW4wRVh5YkR5TkJ5\nQTlDK2RUSkIzWjJwUnp3b2todm5FVVJPWnlZCjhJc1JZcEQ3KzJYRVBQLytOM3JG\nYUFJdHZDTlN3RXpyZlluWU13c0VET1UKLS0tIGhJZmJZNUx5TWl1YnFJN0RWeEkv\nWjZoZzZiM2lkMlFabXEvMnhaYUdxY1UK3d/SWz3fcij+4W4tq1zRFK+4sCYHTCxS\nEuT/a+St02Eirrg9SW3b52T1RvZriOQAR/VUG09pjjGliaf/oW0pVQ==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_3__map_recipient=age1qy04neuzwpasmvljqrcvhwnf0kz5cpyteze38c8avp0czewskasszv9pyw
sops_lastmodified=2024-04-18T10:38:19Z
sops_mac=ENC[AES256_GCM,data:HGGl4gcXnli8Z7P2P1MspygWRIHJqTLesf/QpiUUC6qH07Qx5bhN5fA1Oo3WTcz+fXkfAY8SV2RZb2CHxo/NouCiATw7vodX1ttwvsUE6RLgdjvMJl4ecWucZMiBdBtpFF4IHrKxswQg1FEdT8U/JP3Gw0TxxaRXIqskXkytp9E=,iv:fPcbne5lDa6+b+DqB+Q2pJ11EB9Vfpvqd9yextCjv7U=,tag:MkVoOpIApCIluFzsazl16w==,type:str]
sops_unencrypted_suffix=_unencrypted
sops_version=3.8.1
+3
View File
@@ -0,0 +1,3 @@
[submodule "secrets"]
path = secrets
url = ../../numerique-gouv/secrets
-10
View File
@@ -1,10 +0,0 @@
creation_rules:
# Using a single-key group to be able to use per-key comments,
# see https://github.com/getsops/sops/issues/845#issuecomment-1364109772
- key_groups:
- age:
- age15fyxdwmg5mvldtqqus87xspuws2u0cpvwheehrtvkexj4tnsqqysw6re2x # Jacques
- age1h7d5y4w370whuawk5jxz8zv320u766mzhvlg2n6gh6hxvqmxg4psfpr03e # Jonathan
- age1nma4n6pxaywl39cndnu852vx0xs0vh5stcc3qtasdt89u4dqrussvjk5fj # github-repo
- age1qy04neuzwpasmvljqrcvhwnf0kz5cpyteze38c8avp0czewskasszv9pyw # argocd
+36 -1
View File
@@ -6,7 +6,42 @@ 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]
## [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
## [1.0.3] - 2024-09-02
- set SAML signing algorithm to use SHA256 for compatibility with AccessCheck
## [1.0.2] - 2024-07-22
- check the scope of returned eduPersonPrincipalNames
- require "employee" value in eduPersonAffiliation
## [1.0.1] - 2024-06-04
- enable JSON logging and LOG_LEVEL environment variable
- add production configuration
## [1.0.0] - 2024-05-16
- add Helm chart for deployment
- provide Docker images on https://hub.docker.com/r/lasuite/oidc2fer
- install SATOSA
+6 -12
View File
@@ -1,17 +1,11 @@
# ---- base image to inherit from ----
FROM python:3.11-slim-bookworm as common
FROM python:3.11.12-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 -y \
pkg-config \
gcc \
xmlsec1 \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl && \
apt-get install -qy --no-install-recommends xmlsec1 && \
rm -rf /var/lib/apt/lists/*
# We want the most up-to-date stable pip release
@@ -19,7 +13,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
@@ -38,10 +32,10 @@ RUN mkdir -p /usr/local/etc/gunicorn
COPY docker/files/usr/local/etc/gunicorn/satosa.py /usr/local/etc/gunicorn/satosa.py
# The default command runs gunicorn WSGI server in satosa's main module
CMD ["gunicorn", "-c", "/usr/local/etc/gunicorn/satosa.py", "satosa.wsgi:app"]
CMD ["gunicorn", "-c", "/usr/local/etc/gunicorn/satosa.py"]
# ---- Development image ----
FROM common as development
FROM common AS development
# Playwright browsers
ENV PLAYWRIGHT_BROWSERS_PATH=/pw-browsers
@@ -67,7 +61,7 @@ COPY ./src/satosa /app/
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/
+23
View File
@@ -47,6 +47,29 @@ Finally, you can check all available Make rules using:
$ make help
```
## Creating a release
1. Update `CHANGELOG.md` to change the `Unreleased` header to the new version
number and date.
2. Commit and push to `main`.
3. Create a `vX.Y.Z` tag from `main` and push it.
## 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
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
+4 -2
View File
@@ -13,7 +13,7 @@ services:
BASE_URL: https://satosa.traefik.me
GUNICORN_CMD_ARGS: --workers=2
TEST_E2E: 1
#TEST_E2E_AC: 1
#TEST_E2E_PC: 1
OIDC_DB_URI: redis://redis/0
CLIENT_DB_JSON: |
{
@@ -31,11 +31,13 @@ services:
}
SAML2_DISCOVERY_URL: https://discovery.renater.fr/test/
SAML2_METADATA_URL: https://pub.federation.renater.fr/metadata/test/preview/preview-idps-test-metadata.xml
SAML2_ENTITY_ID: https://satosa.traefik.me/Saml2/proxy_saml2_backend.xml
env_file:
- env.d/development/common
- env.d/development/satosa
volumes:
- ./src/satosa:/app
- ./docker/files/usr/local/etc/gunicorn/satosa.py:/usr/local/etc/gunicorn/satosa.py
depends_on:
- redis
@@ -64,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
OIDC_SCOPES: openid,uid,given_name,usual_name,email,siret
extra_hosts:
- "oidc2fer.127.0.0.1.nip.io:host-gateway"
+142 -1
View File
@@ -1,6 +1,84 @@
import datetime
import json
import logging
import os
import sys
import json_log_formatter
class RequestJSONFormatter(json_log_formatter.JSONFormatter):
"""
Converts Gunicorn request log records to JSON.
See https://docs.gunicorn.org/en/stable/settings.html#access-log-format
"""
def json_record(
self,
message: str,
extra: dict[str, str | int | float],
record: logging.LogRecord,
) -> dict[str, str | int | float]:
# Convert the log record to a JSON object.
response_time = datetime.datetime.strptime(
record.args["t"], "[%d/%b/%Y:%H:%M:%S %z]"
)
url = record.args["U"]
if record.args["q"]:
url += f"?{record.args['q']}"
return {
"logger": record.name,
"level": record.levelname,
"remote_ip": record.args["h"],
"method": record.args["m"],
"path": url,
"status": str(record.args["s"]),
"time": response_time.isoformat(),
"user_agent": record.args["a"],
"referer": record.args["f"],
"duration_in_ms": record.args["M"],
"pid": record.process,
}
class DefaultJSONFormatter(json_log_formatter.JSONFormatter):
"""
Formats a log record as JSON, adding level and pid.
"""
def json_record(
self,
message: str,
extra: dict[str, str | int | float],
record: logging.LogRecord,
) -> dict[str, str | int | float]:
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"
wsgi_app = "oidc2fer.wsgi:app"
# Run
graceful_timeout = 90
@@ -11,4 +89,67 @@ timeout = 90
accesslog = "-"
# Using '-' for the error log file makes gunicorn log errors to stderr
errorlog = "-"
loglevel = "info"
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,
"root": {"level": loglevel.upper(), "handlers": ["json_error"]},
"loggers": {
# The gunicorn.error and gunicorn.access loggers are preconfigured with
# a handler and propagate=False, we need to override this with the
# default values (no handlers and propagate=True)
"gunicorn.error": {
"level": "INFO",
"handlers": ["json_error"],
"propagate": False,
"qualname": "gunicorn.error",
},
"gunicorn.access": {
"level": "INFO",
"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": {
"()": RequestJSONFormatter,
},
"json_error": {
"()": DefaultJSONFormatter,
},
},
"handlers": {
"json_request": {
"class": "logging.StreamHandler",
"stream": sys.stdout,
"formatter": "json_request",
},
"json_error": {
"class": "logging.StreamHandler",
"stream": sys.stdout,
"formatter": "json_error",
},
},
}
+8 -4
View File
@@ -2,13 +2,10 @@ 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 RegistrationResponse
from oic.oic.message import Claims, ClaimsRequest, RegistrationResponse
from oic.utils.http_util import Redirect
from oic.oic.message import AuthorizationResponse
import secrets
import webbrowser
import threading
import time
import logging
import os
@@ -52,6 +49,7 @@ def index():
"nonce": session["nonce"],
"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)
@@ -68,6 +66,12 @@ def oidc_callback():
assert aresp["state"] == session["state"]
if "error" in aresp:
return jsonify(
error_response=aresp.to_dict(),
)
code = aresp["code"]
logging.info("got auth code=%s", code)
+1 -1
View File
@@ -1,2 +1,2 @@
Flask==3.0.3
oic==1.6.1
oic==1.7.0
+93
View File
@@ -0,0 +1,93 @@
# Principe de fonctionnement de la passerelle ProConnect vers RENATER (`OIDC2FER`)
## Vue d'ensemble
Ce premier schéma donne un aperçu des échanges entre :
* un Fournisseur de Service ProConnecté (ex. https://pad.numerique.gouv.fr) ;
* le service ProConnect (hébergé sur `https://auth.agentconnect.gouv.fr`) ;
* la passerelle `OIDC2FER` (hébergé sur `https://renater.agentconnect.gouv.fr`) ;
* le service de _discovery_ ou _Where Are You From (WAYF)_ RENATER qui permet à l'utilisateur de choisir son établissement de rattachement, hébergé sur https://discovery.renater.fr/agentconnect/ ;
* le serveur d'identité SAML de l'établissement sélectionné (par exemple, https://cas.inria.fr).
``` mermaid
sequenceDiagram
participant FS as Fournisseur<br>de Service
participant ProConnect
box LightYellow
participant OIDC2FER
end
participant WAYF as WAYF<br>RENATER
participant IdP as IdP SAML
FS->>ProConnect: OIDC auth request
ProConnect->>OIDC2FER: OIDC auth request
OIDC2FER->>WAYF: discovery request
WAYF->>OIDC2FER: IdP entityID
OIDC2FER->>IdP: SAML AuthnRequest
IdP->>OIDC2FER: SAML Assertion
OIDC2FER->>ProConnect: OIDC tokens+userinfo
ProConnect->>FS: OIDC tokens+userinfo
```
## Détail des échanges
Noter que le schéma précédent ne reflète pas le détail des échanges entre l'utilisateur, son navigateur (qu'il est utile de distinguer pour illustrer que plusieurs échanges se font sans intervention humaine), et les différents services mentionnés. En voici une version exhaustive :
``` mermaid
sequenceDiagram
actor Utilisateur
participant Navigateur
participant FS as Fournisseur<br>de Service
participant ProConnect
box LightYellow
participant OIDC2FER
end
participant WAYF as WAYF<br>RENATER
participant IdP SAML
Utilisateur->>Navigateur: Saisie/clic URL FS
Navigateur->>FS: Requête d'une page du FS
opt si pas de session FS ouverte
FS->>Utilisateur: Présentation page d'accueil avec bouton ProConnect
Utilisateur->>FS: Clic bouton ProConnect
FS->>Navigateur: Redirection OIDC vers ProConnect avec state, nonce
Navigateur->>ProConnect: Requête GET avec state, nonce
opt si pas de session ProConnect ouverte
ProConnect->>Utilisateur: Présentation mire ProConnect
Utilisateur->>ProConnect: Clic bouton RENATER, ou saisie "robert@univ-exemple.fr"
ProConnect->>Navigateur: Redirection OIDC vers OIDC2FER avec state, nonce
Navigateur->>OIDC2FER: Requête GET avec state, nonce
OIDC2FER->>Navigateur: Redirection vers WAYF
Navigateur->>WAYF: Requête GET
opt si pas de préselection enregistrée dans le WAYF
WAYF->>Utilisateur: Présentation liste d'établissements autorisés
Utilisateur->>WAYF: Choix d'un établissement
end
WAYF->>Navigateur: Redirection vers OIDC2FER avec entityID IdP
Navigateur->>OIDC2FER: Requête GET avec entityID IdP
OIDC2FER->>Navigateur: Redirection vers IdP avec AuthnRequest SAML dans l'URL
Navigateur->>IdP SAML: Requête GET avec AuthnRequest SAML dans l'URL
opt si pas de session IdP ouverte
IdP SAML->>Utilisateur: Présentation mire de connexion IdP
Utilisateur->>IdP SAML: Saisie identifiants de connexion
end
IdP SAML->>Navigateur: Page avec formulaire contenant assertion SAML
Navigateur->>OIDC2FER: Requête POST avec assertion SAML
Note over OIDC2FER: Validation<br>eduPersonAffiliation
Note over OIDC2FER: Conversion attributs<br>SAML vers OIDC
OIDC2FER->>Navigateur: Redirection vers callback OIDC ProConnect avec authorization_code
Navigateur->>ProConnect: Requête GET avec authorization_code
ProConnect->>OIDC2FER: Demande tokens avec authorization_code
OIDC2FER->>ProConnect: Tokens OIDC
ProConnect->>OIDC2FER: Demande userinfo avec accessToken
OIDC2FER->>ProConnect: userinfo
end
ProConnect->>Navigateur: Redirection vers callback OIDC FS avec authorization_code
Navigateur->>FS: Requête GET avec authorization_code
FS->>ProConnect: Demande tokens avec authorization_code
ProConnect->>FS: Tokens OIDC
FS->>ProConnect: Demande userinfo avec accessToken
ProConnect->>FS: userinfo
end
FS->>Utilisateur: Contenu du service
```
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
git submodule update --init --recursive
git submodule foreach 'git fetch origin; git checkout $(git rev-parse --abbrev-ref HEAD); git reset --hard origin/$(git rev-parse --abbrev-ref HEAD); git submodule update --recursive; git clean -dfx'
Submodule
+1
Submodule secrets added at b0d485544d
@@ -9,8 +9,11 @@ satosa:
BASE_URL: https://oidc2fer.127.0.0.1.nip.io
GUNICORN_CMD_ARGS: --workers=3
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
SAML2_ENTITY_ID: https://oidc2fer.127.0.0.1.nip.io/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 } }
@@ -33,6 +36,10 @@ satosa:
"token_endpoint_auth_method": "client_secret_post"
}
}
SIRET_MAP: |-
{
"https://test-idp.federation.renater.fr/idp/shibboleth": "12345678200010"
}
ingress:
enabled: true
+1
View File
@@ -0,0 +1 @@
../../../../secrets/numerique-gouv/oidc2fer/env/outscale-production/secrets.enc.yaml
@@ -0,0 +1,121 @@
image:
repository: ghcr.io/proconnect-gouv/oidc2fer
pullPolicy: Always
tag: "v1.0.8"
satosa:
replicas: 2
envVars:
BASE_URL: https://renater.agentconnect.gouv.fr
GUNICORN_CMD_ARGS: --workers=3
LOG_LEVEL: INFO
LOG_LEVELS: '{ "satosa.backends.saml2": "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
SAML2_ENTITY_ID: https://renater.agentconnect.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 } }
# 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": "19251215000363",
"https://idp.univ-tours.fr/idp/shibboleth": "19370800500478",
"https://apps.univ-lr.fr/idp/shibboleth": "19170032700015",
"https://shib.mines-albi.fr/idp/shibboleth": "19811200500022",
"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": "13000375900029",
"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": "18004312700083",
"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": "19311382600013",
"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": "18510200100012",
"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": "13000279300011",
"https://prod-idp.inria.fr": "18008904700013",
"https://idp.ec-nantes.fr/idp/shibboleth": "19440100600011"
}
ingress:
enabled: true
host: renater.agentconnect.gouv.fr
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt
nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
-52
View File
@@ -1,52 +0,0 @@
SAML2_BACKEND_CERT: ENC[AES256_GCM,data:uVs1uGoFfESz0eAkxvPUkSXQw6cCvzi+YfH7pbXLxAXxYhmiKf25JarZX2ur8au3pdOd/rz0mZGpC2mfN8Fh+3+a0hseuvhqTetiFlc7Gc84HTXnyUQFzZa4Rgk6LeKGB8TDLG+s0F46u75KrRdHrwSbthBQhtiZCmoc+JcqSKJfl9Kv9OVMXEPHUy3zYvIy32Gk/+BEt+J9eo3RT1ZmmyRZToWWgXe906n2Bhvm8q05OgHkbCqu29TGKBviwikWTa36aUWNVdWh6XW7CtOPjWDvjYbVgb3O/Vwmw3mPRGviuAK1WS+kzMyP5oYQsWC5F97R0XlhnKElxpIRycCz3m/YLShuM1xtStiiR4mym6mNi99YonQ6hbodHSij9R/udXyMLeC5QZM15XzWHGFA+NemqVMGZ2C/xIhHCNUscKvHzOiQ2RiTk9VxNZfSdkzEXe2gMbl/+o3hSj8usFJOZWgToiYN+oBXgP1KqRlwdPSysCB3hs6mysn/1WlILWXN+EOYJP1jLsv1T4dC3ajujmnmhCn3/EYVI4TKEM6nJNs2KxNqlsC2D4G2n8NDciZVUt5a82tXWW6/ROA5UZDFOILkCHvfk6nkTRZjcXWmtPmCgwy54TysJ1wLsIycAsd8MuBTD3d9SpYFHKNC74O30bJe8GqtKObvHg8Ycp36+oH3gs41Mv5H32DumkUF6FcMJtbOEOYoEiHtuMSVXzuIdr6ikYQsRBU4v0UUpJTdMl2W+tYB8NeWBdI4jYaR+xwLw4+gs/deXhE7USTlPZJDB6a4DValxm+p7dLmUr5McgdhyEAACmxzhiSl33ccV2dIQCMsk4RoBXCZGX10ElY9bMk0vLaB5/n6qQgP51onxOUeJ6poKj4wneIZ7WmvbBfAVFUHoCPMQemjEEk6jVza4la4yQUW9rCgo/Tpfb7e05A+ux0CvNKvhcJLfyxN+LwDOw5X4p80gdVO8aWKyE+Ue6GPQBMZ8Ma31jlX//4KeIVcjpZAdutp38vJ936Ezrowu0HbviC1f0pJWXBVw0G8DGtpgorYhkN3Biwzu30jMEcWJXFSPqBGH0U2I5D4IXsDX1rH3DAR5UOKMVbWJ72zUq99Uip0FiaY1IVZLvfkfbPHwVq3jmuiZ/nXn1HenmNlkYOmOtL5BnZ47gHgXiMgFx6YGMBY50pZvk8ISKcacP52N9ph3EkovJR3Uam4XrtbMV0v9AGHX9aXYF1uB37q+ifZ4KB0b91dbXIUxXMb7FrbZ/iDX56+yTzdxdnXHWsmcBtPNFHxo+QjZvk4+UWlvoahwLJMGlxtgk71TPF65ms8y4L5Qq1vL2BgGiKWARWmq7KnrmrXQQWRZAXQd0lfqhVo7GDhppJUKBgYSQ4rC0q6u2mEm9YJc/3vsudKkwTyoYfBOmmz2+xI8fdaFuzg9UWswG3n61HMFYVf8+WAmoS7Bo21ns+KiIOroGruYdExr5rJOPcsLiPEr/Qmk0M=,iv:VI5LR7U20u6jG2voQgbnKcXclbWnROASzoYYz9N8Qlc=,tag:r1hGWOZRaiBblWvntcdYzg==,type:str]
SAML2_BACKEND_KEY: ENC[AES256_GCM,data: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,iv:itJR8JpZRrwhbQY/kTEURY28vyu1qex7GsRme9icZYw=,tag:JgkJOE+bdK19KjvdH/k1Ug==,type:str]
OIDC_FRONTEND_KEY: ENC[AES256_GCM,data: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,iv:EJEMIEsraig+i+5PlieAwHS6XV5bP+wlxv6Wni/9Dws=,tag:DM1XLnEtTp0vFEjAD4u4ag==,type:str]
STATE_ENCRYPTION_KEY: ENC[AES256_GCM,data:U0BoVcDN6/sNDST8epqmOzs5oWok6lRZ+hLo7O6/Mkz0cqB4rcHe/OOC9HTe39K5WUCD25Rfrb4lSeK0Finl9A==,iv:NM9Wjlx/llNc7Ws8DaNYy9dVq/oFEdlK5a/Alr5ZxVA=,tag:VT8GhiFxeI8AjdcKdRkEcQ==,type:str]
CLIENT_DB_JSON: ENC[AES256_GCM,data:C1UpicWcfmngdnBqGBMujt8aUZ+WpmvGs652NgR3gvN+aeQ1mMFWAjjCza5q9jN2sIhMbmu/NPrcTvLx4UNE0MOIclrWWbPdxk2UyylU+7TEas6V7OrpJa1e5D8msSSAnPLI7YSrRILSrw6pktnM/07d7S8Q1fkLGxJYkRboTGbTSRGRSSMDFdyrdrGiTDnc3H0y7FQerzQ8caRgRvb1JW8OPpJ+Kpk3jfTyFCNwiyJnISb48QCQPQX2jg9GpXl6OaPg2bw5OnnM1hi/qL+43nR/wV1WnvH/Z+Quvwgj7RorpS1TWVeLIC6zo8o49XFg6aHoiKigma3U/8ZeccsxpYHdsqObQ/mrSyYS87YNHXbl5miwjevaBGIj4Vteg16Vgs6R4jd8qgan+zCCfu5OwGmmCFG4M6EnuWhvQ5YpDGjclBc+q5exSnrb2hVOsEpYB2iRpcd5KNexrhCBJQZVMWRtAhnGIsupImctHaFVcZsvDBUQ3tQ+glt8s59VNZlnXPGE7hnB4L60PxMUJVhL6p8pbQtNvm6fCUBORoU+FCXnfGoK70Cxc/O6THvIcMH8L12rM0XEe/QyJIqRrBUtQcXgTZo3cB+tLVKW/MwKMyCk4s41McyEjL420pFh8cJ19iHWw5vZ4pZ4lkdtyaf3OI5eIVrnKj6uU96/daJSTOJCUgdyj/Vve//4m+UN7qkbnbahPRt5MZIY6P6P/Qs0JeV1tUnhe4vU,iv:WWpqoOZaXwIt1uziAdxgYDNSqR0XG0n/QexgIRItbm0=,tag:gzhESbDbnySTMMoJQiQl/g==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age15fyxdwmg5mvldtqqus87xspuws2u0cpvwheehrtvkexj4tnsqqysw6re2x
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZaGtqWVIwVHNQd3lCa0di
ajVwME5zdmZKcGEzSzMzUUNLODRJKzFtMkNrCjRyKzJjWEJaM3J2V29GTnA0TWlH
YmRPdnUzUm5Xb2dqSjBnRkM5U1VJbUkKLS0tIDZVb1Nxc3FYQW9hRGpta09OUllR
ZmJ0OTdRYTVQdlJ3REh0MXQvY21ETlkKyD979zhHLw/hFLBAjSQwVvSYF8vzyIym
D5Qt261RH79ajpnu6Sc11kUXKi0uGWOMBQQXdZKsv15o8RJsXsseWQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1h7d5y4w370whuawk5jxz8zv320u766mzhvlg2n6gh6hxvqmxg4psfpr03e
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGb2pSOUV3WkVwOWpiYWMy
R0NrSFd3TTk5bHZhbWZ6V2lBd3JIbno1QkJjCnNNRFZlUFBsU3pnbUN5eFNjSlNl
MDNlZDgvUWhCbXlTTU4rcEc1azBRK0kKLS0tIGEzK0h5cEowa0sxZ3dTYkp5eU5J
bzRMR1dnbEtLZmc4QStmN3B1YjJodTQKsH6+NmM3WmJoTOVId2jea43Ud4pPm072
x4O78jbVlBNaAWhmRDqF+e25I0ijYPPRICxh7VRnUl6PAsB93F60iQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1nma4n6pxaywl39cndnu852vx0xs0vh5stcc3qtasdt89u4dqrussvjk5fj
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNSWZFOW5SZWM0WFFVcTdH
WlJQa2hUckUyanZnZHQ3Vnk0VG9Zcmt6dUEwCk96WDVLTDlsenpCdU9qSFhVcS80
bXdKZ1FCNmQrV2t1T2ltS0ljUnZrZEUKLS0tIGFuZWpmbjBvRXFKMXNTcFhHZmF1
bFV1ZFFVclVkVS9LZ3lXTnk1Vm43dVUKnskxex7sr+qj/kdYbNJNnjXXIF1P+9r+
wqL2UlpCRwP+I+bX7EsO06stiHzeVskcCjZVBC6Ki7HAaEF2KV6Tow==
-----END AGE ENCRYPTED FILE-----
- recipient: age1qy04neuzwpasmvljqrcvhwnf0kz5cpyteze38c8avp0czewskasszv9pyw
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBINnIyYmtVcXF1TTd4UVpr
VThISkJQT0tYSmxiRWllM3VEb3Fnc3pSZm5FCjVVTUdYR1dkTEtOYjVCdHNWRS82
MFhaMFNITFE2QS9vbC9oNHhDaUpFZ28KLS0tIG16OERadzlIN3lXTDdyRS9tbHAr
NGwvS2J1SjNxMUdpdU01bGxrYVFEeDQKJfcoidOW3qh4hxQgBSFciCQrsothPllK
Zj6d3pUeqZpQlSaYKfr7tywYHOgkmOMJcGUfYXDpk7278gNBSo6T7w==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-04-22T15:46:36Z"
mac: ENC[AES256_GCM,data:Nc8rhWtwyXedqESemEusiy2yeqtDbjlep0PAbQ7UW2+jdZZE/uOW1I7LvqTNOKwPwMwFgT0+PyuxOzcSrYtH24A/QSEA2P5kH9S7VXaAZmrWYANTI1pMzqaMGYdP2KQlgK3SqBPmJlG8OHYAHpYpFMIUduWiYCdgEZCfzGbzsfg=,iv:2so40E91bgVh2AL5D6Ju1CXP1Bd31kN95/86/Oinkg0=,tag:rHy//0ddChLA/dIN4P0EhA==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.8.1
+1
View File
@@ -0,0 +1 @@
../../../../secrets/numerique-gouv/oidc2fer/env/staging/secrets.enc.yaml
@@ -1,7 +1,7 @@
image:
repository: lasuite/oidc2fer
repository: ghcr.io/proconnect-gouv/oidc2fer
pullPolicy: Always
tag: "main"
tag: "latest"
satosa:
replicas: 2
@@ -9,8 +9,12 @@ satosa:
BASE_URL: https://oidc2fer-staging.beta.numerique.gouv.fr
GUNICORN_CMD_ARGS: --workers=3
LOG_LEVEL: DEBUG
LOG_LEVELS: '{ "satosa.backends.saml2": "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
SAML2_ENTITY_ID: https://oidc2fer-staging.beta.numerique.gouv.fr/Saml2/proxy_saml2_backend.xml-test
STATE_ENCRYPTION_KEY: { secretKeyRef: { name: oidc2fer, key: STATE_ENCRYPTION_KEY } }
SAML2_BACKEND_CERT: { secretKeyRef: { name: oidc2fer, key: SAML2_BACKEND_CERT } }
@@ -20,10 +24,15 @@ 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
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
cert-manager.io/cluster-issuer: letsencrypt
nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
+6 -1
View File
@@ -22,7 +22,7 @@ releases:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
- name: oidc2fer
version: 0.0.1 # {{ .Values.version }}
version: {{ .Values.version }}
namespace: {{ .Namespace }}
chart: ./oidc2fer
values:
@@ -41,4 +41,9 @@ environments:
- 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
+7 -4
View File
@@ -1,5 +1,6 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "oidc2fer.fullname" . -}}
{{- $port := .Values.satosa.service.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
@@ -33,13 +34,15 @@ spec:
- host: {{ .Values.ingress.host | quote }}
http:
paths:
- path: /
pathType: Prefix
{{- range .Values.ingress.paths }}
- path: {{ .path | quote }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "oidc2fer.satosa.fullname" . }}
name: {{ $fullName }}-satosa
port:
number: {{ .Values.satosa.service.port }}
number: {{ $port }}
{{- end }}
{{- with .Values.ingress.customBackends }}
{{- toYaml . | nindent 10 }}
{{- end }}
@@ -82,3 +82,16 @@ spec:
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
---
{{ if .Values.satosa.pdb.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
spec:
maxUnavailable: 1
selector:
matchLabels:
{{- include "oidc2fer.common.selectorLabels" (list . $component) | nindent 6 }}
{{ end }}
+10 -1
View File
@@ -33,7 +33,9 @@ ingress:
enabled: false
className: null
host: oidc2fer.example.com
path: /
paths:
- path: '/$|^/\.well-known/openid-configuration$|^/images/|^/Saml2/|^/OIDC/|^/ping$'
pathType: ImplementationSpecific # enables regex matching
## @param ingress.hosts Additional host to configure for the Ingress
hosts: []
# - chart-example.local
@@ -48,10 +50,17 @@ ingress:
## @param ingress.customBackends Add custom backends to ingress
customBackends: []
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
## @section satosa
satosa:
## @param satosa.pdb.enabled Enable pdb on backend
pdb:
enabled: true
## @param satosa.command Override the satosa container command
command: []
+2 -1
View File
@@ -67,7 +67,8 @@ disable=bad-inline-option,
useless-suppression,
missing-module-docstring,
missing-class-docstring,
missing-function-docstring
missing-function-docstring,
wrong-import-order
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
+7 -1
View File
@@ -22,9 +22,15 @@ attributes:
acr:
openid:
- acr
eppn:
eduPersonPrincipalName:
saml:
- eduPersonPrincipalName
eduPersonAffiliation:
saml:
- eduPersonAffiliation
uid:
openid:
- uid
siret:
openid:
- siret
@@ -0,0 +1 @@
from .entity_id_to_siret_mapper import EntityIdToSiretMapper
@@ -0,0 +1,23 @@
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)
+9
View File
@@ -0,0 +1,9 @@
import os
from satosa.wsgi import app as satosa_app
from whitenoise import WhiteNoise
# Wrap the SATOSA WSGI app in WhiteNoise to serve static files
app = WhiteNoise(
satosa_app, root=os.path.join(os.curdir, "static"), index_file="index.html"
)
@@ -3,11 +3,11 @@ name: Saml2
config:
disco_srv: !ENV SAML2_DISCOVERY_URL
entityid_endpoint: true
mirror_force_authn: 'no'
memorize_idp: 'no'
use_memorized_idp_when_force_authn: 'no'
send_requester_id: 'no'
enable_metadata_reload: 'no'
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 OIDC vers FER
@@ -23,7 +23,7 @@ config:
# - url: https://mdq.federation.renater.fr
remote:
- url: !ENV SAML2_METADATA_URL
entityid: <base_url>/<name>/proxy_saml2_backend.xml
entityid: !ENV SAML2_ENTITY_ID
accepted_time_diff: 60
allow_unknown_attributes: true
service:
@@ -38,6 +38,7 @@ config:
height: '200'
authn_requests_signed: true
want_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:1.1:nameid-format:emailAddress
@@ -43,6 +43,7 @@ config:
- uid
- given_name
- usual_name
- siret
# Set code/token lifetimes as short as possible
authorization_code_lifetime: 60
@@ -56,8 +57,5 @@ config:
- given_name
usual_name:
- usual_name
extra_id_token_claims:
oidc-test-client:
- acr
agent-connect:
- acr
siret:
- siret
@@ -0,0 +1,20 @@
module: satosa.micro_services.attribute_authorization.AttributeAuthorization
name: AttributeAuthorization
config:
force_attributes_presence_on_allow: true
attribute_allow:
default: # any requester (SP/RP)
default: # any issuer (IdP/OP)
eduPersonPrincipalName:
# The eduPersonPrincipalName value may have been filtered by the
# AttributeFilter processor, if it did not match an expected scope
# for the IdP.
# We require it to be non-empty here, to avoid an unhandled error
# later in the PrimaryIdentifier processor.
- ".+"
eduPersonAffiliation:
- "^faculty$"
- "^staff$"
- "^employee$"
- "^researcher$"
- "^teacher$"
@@ -0,0 +1,12 @@
module: satosa.micro_services.attribute_modifications.FilterAttributeValues
name: AttributeFilter
config:
attribute_filters:
# default rules for any IdentityProvider
"":
# default rules for any requester
"":
eduPersonPrincipalName:
# enforce correct scope (the part after '@' must match one
# of the scopes declared in the metadata)
shibmdscope_match_scope:
@@ -7,7 +7,7 @@ config:
# names are the internal SATOSA names for the attributes as
# defined in internal_attributes.yaml.
ordered_identifier_candidates:
- attribute_names: [eppn]
- attribute_names: [eduPersonPrincipalName]
# The internal SATOSA attribute into which to place the primary
# identifier value once found from the above configured ordered
@@ -0,0 +1,5 @@
module: oidc2fer.attribute_generators.EntityIdToSiretMapper
name: EntityIdToSiretMapper
config:
attribute: siret
mapping_json: !ENV SIRET_MAP
+6 -24
View File
@@ -12,32 +12,14 @@ FRONTEND_MODULES:
- plugins/frontends/openid_connect_frontend.yaml
- plugins/frontends/ping_frontend.yaml
MICRO_SERVICES:
- plugins/microservices/filter_attributes.yaml
- plugins/microservices/attribute_authorization.yaml
- 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
version: 1
formatters:
simple:
format: '[%(asctime)s] [%(levelname)s] [%(name)s.%(funcName)s] %(message)s'
handlers:
stdout:
class: logging.StreamHandler
stream: ext://sys.stdout
level: DEBUG
formatter: simple
loggers:
satosa:
level: DEBUG
saml2:
level: DEBUG
oidcendpoint:
level: DEBUG
pyop:
level: DEBUG
oic:
level: DEBUG
root:
level: DEBUG
handlers:
- stdout
incremental: true
+8 -6
View File
@@ -23,16 +23,18 @@ license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"SATOSA==8.4.0",
"gunicorn==22.0.0",
"SATOSA==8.5.1",
"gunicorn==23.0.0",
"redis==5.0.4",
"JSON-log-formatter==1.0",
"WhiteNoise==6.7.0",
]
[project.urls]
"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"
"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"
[project.optional-dependencies]
dev = [
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

+9
View File
@@ -0,0 +1,9 @@
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="1; url=https://agentconnect.gouv.fr/">
</head>
<body>
<h1>Passerelle ProConnect vers RENATER. Redirection en cours…</h1>
</body>
</html>
@@ -0,0 +1,42 @@
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
+91 -41
View File
@@ -2,7 +2,7 @@ import json
import os
import pytest
from playwright.sync_api import Browser, BrowserContext, Page, expect
from playwright.sync_api import Browser, Page, expect
@pytest.fixture(scope="session")
@@ -10,36 +10,46 @@ def browser_context_args():
return {"locale": "fr-FR"}
def renater_test_idp(page):
page.get_by_label("Nom d'utilisateur").fill("etudiant1")
page.get_by_label("Mot de passe").fill("etudiant1")
def renater_test_idp(page, login):
page.get_by_label("Nom d'utilisateur").fill(login)
page.get_by_label("Mot de passe").fill(login)
page.get_by_label("Afficher les informations qui vont être transférées").check()
page.get_by_role("button", name="Connexion").click()
page.get_by_role("button", name="Accepter").click()
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()
def oidc_to_renater(context: BrowserContext):
with context.new_page() as page:
page.goto("https://oidc-test-client.traefik.me")
renater_wayf(page)
renater_test_idp(page)
def oidc_to_renater(
page: Page,
login="enseignant1",
expected_email="georges.grospieds@formation.renater.fr",
expected_given_name="Georges",
expected_usual_name="Grospieds",
):
page.goto("https://oidc-test-client.traefik.me")
renater_wayf(page)
renater_test_idp(page, login=login)
expect(page.locator("pre")).to_contain_text('"usual_name":"Dupont"')
text = page.inner_text("pre")
result = json.loads(text)
expect(page.locator("pre")).to_contain_text('"usual_name":')
text = page.inner_text("pre")
result = json.loads(text)
id_token = result["access_token_response"]["id_token"]
assert {"acr": "eidas1"}.items() <= id_token.items()
userinfo = result["userinfo"]
assert {
"email": "jean.dupont@formation.renater.fr",
"given_name": "Jean",
"uid": "etudiant1@test-renater.fr",
"usual_name": "Dupont",
"sub": f"{login}@test-renater.fr",
"uid": f"{login}@test-renater.fr",
"email": expected_email,
"given_name": expected_given_name,
"usual_name": expected_usual_name,
"siret": "12345678200010",
}.items() <= userinfo.items()
return id_token
@@ -47,43 +57,83 @@ def oidc_to_renater(context: BrowserContext):
@pytest.mark.skipif(
"TEST_E2E" not in os.environ, reason="Depends on app running locally"
)
def test_oidc_to_renater(browser: Browser):
id_token1 = oidc_to_renater(browser.new_context())
id_token2 = oidc_to_renater(browser.new_context())
def test_oidc_to_renater_keeps_sub(browser: Browser):
with browser.new_context().new_page() as page:
id_token1 = oidc_to_renater(page)
with browser.new_context().new_page() as page:
id_token2 = oidc_to_renater(page)
assert id_token1["sub"] == id_token2["sub"]
def agent_connect_login(page: Page):
@pytest.mark.skipif(
"TEST_E2E" not in os.environ, reason="Depends on app running locally"
)
def test_oidc_to_renater_student_not_allowed(page: Page):
page.goto("https://oidc-test-client.traefik.me")
renater_wayf(page)
renater_test_idp(page, login="etudiant1")
expect(page.locator("pre")).to_contain_text('"error":"access_denied"')
def pro_connect_login(page: Page, email):
page.goto("https://fsa1v2.integ01.dev-agentconnect.fr/")
page.get_by_label("Connexion à AgentConnect").click()
page.get_by_label("Email professionnel").fill("jean.dupont@formation.renater.fr")
page.get_by_role("button", name="Sidentifier avec ProConnect").click()
page.get_by_label("Email professionnel").fill(email)
page.get_by_test_id("interaction-connection-button").click()
def agent_connect_to_renater(context: BrowserContext):
with context.new_page() as page:
agent_connect_login(page)
renater_wayf(page)
renater_test_idp(page)
def pro_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)
renater_wayf(page)
renater_test_idp(page, login=login)
expect(page.locator("body")).to_contain_text("jean.dupont@formation.renater.fr")
text = page.inner_text("#json")
result = json.loads(text)
assert {
"email": "jean.dupont@formation.renater.fr",
"given_name": "Jean",
"uid": "etudiant1@test-renater.fr",
"usual_name": "Dupont",
}.items() <= result.items()
return result
expect(page.locator("body")).to_contain_text(expected_email)
text = page.inner_text("#userinfo")
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_AC" not in os.environ, reason="Depends on staging deployment"
"TEST_E2E_PC" not in os.environ, reason="Depends on staging deployment"
)
def test_agent_connect_to_renater(browser: Browser):
result1 = agent_connect_to_renater(browser.new_context())
result2 = agent_connect_to_renater(browser.new_context())
def test_pro_connect_to_renater_keeps_sub(browser: Browser):
with browser.new_context().new_page() as page:
result1 = pro_connect_to_renater(page)
with browser.new_context().new_page() as page:
result2 = pro_connect_to_renater(page)
assert result1["sub"] == result2["sub"]
@pytest.mark.skipif(
"TEST_E2E_PC" 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")
renater_wayf(page)
renater_test_idp(page, login="etudiant1")
expect(page.locator("body")).to_contain_text("access_denied")
@pytest.mark.skipif(
"TEST_E2E" not in os.environ, reason="Depends on app running locally"
)
def test_serve_static_assets(page: Page):
page.goto("https://satosa.traefik.me/images/logo.svg")
expect(page.locator("svg")).to_be_visible()