🔒️(saml) use eduPersonPrincipalName rather than NameID for sub

The NameID returned by the SAML server is actually transient so we can't
use it as a sub. We'll use eduPersonPrincipalName both in sub and uid.
This commit is contained in:
Jonathan Perret
2024-05-15 22:14:37 +02:00
parent 7239b366e8
commit 96e244fd6c
6 changed files with 77 additions and 27 deletions
+7 -1
View File
@@ -131,7 +131,13 @@ oidc-test: ## open OIDC test client in browser
@$(MAKE) down
@$(MAKE) run
python -mwebbrowser -n https://oidc-test-client.traefik.me
.PHONY: oidc-test
.phony: oidc-test
e2e-test: ## automated end-to-end test in browser
@$(MAKE) down
@$(MAKE) run
TEST_E2E=1 pytest
.phony: e2e-test
env.d/development/common:
cp -n env.d/development/common.dist env.d/development/common
+1
View File
@@ -7,6 +7,7 @@ services:
DOCKER_USER: ${DOCKER_USER:-1000}
user: ${DOCKER_USER:-1000}
image: oidc2fer:development
pull_policy: never
environment:
PYLINTHOME: /app/.pylint.d
BASE_URL: https://satosa.traefik.me
+3 -2
View File
@@ -22,8 +22,9 @@ attributes:
acr:
openid:
- acr
eppn:
saml:
- eduPersonPrincipalName
uid:
openid:
- uid
saml:
- eduPersonPrincipalName
@@ -0,0 +1,22 @@
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: [eppn]
# 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
View File
@@ -12,6 +12,7 @@ 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:
+28 -9
View File
@@ -2,7 +2,7 @@ import json
import os
import pytest
from playwright.sync_api import Page, expect
from playwright.sync_api import Browser, BrowserContext, Page, expect
@pytest.fixture(scope="session")
@@ -22,10 +22,8 @@ def renater_wayf(page):
page.get_by_role("button", name="Sélection").click()
@pytest.mark.skipif(
"TEST_E2E" not in os.environ, reason="Depends on app running locally"
)
def test_oidc_to_renater(page: Page):
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)
@@ -33,6 +31,7 @@ def test_oidc_to_renater(page: Page):
expect(page.locator("pre")).to_contain_text('"usual_name":"Dupont"')
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"]
@@ -42,6 +41,17 @@ def test_oidc_to_renater(page: Page):
"uid": "etudiant1@test-renater.fr",
"usual_name": "Dupont",
}.items() <= userinfo.items()
return id_token
@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())
assert id_token1["sub"] == id_token2["sub"]
def agent_connect_login(page: Page):
@@ -51,10 +61,8 @@ def agent_connect_login(page: Page):
page.get_by_test_id("interaction-connection-button").click()
@pytest.mark.skipif(
"TEST_E2E_AC" not in os.environ, reason="Depends on staging deployment"
)
def test_agent_connect_to_renater(page: Page):
def agent_connect_to_renater(context: BrowserContext):
with context.new_page() as page:
agent_connect_login(page)
renater_wayf(page)
renater_test_idp(page)
@@ -68,3 +76,14 @@ def test_agent_connect_to_renater(page: Page):
"uid": "etudiant1@test-renater.fr",
"usual_name": "Dupont",
}.items() <= result.items()
return result
@pytest.mark.skipif(
"TEST_E2E_AC" 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())
assert result1["sub"] == result2["sub"]