Implementiere lokales Authentifizierungssystem
- Entferne GitHub OAuth-Abhängigkeiten - Implementiere lokales Authentifizierungssystem mit Passwort-Hashing - Füge Passwort-Hash zum User-Modell hinzu, entferne GitHub-ID - Implementiere Benutzerregistrierung und Login-Endpoints - Erstelle Endpunkt für initialen Admin-Setup - Passe Benutzerrollenverwaltung an 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
73ead5939c
commit
70aeb17cdb
166
backend/app.py
166
backend/app.py
@ -10,15 +10,12 @@ import os
|
||||
import json
|
||||
import logging
|
||||
import uuid
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import requests
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from datetime import timedelta
|
||||
from typing import Dict, Any, List, Optional, Union
|
||||
from dataclasses import dataclass
|
||||
import sqlite3
|
||||
from authlib.integrations.flask_client import OAuth
|
||||
from tapo import ApiClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
@ -38,13 +35,6 @@ app.config['SESSION_COOKIE_SECURE'] = os.environ.get('FLASK_ENV') == 'production
|
||||
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
|
||||
|
||||
# GitHub OAuth Konfiguration
|
||||
app.config['GITHUB_CLIENT_ID'] = os.environ.get('OAUTH_CLIENT_ID')
|
||||
app.config['GITHUB_CLIENT_SECRET'] = os.environ.get('OAUTH_CLIENT_SECRET')
|
||||
app.config['GITHUB_API_BASE_URL'] = os.environ.get('GITHUB_API_BASE_URL', 'https://api.github.com/')
|
||||
app.config['GITHUB_AUTHORIZE_URL'] = os.environ.get('GITHUB_AUTHORIZE_URL', 'https://github.com/login/oauth/authorize')
|
||||
app.config['GITHUB_TOKEN_URL'] = os.environ.get('GITHUB_TOKEN_URL', 'https://github.com/login/oauth/access_token')
|
||||
|
||||
# Tapo-Konfiguration
|
||||
TAPO_USERNAME = os.environ.get('TAPO_USERNAME')
|
||||
TAPO_PASSWORD = os.environ.get('TAPO_PASSWORD')
|
||||
@ -66,32 +56,27 @@ app.logger.info('MYP Backend starting up')
|
||||
db = SQLAlchemy(app)
|
||||
migrate = Migrate(app, db)
|
||||
|
||||
# OAuth Setup
|
||||
oauth = OAuth(app)
|
||||
github = oauth.register(
|
||||
name='github',
|
||||
client_id=app.config['GITHUB_CLIENT_ID'],
|
||||
client_secret=app.config['GITHUB_CLIENT_SECRET'],
|
||||
access_token_url=app.config['GITHUB_TOKEN_URL'],
|
||||
authorize_url=app.config['GITHUB_AUTHORIZE_URL'],
|
||||
api_base_url=app.config['GITHUB_API_BASE_URL'],
|
||||
client_kwargs={'scope': 'user:email'},
|
||||
)
|
||||
# Lokale Authentifizierung statt OAuth
|
||||
|
||||
# Models
|
||||
class User(db.Model):
|
||||
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
github_id = db.Column(db.Integer, unique=True)
|
||||
username = db.Column(db.String(64), index=True, unique=True)
|
||||
password_hash = db.Column(db.String(128))
|
||||
display_name = db.Column(db.String(100))
|
||||
email = db.Column(db.String(120), index=True, unique=True)
|
||||
role = db.Column(db.String(20), default='guest') # admin, user, guest
|
||||
role = db.Column(db.String(20), default='user') # admin, user, guest
|
||||
jobs = db.relationship('PrintJob', backref='user', lazy='dynamic')
|
||||
|
||||
def set_password(self, password):
|
||||
self.password_hash = generate_password_hash(password)
|
||||
|
||||
def check_password(self, password):
|
||||
return check_password_hash(self.password_hash, password)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'github_id': self.github_id,
|
||||
'username': self.username,
|
||||
'displayName': self.display_name,
|
||||
'email': self.email,
|
||||
@ -258,57 +243,72 @@ def create_session(user):
|
||||
return session_id
|
||||
|
||||
# Authentifizierungs-Routen
|
||||
@app.route('/auth/login', methods=['GET'])
|
||||
def login():
|
||||
redirect_uri = url_for('github_callback', _external=True)
|
||||
return github.authorize_redirect(redirect_uri)
|
||||
|
||||
@app.route('/auth/login/callback', methods=['GET'])
|
||||
def github_callback():
|
||||
token = github.authorize_access_token()
|
||||
resp = github.get('user', token=token)
|
||||
github_user = resp.json()
|
||||
@app.route('/auth/register', methods=['POST'])
|
||||
def register():
|
||||
data = request.get_json()
|
||||
|
||||
# GitHub-User-Informationen
|
||||
github_id = github_user['id']
|
||||
username = github_user['login']
|
||||
display_name = github_user.get('name', username)
|
||||
if not data or not data.get('username') or not data.get('password'):
|
||||
return jsonify({'message': 'Benutzername und Passwort sind erforderlich!'}), 400
|
||||
|
||||
# E-Mail abrufen
|
||||
emails_resp = github.get('user/emails', token=token)
|
||||
emails = emails_resp.json()
|
||||
primary_email = next((email['email'] for email in emails if email.get('primary')), None)
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
display_name = data.get('displayName', username)
|
||||
email = data.get('email', '')
|
||||
|
||||
if not primary_email and emails:
|
||||
primary_email = emails[0]['email']
|
||||
if User.query.filter_by(username=username).first():
|
||||
return jsonify({'message': 'Benutzername bereits vergeben!'}), 400
|
||||
|
||||
# Benutzer suchen oder erstellen
|
||||
user = User.query.filter_by(github_id=github_id).first()
|
||||
if email and User.query.filter_by(email=email).first():
|
||||
return jsonify({'message': 'E-Mail-Adresse bereits registriert!'}), 400
|
||||
|
||||
if not user:
|
||||
user = User(
|
||||
github_id=github_id,
|
||||
username=username,
|
||||
display_name=display_name,
|
||||
email=primary_email,
|
||||
role='guest' # Standardrolle für neue Benutzer
|
||||
)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
app.logger.info(f'Neuer Benutzer über GitHub registriert: {username}')
|
||||
else:
|
||||
# Aktualisiere Benutzerdaten, falls sie sich geändert haben
|
||||
user.username = username
|
||||
user.display_name = display_name
|
||||
if primary_email:
|
||||
user.email = primary_email
|
||||
db.session.commit()
|
||||
# Prüfen, ob es bereits einen Admin gibt
|
||||
admin_exists = User.query.filter_by(role='admin').first() is not None
|
||||
|
||||
# Falls kein Admin existiert, wird der erste Benutzer zum Admin
|
||||
role = 'admin' if not admin_exists else 'user'
|
||||
|
||||
user = User(
|
||||
username=username,
|
||||
display_name=display_name,
|
||||
email=email,
|
||||
role=role
|
||||
)
|
||||
user.set_password(password)
|
||||
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
app.logger.info(f'Neuer Benutzer registriert: {username} (Rolle: {role})')
|
||||
|
||||
# Session erstellen
|
||||
create_session(user)
|
||||
|
||||
# Weiterleitung zur Frontend-App
|
||||
return redirect('/')
|
||||
return jsonify({
|
||||
'message': 'Registrierung erfolgreich!',
|
||||
'user': user.to_dict()
|
||||
}), 201
|
||||
|
||||
@app.route('/auth/login', methods=['POST'])
|
||||
def login():
|
||||
data = request.get_json()
|
||||
|
||||
if not data or not data.get('username') or not data.get('password'):
|
||||
return jsonify({'message': 'Benutzername und Passwort sind erforderlich!'}), 400
|
||||
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
|
||||
user = User.query.filter_by(username=username).first()
|
||||
|
||||
if not user or not user.check_password(password):
|
||||
return jsonify({'message': 'Ungültiger Benutzername oder Passwort!'}), 401
|
||||
|
||||
# Session erstellen
|
||||
create_session(user)
|
||||
|
||||
return jsonify({
|
||||
'message': 'Anmeldung erfolgreich!',
|
||||
'user': user.to_dict()
|
||||
})
|
||||
|
||||
@app.route('/auth/logout', methods=['POST'])
|
||||
def logout():
|
||||
@ -681,6 +681,40 @@ def check_jobs():
|
||||
def test():
|
||||
return jsonify({'message': 'MYP Backend API funktioniert!'})
|
||||
|
||||
@app.route('/api/create-initial-admin', methods=['POST'])
|
||||
def create_initial_admin():
|
||||
admin_exists = User.query.filter_by(role='admin').first() is not None
|
||||
|
||||
if admin_exists:
|
||||
return jsonify({'message': 'Es existiert bereits ein Administrator!'}), 400
|
||||
|
||||
data = request.get_json()
|
||||
|
||||
if not data or not data.get('username') or not data.get('password'):
|
||||
return jsonify({'message': 'Benutzername und Passwort sind erforderlich!'}), 400
|
||||
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
display_name = data.get('displayName', username)
|
||||
email = data.get('email', '')
|
||||
|
||||
user = User(
|
||||
username=username,
|
||||
display_name=display_name,
|
||||
email=email,
|
||||
role='admin'
|
||||
)
|
||||
user.set_password(password)
|
||||
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
app.logger.info(f'Initialer Admin-Benutzer erstellt: {username}')
|
||||
|
||||
return jsonify({
|
||||
'message': 'Administrator wurde erfolgreich erstellt!',
|
||||
'user': user.to_dict()
|
||||
}), 201
|
||||
|
||||
# Error Handler
|
||||
@app.errorhandler(404)
|
||||
def not_found(error):
|
||||
|
Loading…
x
Reference in New Issue
Block a user