Skip to content
Snippets Groups Projects
Verified Commit 62413fa6 authored by nd's avatar nd
Browse files

Merge branch 'master' of git.cccv.de:infra/uffd/uffd

parents e66441a7 8aebf783
No related branches found
No related tags found
No related merge requests found
Checking pipeline status
......@@ -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"
......
......@@ -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
#!/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()
......@@ -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)
......
......@@ -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()]
)
......
......@@ -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
......@@ -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
......
......@@ -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 %}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment