Alternate login filters

This commit is contained in:
Éloi Rivard 2020-08-20 10:45:33 +02:00
parent 9199ad8499
commit de1d0a232d
7 changed files with 44 additions and 21 deletions

View file

@ -11,5 +11,11 @@ ROOT_DN = "dc=mydomain,dc=tld"
BIND_DN = "cn=admin,dc=mydomain,dc=tld"
BIND_PW = "admin"
# Filter to match users on sign in. Supports a variable
# {login}. For sigin against uid or mail use:
# USER_FILTER = "(|(uid={login})(mail={login}))"
USER_FILTER = "(|(uid={login})(cn={login}))"
# Filter to match admin users. If your server has memberof
# you can filter against group membership
ADMIN_FILTER = "cn=Jane Doe"

View file

@ -86,7 +86,7 @@ def app(slapd_server):
"URI": slapd_server.ldap_uri,
"BIND_DN": slapd_server.root_dn,
"BIND_PW": slapd_server.root_pw,
"USER_FILTER": "(|(uid={login})(mail={login}))",
"USER_FILTER": "(|(uid={login})(cn={login}))",
},
}
)
@ -128,6 +128,11 @@ def client(app, slapd_connection):
@pytest.fixture
def user(app, slapd_connection):
u = User(cn="John Doe", sn="Doe", uid="user", userpassword="{SSHA}fw9DYeF/gHTHuVMepsQzVYAkffGcU8Fz")
u = User(
cn="John Doe",
sn="Doe",
uid="user",
userpassword="{SSHA}fw9DYeF/gHTHuVMepsQzVYAkffGcU8Fz",
)
u.save(slapd_connection)
return u

View file

@ -49,3 +49,17 @@ def test_login_no_password(testclient, slapd_connection, user, client):
res = res.form.submit()
assert 200 == res.status_code
assert b"Login failed, please check your information" in res.body
def test_login_with_alternate_attribute(testclient, slapd_connection, user, client):
res = testclient.get("/login")
assert 200 == res.status_code
res.form["login"] = "user"
res.form["password"] = "correct horse battery staple"
res = res.form.submit()
res = res.follow()
assert 200 == res.status_code
with testclient.session_transaction() as session:
assert user.dn == session.get("user_dn")

View file

@ -133,10 +133,14 @@ class LDAPObjectHelper:
conn.add_s(self.dn, attributes)
@classmethod
def get(cls, dn, filter=None, conn=None):
def get(cls, dn=None, filter=None, conn=None):
conn = conn or cls.ldap()
if "=" not in dn:
if dn is None:
dn = f"{cls.base},{cls.root_dn}"
elif "=" not in dn:
dn = f"{cls.id}={dn},{cls.base},{cls.root_dn}"
result = conn.search_s(dn, ldap.SCOPE_SUBTREE, filter)
if not result:

View file

@ -18,7 +18,7 @@ class User(LDAPObjectHelper):
admin = False
@classmethod
def get(cls, dn, filter=None, conn=None):
def get(cls, dn=None, filter=None, conn=None):
conn = conn or cls.ldap()
user = super().get(dn, filter, conn)
@ -32,12 +32,15 @@ class User(LDAPObjectHelper):
user.admin = True
return user
def login(self, password):
if not self.check_password(password):
return False
@classmethod
def login(cls, login, password):
filter = current_app.config["LDAP"].get("USER_FILTER").format(login=login)
user = User.get(filter=filter)
if not user or not user.check_password(password):
return None
session["user_dn"] = self.dn
return True
session["user_dn"] = user.dn
return user
def check_password(self, password):
conn = ldap.initialize(current_app.config["LDAP"]["URI"])

View file

@ -21,12 +21,7 @@ def authorize():
if request.method == "GET":
return render_template("login.html", form=form)
if not form.validate():
flash(gettext("Login failed, please check your information"), "error")
return render_template("login.html", form=form)
user = User.get(form.login.data)
if not user or not user.login(form.password.data):
if not form.validate() or not User.login(form.login.data, form.password.data):
flash(gettext("Login failed, please check your information"), "error")
return render_template("login.html", form=form)

View file

@ -24,14 +24,10 @@ def login():
form = LoginForm(request.form or None)
if request.form:
if not form.validate():
if not form.validate() or not User.login(form.login.data, form.password.data):
flash(gettext("Login failed, please check your information"), "error")
return render_template("login.html", form=form)
user = User.get(form.login.data)
if not user or not user.login(form.password.data):
flash(gettext("Login failed, please check your information"), "error")
return render_template("login.html", form=form)
return redirect(url_for("web.routes.index"))
return render_template("login.html", form=form)