Option to not use OIDC

This commit is contained in:
Éloi Rivard 2021-12-07 00:07:32 +01:00
parent f718ed7fae
commit 3645171dd8
7 changed files with 47 additions and 27 deletions

View file

@ -82,7 +82,7 @@ GROUP_USER_FILTER = "member={user.dn}"
# You can define access controls that define what users can do on canaille
# An access control consists in a FILTER to match users, a list of PERMISSIONS
# matched users will be able to perform, and fields users will be able
# to READ and WRITE.
# to READ and WRITE. Users matching several filters will cumulate permissions.
#
# A 'FILTER' parameter that is a LDAP filter used to determine if a user
# belongs to an access control. If absent, all the users will match this
@ -94,9 +94,10 @@ GROUP_USER_FILTER = "member={user.dn}"
#
# The 'PERMISSIONS' parameter that is an list of items the users in the access
# control will be able to manage. 'PERMISSIONS' is optionnal. Values can be:
# - "use_oidc" to allow OpenID Connect authentication
# - "manage_oidc" to allow OpenID Connect client managements
# - "manage_users" to allow other users management
# - "manage_groups" to allow group edition and creation
# - "manage_oidc" to allow OpenID Connect client managements
# - "delete_account" allows a user to delete his own account. If used with
# manage_users, the user can delete any account
# - "impersonate_users" to allow a user to take the identity of another user
@ -105,6 +106,7 @@ GROUP_USER_FILTER = "member={user.dn}"
# object that users will be able to read and/or write.
[ACL.DEFAULT]
READ = ["uid", "groups"]
PERMISSIONS = ["use_oidc"]
WRITE = ["givenName", "sn", "userPassword", "telephoneNumber"]
[ACL.ADMIN]
@ -116,8 +118,7 @@ PERMISSIONS = [
"delete_account",
"impersonate_users",
]
READ = ["uid"]
WRITE = ["groups", "givenName", "sn", "userPassword"]
WRITE = ["groups"]
# The jwt configuration. You can generate a RSA keypair with:
# openssl genrsa -out private.pem 4096

View file

@ -150,6 +150,10 @@ class User(LDAPObject):
self.read |= set(details.get("READ", []))
self.write |= set(details.get("WRITE", []))
@property
def can_use_oidc(self):
return "use_oidc" in self.permissions
@property
def can_manage_users(self):
return "manage_users" in self.permissions

View file

@ -76,6 +76,9 @@ def authorize():
return redirect(request.url)
if not user.can_use_oidc:
abort(400)
# CONSENT
consents = Consent.filter(

View file

@ -38,11 +38,13 @@
<i class="id card icon"></i>
{% trans %}My profile{% endtrans %}
</a>
{% if user.can_use_oidc %}
<a class="item {% if menuitem == "consents" %}active{% endif %}"
href="{{ url_for('consents.consents') }}">
<i class="handshake icon"></i>
{% trans %}My consents{% endtrans %}
</a>
{% endif %}
{% if user.can_manage_users %}
<a class="item {% if menuitem == "users" %}active{% endif %}"
href="{{ url_for('account.users') }}">

View file

@ -84,7 +84,7 @@ GROUP_USER_FILTER = "member={user.dn}"
# You can define access controls that define what users can do on canaille
# An access control consists in a FILTER to match users, a list of PERMISSIONS
# matched users will be able to perform, and fields users will be able
# to READ and WRITE.
# to READ and WRITE. Users matching several filters will cumulate permissions.
#
# A 'FILTER' parameter that is a LDAP filter used to determine if a user
# belongs to an access control. If absent, all the users will match this
@ -96,9 +96,10 @@ GROUP_USER_FILTER = "member={user.dn}"
#
# The 'PERMISSIONS' parameter that is an list of items the users in the access
# control will be able to manage. 'PERMISSIONS' is optionnal. Values can be:
# - "use_oidc" to allow OpenID Connect authentication
# - "manage_oidc" to allow OpenID Connect client managements
# - "manage_users" to allow other users management
# - "manage_groups" to allow group edition and creation
# - "manage_oidc" to allow OpenID Connect client managements
# - "delete_account" allows a user to delete his own account. If used with
# manage_users, the user can delete any account
# - "impersonate_users" to allow a user to take the identity of another user
@ -107,6 +108,7 @@ GROUP_USER_FILTER = "member={user.dn}"
# object that users will be able to read and/or write.
[ACL.DEFAULT]
READ = ["uid", "groups"]
PERMISSIONS = ["use_oidc"]
WRITE = ["givenName", "sn", "userPassword", "telephoneNumber"]
[ACL.ADMIN]
@ -118,14 +120,12 @@ PERMISSIONS = [
"delete_account",
"impersonate_users",
]
READ = ["uid"]
WRITE = ["groups", "givenName", "sn", "userPassword"]
WRITE = ["groups"]
[ACL.HALF_ADMIN]
FILTER = "memberof=cn=moderators,ou=groups,dc=mydomain,dc=tld"
PERMISSIONS = ["manage_users", "manage_groups", "delete_account"]
READ = ["uid"]
WRITE = ["groups", "givenName", "sn", "userPassword"]
WRITE = ["groups"]
# The jwt configuration. You can generate a RSA keypair with:
# openssl genrsa -out private.pem 4096

View file

@ -147,7 +147,7 @@ def configuration(slapd_server, smtpd, keypair_path):
"ACL": {
"DEFAULT": {
"READ": ["uid", "groups"],
"PERMISSIONS": [],
"PERMISSIONS": ["use_oidc"],
"WRITE": [
"mail",
"givenName",
@ -166,28 +166,14 @@ def configuration(slapd_server, smtpd, keypair_path):
"impersonate_users",
"manage_groups",
],
"READ": ["uid"],
"WRITE": [
"mail",
"givenName",
"sn",
"userPassword",
"telephoneNumber",
"employeeNumber",
"groups",
],
},
"MODERATOR": {
"FILTER": "(|(uid=moderator)(sn=moderator))",
"PERMISSIONS": ["manage_users", "manage_groups", "delete_account"],
"READ": ["uid"],
"WRITE": [
"mail",
"givenName",
"sn",
"userPassword",
"telephoneNumber",
"employeeNumber",
"groups",
],
},

View file

@ -438,6 +438,30 @@ def test_authorization_code_flow_when_consent_already_given_but_for_a_smaller_sc
assert "groups" in consents[0].oauthScope
def test_authorization_code_flow_but_user_cannot_use_oidc(
testclient, slapd_connection, user, client, keypair, other_client
):
testclient.app.config["ACL"]["DEFAULT"]["PERMISSIONS"] = []
res = testclient.get(
"/oauth/authorize",
params=dict(
response_type="code",
client_id=client.oauthClientID,
scope="profile",
nonce="somenonce",
),
status=200,
)
res.form["login"] = "John (johnny) Doe"
res = res.form.submit(status=200)
res.form["password"] = "correct horse battery staple"
res = res.form.submit(status=302)
res = res.follow(status=400)
def test_prompt_none(testclient, slapd_connection, logged_user, client):
Consent(
oauthClient=client.dn,