more ldap migration
This commit is contained in:
parent
927562fecb
commit
c6042973fe
|
@ -1,7 +1,8 @@
|
|||
from flask import current_app
|
||||
from flask_wtf import FlaskForm
|
||||
from .form.auth import PasswordForm, TotpForm, Fido2Form
|
||||
from ldap3 import Server, Connection, HASHED_SALTED_SHA256
|
||||
from ldap3.core.exceptions import LDAPException
|
||||
from hmac import compare_digest as compare_hash
|
||||
import crypt
|
||||
from .model import User
|
||||
import logging
|
||||
|
||||
|
@ -17,11 +18,11 @@ class AuthProvider:
|
|||
return csl.__name__
|
||||
|
||||
@staticmethod
|
||||
def get_form():
|
||||
def get_form() -> FlaskForm:
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def check_auth(user, form) -> bool:
|
||||
def check_auth(user: User, form) -> bool:
|
||||
'''
|
||||
checks the submited form is valid
|
||||
return true if user is allowed to auth
|
||||
|
@ -29,30 +30,26 @@ class AuthProvider:
|
|||
return False
|
||||
|
||||
|
||||
class LdapAuthProvider(AuthProvider):
|
||||
class PasswordAuthProvider(AuthProvider):
|
||||
|
||||
@staticmethod
|
||||
def get_form():
|
||||
def get_form() -> FlaskForm:
|
||||
return PasswordForm(prefix='password')
|
||||
|
||||
@staticmethod
|
||||
def check_auth(user: User, form):
|
||||
return LdapAuthProvider.check_auth_internal(
|
||||
user, form.data['password'])
|
||||
|
||||
@staticmethod
|
||||
def check_auth_internal(user, password):
|
||||
server = Server(current_app.config['LDAP_URL'])
|
||||
ldap_conn = Connection(server, user.entry_dn, password)
|
||||
try:
|
||||
return ldap_conn.bind()
|
||||
except LDAPException:
|
||||
def check_auth(user: User, form: FlaskForm) -> bool:
|
||||
if isinstance(form.data['password'], str):
|
||||
return PasswordAuthProvider.check_auth_internal(user, form.data['password'])
|
||||
else:
|
||||
return False
|
||||
@staticmethod
|
||||
def check_auth_internal(user: User, password: str) -> bool:
|
||||
return compare_hash(crypt.crypt(password, user.password_hashed),user.password_hashed)
|
||||
|
||||
|
||||
class U2FAuthProvider(AuthProvider):
|
||||
@staticmethod
|
||||
def get_from():
|
||||
def get_from() -> FlaskForm:
|
||||
return Fido2Form(prefix='fido2')
|
||||
|
||||
|
||||
|
@ -67,7 +64,7 @@ class TotpAuthProvider(AuthProvider):
|
|||
return TotpForm(prefix='totp')
|
||||
|
||||
@staticmethod
|
||||
def check_auth(user, form):
|
||||
def check_auth(user: User, form: FlaskForm) -> bool:
|
||||
data = form.data['totp']
|
||||
if data is not None:
|
||||
#print(f'data totp: {data}')
|
||||
|
@ -80,7 +77,7 @@ class TotpAuthProvider(AuthProvider):
|
|||
|
||||
|
||||
AUTH_PROVIDER_LIST = [
|
||||
LdapAuthProvider,
|
||||
PasswordAuthProvider,
|
||||
TotpAuthProvider
|
||||
]
|
||||
|
||||
|
|
|
@ -7,6 +7,12 @@ Create Date: 2022-06-17 13:15:33.450531
|
|||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from flask import current_app
|
||||
from lenticular_cloud.model import User
|
||||
from ldap3_orm import AttrDef, EntryBase as _EntryBase, ObjectDef, EntryType
|
||||
from ldap3_orm import Reader
|
||||
from ldap3 import Connection, Server, ALL
|
||||
import logging
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
@ -17,6 +23,14 @@ depends_on = None
|
|||
|
||||
|
||||
def upgrade():
|
||||
app = current_app
|
||||
server = Server(app.config['LDAP_URL'], get_info=ALL)
|
||||
ldap_conn = Connection(server, app.config['LDAP_BIND_DN'], app.config['LDAP_BIND_PW'], auto_bind=True) # TODO auto_bind read docu
|
||||
base_dn = app.config['LDAP_BASE_DN']
|
||||
object_def = ObjectDef(["inetOrgPerson"], ldap_conn)
|
||||
user_base_dn = f"ou=users,{base_dn}"
|
||||
|
||||
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('app_token',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
|
@ -36,8 +50,21 @@ def upgrade():
|
|||
op.add_column('user', sa.Column('enabled', sa.Boolean(), server_default="false", nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
op.execute("UPDATE `user` SET enabled= 1;")
|
||||
#op.execute('UPDATE `user` SET password_hashed = "";')
|
||||
op.execute(User.__table__.update().values({'enabled': True}))
|
||||
conn = op.get_bind()
|
||||
users = conn.execute(User.__table__.select())
|
||||
|
||||
for user in users:
|
||||
print(f"migrating user {user.username}")
|
||||
reader = Reader(ldap_conn, object_def, user_base_dn, f'(uid={user.username})')
|
||||
result = reader.search()
|
||||
if len(result) == 0:
|
||||
print(f"WARNING: could not migrate user {user.username}")
|
||||
continue
|
||||
ldap_object = result[0]
|
||||
password_hashed = ldap_object.userPassword[0].decode().replace('{CRYPT}','')
|
||||
op.execute(User.__table__.update().values({'password_hashed': password_hashed}).where(User.id == user.id))
|
||||
|
||||
|
||||
|
||||
def downgrade():
|
||||
|
|
|
@ -49,7 +49,7 @@ class Service(object):
|
|||
}
|
||||
|
||||
@staticmethod
|
||||
def from_config(name, config) -> Service:
|
||||
def from_config(name, config) -> 'Service':
|
||||
"""
|
||||
"""
|
||||
service = Service(name)
|
||||
|
@ -189,7 +189,7 @@ class Totp(BaseModel):
|
|||
secret = db.Column(db.String, nullable=False)
|
||||
name = db.Column(db.String, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.now, nullable=False)
|
||||
last_used = db.Column(db.DateTime, nullable=True)
|
||||
#last_used = db.Column(db.DateTime, nullable=True)
|
||||
|
||||
user_id = db.Column(
|
||||
db.String(length=36),
|
||||
|
|
|
@ -11,7 +11,6 @@ import logging
|
|||
import httpx
|
||||
|
||||
from ..model import User
|
||||
from ..auth_providers import LdapAuthProvider
|
||||
from ..hydra import hydra_service
|
||||
from ory_hydra_client.api.admin import introspect_o_auth_2_token
|
||||
from ory_hydra_client.models import GenericError
|
||||
|
@ -37,7 +36,7 @@ def user_list() -> ResponseReturnValue:
|
|||
|
||||
return jsonify([
|
||||
{'username': str(user.username), 'email': str(user.email)}
|
||||
for user in User.query_().all()])
|
||||
for user in User.query.all()])
|
||||
|
||||
@api_views.route('/introspect', methods=['POST'])
|
||||
def introspect() -> ResponseReturnValue:
|
||||
|
@ -66,7 +65,7 @@ def email_login() -> ResponseReturnValue:
|
|||
if not request.is_json:
|
||||
return jsonify({}), 400
|
||||
req_payload = request.get_json()
|
||||
logger.error(f'{req_payload}')
|
||||
logger.debug(f'{req_payload}')
|
||||
if not isinstance(req_payload, dict):
|
||||
return 'bad request', 400
|
||||
password = req_payload["password"]
|
||||
|
|
|
@ -147,6 +147,7 @@ async def login_auth() -> ResponseReturnValue:
|
|||
if auth_provider.get_name() not in session['auth_providers'] and\
|
||||
auth_provider.check_auth(user, form):
|
||||
session['auth_providers'].append(auth_provider.get_name())
|
||||
session.modified = True
|
||||
|
||||
if auth_provider.get_name() not in session['auth_providers']:
|
||||
auth_forms[auth_provider.get_name()]=form
|
||||
|
|
|
@ -27,7 +27,7 @@ from ..model import db, User, SecurityUser, Totp, WebauthnCredential
|
|||
from ..form.frontend import ClientCertForm, TOTPForm, \
|
||||
TOTPDeleteForm, PasswordChangeForm, WebauthnRegisterForm
|
||||
from ..form.base import ButtonForm
|
||||
from ..auth_providers import LdapAuthProvider
|
||||
from ..auth_providers import PasswordAuthProvider
|
||||
from .auth import webauthn
|
||||
from .oauth2 import redirect_login, oauth2
|
||||
from ..hydra import hydra_service
|
||||
|
@ -275,7 +275,7 @@ def password_change_post() -> ResponseReturnValue:
|
|||
if form.validate():
|
||||
password_old = str(form.data['password_old'])
|
||||
password_new = str(form.data['password_new'])
|
||||
if not LdapAuthProvider.check_auth_internal(
|
||||
if not PasswordAuthProvider.check_auth_internal(
|
||||
current_user, password_old):
|
||||
return jsonify(
|
||||
{'errors': {'password_old': 'Old Password is invalid'}})
|
||||
|
|
|
@ -28,8 +28,8 @@ def redirect_login() -> ResponseReturnValue:
|
|||
session['next_url'] = request.path
|
||||
redirect_uri = url_for('oauth2.authorized', _external=True)
|
||||
response = oauth2.custom.authorize_redirect(redirect_uri)
|
||||
#if isinstance(response, ResponseReturnValue):
|
||||
# raise RuntimeError("invalid redirect")
|
||||
if isinstance(response, Response):
|
||||
raise RuntimeError("invalid redirect")
|
||||
return response
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue