Anmeldung per LDAP #35
1
Pipfile
1
Pipfile
@ -16,6 +16,7 @@ sqlalchemy = "~=1.4.18"
|
||||
waitress = "~=2.0.0"
|
||||
pyyaml = "~=5.4.1"
|
||||
flask-cors = "~=3.0.10"
|
||||
ldap3 = "*"
|
||||
|
||||
[dev-packages]
|
||||
flake8 = "~=3.9.2"
|
||||
|
37
Pipfile.lock
generated
37
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "128a3b90d2e3d5876c5942fb256dba2d5e3d0b3a545cae6d51aeda6925a0f593"
|
||||
"sha256": "0974e530bf219a5408b754c526f3699f2caf1620ea63cc991377574b5eeb1709"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@ -135,6 +135,17 @@
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"ldap3": {
|
||||
"hashes": [
|
||||
"sha256:18c3ee656a6775b9b0d60f7c6c5b094d878d1d90fc03d56731039f0a4b546a91",
|
||||
"sha256:4139c91f0eef9782df7b77c8cbc6243086affcb6a8a249b768a9658438e5da59",
|
||||
"sha256:8c949edbad2be8a03e719ba48bd6779f327ec156929562814b3e84ab56889c8c",
|
||||
"sha256:afc6fc0d01f02af82cd7bfabd3bbfd5dc96a6ae91e97db0a2dab8a0f1b436056",
|
||||
"sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.9"
|
||||
},
|
||||
"mako": {
|
||||
"hashes": [
|
||||
"sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab",
|
||||
@ -183,6 +194,24 @@
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.0.1"
|
||||
},
|
||||
"pyasn1": {
|
||||
"hashes": [
|
||||
"sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
|
||||
"sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
|
||||
"sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
|
||||
"sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
|
||||
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
|
||||
"sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
|
||||
"sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
|
||||
"sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
|
||||
"sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
|
||||
"sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
|
||||
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
|
||||
"sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
|
||||
"sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
|
||||
],
|
||||
"version": "==0.4.8"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
||||
@ -374,11 +403,11 @@
|
||||
},
|
||||
"identify": {
|
||||
"hashes": [
|
||||
"sha256:18d0c531ee3dbc112fa6181f34faa179de3f57ea57ae2899754f16a7e0ff6421",
|
||||
"sha256:5b41f71471bc738e7b586308c3fca172f78940195cb3bf6734c1e66fdac49306"
|
||||
"sha256:7abaecbb414e385752e8ce02d8c494f4fbc780c975074b46172598a28f1ab839",
|
||||
"sha256:a0e700637abcbd1caae58e0463861250095dfe330a8371733a471af706a4a29a"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.1'",
|
||||
"version": "==2.2.10"
|
||||
"version": "==2.2.11"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
|
@ -68,9 +68,18 @@ flake8
|
||||
|
||||
### Testbenutzer
|
||||
|
||||
#### Lokal ohne LDAP
|
||||
|
||||
Für ein Login ohne LDAP werden die Benutzer aus der [`auth.yml`](./data/auth.yml) benutzt.
|
||||
|
||||
|
||||
#### Lokal mit LDAP
|
||||
|
||||
Einen LDAP Server aufsetzen. Z.B. https://directory.apache.org/apacheds/
|
||||
|
||||
In der `.env` die LDAP Dinge ausfüllen (siehe [`env.dev`](./env.dev)).
|
||||
|
||||
|
||||
### Beispiel-Requests
|
||||
|
||||
Beispiele brauchen curl und jq.
|
||||
|
19
app.py
19
app.py
@ -8,25 +8,36 @@ import os
|
||||
from dotenv import load_dotenv, find_dotenv
|
||||
from flask import Flask
|
||||
from flask_cors import CORS
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask.logging import default_handler
|
||||
from flask_migrate import Migrate
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from ldap3.utils.log import logger as ldap3_logger
|
||||
from ldap3.utils.log import set_library_log_detail_level, BASIC
|
||||
|
||||
load_dotenv(find_dotenv())
|
||||
|
||||
app = Flask(__name__)
|
||||
loglevel = os.getenv("KI_LOGLEVEL", logging.WARNING)
|
||||
loglevel = int(loglevel)
|
||||
app.logger.setLevel(loglevel)
|
||||
logging.basicConfig(level=loglevel)
|
||||
logging.debug("Hello from KI")
|
||||
|
||||
app = Flask(__name__)
|
||||
set_library_log_detail_level(BASIC)
|
||||
ldap3_logger.addHandler(default_handler)
|
||||
|
||||
app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv("SQLALCHEMY_DATABASE_URI")
|
||||
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
||||
app.config["KI_DATA_DIR"] = os.path.dirname(__file__) + "/data"
|
||||
app.config["KI_AUTH"] = os.getenv("KI_AUTH")
|
||||
app.config["CORS_ORIGINS"] = os.getenv("CORS_ORIGINS", "*")
|
||||
|
||||
app.config["KI_AUTH"] = os.getenv("KI_AUTH")
|
||||
app.config["KI_LDAP_URL"] = os.getenv("KI_LDAP_URL")
|
||||
app.config["KI_LDAP_ROOT_DN"] = os.getenv("KI_LDAP_ROOT_DN")
|
||||
|
||||
CORS(app)
|
||||
db = SQLAlchemy(app)
|
||||
migrate = Migrate(app, db)
|
||||
|
||||
logging.debug("Hello from KI")
|
||||
|
||||
from ki import module # noqa
|
||||
|
5
env.dev
5
env.dev
@ -9,7 +9,12 @@ CORS_ORIGINS=*
|
||||
FLASK_APP=app.py
|
||||
FLASK_ENV=development
|
||||
|
||||
# auth method: file or ldap
|
||||
KI_AUTH=file
|
||||
|
||||
# ldap auth only
|
||||
KI_LDAP_URL=ldap://localhost:10389
|
||||
KI_LDAP_ROOT_DN=dc=example,dc=com
|
||||
|
||||
# 10 = debug
|
||||
KI_LOGLEVEL=10
|
||||
|
53
ki/auth.py
53
ki/auth.py
@ -5,11 +5,28 @@
|
||||
import uuid
|
||||
import yaml
|
||||
|
||||
from ldap3 import Server, Connection, ALL
|
||||
|
||||
from app import app, db
|
||||
from ki.models import User, Token
|
||||
|
||||
|
||||
def auth(username, password):
|
||||
def create_user_token(username):
|
||||
user = User.query.filter(User.auth_id.__eq__(username)).first()
|
||||
|
||||
if user is None:
|
||||
user = User(auth_id=username)
|
||||
db.session.add(user)
|
||||
|
||||
token = Token(token=str(uuid.uuid4()), user=user)
|
||||
db.session.add(token)
|
||||
db.session.commit()
|
||||
return token
|
||||
|
||||
|
||||
def file_auth(username, password):
|
||||
app.logger.debug("performing file authentication")
|
||||
|
||||
auth_file_path = app.config["KI_DATA_DIR"] + "/auth.yml"
|
||||
|
||||
with open(auth_file_path, "r") as auth_file_stream:
|
||||
@ -23,14 +40,32 @@ def auth(username, password):
|
||||
if auth_user["password"] != password:
|
||||
return None
|
||||
|
||||
user = User.query.filter(User.auth_id.__eq__(username)).first()
|
||||
return create_user_token(username)
|
||||
|
||||
if user is None:
|
||||
user = User(auth_id=username)
|
||||
db.session.add(user)
|
||||
|
||||
token = Token(token=str(uuid.uuid4()), user=user)
|
||||
db.session.add(token)
|
||||
db.session.commit()
|
||||
def ldap_auth(username, password):
|
||||
app.logger.debug("performing LDAP authentication")
|
||||
|
||||
return token
|
||||
server = Server(app.config['KI_LDAP_URL'], get_info=ALL)
|
||||
root_dn = app.config['KI_LDAP_ROOT_DN']
|
||||
ldap_user = f"cn={username},{root_dn}"
|
||||
|
||||
app.logger.debug(f"server: {server}")
|
||||
connection = Connection(server, user=ldap_user, password=password)
|
||||
|
||||
|
||||
if connection.bind():
|
||||
connection.unbind()
|
||||
return create_user_token(username)
|
||||
|
||||
connection.unbind()
|
||||
return None
|
||||
|
||||
|
||||
def auth(username, password):
|
||||
if app.config['KI_AUTH'] == 'file':
|
||||
return file_auth(username, password)
|
||||
|
||||
if app.config['KI_AUTH'] == 'ldap':
|
||||
return ldap_auth(username, password)
|
||||
|
||||
raise RuntimeError('unknown auth method')
|
||||
|
@ -17,6 +17,7 @@ class ApiTest(unittest.TestCase):
|
||||
app.debug = True
|
||||
app.config["TESTING"] = True
|
||||
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:"
|
||||
|
||||
self.client = app.test_client()
|
||||
|
||||
with app.app_context():
|
||||
|
Loading…
Reference in New Issue
Block a user
Sollten wir das nicht über ein bind machen? Was sagt die AG Admin dazu?