remove ldap
This commit is contained in:
parent
9387c44cd1
commit
161df8a473
|
@ -3,16 +3,17 @@ from flask import g, redirect, request
|
|||
from flask.helpers import url_for
|
||||
import time
|
||||
import subprocess
|
||||
from lenticular_cloud.lenticular_services import lenticular_services
|
||||
from ory_hydra_client import Client
|
||||
import os
|
||||
|
||||
from pathlib import Path
|
||||
from ldap3 import Connection, Server, ALL
|
||||
|
||||
from . import model
|
||||
from .pki import Pki
|
||||
from .pki import pki
|
||||
from .hydra import hydra_service
|
||||
from .translations import init_babel
|
||||
from .model import db, migrate
|
||||
from .views import auth_views, frontend_views, init_login_manager, api_views, pki_views, admin_views, oauth2_views
|
||||
|
||||
|
||||
def get_git_hash():
|
||||
|
@ -31,13 +32,6 @@ def create_app() -> Flask:
|
|||
|
||||
app.jinja_env.globals['GIT_HASH'] = get_git_hash()
|
||||
|
||||
#app.ldap_orm = Connection(app.config['LDAP_URL'], app.config['LDAP_BIND_DN'], app.config['LDAP_BIND_PW'], auto_bind=True)
|
||||
server = Server(app.config['LDAP_URL'], get_info=ALL)
|
||||
app.ldap_conn = Connection(server, app.config['LDAP_BIND_DN'], app.config['LDAP_BIND_PW'], auto_bind=True) # TODO auto_bind read docu
|
||||
model.ldap_conn = app.ldap_conn
|
||||
model.base_dn = app.config['LDAP_BASE_DN']
|
||||
|
||||
from .model import db, migrate
|
||||
db.init_app(app)
|
||||
migration_dir = Path(app.root_path) / 'migrations'
|
||||
migrate.init_app(app, db, directory=str(migration_dir))
|
||||
|
@ -53,9 +47,7 @@ def create_app() -> Flask:
|
|||
# password=app.config['HYDRA_ADMIN_PASSWORD'])
|
||||
hydra_service.set_hydra_client(Client(base_url=app.config['HYDRA_ADMIN_URL']))
|
||||
|
||||
from .views import auth_views, frontend_views, init_login_manager, api_views, pki_views, admin_views, oauth2_views
|
||||
init_login_manager(app)
|
||||
#oauth2.init_app(app)
|
||||
app.register_blueprint(auth_views)
|
||||
app.register_blueprint(frontend_views)
|
||||
app.register_blueprint(api_views)
|
||||
|
@ -68,11 +60,9 @@ def create_app() -> Flask:
|
|||
request_start_time = time.time()
|
||||
g.request_time = lambda: "%.5fs" % (time.time() - request_start_time)
|
||||
|
||||
app.lenticular_services = {}
|
||||
for service_name, service_config in app.config['LENTICULAR_CLOUD_SERVICES'].items():
|
||||
app.lenticular_services[service_name] = model.Service.from_config(service_name, service_config)
|
||||
lenticular_services.init_app(app)
|
||||
|
||||
app.pki = Pki(app.config['PKI_PATH'], app.config['DOMAIN'])
|
||||
pki.init_app(app)
|
||||
|
||||
return app
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import argparse
|
||||
from .model import db, User, UserSignUp
|
||||
from .model import db, User
|
||||
from .app import create_app
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
from flask_migrate import upgrade
|
||||
|
@ -19,7 +19,7 @@ def entry_point():
|
|||
parser_user.set_defaults(func=cli_user)
|
||||
|
||||
parser_signup = subparsers.add_parser('signup')
|
||||
parser_signup.add_argument('--signup_id', type=int)
|
||||
parser_signup.add_argument('--signup_id', type=str)
|
||||
parser_signup.set_defaults(func=cli_signup)
|
||||
|
||||
parser_run = subparsers.add_parser('run')
|
||||
|
@ -55,22 +55,25 @@ def entry_point():
|
|||
|
||||
|
||||
def cli_user(args):
|
||||
print(User.query.all())
|
||||
for user in User.query.all():
|
||||
print(f'{user.id} - Enabled: {user.enabled} - Name:`{user.username}`')
|
||||
pass
|
||||
|
||||
def cli_signup(args):
|
||||
|
||||
print(args.signup_id)
|
||||
if args.signup_id is not None:
|
||||
user_data = UserSignUp.query.get(args.signup_id)
|
||||
user = User.new(user_data)
|
||||
user = User.query.get(args.signup_id)
|
||||
if user == None:
|
||||
print("user not found")
|
||||
return
|
||||
user.enabled = True
|
||||
|
||||
db.session.add(user)
|
||||
db.session.delete(user_data)
|
||||
db.session.commit()
|
||||
else:
|
||||
# list
|
||||
print(UserSignUp.query.all())
|
||||
print('disabled users:')
|
||||
for user in User.query.filter_by(enabled=False).all():
|
||||
print(f'<Signup id={user.id}, username={user.username}>')
|
||||
|
||||
|
||||
def cli_run(app, args):
|
||||
|
|
15
lenticular_cloud/lenticular_services.py
Normal file
15
lenticular_cloud/lenticular_services.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from flask import Flask
|
||||
from .model import Service
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LenticularServices(dict):
|
||||
|
||||
def init_app(self, app: Flask) -> None:
|
||||
for service_name, service_config in app.config['LENTICULAR_CLOUD_SERVICES'].items():
|
||||
self[service_name] = Service.from_config(service_name, service_config)
|
||||
|
||||
|
||||
lenticular_services = LenticularServices()
|
|
@ -13,7 +13,9 @@ config = context.config
|
|||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
fileConfig(config.config_file_name)
|
||||
if type(config.config_file_name) == str:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
logger = logging.getLogger('alembic.env')
|
||||
|
||||
# add your model's MetaData object here
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
"""remove ldap, add rest to db
|
||||
|
||||
Revision ID: 0518a8625b50
|
||||
Revises: 52a21983d2a8
|
||||
Create Date: 2022-06-17 13:15:33.450531
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0518a8625b50'
|
||||
down_revision = '52a21983d2a8'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('app_token',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('service_name', sa.String(), nullable=False),
|
||||
sa.Column('token', sa.String(), nullable=False),
|
||||
sa.Column('name', sa.String(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('group',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.drop_table('user_sign_up')
|
||||
op.add_column('user', sa.Column('password_hashed', sa.String(), server_default="", nullable=False))
|
||||
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 = "";')
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('user', 'enabled')
|
||||
op.drop_column('user', 'password_hashed')
|
||||
op.create_table('user_sign_up',
|
||||
sa.Column('id', sa.INTEGER(), nullable=False),
|
||||
sa.Column('username', sa.VARCHAR(), nullable=False),
|
||||
sa.Column('password', sa.VARCHAR(), nullable=False),
|
||||
sa.Column('alternative_email', sa.VARCHAR(), nullable=True),
|
||||
sa.Column('created_at', sa.DATETIME(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.drop_table('group')
|
||||
op.drop_table('app_token')
|
||||
# ### end Alembic commands ###
|
|
@ -1,11 +1,5 @@
|
|||
from flask import current_app
|
||||
from ldap3_orm import AttrDef, EntryBase as _EntryBase, ObjectDef, EntryType
|
||||
from ldap3_orm import Reader
|
||||
from ldap3 import Connection, Entry, HASHED_SALTED_SHA256
|
||||
from ldap3.utils.conv import escape_filter_chars
|
||||
from ldap3.utils.hashed import hashed
|
||||
from flask_login import UserMixin
|
||||
from ldap3.core.exceptions import LDAPSessionTerminatedByServerError
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from collections.abc import MutableSequence
|
||||
|
@ -21,24 +15,17 @@ from datetime import datetime
|
|||
import uuid
|
||||
import pyotp
|
||||
from typing import Optional, Callable
|
||||
|
||||
from cryptography.x509 import Certificate as CertificateObj
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
ldap_conn = None # type: Connection
|
||||
base_dn = ''
|
||||
|
||||
|
||||
|
||||
|
||||
db = SQLAlchemy() # type: SQLAlchemy
|
||||
migrate = Migrate()
|
||||
|
||||
class UserSignUp(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String, nullable=False)
|
||||
password = db.Column(db.String, nullable=False)
|
||||
alternative_email = db.Column(db.String)
|
||||
created_at = db.Column(db.DateTime, nullable=False,
|
||||
default=datetime.now)
|
||||
|
||||
|
||||
class SecurityUser(UserMixin):
|
||||
|
||||
def __init__(self, username):
|
||||
|
@ -48,90 +35,6 @@ class SecurityUser(UserMixin):
|
|||
return self._username
|
||||
|
||||
|
||||
class LambdaStr:
|
||||
|
||||
def __init__(self, lam: Callable[[],str]):
|
||||
self.lam = lam
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.lam()
|
||||
|
||||
|
||||
class EntryBase(db.Model):
|
||||
__abstract__ = True # for sqlalchemy
|
||||
|
||||
_type = None # will get replaced by the local type
|
||||
_ldap_query_object = None # will get replaced by the local type
|
||||
_base_dn = LambdaStr(lambda: base_dn)
|
||||
|
||||
# def __init__(self, ldap_object=None, **kwargs):
|
||||
# if ldap_object is None:
|
||||
# self._ldap_object = self.get_type()(**kwargs)
|
||||
# else:
|
||||
# self._ldap_object = ldap_object
|
||||
dn = ''
|
||||
base_dn = ''
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self._ldap_object)
|
||||
|
||||
@classmethod
|
||||
def get_object_def(cls) -> ObjectDef:
|
||||
return ObjectDef(cls.object_classes, ldap_conn)
|
||||
|
||||
@classmethod
|
||||
def get_entry_type(cls) -> EntryType:
|
||||
return EntryType(cls.get_dn(), cls.object_classes, ldap_conn)
|
||||
|
||||
@classmethod
|
||||
def get_base(cls) -> str:
|
||||
return cls.base_dn.format(_base_dn=base_dn)
|
||||
|
||||
@classmethod
|
||||
def get_dn(cls) -> str:
|
||||
return cls.dn.replace('{base_dn}', cls.get_base())
|
||||
|
||||
@classmethod
|
||||
def get_type(cls):
|
||||
if cls._type is None:
|
||||
cls._type = EntryType(cls.get_dn(), cls.object_classes, ldap_conn)
|
||||
return cls._type
|
||||
|
||||
def ldap_commit(self):
|
||||
self._ldap_object.entry_commit_changes()
|
||||
|
||||
def ldap_add(self):
|
||||
ret = ldap_conn.add(
|
||||
self.entry_dn, self.object_classes, self._ldap_object.entry_attributes_as_dict)
|
||||
if not ret:
|
||||
raise Exception('ldap error')
|
||||
|
||||
@classmethod
|
||||
def query_(cls):
|
||||
if cls._ldap_query_object is None:
|
||||
cls._ldap_query_object = cls._query(cls)
|
||||
return cls._ldap_query_object
|
||||
|
||||
class _query(object):
|
||||
def __init__(self, clazz):
|
||||
self._class = clazz
|
||||
|
||||
def _mapping(self, ldap_object):
|
||||
return ldap_object
|
||||
|
||||
def _query(self, ldap_filter: str):
|
||||
reader = Reader(ldap_conn, self._class.get_object_def(), self._class.get_base(), ldap_filter)
|
||||
try:
|
||||
reader.search()
|
||||
except LDAPSessionTerminatedByServerError:
|
||||
ldap_conn.bind()
|
||||
reader.search()
|
||||
return [self._mapping(entry) for entry in reader]
|
||||
|
||||
def all(self):
|
||||
return self._query(None)
|
||||
|
||||
|
||||
class Service(object):
|
||||
|
||||
def __init__(self, name):
|
||||
|
@ -171,7 +74,7 @@ class Service(object):
|
|||
|
||||
class Certificate(object):
|
||||
|
||||
def __init__(self, cn, ca_name: str, cert_data, revoked=False):
|
||||
def __init__(self, cn, ca_name: str, cert_data: CertificateObj, revoked=False):
|
||||
self._cn = cn
|
||||
self._ca_name = ca_name
|
||||
self._cert_data = cert_data
|
||||
|
@ -225,11 +128,13 @@ def generate_uuid():
|
|||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
class User(EntryBase):
|
||||
class User(db.Model):
|
||||
id = db.Column(
|
||||
db.String(length=36), primary_key=True, default=generate_uuid)
|
||||
username = db.Column(
|
||||
db.String, unique=True, nullable=False)
|
||||
password_hashed = db.Column(
|
||||
db.String, nullable=False)
|
||||
alternative_email = db.Column(
|
||||
db.String, nullable=True)
|
||||
created_at = db.Column(db.DateTime, nullable=False,
|
||||
|
@ -238,15 +143,12 @@ class User(EntryBase):
|
|||
default=datetime.now, onupdate=datetime.now)
|
||||
last_login = db.Column(db.DateTime, nullable=True)
|
||||
|
||||
enabled = db.Column(db.Boolean, nullable=False, default=False)
|
||||
|
||||
totps = db.relationship('Totp', back_populates='user')
|
||||
webauthn_credentials = db.relationship('WebauthnCredential', back_populates='user', cascade='delete,delete-orphan', passive_deletes=True)
|
||||
|
||||
dn = "uid={uid},{base_dn}"
|
||||
base_dn = "ou=users,{_base_dn}"
|
||||
object_classes = ["inetOrgPerson"] #, "LenticularUser"]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._ldap_object = None
|
||||
super(db.Model).__init__(**kwargs)
|
||||
|
||||
@property
|
||||
|
@ -256,9 +158,6 @@ class User(EntryBase):
|
|||
def get(self, key):
|
||||
print(f'getitem: {key}') # TODO
|
||||
|
||||
def make_writeable(self):
|
||||
self._ldap_object = self._ldap_object.entry_writable()
|
||||
|
||||
@property
|
||||
def groups(self) -> list[str]:
|
||||
if self.username == 'tuxcoder':
|
||||
|
@ -266,58 +165,20 @@ class User(EntryBase):
|
|||
else:
|
||||
return []
|
||||
|
||||
@property
|
||||
def entry_dn(self) -> str:
|
||||
return self._ldap_object.entry_dn
|
||||
|
||||
@property
|
||||
def email(self) -> str:
|
||||
domain = current_app.config['DOMAIN']
|
||||
return f'{self.username}@{domain}'
|
||||
return self._ldap_object.mail
|
||||
|
||||
def change_password(self, password_new: str) -> bool:
|
||||
self.make_writeable()
|
||||
password_hashed = crypt.crypt(password_new)
|
||||
self._ldap_object.userPassword = ('{CRYPT}' + password_hashed).encode()
|
||||
self.ldap_commit()
|
||||
return True
|
||||
|
||||
class _query(EntryBase._query):
|
||||
|
||||
def _mapping(self, ldap_object):
|
||||
user = User.query.filter(User.username == str(ldap_object.uid)).first()
|
||||
if user is None:
|
||||
# migration time
|
||||
user = User()
|
||||
user.username = str(ldap_object.uid)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
user._ldap_object = ldap_object
|
||||
return user
|
||||
|
||||
def by_username(self, username) -> Optional['User']:
|
||||
result = self._query('(uid={username:s})'.format(username=escape_filter_chars(username)))
|
||||
if len(result) > 0 and isinstance(result[0], User):
|
||||
return result[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def new(user_data: UserSignUp):
|
||||
user = User()
|
||||
user.username = user_data.username.lower()
|
||||
domain = current_app.config['DOMAIN']
|
||||
ldap_object = User.get_entry_type()(
|
||||
uid=user_data.username.lower(),
|
||||
sn=user_data.username,
|
||||
cn=user_data.username,
|
||||
userPassword='{CRYPT}' + user_data.password,
|
||||
mail=f'{user_data.username}@{domain}')
|
||||
user._ldap_object = ldap_object
|
||||
user.ldap_add()
|
||||
return user
|
||||
|
||||
class AppToken(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
service_name = db.Column(db.String, nullable=False)
|
||||
token = db.Column(db.String, nullable=False)
|
||||
name = db.Column(db.String, nullable=False)
|
||||
|
||||
|
||||
class Totp(db.Model):
|
||||
|
@ -349,14 +210,7 @@ class WebauthnCredential(db.Model): # pylint: disable=too-few-public-methods
|
|||
user = db.relationship('User', back_populates='webauthn_credentials')
|
||||
|
||||
|
||||
class Group(EntryBase):
|
||||
__abstract__ = True # for sqlalchemy, disable for now
|
||||
dn = "cn={cn},{base_dn}"
|
||||
base_dn = "ou=Users,{_base_dn}"
|
||||
object_classes = ["top"]
|
||||
|
||||
fullname = AttrDef("cn")
|
||||
|
||||
class Group(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(), nullable=False, unique=True)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from flask import current_app
|
||||
from flask import Flask
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
|
@ -39,12 +39,18 @@ def safe_filename(name):
|
|||
|
||||
class Pki(object):
|
||||
|
||||
def __init__(self, pki_path: str, domain: str):
|
||||
def __init__(self):
|
||||
self._pki_path = Path()
|
||||
self._domain = ""
|
||||
|
||||
|
||||
def init_app(self, app: Flask) -> None:
|
||||
'''
|
||||
pki_path: str base path from the pkis
|
||||
'''
|
||||
self._pki_path = Path(pki_path)
|
||||
self._domain = domain
|
||||
self._pki_path = Path(os.getcwd()) / app.config['PKI_PATH']
|
||||
self._domain = app.config['DOMAIN']
|
||||
|
||||
|
||||
|
||||
def _init_ca(self, service: Service):
|
||||
|
@ -322,3 +328,4 @@ class Pki(object):
|
|||
backend=default_backend())
|
||||
return crl
|
||||
|
||||
pki = Pki()
|
|
@ -11,7 +11,7 @@ from ory_hydra_client.models import OAuth2Client, GenericError
|
|||
from typing import Optional
|
||||
import logging
|
||||
|
||||
from ..model import db, User, UserSignUp
|
||||
from ..model import db, User
|
||||
from .oauth2 import redirect_login, oauth2
|
||||
from ..form.admin import OAuth2ClientForm
|
||||
from ..hydra import hydra_service
|
||||
|
@ -50,28 +50,24 @@ async def users():
|
|||
|
||||
@admin_views.route('/registrations', methods=['GET'])
|
||||
def registrations() -> ResponseReturnValue:
|
||||
users = UserSignUp.query.all()
|
||||
users = User.query.filter_by(enabled=False).all()
|
||||
return render_template('admin/registrations.html.j2', users=users)
|
||||
|
||||
|
||||
@admin_views.route('/registration/<registration_id>', methods=['DELETE'])
|
||||
def registration_delete(registration_id) -> ResponseReturnValue:
|
||||
user_data = UserSignUp.query.get(registration_id)
|
||||
if user_data is None:
|
||||
user = User.query.get(registration_id)
|
||||
if user is None:
|
||||
return jsonify({}), 404
|
||||
db.session.delete(user_data)
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
return jsonify({})
|
||||
|
||||
|
||||
@admin_views.route('/registration/<registration_id>', methods=['PUT'])
|
||||
def registration_accept(registration_id) -> ResponseReturnValue:
|
||||
user_data = UserSignUp.query.get(registration_id)
|
||||
#create user
|
||||
user = User.new(user_data)
|
||||
|
||||
db.session.add(user)
|
||||
db.session.delete(user_data)
|
||||
user = User.query.get(registration_id)
|
||||
user.enabled = True
|
||||
db.session.commit()
|
||||
return jsonify({})
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ def email_login() -> ResponseReturnValue:
|
|||
logger.error(f'{request}')
|
||||
logger.error(f'{request.headers}')
|
||||
if not request.is_json:
|
||||
return {}, 400
|
||||
return jsonify({}), 400
|
||||
req_payload = request.get_json()
|
||||
logger.error(f'{req_payload}')
|
||||
password = req_payload["password"]
|
||||
|
|
|
@ -21,7 +21,7 @@ from ory_hydra_client.api.admin import get_consent_request, accept_consent_reque
|
|||
from ory_hydra_client.models import AcceptLoginRequest, AcceptConsentRequest, ConsentRequestSession, GenericError, ConsentRequestSessionAccessToken, ConsentRequestSessionIdToken
|
||||
from typing import Optional
|
||||
|
||||
from ..model import db, User, SecurityUser, UserSignUp
|
||||
from ..model import db, User, SecurityUser
|
||||
from ..form.auth import ConsentForm, LoginForm, RegistrationForm
|
||||
from ..auth_providers import AUTH_PROVIDER_LIST
|
||||
from ..hydra import hydra_service
|
||||
|
@ -118,7 +118,7 @@ async def login() -> ResponseReturnValue:
|
|||
return redirect(resp.redirect_to)
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
user = User.query_().by_username(form.data['name'])
|
||||
user = User.query.filter_by(username=form.data['name']).first()
|
||||
if user:
|
||||
session['username'] = str(user.username)
|
||||
else:
|
||||
|
@ -141,7 +141,7 @@ async def login_auth() -> ResponseReturnValue:
|
|||
if 'username' not in session:
|
||||
return redirect(url_for('auth.login'))
|
||||
auth_forms = {}
|
||||
user = User.query_().by_username(session['username'])
|
||||
user = User.query.filter_by(username=session['username']).first()
|
||||
for auth_provider in AUTH_PROVIDER_LIST:
|
||||
form = auth_provider.get_form()
|
||||
if auth_provider.get_name() not in session['auth_providers'] and\
|
||||
|
@ -216,9 +216,9 @@ def sign_up():
|
|||
def sign_up_submit():
|
||||
form = RegistrationForm()
|
||||
if form.validate_on_submit():
|
||||
user = UserSignUp()
|
||||
user = User()
|
||||
user.username = form.data['username']
|
||||
user.password = crypt.crypt(form.data['password'])
|
||||
user.password_hashed = crypt.crypt(form.data['password'])
|
||||
user.alternative_email = form.data['alternative_email']
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
|
|
@ -31,6 +31,8 @@ from ..auth_providers import LdapAuthProvider
|
|||
from .auth import webauthn
|
||||
from .oauth2 import redirect_login, oauth2
|
||||
from ..hydra import hydra_service
|
||||
from ..pki import pki
|
||||
from ..lenticular_services import lenticular_services
|
||||
|
||||
frontend_views = Blueprint('frontend', __name__, url_prefix='')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -43,8 +45,10 @@ def before_request() -> Optional[ResponseReturnValue]:
|
|||
logger.info('user not logged in redirect')
|
||||
return redirect_login()
|
||||
except MissingTokenError:
|
||||
logger.info('MissingTokenError redirect user to login')
|
||||
return redirect_login()
|
||||
except InvalidTokenError:
|
||||
logger.info('InvalidTokenError redirect user to login')
|
||||
return redirect_login()
|
||||
|
||||
return None
|
||||
|
@ -72,20 +76,20 @@ def index() -> ResponseReturnValue:
|
|||
@frontend_views.route('/client_cert')
|
||||
def client_cert() -> ResponseReturnValue:
|
||||
client_certs = {}
|
||||
for service in current_app.lenticular_services.values():
|
||||
for service in lenticular_services.values():
|
||||
client_certs[str(service.name)] = \
|
||||
current_app.pki.get_client_certs(current_user, service)
|
||||
pki.get_client_certs(current_user, service)
|
||||
|
||||
return render_template(
|
||||
'frontend/client_cert.html.j2',
|
||||
services=current_app.lenticular_services,
|
||||
services=lenticular_services,
|
||||
client_certs=client_certs)
|
||||
|
||||
|
||||
@frontend_views.route('/client_cert/<service_name>/<serial_number>')
|
||||
def get_client_cert(service_name, serial_number) -> ResponseReturnValue:
|
||||
service = current_app.lenticular_services[service_name]
|
||||
cert = current_app.pki.get_client_cert(
|
||||
service = lenticular_services[service_name]
|
||||
cert = pki.get_client_cert(
|
||||
current_user, service, serial_number)
|
||||
return jsonify({
|
||||
'data': {
|
||||
|
@ -96,10 +100,10 @@ def get_client_cert(service_name, serial_number) -> ResponseReturnValue:
|
|||
@frontend_views.route(
|
||||
'/client_cert/<service_name>/<serial_number>', methods=['DELETE'])
|
||||
def revoke_client_cert(service_name, serial_number) -> ResponseReturnValue:
|
||||
service = current_app.lenticular_services[service_name]
|
||||
cert = current_app.pki.get_client_cert(
|
||||
service = lenticular_services[service_name]
|
||||
cert = pki.get_client_cert(
|
||||
current_user, service, serial_number)
|
||||
current_app.pki.revoke_certificate(cert)
|
||||
pki.revoke_certificate(cert)
|
||||
return jsonify({})
|
||||
|
||||
|
||||
|
@ -107,11 +111,11 @@ def revoke_client_cert(service_name, serial_number) -> ResponseReturnValue:
|
|||
'/client_cert/<service_name>/new',
|
||||
methods=['GET', 'POST'])
|
||||
def client_cert_new(service_name) -> ResponseReturnValue:
|
||||
service = current_app.lenticular_services[service_name]
|
||||
service = lenticular_services[service_name]
|
||||
form = ClientCertForm()
|
||||
if form.validate_on_submit():
|
||||
valid_time = int(form.data['valid_time']) * timedelta(1, 0, 0)
|
||||
cert = current_app.pki.signing_publickey(
|
||||
cert = pki.signing_publickey(
|
||||
current_user,
|
||||
service,
|
||||
form.data['publickey'],
|
||||
|
@ -120,7 +124,7 @@ def client_cert_new(service_name) -> ResponseReturnValue:
|
|||
'status': 'ok',
|
||||
'data': {
|
||||
'cert': cert.pem(),
|
||||
'ca_cert': current_app.pki.get_ca_cert_pem(service)
|
||||
'ca_cert': pki.get_ca_cert_pem(service)
|
||||
}})
|
||||
elif form.is_submitted():
|
||||
return jsonify({
|
||||
|
@ -252,7 +256,7 @@ def webauthn_register_route() -> ResponseReturnValue:
|
|||
|
||||
return redirect(url_for('app.webauthn_list_route'))
|
||||
except (KeyError, ValueError) as e:
|
||||
current_app.logger.exception(e)
|
||||
logger.exception(e)
|
||||
flash('Error during registration.', 'error')
|
||||
|
||||
return render_template('frontend/webauthn_register.html', form=form)
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
from authlib.integrations.flask_client import OAuth
|
||||
from authlib.integrations.base_client.errors import MismatchingStateError
|
||||
from flask import Flask, Blueprint, session, request, redirect, url_for
|
||||
from flask import Flask, Blueprint, Response, session, request, redirect, url_for
|
||||
from flask_login import login_user, logout_user, current_user
|
||||
from flask.typing import ResponseReturnValue
|
||||
from flask_login import LoginManager
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
from ..model import User, SecurityUser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def fetch_token(name: str) -> Optional[dict]:
|
||||
token = session.get('token', None)
|
||||
if isinstance(token, dict):
|
||||
|
@ -24,7 +27,10 @@ def redirect_login() -> ResponseReturnValue:
|
|||
logout_user()
|
||||
session['next_url'] = request.path
|
||||
redirect_uri = url_for('oauth2.authorized', _external=True)
|
||||
return oauth2.custom.authorize_redirect(redirect_uri)
|
||||
response = oauth2.custom.authorize_redirect(redirect_uri)
|
||||
#if isinstance(response, ResponseReturnValue):
|
||||
# raise RuntimeError("invalid redirect")
|
||||
return response
|
||||
|
||||
|
||||
@oauth2_views.route('/authorized')
|
||||
|
@ -32,29 +38,38 @@ def authorized() -> ResponseReturnValue:
|
|||
try:
|
||||
token = oauth2.custom.authorize_access_token()
|
||||
except MismatchingStateError:
|
||||
logger.warning("MismatchingStateError redirect user")
|
||||
return redirect(url_for('oauth2.login'))
|
||||
if token is None:
|
||||
return 'bad request', 400
|
||||
session['token'] = token
|
||||
userinfo = oauth2.custom.get('/userinfo').json()
|
||||
db_user = User.query.get(str(userinfo["sub"]))
|
||||
login_user(SecurityUser(db_user.username))
|
||||
|
||||
logger.info(f"userinfo `{userinfo}`")
|
||||
user = User.query.get(str(userinfo["sub"]))
|
||||
if user is None:
|
||||
return "user not found", 404
|
||||
logger.info(f"login user `{user.username}`")
|
||||
login_user(SecurityUser(user.username))
|
||||
logger.info(f"session user `{session}`")
|
||||
|
||||
next_url = request.args.get('next_url')
|
||||
if next_url is None:
|
||||
next_url = '/'
|
||||
return redirect(next_url)
|
||||
|
||||
|
||||
@oauth2_views.route('login')
|
||||
def login() -> ResponseReturnValue:
|
||||
redirect_uri = url_for('.authorized', _external=True)
|
||||
return oauth2.custom.authorize_redirect(redirect_uri)
|
||||
response = oauth2.custom.authorize_redirect(redirect_uri)
|
||||
#if type(response) != Response:
|
||||
# raise RuntimeError("invalid redirect")
|
||||
return response
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
def user_loader(username) -> Optional[User]:
|
||||
user = User.query_().by_username(username)
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if isinstance(user, User):
|
||||
return user
|
||||
else:
|
||||
|
@ -65,12 +80,15 @@ def request_loader(_request):
|
|||
pass
|
||||
|
||||
@login_manager.unauthorized_handler
|
||||
def unauthorized():
|
||||
redirect_login()
|
||||
def unauthorized() -> Optional[User]:
|
||||
pass
|
||||
|
||||
def init_login_manager(app: Flask):
|
||||
def init_login_manager(app: Flask) -> None:
|
||||
|
||||
base_url = app.config['HYDRA_PUBLIC_URL']
|
||||
if not isinstance(base_url, str):
|
||||
raise RuntimeError("HYDRA_PUBLIC_URL not set")
|
||||
|
||||
oauth2.register(
|
||||
name="custom",
|
||||
client_id=app.config['OAUTH_ID'],
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from flask import current_app, Blueprint
|
||||
from flask import Blueprint
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from ..lenticular_services import lenticular_services
|
||||
from ..pki import pki
|
||||
|
||||
|
||||
pki_views = Blueprint('pki', __name__, url_prefix='/')
|
||||
|
@ -7,7 +9,7 @@ pki_views = Blueprint('pki', __name__, url_prefix='/')
|
|||
|
||||
@pki_views.route('/<service_name>.crl')
|
||||
def crl(service_name: str):
|
||||
service = current_app.lenticular_services[service_name]
|
||||
crl = current_app.pki.get_crl(service)
|
||||
service = lenticular_services[service_name]
|
||||
crl = pki.get_crl(service)
|
||||
return crl.public_bytes(encoding=serialization.Encoding.DER)
|
||||
|
||||
|
|
Loading…
Reference in a new issue