128 lines
3.7 KiB
Python
128 lines
3.7 KiB
Python
# Copyright (c) 2019. Tobias Kurze
|
|
"""
|
|
This module provides functions related to authentication through the API.
|
|
For example: listing of available auth providers or registration of users.
|
|
|
|
Login through API does not start a new session, but instead returns JWT.
|
|
"""
|
|
import flask
|
|
from datetime import datetime, timedelta
|
|
import jwt
|
|
from flask import request, jsonify, current_app, url_for
|
|
from functools import wraps
|
|
from random import randint
|
|
|
|
from flask_login import logout_user, login_user
|
|
from typing import Iterable
|
|
from werkzeug.routing import BuildError
|
|
|
|
from backend import db, app
|
|
from backend.api import auth_api_bp
|
|
from backend.auth import AUTH_PROVIDERS, oidc_auth
|
|
from backend.models.user_model import User, Group
|
|
|
|
|
|
def create_jwt(user: User, validity_min=30):
|
|
return jwt.encode({
|
|
'sub': user.email,
|
|
'iat': datetime.utcnow(),
|
|
'exp': datetime.utcnow() + timedelta(minutes=validity_min)},
|
|
current_app.config['SECRET_KEY'])
|
|
|
|
|
|
@auth_api_bp.route('/providers', methods=('GET',))
|
|
def get_auth_providers():
|
|
providers = dict()
|
|
for p in AUTH_PROVIDERS:
|
|
provider = dict(AUTH_PROVIDERS[p])
|
|
try:
|
|
provider["url"] = url_for(AUTH_PROVIDERS[p]["url"])
|
|
except BuildError:
|
|
provider["url"] = AUTH_PROVIDERS[p]["url"]
|
|
providers[p] = provider
|
|
return jsonify(providers)
|
|
|
|
|
|
@auth_api_bp.route('/register', methods=('POST',))
|
|
def register():
|
|
data = request.get_json()
|
|
user = User(**data)
|
|
db.session.add(user)
|
|
db.session.commit()
|
|
return jsonify(user.to_dict()), 201
|
|
|
|
|
|
@auth_api_bp.route('/login', methods=('GET', 'POST',))
|
|
def login():
|
|
print("login")
|
|
print(request)
|
|
data = request.get_json()
|
|
if not data:
|
|
return jsonify({'message': 'Invalid request data', 'authenticated': False}), 401
|
|
print(data)
|
|
user = User.authenticate(**data)
|
|
|
|
if not user:
|
|
return jsonify({'message': 'Invalid credentials', 'authenticated': False}), 401
|
|
|
|
token = create_jwt(user)
|
|
return jsonify({'token': token.decode('UTF-8')})
|
|
|
|
|
|
def check_and_create_groups(groups: Iterable[str]):
|
|
user_groups = []
|
|
for g in groups:
|
|
group = Group.get_by_name(g)
|
|
if group is None:
|
|
group = Group(name=g)
|
|
db.session.add(group)
|
|
user_groups.append(group)
|
|
|
|
db.session.commit()
|
|
return user_groups
|
|
|
|
|
|
def create_or_retrieve_user_from_userinfo(userinfo):
|
|
try:
|
|
email = userinfo["email"]
|
|
except KeyError:
|
|
return None
|
|
|
|
user_groups = check_and_create_groups(groups=userinfo.get("memberOf", []))
|
|
user = User.get_by_identifier(email)
|
|
|
|
if user is not None:
|
|
app.logger.info("user found -> update user")
|
|
user.first_name = userinfo.get("given_name", "")
|
|
user.last_name = userinfo.get("family_name", "")
|
|
for g in user_groups:
|
|
user.groups.append(g)
|
|
db.session.commit()
|
|
return user
|
|
|
|
user = User(email=email, first_name=userinfo.get("given_name", ""),
|
|
last_name=userinfo.get("family_name", ""), external_user=True,
|
|
groups=userinfo.get("memberOf", []))
|
|
|
|
app.logger.info("creating new user")
|
|
|
|
db.session.add(user)
|
|
db.session.commit()
|
|
return user
|
|
|
|
|
|
@auth_api_bp.route('/oidc', methods=['GET'])
|
|
@oidc_auth.oidc_auth()
|
|
def oidc():
|
|
|
|
user = create_or_retrieve_user_from_userinfo(flask.session['userinfo'])
|
|
|
|
return jsonify(user.to_dict())
|
|
if user is None:
|
|
return "Could not authenticate: could not find or create user.", 401
|
|
if current_app.config.get("AUTH_RETURN_EXTERNAL_JWT", False):
|
|
token = jwt.encode(flask.session['id_token'], current_app.config['SECRET_KEY'])
|
|
else:
|
|
token = create_jwt(user)
|
|
return token
|