diff --git a/README.md b/README.md index 45eeac10b3bc7ea60db19a0ff78777098f812c50..859d4e16ccbc75f9bc331c0b77a7cd4bbfe534ba 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,9 @@ A web service to manage LDAP users, groups and permissions. - python3-flask-oauthlib - git (cli utility, musst be in path) +Some of the dependencies (especially fido2 and flask-oauthlib) changed their API in recent versions, so make sure to install the versions from Debian Buster. +You can also use virtualenv with the supplied `requirements.txt`. + ## development During development, you may want to enable LDAP mocking, as you otherwise need to have access to an actual LDAP server with the required schema. @@ -46,6 +49,7 @@ The userinfo endpoint returns json data with the following structure: "name": "Test User", "nickname": "testuser" "email": "testuser@example.com", + "ldap_dn": "uid=testuser,ou=users,dc=example,dc=com", "groups": [ "uffd_access", "users" diff --git a/requirements.txt b/requirements.txt index 347cf2a9073124b259908bfa7bb8f851ce0af5ef..e5d9378ef3c596fc10d4a4efae409a99ef76774a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,33 @@ Flask-SQLAlchemy==2.1 qrcode==6.1 fido2==0.5.0 Flask-OAuthlib==0.9.5 + +# The main dependencies on their own lead to version collisions and pip is +# not very good at resolving them, so we pin the versions from Debian Buster +# for all dependencies. +certifi==2018.8.24 +cffi==1.12.2 +chardet==3.0.4 +click==7.0 +cryptography==2.6.1 +idna==2.6 +itsdangerous==0.24 +Jinja2==2.10 +MarkupSafe==1.1.0 +oauthlib==2.1.0 +pyasn1==0.4.2 +pycparser==2.19 +requests==2.21.0 +requests-oauthlib==1.0.0 +six==1.12.0 +SQLAlchemy==1.2.18 +urllib3==1.24.1 +Werkzeug==0.14.1 + +# Testing +pytest==3.10.1 +atomicwrites==1.1.5 +attrs==18.2.0 +more-itertools==4.2.0 +pluggy==0.8.0 +py==1.7.0 diff --git a/runTests.py b/runTests.py deleted file mode 100755 index fabbb5e767c8f2bed38e1f4227e0e2f92185c347..0000000000000000000000000000000000000000 --- a/runTests.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 -import unittest -import os -from src import server - -def setUp(): - server.app.testing = True - -def tearDown(): - os.unlink(server.app.config['SQLITE_DB']) - -if __name__ == '__main__': - setUp() - try: - suite = unittest.defaultTestLoader.discover('./tests/', pattern="*") - unittest.TextTestRunner(verbosity=2, failfast=True).run(suite) - finally: - tearDown() - diff --git a/tests/test_oauth2.py b/tests/test_oauth2.py index c5d024eca7f63b9fb58538836d99bba8efcf66f9..1d9751b7faa8494c475948868b9af773217cb81f 100644 --- a/tests/test_oauth2.py +++ b/tests/test_oauth2.py @@ -87,6 +87,10 @@ class TestViews(UffdTestCase): data={'loginname': 'testuser', 'password': 'userpassword'}, follow_redirects=True) state = 'teststate' r = self.client.get(path=url_for('oauth2.authorize', response_type='code', client_id='test', state=state, redirect_uri='http://localhost:5009/callback'), follow_redirects=False) + while True: + if r.status_code != 302 or r.location.startswith('http://localhost:5009/callback'): + break + r = self.client.get(r.location, follow_redirects=False) self.assertEqual(r.status_code, 302) self.assertTrue(r.location.startswith('http://localhost:5009/callback')) args = parse_qs(urlparse(r.location).query) diff --git a/uffd/oauth2/views.py b/uffd/oauth2/views.py index 011a956acc0c7b6434758049d83fbf25930012ce..a054b0d1ad25d1326395a8d5b42505f6c3449c8e 100644 --- a/uffd/oauth2/views.py +++ b/uffd/oauth2/views.py @@ -3,7 +3,6 @@ import functools import urllib.parse from flask import Blueprint, request, jsonify, render_template, session, redirect -from werkzeug.datastructures import ImmutableMultiDict from flask_oauthlib.provider import OAuth2Provider @@ -101,13 +100,15 @@ def token(): @oauth.require_oauth('profile') def userinfo(): user = request.oauth.user + # We once exposed the entryUUID here as "ldap_uuid" until realising that it + # can (and does!) change randomly and is therefore entirely useless as an + # indentifier. return jsonify( id=user.uid, name=user.displayname, nickname=user.loginname, email=user.mail, ldap_dn=user.dn, - ldap_uuid=user.uuid, groups=[group.name for group in user.get_groups()] ) diff --git a/uffd/selfservice/templates/newuser.mail.txt b/uffd/selfservice/templates/newuser.mail.txt index afae818151964b3c1882bf6f5923b68d273dfcd2..7ef8a46964ae799078e8e2b0c108bd78cf9b235f 100644 --- a/uffd/selfservice/templates/newuser.mail.txt +++ b/uffd/selfservice/templates/newuser.mail.txt @@ -4,7 +4,9 @@ welcome to the CCCV infrastructure. An account was created for you, visit this url to set your password: {{ url_for('selfservice.token_password', token=token, _external=True) }} **Please note this link is only valid for 48h** +You can find more information here: https://docs.cccv.de/ + If you have no idea why someone would create an account for you to be used for the next CCC event organization, please contact it@cccv.de. Kind regards, -uffd +uffd \ No newline at end of file diff --git a/uffd/user/models.py b/uffd/user/models.py index 277e694260f9c20d83a953db09d36d09ec71a936..5158b32305162f164178dee5618d9e21c44b9c6e 100644 --- a/uffd/user/models.py +++ b/uffd/user/models.py @@ -7,14 +7,13 @@ from flask import current_app from uffd import ldap class User(): - def __init__(self, uid=None, loginname='', displayname='', mail='', groups=None, dn=None, uuid=None): # pylint: disable=too-many-arguments + def __init__(self, uid=None, loginname='', displayname='', mail='', groups=None, dn=None): self.uid = uid self.loginname = loginname self.displayname = displayname self.mail = mail self.newpassword = None self.dn = dn - self.uuid = uuid self.groups_ldap = groups or [] self.initial_groups_ldap = groups or [] @@ -30,12 +29,6 @@ class User(): mail=ldapobject['mail'].value, groups=ldap.get_ldap_array_attribute_safe(ldapobject, 'memberOf'), dn=ldapobject.entry_dn, - # The LDAP mock does not generate UUIDs for newly created LDAP objects, - # so we use a dummy value if the attribute is missing (only for testing!) - uuid=ldapobject['entryUUID'].value \ - if 'entryUUID' in ldapobject.entry_attributes_as_dict \ - or not current_app.config.get('LDAP_SERVICE_MOCK', False) \ - else '00000000-0000-0000-0000-000000000000' ) @classmethod diff --git a/uffd/user/templates/user.html b/uffd/user/templates/user.html index 1411c9f12269143f62b7c80bfe05704ca5337651..3458d375178a0385375c6f218277ca5902c0b9fa 100644 --- a/uffd/user/templates/user.html +++ b/uffd/user/templates/user.html @@ -8,7 +8,7 @@ <a href="{{ url_for("user.index") }}" class="btn btn-secondary">Cancel</a> {% if user.uid %} <a href="{{ url_for("mfa.admin_disable", uid=user.uid) }}" class="btn btn-secondary">Reset 2FA</a> - <a href="{{ url_for("user.delete", uid=user.uid) }}" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i> Delete</a> + <a href="{{ url_for("user.delete", uid=user.uid) }}" onClick="return confirm('Are you sure?');" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i> Delete</a> {% else %} <a href="#" class="btn btn-danger disabled"><i class="fa fa-trash" aria-hidden="true"></i> Delete</a> {% endif %}