forked from Github-Mirrors/canaille
Client only return the asked scopes
This commit is contained in:
parent
c8281969d8
commit
21a2c306ac
9 changed files with 130 additions and 20 deletions
|
@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file.
|
|||
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>`_.
|
||||
|
||||
[0.0.10] - Unreleased
|
||||
=====================
|
||||
|
||||
Fixed
|
||||
*****
|
||||
|
||||
- The consent page was displaying scopes not supported by clients. :pr:`56`
|
||||
|
||||
[0.0.9] - 2022-06-05
|
||||
====================
|
||||
|
||||
|
|
|
@ -42,7 +42,9 @@ class Client(LDAPObject, ClientMixin):
|
|||
return self.redirect_uris[0]
|
||||
|
||||
def get_allowed_scope(self, scope):
|
||||
return util.list_to_scope(self.scope)
|
||||
return util.list_to_scope(
|
||||
[scope_piece for scope_piece in self.scope if scope_piece in scope]
|
||||
)
|
||||
|
||||
def check_redirect_uri(self, redirect_uri):
|
||||
return redirect_uri in self.redirect_uris
|
||||
|
@ -99,7 +101,7 @@ class AuthorizationCode(LDAPObject, AuthorizationCodeMixin):
|
|||
return self.redirect_uri
|
||||
|
||||
def get_scope(self):
|
||||
return self.scope
|
||||
return self.scope[0].split(" ")
|
||||
|
||||
def get_nonce(self):
|
||||
return self.nonce
|
||||
|
|
|
@ -70,7 +70,9 @@ def authorize():
|
|||
abort(400)
|
||||
|
||||
user = current_user()
|
||||
scopes = request.args.get("scope", "").split(" ")
|
||||
scopes = client.get_allowed_scope(request.args.get("scope", "").split(" ")).split(
|
||||
" "
|
||||
)
|
||||
|
||||
# LOGIN
|
||||
|
||||
|
@ -143,9 +145,10 @@ def authorize():
|
|||
grant_user = user.dn
|
||||
|
||||
if consent:
|
||||
consent.scope = list(set(scopes + consents[0].scope))
|
||||
consent.scope = client.get_allowed_scope(
|
||||
list(set(scopes + consents[0].scope))
|
||||
).split(" ")
|
||||
else:
|
||||
|
||||
consent = Consent(
|
||||
client=client.dn,
|
||||
subject=user.dn,
|
||||
|
|
|
@ -107,13 +107,14 @@ def generate_user_claims(user, claims, jwt_mapping_config=None):
|
|||
def save_authorization_code(code, request):
|
||||
nonce = request.data.get("nonce")
|
||||
now = datetime.datetime.now()
|
||||
scope = request.client.get_allowed_scope(request.scope)
|
||||
code = AuthorizationCode(
|
||||
authorization_code_id=gen_salt(48),
|
||||
code=code,
|
||||
subject=request.user,
|
||||
client=request.client.dn,
|
||||
redirect_uri=request.redirect_uri or request.client.redirect_uris[0],
|
||||
scope=request.scope,
|
||||
scope=scope,
|
||||
nonce=nonce,
|
||||
issue_date=now,
|
||||
lifetime=str(84000),
|
||||
|
|
|
@ -33,7 +33,7 @@ def client(testclient, other_client, slapd_connection):
|
|||
"refresh_token",
|
||||
],
|
||||
response_type=["code", "token", "id_token"],
|
||||
scope=["openid", "profile", "groups"],
|
||||
scope=["openid", "email", "profile", "groups", "address", "phone"],
|
||||
tos_uri="https://mydomain.tld/tos",
|
||||
policy_uri="https://mydomain.tld/policy",
|
||||
jwk_uri="https://mydomain.tld/jwk",
|
||||
|
|
|
@ -22,7 +22,7 @@ def test_authorization_code_flow(
|
|||
params=dict(
|
||||
response_type="code",
|
||||
client_id=client.client_id,
|
||||
scope="openid profile",
|
||||
scope="openid profile email groups address phone",
|
||||
nonce="somenonce",
|
||||
),
|
||||
status=200,
|
||||
|
@ -35,16 +35,31 @@ def test_authorization_code_flow(
|
|||
code = params["code"][0]
|
||||
authcode = AuthorizationCode.get(code=code)
|
||||
assert authcode is not None
|
||||
assert set(authcode.scope[0].split(" ")) == {
|
||||
"openid",
|
||||
"profile",
|
||||
"email",
|
||||
"groups",
|
||||
"address",
|
||||
"phone",
|
||||
}
|
||||
|
||||
consents = Consent.filter(client=client.dn, subject=logged_user.dn)
|
||||
assert "profile" in consents[0].scope
|
||||
assert set(consents[0].scope) == {
|
||||
"openid",
|
||||
"profile",
|
||||
"email",
|
||||
"groups",
|
||||
"address",
|
||||
"phone",
|
||||
}
|
||||
|
||||
res = testclient.post(
|
||||
"/oauth/token",
|
||||
params=dict(
|
||||
grant_type="authorization_code",
|
||||
code=code,
|
||||
scope="openid profile",
|
||||
scope="openid profile email groups address phone",
|
||||
redirect_uri=client.redirect_uris[0],
|
||||
),
|
||||
headers={"Authorization": f"Basic {client_credentials(client)}"},
|
||||
|
@ -55,6 +70,14 @@ def test_authorization_code_flow(
|
|||
token = Token.get(access_token=access_token)
|
||||
assert token.client == client.dn
|
||||
assert token.subject == logged_user.dn
|
||||
assert set(token.scope[0].split(" ")) == {
|
||||
"openid",
|
||||
"profile",
|
||||
"email",
|
||||
"groups",
|
||||
"address",
|
||||
"phone",
|
||||
}
|
||||
|
||||
id_token = res.json["id_token"]
|
||||
claims = jwt.decode(id_token, keypair[1])
|
||||
|
@ -70,6 +93,7 @@ def test_authorization_code_flow(
|
|||
assert {
|
||||
"name": "John (johnny) Doe",
|
||||
"family_name": "Doe",
|
||||
"email": "john@doe.com",
|
||||
"sub": "user",
|
||||
"groups": [],
|
||||
} == res.json
|
||||
|
@ -138,9 +162,11 @@ def test_authorization_code_flow_preconsented(
|
|||
"name": "John (johnny) Doe",
|
||||
"family_name": "Doe",
|
||||
"sub": "user",
|
||||
"groups": [],
|
||||
} == res.json
|
||||
|
||||
for consent in consents:
|
||||
consent.delete()
|
||||
|
||||
|
||||
def test_logout_login(testclient, logged_user, client):
|
||||
assert not Consent.all()
|
||||
|
@ -206,7 +232,6 @@ def test_logout_login(testclient, logged_user, client):
|
|||
"name": "John (johnny) Doe",
|
||||
"family_name": "Doe",
|
||||
"sub": "user",
|
||||
"groups": [],
|
||||
} == res.json
|
||||
|
||||
for consent in consents:
|
||||
|
@ -288,7 +313,6 @@ def test_refresh_token(testclient, user, client):
|
|||
"name": "John (johnny) Doe",
|
||||
"family_name": "Doe",
|
||||
"sub": "user",
|
||||
"groups": [],
|
||||
} == res.json
|
||||
|
||||
for consent in consents:
|
||||
|
@ -355,7 +379,6 @@ def test_code_challenge(testclient, logged_user, client):
|
|||
"name": "John (johnny) Doe",
|
||||
"family_name": "Doe",
|
||||
"sub": "user",
|
||||
"groups": [],
|
||||
} == res.json
|
||||
|
||||
client.token_endpoint_auth_method = "client_secret_basic"
|
||||
|
@ -595,6 +618,7 @@ def test_nonce_required_in_oidc_requests(testclient, logged_user, client):
|
|||
|
||||
|
||||
def test_nonce_not_required_in_oauth_requests(testclient, logged_user, client):
|
||||
assert not Consent.all()
|
||||
testclient.app.config["REQUIRE_NONCE"] = False
|
||||
|
||||
res = testclient.get(
|
||||
|
@ -610,3 +634,79 @@ def test_nonce_not_required_in_oauth_requests(testclient, logged_user, client):
|
|||
res = res.form.submit(name="answer", value="accept", status=302)
|
||||
|
||||
assert res.location.startswith(client.redirect_uris[0])
|
||||
for consent in Consent.all():
|
||||
consent.delete()
|
||||
|
||||
|
||||
def test_authorization_code_request_scope_too_large(
|
||||
testclient, logged_user, keypair, other_client
|
||||
):
|
||||
assert not Consent.all()
|
||||
assert "email" not in other_client.scope
|
||||
|
||||
res = testclient.get(
|
||||
"/oauth/authorize",
|
||||
params=dict(
|
||||
response_type="code",
|
||||
client_id=other_client.client_id,
|
||||
scope="openid profile email",
|
||||
nonce="somenonce",
|
||||
),
|
||||
status=200,
|
||||
)
|
||||
|
||||
res = res.form.submit(name="answer", value="accept", status=302)
|
||||
|
||||
params = parse_qs(urlsplit(res.location).query)
|
||||
code = params["code"][0]
|
||||
authcode = AuthorizationCode.get(code=code)
|
||||
assert set(authcode.scope[0].split(" ")) == {
|
||||
"openid",
|
||||
"profile",
|
||||
}
|
||||
|
||||
consents = Consent.filter(client=other_client.dn, subject=logged_user.dn)
|
||||
assert set(consents[0].scope) == {
|
||||
"openid",
|
||||
"profile",
|
||||
}
|
||||
|
||||
res = testclient.post(
|
||||
"/oauth/token",
|
||||
params=dict(
|
||||
grant_type="authorization_code",
|
||||
code=code,
|
||||
scope="openid profile email groups address phone",
|
||||
redirect_uri=other_client.redirect_uris[0],
|
||||
),
|
||||
headers={"Authorization": f"Basic {client_credentials(other_client)}"},
|
||||
status=200,
|
||||
)
|
||||
|
||||
access_token = res.json["access_token"]
|
||||
token = Token.get(access_token=access_token)
|
||||
assert token.client == other_client.dn
|
||||
assert token.subject == logged_user.dn
|
||||
assert set(token.scope[0].split(" ")) == {
|
||||
"openid",
|
||||
"profile",
|
||||
}
|
||||
|
||||
id_token = res.json["id_token"]
|
||||
claims = jwt.decode(id_token, keypair[1])
|
||||
assert logged_user.uid[0] == claims["sub"]
|
||||
assert logged_user.cn[0] == claims["name"]
|
||||
|
||||
res = testclient.get(
|
||||
"/oauth/userinfo",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
status=200,
|
||||
)
|
||||
assert {
|
||||
"name": "John (johnny) Doe",
|
||||
"family_name": "Doe",
|
||||
"sub": "user",
|
||||
} == res.json
|
||||
|
||||
for consent in consents:
|
||||
consent.delete()
|
||||
|
|
|
@ -50,7 +50,6 @@ def test_oauth_hybrid(testclient, slapd_connection, user, client):
|
|||
"name": "John (johnny) Doe",
|
||||
"family_name": "Doe",
|
||||
"sub": "user",
|
||||
"groups": [],
|
||||
} == res.json
|
||||
|
||||
|
||||
|
@ -96,5 +95,4 @@ def test_oidc_hybrid(
|
|||
"name": "John (johnny) Doe",
|
||||
"family_name": "Doe",
|
||||
"sub": "user",
|
||||
"groups": [],
|
||||
} == res.json
|
||||
|
|
|
@ -46,7 +46,6 @@ def test_oauth_implicit(testclient, user, client):
|
|||
"name": "John (johnny) Doe",
|
||||
"sub": "user",
|
||||
"family_name": "Doe",
|
||||
"groups": [],
|
||||
} == res.json
|
||||
|
||||
client.grant_type = ["code"]
|
||||
|
@ -103,7 +102,6 @@ def test_oidc_implicit(testclient, keypair, user, client, other_client):
|
|||
"name": "John (johnny) Doe",
|
||||
"sub": "user",
|
||||
"family_name": "Doe",
|
||||
"groups": [],
|
||||
} == res.json
|
||||
|
||||
client.grant_type = ["code"]
|
||||
|
|
|
@ -10,7 +10,7 @@ def test_password_flow_basic(testclient, user, client):
|
|||
grant_type="password",
|
||||
username="John (johnny) Doe",
|
||||
password="correct horse battery staple",
|
||||
scope="profile",
|
||||
scope="openid profile groups",
|
||||
),
|
||||
headers={"Authorization": f"Basic {client_credentials(client)}"},
|
||||
status=200,
|
||||
|
@ -46,7 +46,7 @@ def test_password_flow_post(testclient, user, client):
|
|||
grant_type="password",
|
||||
username="John (johnny) Doe",
|
||||
password="correct horse battery staple",
|
||||
scope="profile",
|
||||
scope="openid profile groups",
|
||||
client_id=client.client_id,
|
||||
client_secret=client.secret,
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue