diff --git a/backend/app.py b/backend/app.py index 82347a3..56a02af 100644 --- a/backend/app.py +++ b/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):