Ajout du support de LDAP
Mettre le flag à True dans local_config.py pour l'activer, et la doc dans VPS-config pour setup l'environnement
This commit is contained in:
parent
a8756c2990
commit
279c194a59
|
@ -9,6 +9,8 @@ from app.models.notification import Notification
|
|||
import app.utils.unicode_names as unicode_names
|
||||
from app.utils.notify import notify
|
||||
from config import V5Config
|
||||
from local_config import USE_LDAP
|
||||
import app.utils.ldap as ldap
|
||||
|
||||
import werkzeug.security
|
||||
import re
|
||||
|
@ -108,7 +110,9 @@ class Member(User):
|
|||
self.name = name
|
||||
self.norm = unicode_names.normalize(name)
|
||||
self.email = email
|
||||
self.set_password(password)
|
||||
if not USE_LDAP:
|
||||
self.set_password(password)
|
||||
# Workflow with LDAP enabled is User → Postgresql → LDAP → set password
|
||||
self.xp = 0
|
||||
|
||||
self.bio = ""
|
||||
|
@ -163,8 +167,11 @@ class Member(User):
|
|||
data = {key: data[key] for key in data if data[key] is not None}
|
||||
|
||||
# TODO: verify good type of those args, think about the password mgt
|
||||
# Beware of LDAP injections
|
||||
if "email" in data:
|
||||
self.email = data["email"]
|
||||
if USE_LDAP:
|
||||
ldap.set_email(self.norm, self.email)
|
||||
if "password" in data:
|
||||
self.set_password(data["password"])
|
||||
if "bio" in data:
|
||||
|
@ -203,13 +210,19 @@ class Member(User):
|
|||
Set the user's password. Check whether the request sender has the right
|
||||
to do this!
|
||||
"""
|
||||
self.password_hash = werkzeug.security.generate_password_hash(password,
|
||||
method='pbkdf2:sha512', salt_length=10)
|
||||
if USE_LDAP:
|
||||
ldap.set_password(self, password)
|
||||
else:
|
||||
self.password_hash = werkzeug.security.generate_password_hash(
|
||||
password, method='pbkdf2:sha512', salt_length=10)
|
||||
|
||||
def check_password(self, password):
|
||||
"""Compares password against member hash."""
|
||||
return werkzeug.security.check_password_hash(self.password_hash,
|
||||
password)
|
||||
if USE_LDAP:
|
||||
return ldap.check_password(self, password)
|
||||
else:
|
||||
return werkzeug.security.check_password_hash(self.password_hash,
|
||||
password)
|
||||
|
||||
def notify(self, message, href=None):
|
||||
""" Notify a user with a message.
|
||||
|
|
|
@ -4,6 +4,8 @@ from app import app, db
|
|||
from app.forms.account import UpdateAccountForm, RegistrationForm, DeleteAccountForm
|
||||
from app.models.users import Member
|
||||
from app.utils.render import render
|
||||
import app.utils.ldap as ldap
|
||||
from local_config import USE_LDAP
|
||||
|
||||
|
||||
@app.route('/account', methods=['GET', 'POST'])
|
||||
|
@ -60,6 +62,10 @@ def register():
|
|||
member.newsletter = form.newsletter.data
|
||||
db.session.add(member)
|
||||
db.session.commit()
|
||||
# Workflow with LDAP is User → Postgresql → LDAP → Change password
|
||||
if USE_LDAP:
|
||||
ldap.add_member(member)
|
||||
ldap.set_password(member, form.password.data)
|
||||
flash('Inscription réussie', 'ok')
|
||||
return redirect(url_for('validation') + "?email=" + form.email.data)
|
||||
return render('register.html', title='Register', form=form)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from flask import redirect, url_for, request, flash
|
||||
from flask_login import login_user, logout_user, login_required, current_user
|
||||
from urllib.parse import urlparse
|
||||
from urllib.parse import urlparse, urljoin
|
||||
from app import app
|
||||
from app.forms.login import LoginForm
|
||||
from app.models.users import Member
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import ldap
|
||||
from ldap.modlist import addModlist, modifyModlist
|
||||
from local_config import LDAP_PASSWORD, LDAP_ORGANIZATION
|
||||
|
||||
|
||||
def get_member(username):
|
||||
""" Get informations about member. Username must be normalized! """
|
||||
conn = ldap.initialize("ldap://localhost")
|
||||
# Search for user
|
||||
r = conn.search_s(LDAP_ORGANIZATION, ldap.SCOPE_SUBTREE, f'(cn={username})')
|
||||
if len(r) > 0:
|
||||
return r[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def edit(user, fields):
|
||||
""" Edit a user. Fields is {'name': ['value'], …} """
|
||||
conn = ldap.initialize("ldap://localhost")
|
||||
# Connect as root
|
||||
# conn.simple_bind_s(f'cn=ldap-root,{LDAP_ORGANIZATION}', LDAP_PASSWORD)
|
||||
# old_value = {"userPassword": ["my_old_password"]}
|
||||
# new_value = {"userPassword": ["my_new_password"]}
|
||||
|
||||
modlist = ldap.modlist.modifyModlist(old_value, new_value)
|
||||
con.modify_s(dn, modlist)
|
||||
|
||||
|
||||
def set_email(user, email):
|
||||
pass
|
||||
|
||||
|
||||
def set_password(user, password):
|
||||
""" Set password for a user. """
|
||||
conn = ldap.initialize("ldap://localhost")
|
||||
# Connect as root
|
||||
conn.simple_bind_s(f'cn=ldap-root,{LDAP_ORGANIZATION}',
|
||||
LDAP_PASSWORD)
|
||||
conn.passwd_s(f"cn={user.norm},{LDAP_ORGANIZATION}", None, password)
|
||||
|
||||
|
||||
def check_password(user, password):
|
||||
""" Try to login a user through LDAP register. """
|
||||
conn = ldap.initialize("ldap://localhost")
|
||||
try:
|
||||
conn.simple_bind_s(f"cn={user.norm},{LDAP_ORGANIZATION}", password)
|
||||
except ldap.INVALID_CREDENTIALS:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def add_member(member):
|
||||
""" Add a member to LDAP register. Fields must have been sanitized! """
|
||||
if get_member(member.norm) is not None:
|
||||
print("User already exists")
|
||||
return
|
||||
conn = ldap.initialize("ldap://localhost")
|
||||
# Connect as root
|
||||
conn.simple_bind_s(f'cn=ldap-root,{LDAP_ORGANIZATION}', LDAP_PASSWORD)
|
||||
# Create fields
|
||||
dn = f'cn={member.norm},{LDAP_ORGANIZATION}'
|
||||
modlist = addModlist({
|
||||
'objectClass': [bytes('inetOrgPerson', 'UTF8')],
|
||||
'cn': [bytes(member.norm, 'UTF8')],
|
||||
'sn': [bytes(member.norm, 'UTF8')],
|
||||
'displayName': [bytes(member.name, 'UTF8')],
|
||||
'mail': [bytes(member.email, 'UTF8')],
|
||||
'uid': [bytes(str(member.id), 'UTF8')],
|
||||
'userPassword': [bytes("", 'UTF8')]
|
||||
})
|
||||
# Add the member
|
||||
conn.add_s(dn, modlist)
|
||||
|
||||
|
||||
def delete_member(member):
|
||||
""" Remove a member from LDAP register """
|
||||
conn = ldap.initialize("ldap://localhost")
|
||||
# Connect as root
|
||||
conn.simple_bind_s(f'cn=ldap-root,{LDAP_ORGANIZATION}', LDAP_PASSWORD)
|
||||
# Create fields
|
||||
dn = f'cn={member.norm},{LDAP_ORGANIZATION}'
|
||||
# Delete the user
|
||||
conn.delete_s(dn)
|
|
@ -3,7 +3,9 @@ from wtforms.validators import ValidationError
|
|||
from app.models.users import Member
|
||||
from app.utils.valid_name import valid_name
|
||||
from app.utils.unicode_names import normalize
|
||||
import app.utils.ldap as ldap
|
||||
from config import V5Config
|
||||
from local_config import USE_LDAP
|
||||
|
||||
|
||||
def name_valid(form, name):
|
||||
|
@ -42,6 +44,13 @@ def name_available(form, name):
|
|||
if member is not None:
|
||||
raise ValidationError("Ce nom d'utilisateur est indisponible.")
|
||||
|
||||
# Double check with LDAP if needed
|
||||
if USE_LDAP:
|
||||
member = ldap.get_member(norm)
|
||||
if member is not None:
|
||||
raise ValidationError("Ce nom d'utilisateur est indisponible.")
|
||||
|
||||
|
||||
|
||||
def email(form, email):
|
||||
member = Member.query.filter_by(email=email.data).first()
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
DB_NAME = "pcv5"
|
||||
LDAP_PASSWORD = "openldap"
|
||||
USE_LDAP = False
|
||||
LDAP_PASSWORD = "openldap"
|
||||
LDAP_ORGANIZATION = "o=planet-casio"
|
||||
|
|
Loading…
Reference in New Issue