diff --git a/uffd/default_config.cfg b/uffd/default_config.cfg index fb837a154ec9bb2f1a739ceb6406c38ce77fd18f..dde6df5d5ec981079c80e6c3fdd1d56f71569b92 100644 --- a/uffd/default_config.cfg +++ b/uffd/default_config.cfg @@ -61,6 +61,8 @@ MAIL_FROM_ADDRESS='foo@bar.com' # Do not enable this on a public service! There is no spam protection implemented at the moment. SELF_SIGNUP=False +LOGINNAME_BLACKLIST=['^admin$', '^root$'] + #MFA_ICON_URL = 'https://example.com/logo.png' #MFA_RP_ID = 'example.com' # If unset, hostname from current request is used MFA_RP_NAME = 'Uffd Test Service' # Service name passed to U2F/FIDO2 authenticators diff --git a/uffd/user/models.py b/uffd/user/models.py index e9f0ea7a64afcd4ca5d7769aa58c2f70d578dd3e..cb44990a42b63b20030b7e1e4e691b8666de039e 100644 --- a/uffd/user/models.py +++ b/uffd/user/models.py @@ -1,5 +1,6 @@ import secrets import string +import re from flask import current_app from ldap3.utils.hashed import hashed, HASHED_SALTED_SHA512 @@ -87,12 +88,16 @@ class BaseUser(ldap.Model): return True return False - def set_loginname(self, value): + def set_loginname(self, value, ignore_blacklist=False): if len(value) > 32 or len(value) < 1: return False for char in value: if not char in string.ascii_lowercase + string.digits + '_-': return False + if not ignore_blacklist: + for expr in current_app.config['LOGINNAME_BLACKLIST']: + if re.match(expr, value): + return False self.loginname = value return True diff --git a/uffd/user/templates/user.html b/uffd/user/templates/user.html index e289ea8d38c9d41936340c0ea106ca03dd97b64b..b2feedf9a0417e59636d3c19bc8303342a211ea2 100644 --- a/uffd/user/templates/user.html +++ b/uffd/user/templates/user.html @@ -29,28 +29,36 @@ <div class="form-group col"> <label for="user-uid">uid</label> {% if user.uid %} - <input type="number" class="form-control" id="user-uid" name="uid" value="{{ user.uid }}" readonly> + <input type="number" class="form-control" id="user-uid" name="uid" value="{{ user.uid or '' }}" readonly> {% else %} <input type="text" class="form-control" id="user-uid" name="uid" placeholder="will be choosen" readonly> {% endif %} </div> <div class="form-group col"> <label for="user-loginname">Login Name</label> - <input type="text" class="form-control" id="user-loginname" name="loginname" value="{{ user.loginname }}" {% if user.uid %}readonly{% endif %}> + <input type="text" class="form-control" id="user-loginname" name="loginname" value="{{ user.loginname or '' }}" {% if user.uid %}readonly{% endif %}> <small class="form-text text-muted"> Only letters, numbers and underscore ("_") are allowed. At most 32, at least 2 characters. There is a word blacklist. Musst be unique. </small> </div> + {% if not user.uid %} + <div class="form-group col"> + <div class="form-check"> + <input class="form-check-input" type="checkbox" id="ignore-loginname-blacklist" name="ignore-loginname-blacklist" value="1" aria-label="enabled"> + <label class="form-check-label" for="ignore-loginname-blacklist">Ignore login name blacklist</label> + </div> + </div> + {% endif %} <div class="form-group col"> <label for="user-loginname">Display Name</label> - <input type="text" class="form-control" id="user-displayname" name="displayname" value="{{ user.displayname }}"> + <input type="text" class="form-control" id="user-displayname" name="displayname" value="{{ user.displayname or '' }}"> <small class="form-text text-muted"> If you leave this empty it will be set to the login name. At most 128, at least 2 characters. No character restrictions. </small> </div> <div class="form-group col"> <label for="user-mail">Mail</label> - <input type="email" class="form-control" id="user-mail" name="mail" value="{{ user.mail }}"> + <input type="email" class="form-control" id="user-mail" name="mail" value="{{ user.mail or '' }}"> <small class="form-text text-muted"> Do a sanity check here. A user can take over another account if both have the same mail address set. </small> diff --git a/uffd/user/templates/user_list.html b/uffd/user/templates/user_list.html index 9e420323bf8cbe7824d4a76a93a9a9dfa49e30bc..ddcaab3e89023d8fce96db1bf59a7b45bf5eeccd 100644 --- a/uffd/user/templates/user_list.html +++ b/uffd/user/templates/user_list.html @@ -69,6 +69,10 @@ testuser5,foobadfar@example.com,0;5;2 testuser2,foobaadsfr@example.com,5;2 </pre> <textarea rows="10" class="form-control" name="csv"></textarea> + <div class="form-check mt-2"> + <input class="form-check-input" type="checkbox" id="ignore-loginname-blacklist" name="ignore-loginname-blacklist" value="1" aria-label="enabled"> + <label class="form-check-label" for="ignore-loginname-blacklist">Ignore login name blacklist</label> + </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> diff --git a/uffd/user/views_user.py b/uffd/user/views_user.py index 78ad85c26074927d79dda1fbb123f0f972125690..9a824945ab262653acf416b5d2705db8ed5581a2 100644 --- a/uffd/user/views_user.py +++ b/uffd/user/views_user.py @@ -41,7 +41,8 @@ def show(uid=None): def update(uid=None): if uid is None: user = User() - if not user.set_loginname(request.form['loginname']): + ignore_blacklist = request.form.get('ignore-loginname-blacklist', False) + if not user.set_loginname(request.form['loginname'], ignore_blacklist=ignore_blacklist): flash('Login name does not meet requirements') return redirect(url_for('user.show')) else: @@ -90,6 +91,8 @@ def csvimport(): flash('No data for csv import!') return redirect(url_for('user.index')) + ignore_blacklist = request.values.get('ignore-loginname-blacklist', False) + roles = Role.query.all() usersadded = 0 with io.StringIO(initial_value=csvdata) as csvfile: @@ -99,7 +102,7 @@ def csvimport(): flash("invalid line, ignored : {}".format(row)) continue newuser = User() - if not newuser.set_loginname(row[0]) or not newuser.set_displayname(row[0]): + if not newuser.set_loginname(row[0], ignore_blacklist=ignore_blacklist) or not newuser.set_displayname(row[0]): flash("invalid login name, skipped : {}".format(row)) continue if not newuser.set_mail(row[1]):