diff --git a/uffd/default_config.cfg b/uffd/default_config.cfg
index 95f0934817c38f404b93d8f79e465758403fbffb..55a8d577b296fa23638a5b6d4d915e67e129a6d1 100644
--- a/uffd/default_config.cfg
+++ b/uffd/default_config.cfg
@@ -5,12 +5,23 @@ LDAP_BASE_MAIL="ou=postfix,dc=example,dc=com"
 LDAP_SERVICE_BIND_DN=""
 LDAP_SERVICE_BIND_PASSWORD=""
 LDAP_SERVICE_URL="ldapi:///"
+LDAP_SERVICE_USE_STARTTLS=True
 
 LDAP_USER_OBJECTCLASSES=["top", "inetOrgPerson", "organizationalPerson", "person", "posixAccount"]
+LDAP_USER_ATTRIBUTE_UID="uidNumber"
+LDAP_USER_ATTRIBUTE_DISPLAYNAME="cn"
+LDAP_USER_ATTRIBUTE_MAIL="mail"
+# The User class gets filled by which LDAP attribute and to type (single/list)
+LDAP_USER_ATTRIBUTE_EXTRA={
+#"phone": {"type": "single", "name": "mobile"},
+}
+LDAP_USER_FILTER="(objectClass=person)"
 LDAP_USER_GID=20001
 LDAP_USER_MIN_UID=10000
 LDAP_USER_MAX_UID=18999
 
+LDAP_GROUP_FILTER='(objectClass=groupOfUniqueNames)'
+
 SESSION_LIFETIME_SECONDS=3600
 # CSRF protection
 SESSION_COOKIE_SECURE=True
diff --git a/uffd/ldap/ldap.py b/uffd/ldap/ldap.py
index 880e86ad0984bcccc99cd06d06037ac315158f9d..e1fbf5ac759e4bba4d45fccf5e8b34ef162b406a 100644
--- a/uffd/ldap/ldap.py
+++ b/uffd/ldap/ldap.py
@@ -4,7 +4,7 @@ from flask import Blueprint, current_app
 from ldap3.utils.conv import escape_filter_chars
 from ldap3.core.exceptions import LDAPBindError, LDAPCursorError, LDAPPasswordIsMandatoryError
 
-from ldap3 import Server, Connection, ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, MOCK_SYNC
+from ldap3 import Server, Connection, ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, MOCK_SYNC, AUTO_BIND_TLS_BEFORE_BIND
 
 bp = Blueprint("ldap", __name__)
 
@@ -33,7 +33,9 @@ def service_conn():
 	if current_app.config.get('LDAP_SERVICE_MOCK', False):
 		return get_mock_conn()
 	server = Server(current_app.config["LDAP_SERVICE_URL"], get_info=ALL)
-	return fix_connection(Connection(server, current_app.config["LDAP_SERVICE_BIND_DN"], current_app.config["LDAP_SERVICE_BIND_PASSWORD"], auto_bind=True))
+	return fix_connection(Connection(server, current_app.config["LDAP_SERVICE_BIND_DN"],
+									 current_app.config["LDAP_SERVICE_BIND_PASSWORD"],
+									 auto_bind=AUTO_BIND_TLS_BEFORE_BIND if current_app.config["LDAP_SERVICE_USE_STARTTLS"] else True))
 
 def user_conn(loginname, password):
 	if not loginname_is_safe(loginname):
@@ -53,7 +55,8 @@ def user_conn(loginname, password):
 		return get_mock_conn()
 	server = Server(current_app.config["LDAP_SERVICE_URL"], get_info=ALL)
 	try:
-		return fix_connection(Connection(server, loginname_to_dn(loginname), password, auto_bind=True))
+		return fix_connection(Connection(server, loginname_to_dn(loginname), password,
+										 auto_bind=AUTO_BIND_TLS_BEFORE_BIND if current_app.config["LDAP_SERVICE_USE_STARTTLS"] else True))
 	except (LDAPBindError, LDAPPasswordIsMandatoryError):
 		return False
 
