mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-03 20:02:47 +00:00 
			
		
		
		
	Switch from Authlib to pyJWT
This commit is contained in:
		@@ -91,7 +91,7 @@ def register():
 | 
				
			|||||||
            db.session.rollback()
 | 
					            db.session.rollback()
 | 
				
			||||||
            flash('Internal Server Error', category='error')
 | 
					            flash('Internal Server Error', category='error')
 | 
				
			||||||
            abort(500)
 | 
					            abort(500)
 | 
				
			||||||
        token = user.generate_confirmation_token()
 | 
					        token = user.generate_confirm_user_token()
 | 
				
			||||||
        msg = create_message(
 | 
					        msg = create_message(
 | 
				
			||||||
            user.email,
 | 
					            user.email,
 | 
				
			||||||
            'Confirm Your Account',
 | 
					            'Confirm Your Account',
 | 
				
			||||||
@@ -115,7 +115,7 @@ def register():
 | 
				
			|||||||
def confirm(token):
 | 
					def confirm(token):
 | 
				
			||||||
    if current_user.confirmed:
 | 
					    if current_user.confirmed:
 | 
				
			||||||
        return redirect(url_for('main.dashboard'))
 | 
					        return redirect(url_for('main.dashboard'))
 | 
				
			||||||
    if current_user.confirm(token):
 | 
					    if current_user.confirm_user(token):
 | 
				
			||||||
        db.session.commit()
 | 
					        db.session.commit()
 | 
				
			||||||
        flash('You have confirmed your account')
 | 
					        flash('You have confirmed your account')
 | 
				
			||||||
        return redirect(url_for('main.dashboard'))
 | 
					        return redirect(url_for('main.dashboard'))
 | 
				
			||||||
@@ -139,7 +139,7 @@ def unconfirmed():
 | 
				
			|||||||
@bp.route('/confirm')
 | 
					@bp.route('/confirm')
 | 
				
			||||||
@login_required
 | 
					@login_required
 | 
				
			||||||
def resend_confirmation():
 | 
					def resend_confirmation():
 | 
				
			||||||
    token = current_user.generate_confirmation_token()
 | 
					    token = current_user.generate_confirm_user_token()
 | 
				
			||||||
    msg = create_message(
 | 
					    msg = create_message(
 | 
				
			||||||
        current_user.email,
 | 
					        current_user.email,
 | 
				
			||||||
        'Confirm Your Account',
 | 
					        'Confirm Your Account',
 | 
				
			||||||
@@ -160,7 +160,7 @@ def reset_password_request():
 | 
				
			|||||||
    if form.validate_on_submit():
 | 
					    if form.validate_on_submit():
 | 
				
			||||||
        user = User.query.filter_by(email=form.email.data.lower()).first()
 | 
					        user = User.query.filter_by(email=form.email.data.lower()).first()
 | 
				
			||||||
        if user is not None:
 | 
					        if user is not None:
 | 
				
			||||||
            token = user.generate_reset_token()
 | 
					            token = user.generate_password_reset_token()
 | 
				
			||||||
            msg = create_message(
 | 
					            msg = create_message(
 | 
				
			||||||
                user.email,
 | 
					                user.email,
 | 
				
			||||||
                'Reset Your Password',
 | 
					                'Reset Your Password',
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										112
									
								
								app/models.py
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								app/models.py
									
									
									
									
									
								
							@@ -1,7 +1,6 @@
 | 
				
			|||||||
from app import db, hashids, login, mail, socketio
 | 
					from app import db, login, mail, socketio
 | 
				
			||||||
from app.converters.vrt import normalize_vrt_file
 | 
					from app.converters.vrt import normalize_vrt_file
 | 
				
			||||||
from app.email import create_message
 | 
					from app.email import create_message
 | 
				
			||||||
from authlib.jose import jwt, JoseError
 | 
					 | 
				
			||||||
from datetime import datetime, timedelta
 | 
					from datetime import datetime, timedelta
 | 
				
			||||||
from enum import Enum, IntEnum
 | 
					from enum import Enum, IntEnum
 | 
				
			||||||
from flask import current_app, url_for
 | 
					from flask import current_app, url_for
 | 
				
			||||||
@@ -12,10 +11,10 @@ from tqdm import tqdm
 | 
				
			|||||||
from werkzeug.security import generate_password_hash, check_password_hash
 | 
					from werkzeug.security import generate_password_hash, check_password_hash
 | 
				
			||||||
import base64
 | 
					import base64
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
 | 
					import jwt
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import requests
 | 
					import requests
 | 
				
			||||||
import shutil
 | 
					import shutil
 | 
				
			||||||
import time
 | 
					 | 
				
			||||||
import xml.etree.ElementTree as ET
 | 
					import xml.etree.ElementTree as ET
 | 
				
			||||||
import yaml
 | 
					import yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -213,6 +212,29 @@ class Role(HashidMixin, db.Model):
 | 
				
			|||||||
        db.session.commit()
 | 
					        db.session.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Token(db.Model):
 | 
				
			||||||
 | 
					    __tablename__ = 'tokens'
 | 
				
			||||||
 | 
					    # Primary key
 | 
				
			||||||
 | 
					    id = db.Column(db.Integer, primary_key=True)
 | 
				
			||||||
 | 
					    # Foreign keys
 | 
				
			||||||
 | 
					    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
 | 
				
			||||||
 | 
					    # Fields
 | 
				
			||||||
 | 
					    access_token = db.Column(db.String(64), nullable=False, index=True)
 | 
				
			||||||
 | 
					    access_expiration = db.Column(db.DateTime, nullable=False)
 | 
				
			||||||
 | 
					    refresh_token = db.Column(db.String(64), nullable=False, index=True)
 | 
				
			||||||
 | 
					    refresh_expiration = db.Column(db.DateTime, nullable=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # def generate(self):
 | 
				
			||||||
 | 
					    #     header = {'alg': 'HS256', 'exp': int(time.time()) + expiration}
 | 
				
			||||||
 | 
					    #     payload = {'confirm': self.hashid}
 | 
				
			||||||
 | 
					    #     return jwt.encode(header, payload, current_app.config['SECRET_KEY'])
 | 
				
			||||||
 | 
					    #     self.access_token = secrets.token_urlsafe()
 | 
				
			||||||
 | 
					    #     self.access_expiration = datetime.utcnow() + \
 | 
				
			||||||
 | 
					    #         timedelta(minutes=current_app.config['ACCESS_TOKEN_MINUTES'])
 | 
				
			||||||
 | 
					    #     self.refresh_token = secrets.token_urlsafe()
 | 
				
			||||||
 | 
					    #     self.refresh_expiration = datetime.utcnow() + \
 | 
				
			||||||
 | 
					    #         timedelta(days=current_app.config['REFRESH_TOKEN_DAYS'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class User(HashidMixin, UserMixin, db.Model):
 | 
					class User(HashidMixin, UserMixin, db.Model):
 | 
				
			||||||
    __tablename__ = 'users'
 | 
					    __tablename__ = 'users'
 | 
				
			||||||
    # Primary key
 | 
					    # Primary key
 | 
				
			||||||
@@ -292,17 +314,20 @@ class User(HashidMixin, UserMixin, db.Model):
 | 
				
			|||||||
    def can(self, permission):
 | 
					    def can(self, permission):
 | 
				
			||||||
        return self.role.has_permission(permission)
 | 
					        return self.role.has_permission(permission)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def confirm(self, token):
 | 
					    def confirm_user(self, token):
 | 
				
			||||||
        # s = TimedJSONWebSignatureSerializer(current_app.config['SECRET_KEY'])
 | 
					 | 
				
			||||||
        # try:
 | 
					 | 
				
			||||||
        #     data = s.loads(token.encode('utf-8'))
 | 
					 | 
				
			||||||
        # except BadSignature:
 | 
					 | 
				
			||||||
        #     return False
 | 
					 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            data = jwt.decode(token, current_app.config['SECRET_KEY'])
 | 
					            payload = jwt.decode(
 | 
				
			||||||
        except JoseError:
 | 
					                token,
 | 
				
			||||||
 | 
					                current_app.config['SECRET_KEY'],
 | 
				
			||||||
 | 
					                algorithms=['HS256'],
 | 
				
			||||||
 | 
					                issuer=current_app.config['SERVER_NAME'],
 | 
				
			||||||
 | 
					                options={'require': ['exp', 'iat', 'iss', 'purpose', 'sub']}
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        except jwt.PyJWTError:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        if data.get('confirm') != self.hashid:
 | 
					        if payload.get('purpose') != 'confirm_user':
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        if payload.get('sub') != self.id:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        self.confirmed = True
 | 
					        self.confirmed = True
 | 
				
			||||||
        db.session.add(self)
 | 
					        db.session.add(self)
 | 
				
			||||||
@@ -312,22 +337,27 @@ class User(HashidMixin, UserMixin, db.Model):
 | 
				
			|||||||
        shutil.rmtree(self.path, ignore_errors=True)
 | 
					        shutil.rmtree(self.path, ignore_errors=True)
 | 
				
			||||||
        db.session.delete(self)
 | 
					        db.session.delete(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def generate_confirmation_token(self, expiration=3600):
 | 
					    def generate_confirm_user_token(self, expiration=3600):
 | 
				
			||||||
        # s = TimedJSONWebSignatureSerializer(
 | 
					        utc_now = datetime.utcnow()
 | 
				
			||||||
        #     current_app.config['SECRET_KEY'], expiration)
 | 
					        payload = {
 | 
				
			||||||
        # return s.dumps({'confirm': self.hashid}).decode('utf-8')
 | 
					            'exp': utc_now + timedelta(seconds=expiration),
 | 
				
			||||||
        header = {'alg': 'HS256', 'exp': int(time.time()) + expiration}
 | 
					            'iat': utc_now,
 | 
				
			||||||
        payload = {'confirm': self.hashid}
 | 
					            'iss': current_app.config['SERVER_NAME'],
 | 
				
			||||||
        return jwt.encode(header, payload, current_app.config['SECRET_KEY'])
 | 
					            'purpose': 'confirm_user',
 | 
				
			||||||
 | 
					            'sub': self.id
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return jwt.encode(payload, current_app.config['SECRET_KEY'], algorithm='HS256')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate_password_reset_token(self, expiration=3600):
 | 
				
			||||||
    def generate_reset_token(self, expiration=3600):
 | 
					        utc_now = datetime.utcnow()
 | 
				
			||||||
        # s = TimedJSONWebSignatureSerializer(
 | 
					        payload = {
 | 
				
			||||||
        #     current_app.config['SECRET_KEY'], expiration)
 | 
					            'exp': utc_now + timedelta(seconds=expiration),
 | 
				
			||||||
        # return s.dumps({'reset': self.hashid}).decode('utf-8')
 | 
					            'iat': utc_now,
 | 
				
			||||||
        header = {'alg': 'HS256', 'exp': int(time.time()) + expiration}
 | 
					            'iss': current_app.config['SERVER_NAME'],
 | 
				
			||||||
        payload = {'reset': self.hashid}
 | 
					            'purpose': 'reset_password',
 | 
				
			||||||
        return jwt.encode(header, payload, current_app.config['SECRET_KEY'])
 | 
					            'sub': self.id
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return jwt.encode(payload, current_app.config['SECRET_KEY'], algorithm='HS256')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_token(self, expires_in=3600):
 | 
					    def get_token(self, expires_in=3600):
 | 
				
			||||||
        now = datetime.utcnow()
 | 
					        now = datetime.utcnow()
 | 
				
			||||||
@@ -410,25 +440,21 @@ class User(HashidMixin, UserMixin, db.Model):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def reset_password(token, new_password):
 | 
					    def reset_password(token, new_password):
 | 
				
			||||||
        # s = TimedJSONWebSignatureSerializer(current_app.config['SECRET_KEY'])
 | 
					 | 
				
			||||||
        # try:
 | 
					 | 
				
			||||||
        #     data = s.loads(token.encode('utf-8'))
 | 
					 | 
				
			||||||
        # except BadSignature:
 | 
					 | 
				
			||||||
        #     return False
 | 
					 | 
				
			||||||
        # user = User.query.get(data.get('reset'))
 | 
					 | 
				
			||||||
        # if user is None:
 | 
					 | 
				
			||||||
        #     return False
 | 
					 | 
				
			||||||
        # user.password = new_password
 | 
					 | 
				
			||||||
        # db.session.add(user)
 | 
					 | 
				
			||||||
        # return True
 | 
					 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            data = jwt.decode(token, current_app.config['SECRET_KEY'])
 | 
					            payload = jwt.decode(
 | 
				
			||||||
        except JoseError:
 | 
					                token,
 | 
				
			||||||
 | 
					                current_app.config['SECRET_KEY'],
 | 
				
			||||||
 | 
					                algorithms=['HS256'],
 | 
				
			||||||
 | 
					                issuer=current_app.config['SERVER_NAME'],
 | 
				
			||||||
 | 
					                options={'require': ['exp', 'iat', 'iss', 'purpose', 'sub']}
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        except jwt.PyJWTError:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        user_hashid = data.get('reset')
 | 
					        if payload.get('purpose') != 'reset_password':
 | 
				
			||||||
        if user_hashid is None:
 | 
					            return False
 | 
				
			||||||
 | 
					        user_id = payload.get('sub')
 | 
				
			||||||
 | 
					        if user_id is None:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        user_id = hashids.decode(user_hashid)
 | 
					 | 
				
			||||||
        user = User.query.get(user_id)
 | 
					        user = User.query.get(user_id)
 | 
				
			||||||
        if user is None:
 | 
					        if user is None:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
Authlib
 | 
					 | 
				
			||||||
cqi
 | 
					cqi
 | 
				
			||||||
docker
 | 
					docker
 | 
				
			||||||
eventlet
 | 
					eventlet
 | 
				
			||||||
@@ -18,6 +17,7 @@ Flask-WTF
 | 
				
			|||||||
hiredis
 | 
					hiredis
 | 
				
			||||||
MarkupSafe==2.0.1
 | 
					MarkupSafe==2.0.1
 | 
				
			||||||
psycopg2
 | 
					psycopg2
 | 
				
			||||||
 | 
					PyJWT
 | 
				
			||||||
pyScss
 | 
					pyScss
 | 
				
			||||||
python-dotenv
 | 
					python-dotenv
 | 
				
			||||||
pyyaml
 | 
					pyyaml
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user