2019-07-08 11:55:56 +00:00
from flask import current_app
2019-07-09 13:41:16 +00:00
from flask_login import UserMixin , AnonymousUserMixin
2019-07-08 11:55:56 +00:00
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
2019-07-05 12:47:35 +00:00
from werkzeug . security import generate_password_hash , check_password_hash
from . import db
from . import login_manager
2019-07-09 13:41:16 +00:00
class Permission :
2019-07-11 13:33:48 +00:00
"""
Defines User permissions as integers by the power of 2. User permission
can be evaluated using the bitwise operator & . 3 equals to CREATE_JOB and
DELETE_JOB and so on .
"""
2019-07-09 13:41:16 +00:00
CREATE_JOB = 1
DELETE_JOB = 2
# WRITE = 4
# MODERATE = 8
ADMIN = 16
2019-07-05 12:47:35 +00:00
class Role ( db . Model ) :
2019-07-11 13:33:48 +00:00
"""
Model for the different roles Users can have . Is a one - to - many relationship .
A Role can be associated with many User rows .
"""
2019-07-05 12:47:35 +00:00
__tablename__ = ' roles '
id = db . Column ( db . Integer , primary_key = True )
name = db . Column ( db . String ( 64 ) , unique = True )
2019-07-09 13:41:16 +00:00
default = db . Column ( db . Boolean , default = False , index = True )
permissions = db . Column ( db . Integer )
users = db . relationship ( ' User ' , backref = ' role ' , lazy = ' dynamic ' )
def __init__ ( self , * * kwargs ) :
super ( Role , self ) . __init__ ( * * kwargs )
if self . permissions is None :
self . permissions = 0
2019-07-05 12:47:35 +00:00
def __repr__ ( self ) :
2019-07-11 13:33:48 +00:00
"""
String representation of the Role . For human readability .
"""
2019-07-05 12:47:35 +00:00
return ' <Role %r > ' % self . name
2019-07-09 13:41:16 +00:00
def add_permission ( self , perm ) :
2019-07-11 13:33:48 +00:00
"""
Add new permission to Role . Input is a Permission .
"""
2019-07-09 13:41:16 +00:00
if not self . has_permission ( perm ) :
self . permissions + = perm
def remove_permission ( self , perm ) :
2019-07-11 13:33:48 +00:00
"""
Removes permission from a Role . Input a Permission .
"""
2019-07-09 13:41:16 +00:00
if self . has_permission ( perm ) :
self . permissions - = perm
def reset_permissions ( self ) :
2019-07-11 13:33:48 +00:00
"""
Resets permissions to zero . Zero equals no permissions at all .
"""
2019-07-09 13:41:16 +00:00
self . permissions = 0
def has_permission ( self , perm ) :
2019-07-11 13:33:48 +00:00
"""
Checks if a Role has a specific Permission . Does this wit hthe bitwise
operator .
"""
2019-07-09 13:41:16 +00:00
return self . permissions & perm == perm
@staticmethod
def insert_roles ( ) :
2019-07-11 13:33:48 +00:00
"""
Inserts roles into the databes . This has to be executed befor Users are
added to the database . Otherwiese Users will not have a Role assigned
to them . Order of the roles dictionary determines the ID of each role .
User hast the ID 1 and Administrator has the ID 2.
"""
2019-07-09 13:41:16 +00:00
roles = {
' User ' : [ Permission . CREATE_JOB ] ,
' Administrator ' : [ Permission . ADMIN ,
Permission . CREATE_JOB ,
Permission . DELETE_JOB ]
}
default_role = ' User '
for r in roles :
role = Role . query . filter_by ( name = r ) . first ( )
if role is None :
role = Role ( name = r )
role . reset_permissions ( )
for perm in roles [ r ] :
role . add_permission ( perm )
role . default = ( role . name == default_role )
db . session . add ( role )
db . session . commit ( )
2019-07-05 12:47:35 +00:00
class User ( UserMixin , db . Model ) :
2019-07-11 13:33:48 +00:00
"""
Model for Users that are registered to Opaque .
"""
2019-07-05 12:47:35 +00:00
__tablename__ = ' users '
id = db . Column ( db . Integer , primary_key = True )
email = db . Column ( db . String ( 64 ) , unique = True , index = True )
username = db . Column ( db . String ( 64 ) , unique = True , index = True )
password_hash = db . Column ( db . String ( 128 ) )
role_id = db . Column ( db . Integer , db . ForeignKey ( ' roles.id ' ) )
2019-07-08 13:59:15 +00:00
confirmed = db . Column ( db . Boolean , default = False )
2019-07-05 12:47:35 +00:00
def __repr__ ( self ) :
2019-07-11 13:33:48 +00:00
"""
String representation of the User . For human readability .
"""
2019-07-05 12:47:35 +00:00
return ' <User %r > ' % self . username
2019-07-09 13:41:16 +00:00
def __init__ ( self , * * kwargs ) :
super ( User , self ) . __init__ ( * * kwargs )
if self . role is None :
if self . email == current_app . config [ ' OPAQUE_ADMIN ' ] :
self . role = Role . query . filter_by ( name = ' Administrator ' ) . first ( )
if self . role is None :
self . role = Role . query . filter_by ( default = True ) . first ( )
2019-07-08 13:59:15 +00:00
def generate_confirmation_token ( self , expiration = 3600 ) :
2019-07-11 13:33:48 +00:00
"""
Generates a confirmation token for user confirmation via email .
"""
2019-07-08 13:59:15 +00:00
s = Serializer ( current_app . config [ ' SECRET_KEY ' ] , expiration )
return s . dumps ( { ' confirm ' : self . id } ) . decode ( ' utf-8 ' )
2019-07-05 12:47:35 +00:00
2019-07-08 11:55:56 +00:00
def generate_reset_token ( self , expiration = 3600 ) :
2019-07-11 13:33:48 +00:00
"""
Generates a reset token for password reset via email .
"""
2019-07-08 11:55:56 +00:00
s = Serializer ( current_app . config [ ' SECRET_KEY ' ] , expiration )
return s . dumps ( { ' reset ' : self . id } ) . decode ( ' utf-8 ' )
2019-07-08 13:59:15 +00:00
def confirm ( self , token ) :
2019-07-11 13:33:48 +00:00
"""
Confirms User if the given token is valid and not expired .
"""
2019-07-08 13:59:15 +00:00
s = Serializer ( current_app . config [ ' SECRET_KEY ' ] )
try :
data = s . loads ( token . encode ( ' utf-8 ' ) )
except :
return False
if data . get ( ' confirm ' ) != self . id :
return False
self . confirmed = True
db . session . add ( self )
return True
2019-07-08 13:13:32 +00:00
@staticmethod
def reset_password ( token , new_password ) :
2019-07-11 13:33:48 +00:00
"""
Resets password for User if the given token is valid and not expired .
"""
2019-07-08 13:13:32 +00:00
s = Serializer ( current_app . config [ ' SECRET_KEY ' ] )
try :
data = s . loads ( token . encode ( ' utf-8 ' ) )
except :
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
2019-07-05 12:47:35 +00:00
@property
def password ( self ) :
raise AttributeError ( ' password is not a readable attribute ' )
@password.setter
def password ( self , password ) :
self . password_hash = generate_password_hash ( password )
def verify_password ( self , password ) :
return check_password_hash ( self . password_hash , password )
2019-07-09 13:41:16 +00:00
def can ( self , perm ) :
2019-07-11 13:33:48 +00:00
"""
Checks if a User with its current role can doe something . Checks if the
associated role actually has the needed Permission .
"""
2019-07-09 13:41:16 +00:00
return self . role is not None and self . role . has_permission ( perm )
def is_administrator ( self ) :
2019-07-11 13:33:48 +00:00
"""
Checks if User has Admin permissions .
"""
2019-07-09 13:41:16 +00:00
return self . can ( Permission . ADMIN )
class AnonymousUser ( AnonymousUserMixin ) :
2019-07-11 13:33:48 +00:00
"""
Model replaces the default AnonymousUser .
"""
2019-07-09 13:41:16 +00:00
def can ( self , permissions ) :
return False
def is_administrator ( self ) :
return False
2019-08-05 14:45:38 +00:00
class Job ( ) :
"""
Class to define Jobs .
"""
__tablename__ = ' jobs '
id = db . Column ( db . Integer , primary_key = True )
title = db . Column ( db . String ( 32 ) )
description = db . Column ( db . String ( 64 ) )
service = db . Column ( db . String ( 64 ) , index = True )
user_id = db . Column ( db . Integer , db . ForeignKey ( ' users.id ' ) , index = True )
requested_cpus = db . Column ( db . Integer , default = None )
requested_memory = db . Column ( db . Integer , default = None )
service_args = db . Column ( db . String ( 255 ) ) # JSON string representation {'lang': new_ocr_job_form.language.data, 'version': new_ocr_job_form.version. }
status = db . Column ( db . String ( 64 ) )
cmd_args = db . Column ( db . String ( 255 ) ) # For extra cmd arguments
def __init__ ( self , * * kwargs ) :
super ( Job , self ) . __init__ ( * * kwargs )
def __repr__ ( self ) :
"""
String representation of the Job . For human readability .
"""
return ' <Job %r > ' % self . title
2019-07-09 13:41:16 +00:00
login_manager . anonymous_user = AnonymousUser # Flask-Login is told to use the application’ s custom anonymous user by setting its class in the login_manager.anonymous_user attribute.
2019-07-05 12:47:35 +00:00
@login_manager.user_loader
def load_user ( user_id ) :
return User . query . get ( int ( user_id ) )