@@ -62,7 +65,8 @@ def get_conn():
 
 def uid_to_dn(uid):
 	conn = get_conn()
-	conn.search(current_app.config["LDAP_BASE_USER"], '(&(objectclass=person)(uidNumber={}))'.format(escape_filter_chars(uid)))
+	conn.search(current_app.config["LDAP_BASE_USER"],
+				'(&{}(uidNumber={}))'.format(current_app.config["LDAP_USER_FILTER"], escape_filter_chars(uid)))
 	if not len(conn.entries) == 1:
 		return None
 	return conn.entries[0].entry_dn
@@ -90,7 +94,7 @@ def mailname_is_safe(value):
 
 def get_next_uid():
 	conn = get_conn()
-	conn.search(current_app.config["LDAP_BASE_USER"], '(objectclass=person)')
+	conn.search(current_app.config["LDAP_BASE_USER"], current_app.config["LDAP_USER_FILTER"])
 	max_uid = current_app.config["LDAP_USER_MIN_UID"]
 	for i in conn.entries:
 		# skip out of range entries
@@ -106,7 +110,7 @@ def get_next_uid():
 
 def get_ldap_attribute_safe(ldapobject, attribute):
 	try:
-		result = ldapobject[attribute].value if attribute in ldapobject  else None
+		result = ldapobject[attribute].value if attribute in ldapobject else None
 	# we have to catch LDAPCursorError here, because ldap3 in older versions has a broken __contains__ function
 	# see https://github.com/cannatag/ldap3/issues/493
 	# fixed in version 2.5
diff --git a/uffd/session/views.py b/uffd/session/views.py
index 50eaecea1301d745fb42f41fe3f949ad0d2a0585..5b2d9bef52453989beaa8c80df3b9218d9748b26 100644
--- a/uffd/session/views.py
+++ b/uffd/session/views.py
@@ -37,7 +37,7 @@ def login():
 		return render_template('login.html', ref=request.values.get('ref'))
 	conn = user_conn(username, password)
 	if conn:
-		conn.search(conn.user, '(objectClass=person)')
+		conn.search(conn.user, current_app.config["LDAP_USER_FILTER"])
 	if not conn or len(conn.entries) != 1:
 		login_ratelimit.log(username)
 		host_ratelimit.log()
diff --git a/uffd/user/models.py b/uffd/user/models.py
index 3cf845836fde50f872870ddd3d470625656dfa41..294cdb231cd959e9209eb581735724cb5854b29a 100644
--- a/uffd/user/models.py
+++ b/uffd/user/models.py
@@ -6,15 +6,20 @@ from flask import current_app
 
 from uffd import ldap
 
-class User():
-	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
 
+class BaseUser:
+	def __init__(self, attributes=None, groups=None, dn=None):
+		self.uid = None
+		self.mail = ''
+		self.loginname = ''
+		self.displayname = ''
+
+		if attributes is not None:
+			for attribute_name, attribute_value in attributes.items():
+				setattr(self, attribute_name, attribute_value)
+
+		self.dn = dn
+		self.newpassword = None
 		self.groups_ldap = groups or []
 		self.initial_groups_ldap = groups or []
 		self.groups_changed = False
@@ -22,19 +27,30 @@ class User():
 
 	@classmethod
 	def from_ldap(cls, ldapobject):
+		ldap_attributes = {
+			"loginname": ldap.get_ldap_attribute_safe(ldapobject, "uid"),
+			"uid": ldap.get_ldap_attribute_safe(ldapobject, current_app.config["LDAP_USER_ATTRIBUTE_UID"]),
+			"displayname": ldap.get_ldap_attribute_safe(ldapobject, current_app.config["LDAP_USER_ATTRIBUTE_DISPLAYNAME"]),
+			"mail": ldap.get_ldap_attribute_safe(ldapobject, current_app.config["LDAP_USER_ATTRIBUTE_MAIL"]),
+		}
+
+		for user_attribute, ldap_attribute in current_app.config["LDAP_USER_ATTRIBUTE_EXTRA"].items():
+			ldap_attribute_name = ldap_attribute.get("name", "")
+			if ldap_attribute.get("type", "single"):
+				ldap_attributes[user_attribute] = ldap.get_ldap_attribute_safe(ldapobject, ldap_attribute_name)
+			else:
+				ldap_attributes[user_attribute] = ldap.get_ldap_array_attribute_safe(ldapobject, ldap_attribute_name)
+
 		return User(
-				uid=ldapobject['uidNumber'].value,
-				loginname=ldapobject['uid'].value,
-				displayname=ldapobject['cn'].value,
-				mail=ldapobject['mail'].value,
 				groups=ldap.get_ldap_array_attribute_safe(ldapobject, 'memberOf'),
 				dn=ldapobject.entry_dn,
+				attributes=ldap_attributes,
 			)
 
 	@classmethod
 	def from_ldap_dn(cls, dn):
 		conn = ldap.get_conn()
-		conn.search(dn, '(objectClass=person)')
+		conn.search(dn, current_app.config["LDAP_USER_FILTER"])
 		if not len(conn.entries) == 1:
 			return None
 		return User.from_ldap(conn.entries[0])
@@ -44,7 +60,9 @@ class User():
 		if new:
 			self.uid = ldap.get_next_uid()
 			attributes = {
-				'uidNumber': self.uid,
+				current_app.config["LDAP_USER_ATTRIBUTE_UID"]: self.uid,
+				current_app.config["LDAP_USER_ATTRIBUTE_DISPLAYNAME"]: self.displayname,
+				current_app.config["LDAP_USER_ATTRIBUTE_MAIL"]: self.mail,
 				'gidNumber': current_app.config['LDAP_USER_GID'],
 				'homeDirectory': '/home/'+self.loginname,
 				'sn': ' ',
@@ -52,8 +70,6 @@ class User():
 				# same as for update
 				'givenName': self.displayname,
 				'displayName': self.displayname,
-				'cn': self.displayname,
-				'mail': self.mail,
 			}
 			dn = ldap.loginname_to_dn(self.loginname)
 			result = conn.add(dn, current_app.config['LDAP_USER_OBJECTCLASSES'], attributes)
@@ -61,8 +77,8 @@ class User():
 			attributes = {
 				'givenName': [(MODIFY_REPLACE, [self.displayname])],
 				'displayName': [(MODIFY_REPLACE, [self.displayname])],
-				'cn': [(MODIFY_REPLACE, [self.displayname])],
-				'mail': [(MODIFY_REPLACE, [self.mail])],
+				current_app.config["LDAP_USER_ATTRIBUTE_DISPLAYNAME"]: [(MODIFY_REPLACE, [self.displayname])],
+				current_app.config["LDAP_USER_ATTRIBUTE_MAIL"]: [(MODIFY_REPLACE, [self.mail])],
 				}
 			if self.newpassword:
 				attributes['userPassword'] = [(MODIFY_REPLACE, [hashed(HASHED_SALTED_SHA512, self.newpassword)])]
@@ -145,7 +161,11 @@ class User():
 		self.mail = value
 		return True
 
-class Group():
+
+User = BaseUser
+
+
+class Group:
 	def __init__(self, gid=None, name='', members=None, description='', dn=None):
 		self.gid = gid
 		self.name = name
@@ -167,7 +187,7 @@ class Group():
 	@classmethod
 	def from_ldap_dn(cls, dn):
 		conn = ldap.get_conn()
-		conn.search(dn, '(objectClass=groupOfUniqueNames)')
+		conn.search(dn, current_app.config["LDAP_GROUP_FILTER"])
 		if not len(conn.entries) == 1:
 			return None
 		return Group.from_ldap(conn.entries[0])
@@ -175,7 +195,7 @@ class Group():
 	@classmethod
 	def from_ldap_all(cls):
 		conn = ldap.get_conn()
-		conn.search(current_app.config["LDAP_BASE_GROUPS"], '(objectclass=groupOfUniqueNames)')
+		conn.search(current_app.config["LDAP_BASE_GROUPS"], current_app.config["LDAP_GROUP_FILTER"])
 		groups = []
 		for i in conn.entries:
 			groups.append(Group.from_ldap(i))
diff --git a/uffd/user/views_group.py b/uffd/user/views_group.py
index 8c1c805eeb00ca6fe43e054bb97aeb590b130df3..a1d89b244082b5ff22a930c8198c9349a564f287 100644
--- a/uffd/user/views_group.py
+++ b/uffd/user/views_group.py
@@ -25,7 +25,8 @@ def index():
 @bp.route("/<int:gid>")
 def show(gid):
 	conn = get_conn()
-	conn.search(current_app.config["LDAP_BASE_GROUPS"], '(&(objectclass=groupOfUniqueNames)(gidNumber={}))'.format((escape_filter_chars(gid))))
+	conn.search(current_app.config["LDAP_BASE_GROUPS"],
+				'(&{}(gidNumber={}))'.format(current_app.config["LDAP_GROUP_FILTER"], escape_filter_chars(gid)))
 	assert len(conn.entries) == 1
 	group = Group.from_ldap(conn.entries[0])
 	return render_template('group.html', group=group)
diff --git a/uffd/user/views_user.py b/uffd/user/views_user.py
index 77488a229f51fefec2cbe016b1380a9b83a85852..7e9d7f2f681d75bef81a114fb6c92cf963d912a9 100644
--- a/uffd/user/views_user.py
+++ b/uffd/user/views_user.py
@@ -29,7 +29,7 @@ def user_acl_check():
 @register_navbar('Users', icon='users', blueprint=bp, visible=user_acl_check)
 def index():
 	conn = get_conn()
-	conn.search(current_app.config["LDAP_BASE_USER"], '(objectclass=person)')
+	conn.search(current_app.config["LDAP_BASE_USER"], current_app.config["LDAP_USER_FILTER"])
 	users = []
 	for i in conn.entries:
 		users.append(User.from_ldap(i))
@@ -43,7 +43,8 @@ def show(uid=None):
 		ldif = '<none yet>'
 	else:
 		conn = get_conn()
-		conn.search(current_app.config["LDAP_BASE_USER"], '(&(objectclass=person)(uidNumber={}))'.format((escape_filter_chars(uid))))
+		conn.search(current_app.config["LDAP_BASE_USER"],
+					'(&{}(uidNumber={}))'.format(current_app.config["LDAP_USER_FILTER"], escape_filter_chars(uid)))
 		assert len(conn.entries) == 1
 		user = User.from_ldap(conn.entries[0])
 		ldif = conn.entries[0].entry_to_ldif()
@@ -61,7 +62,8 @@ def update(uid=False):
 			flash('Login name does not meet requirements')
 			return redirect(url_for('user.show'))
 	else:
-		conn.search(current_app.config["LDAP_BASE_USER"], '(&(objectclass=person)(uidNumber={}))'.format((escape_filter_chars(uid))))
+		conn.search(current_app.config["LDAP_BASE_USER"],
+					'(&{}(uidNumber={}))'.format(current_app.config["LDAP_USER_FILTER"], escape_filter_chars(uid)))
 		assert len(conn.entries) == 1
 		user = User.from_ldap(conn.entries[0])
 	if not user.set_mail(request.form['mail']):
@@ -106,7 +108,8 @@ def update(uid=False):
 @csrf_protect(blueprint=bp)
 def delete(uid):
 	conn = get_conn()
-	conn.search(current_app.config["LDAP_BASE_USER"], '(&(objectclass=person)(uidNumber={}))'.format((escape_filter_chars(uid))))
+	conn.search(current_app.config["LDAP_BASE_USER"],
+				'(&{}(uidNumber={}))'.format(current_app.config["LDAP_USER_FILTER"], escape_filter_chars(uid)))
 	assert len(conn.entries) == 1
 	user = User.from_ldap(conn.entries[0])