class Category(db.Model, Base, EntityBase): __tablename__ = 'category' id = db.Column(guid(), primary_key=True) # Relationship with parent Category parent_id = db.Column(guid(), db.ForeignKey('category.id', name='fk_category_id_category', onupdate='CASCADE', ondelete='CASCADE'), index=True) parent = db.relationship(lambda: Category, remote_side=id, backref='sub_products') name = db.Column(db.String(255), nullable=False, unique=True) description = db.Column(db.Text, nullable=False) photo = db.Column(db.String(255)) @property def entity_type(self): return EntityBase.TYPE_CATEGORY @property def resource_type(self): from models.acl import AclResource return AclResource.CATEGORY
class Product(db.Model, Base, EntityBase): __tablename__ = 'product' id = db.Column(guid(), primary_key=True) # Relationships with Category category_id = db.Column(guid(), db.ForeignKey('category.id', name='fk_product_id_category', onupdate='CASCADE', ondelete='CASCADE'), index=True) category = db.relationship('Category') name = db.Column(db.String(255), nullable=False) photo = db.Column(db.String(255)) code = db.Column(db.String(255), nullable=False) price = db.Column(db.Numeric(10, 2), nullable=False) quantity = db.Column(db.Integer, nullable=False, default=0) description = db.Column(db.Text) @property def entity_type(self): return EntityBase.TYPE_PRODUCT @property def resource_type(self): from models.acl import AclResource return AclResource.PRODUCT
class AclUserResource(db.Model): __tablename__ = 'acl_user_resource' user_id = db.Column(guid(), db.ForeignKey('user.id', name='fk_acl_user_resource_id_user', onupdate='CASCADE', ondelete='CASCADE'), primary_key=True, nullable=False) user = db.relationship('User') resource_id = db.Column(guid(), primary_key=True, nullable=False) resource_type = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=False) _privilege = db.Column(db.Integer, name='privilege', primary_key=True, autoincrement=False, nullable=False) _permission = db.Column(db.Integer, name='permission', primary_key=True, autoincrement=False, nullable=False) @property def privilege(self): from libraries.bitwise import Bitwise return Bitwise(self._privilege) @privilege.setter def privilege(self, bit): self._privilege = bit @property def permission(self): from libraries.bitwise import Bitwise return Bitwise(self._permission) @permission.setter def permission(self, bit): self._permission = bit @staticmethod def get_permission_for_user(user_id, resource_id, resource_type, privilege): from sqlalchemy import and_, or_ return AclUserResource.query.filter( and_( AclUserResource.user_id == user_id, AclUserResource.resource_id == resource_id, or_(resource_type is None, AclUserResource.resource_type == resource_type), AclUserResource._privilege.op('&')(privilege) > 0, )).all()
class EntityMeta(db.Model, Base): __tablename__ = 'sys_entity_meta' entity_id = db.Column(guid(), nullable=False, autoincrement=False, primary_key=True) entity_type = db.Column(db.Integer, nullable=False, autoincrement=False, primary_key=True) meta_id = db.Column(guid(), db.ForeignKey( 'sys_meta_value.id', onupdate='CASCADE', ondelete='CASCADE' ), nullable=False, autoincrement=False, primary_key=True)
class AclRole(db.Model): __tablename__ = 'acl_role' id = db.Column(guid(), primary_key=True) name = db.Column(db.String(255), unique=True, nullable=False) parent_id = db.Column(guid(), db.ForeignKey('acl_role.id', name='fk_acl_role_id_acl_role', ondelete='CASCADE', onupdate='CASCADE'), index=True) parent = db.relationship('AclRole') def get_parents(self): """ :return: array of role.id """ if not self.parent_id: return [] results = [self.parent_id] # Include parent's parent parent = self.query.filter_by(id=self.parent_id).first() results.extend(parent.get_parents()) return results @staticmethod def get_default_role(): from flask import current_app as app result = AclRole.query.filter_by( name=app.config.get('DEFAULT_USER_ROLE')).first() if not result: from sqlalchemy.exc import DataError raise DataError('System is missing DEFAULT_ROLE') return result @staticmethod def get_owner_role(): from flask import current_app as app result = AclRole.query.filter_by( name=app.config.get('OWNER_USER_ROLE')).first() if not result: from sqlalchemy.exc import DataError raise DataError('System is missing OWNER_ROLE') return result
class AclUserRole(db.Model): __tablename__ = 'acl_user_role' user_id = db.Column(guid(), db.ForeignKey('user.id', name='fk_acl_user_role_id_user', onupdate='CASCADE', ondelete='CASCADE'), primary_key=True, nullable=False) user = db.relationship('User') role_id = db.Column(guid(), db.ForeignKey('acl_role.id', name='fk_acl_user_role_id_acl_role', onupdate='CASCADE', ondelete='CASCADE'), primary_key=True, nullable=False) role = db.relationship('AclRole') @staticmethod def get_roles_for(user_id, is_owner): """ :param user_id: :return: arrays of role.id """ default_role = AclRole.get_default_role() results = [default_role.id] if is_owner: owner_role = AclRole.get_owner_role() results.append(owner_role.id) roles = AclUserRole.query.filter_by(user_id=user_id).all() for r in roles: # Add role.id results.append(r.role_id) if not r.role.parent_id: continue # Find parent recursive results.extend(r.role.get_parents()) return filter(None, set(results))
class AclRoleResource(db.Model): __tablename__ = 'acl_role_resource' role_id = db.Column(guid(), db.ForeignKey('acl_role.id', name='fk_acl_role_resource_id_acl_role', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True, nullable=False) role = db.relationship('AclRole') resource_name = db.Column(db.String(255), primary_key=True, nullable=False) _privilege = db.Column(db.Integer, name='privilege', primary_key=True, autoincrement=False, nullable=False) _permission = db.Column(db.Integer, name='permission', primary_key=True, autoincrement=False, nullable=False) @property def privilege(self): from libraries.bitwise import Bitwise return Bitwise(self._privilege) @privilege.setter def privilege(self, bit): self._privilege = bit @property def permission(self): from libraries.bitwise import Bitwise return Bitwise(self._permission) @permission.setter def permission(self, bit): self._permission = bit @staticmethod def get_permission_for_user(user_id, resource_name, privilege, is_owner): from sqlalchemy import and_, or_ from sqlalchemy.sql.operators import in_op roles = AclUserRole.get_roles_for(user_id=user_id, is_owner=is_owner) return AclRoleResource.query.filter( and_( or_( AclRoleResource.resource_name == '*', AclRoleResource.resource_name == resource_name, ), AclRoleResource._privilege.op('&')(privilege) > 0, in_op(AclRoleResource.role_id, roles))).all()
class Client(db.Model): __tablename__ = 'client' # human readable name, not required name = db.Column(db.String(40)) # human readable description, not required description = db.Column(db.String(400)) # creator of the client, not required user_id = db.Column( guid(), db.ForeignKey('user.id', onupdate='CASCADE', ondelete='CASCADE')) user = db.relationship('User') client_id = db.Column(db.String(40), primary_key=True) client_secret = db.Column(db.String(55), unique=True, index=True, nullable=False) # public or confidential is_confidential = db.Column(db.Boolean) _redirect_uris = db.Column(db.Text) _default_scopes = db.Column(db.Text) @property def client_type(self): if self.is_confidential: return 'confidential' return 'public' @property def redirect_uris(self): if self._redirect_uris: return self._redirect_uris.split() return [] @property def default_redirect_uri(self): return self.redirect_uris[0] @property def default_scopes(self): return ['email']
class Grant(db.Model): __tablename__ = 'grant' id = db.Column(db.String(40), primary_key=True) user_id = db.Column( guid(), db.ForeignKey('user.id', onupdate='CASCADE', ondelete='CASCADE')) user = db.relationship('User') client_id = db.Column( db.String(40), db.ForeignKey('client.client_id', onupdate='CASCADE', ondelete='CASCADE'), nullable=False, ) client = db.relationship('Client') code = db.Column(db.String(255), index=True, nullable=False) redirect_uri = db.Column(db.String(255)) expires = db.Column(db.DateTime) _scopes = db.Column(db.Text) def delete(self): from flask import current_app as app session = app.db.session session.delete(self) session.commit() return self @property def scopes(self): if self._scopes: return self._scopes.split() return [] def validate_redirect_uri(self, redirect_uri): return True
class Token(db.Model): __tablename__ = 'token' id = db.Column(db.Integer, primary_key=True) client_id = db.Column( db.String(40), db.ForeignKey('client.client_id', onupdate='CASCADE', ondelete='CASCADE'), nullable=False, ) client = db.relationship('Client') user_id = db.Column( guid(), db.ForeignKey('user.id', onupdate='CASCADE', ondelete='CASCADE')) user = db.relationship('User') # currently only bearer is supported token_type = db.Column(db.String(40)) access_token = db.Column(db.String(255), unique=True) refresh_token = db.Column(db.String(255), unique=True) expires = db.Column(db.DateTime) _scopes = db.Column(db.Text) def delete(self): from flask import current_app as app session = app.db.session session.delete(self) session.commit() return self @property def scopes(self): if self._scopes: return self._scopes.split() return []
class User(db.Model, Base, EntityBase): __tablename__ = 'user' id = db.Column(guid(), primary_key=True) username = db.Column(db.String(255), unique=True, nullable=False) facebook = db.Column(db.String(255)) skype = db.Column(db.String(255)) first_name = db.Column(db.String(255)) last_name = db.Column(db.String(255)) dob = db.Column(db.Integer()) email = db.Column(db.String(255), unique=True, nullable=False) photo = db.Column(db.String(255)) _password = db.Column(db.String(4000), name='password') about = db.Column(db.String(4000)) @property def entity_type(self): return EntityBase.TYPE_USER @property def resource_type(self): from models.acl import AclResource return AclResource.USER @property def password(self): # No one should be able to find user's password return None @password.setter def password(self, password): from werkzeug.security import generate_password_hash self._password = generate_password_hash(password) def check_password(self, password): from werkzeug.security import check_password_hash return check_password_hash(self._password, password) def is_allowed(self, resource, privilege): from models.acl import acl_manager return acl_manager.is_allowed(self, resource, privilege)
class MetaGroup(db.Model, Base): __tablename__ = 'sys_meta_group' id = db.Column(guid(), primary_key=True) name = db.Column(db.String(255), nullable=False, unique=True) description = db.Column(db.Text, nullable=False)
def upgrade(): ### commands auto generated by Alembic - please adjust! ### # Insert roles from flask import current_app as app role_table = table('acl_role', column('id', guid()), column('name', String(255)), column('parent_id', guid())) user_role_uuid = str(uuid.uuid4()) owner_role_uuid = str(uuid.uuid4()) super_user_role_uuid = str(uuid.uuid4()) op.bulk_insert(role_table, [ { 'id': user_role_uuid, 'name': app.config.get('DEFAULT_USER_ROLE') } ]) op.bulk_insert(role_table, [ { 'id': super_user_role_uuid, 'name': app.config.get('SUPER_USER_ROLE'), 'parent_id': user_role_uuid }, { 'id': owner_role_uuid, 'name': app.config.get('OWNER_USER_ROLE'), 'parent_id': user_role_uuid } ]) # Create users user_uuid = str(uuid.uuid4()) super_user_uuid = str(uuid.uuid4()) from werkzeug.security import generate_password_hash import string, random user_table = table('user', column('id', guid()), column('username', String(255)), column('email', String(255)), column('password', String(255)) ) import os from colorama import Fore bold = '\033[1m' end_bold = '\033[0m' superuser_name = 'superuser' superuser_password = ''.join(random.choice(string.ascii_uppercase) for i in range(10)) superuser_email = os.getenv('SUPERUSER_EMAIL', '*****@*****.**') user_name = 'normal_user' user_password = ''.join(random.choice(string.ascii_uppercase) for i in range(10)) user_email = os.getenv('NORMAL_USER_EMAIL', '*****@*****.**') print '* Generating {2}{0}SUPER_USER{1}{3}...'.format( bold, # {0} end_bold, # {1} Fore.YELLOW, # {2} Fore.RESET # {3} ) print ' - {6}Username{7}: {5}{3}{0}{4}{7}\n - {6}Password{7}: {5}{3}{1}{4}{7}\n - {6}Email{7}: {5}{3}{2}{4}{7}'.format( superuser_name, superuser_password, superuser_email, bold, # {3} end_bold, # {4} Fore.GREEN, # {5} Fore.YELLOW, # {6} Fore.RESET # {7} ) print '* Generating {2}{0}NORMAL_USER{1}{3}...'.format( bold, # {0} end_bold, # {1} Fore.YELLOW, # {2} Fore.RESET # {3} ) print ' - {6}Username{7}: {5}{3}{0}{4}{7}\n - {6}Password{7}: {5}{3}{1}{4}{7}\n - {6}Email{7}: {5}{3}{2}{4}{7}'.format( user_name, user_password, user_email, bold, # {3} end_bold, # {4} Fore.GREEN, # {5} Fore.YELLOW, # {6} Fore.RESET # {7} ) op.bulk_insert(user_table, [ { 'id': super_user_uuid, 'username': superuser_name, 'email': superuser_email, 'password': generate_password_hash(superuser_password) }, { 'id': user_uuid, 'username': user_name, 'email': user_email, 'password': generate_password_hash(user_password) } ]) # Create user_roles user_roles_table = table('acl_user_role', column('user_id', guid()), column('role_id', guid())) op.bulk_insert(user_roles_table, [ { 'user_id': super_user_uuid, 'role_id': super_user_role_uuid } ]) # Create roles_resources role_resource_table = table('acl_role_resource', column('role_id', guid()), column('resource_name', String(255)), column('privilege', Integer), column('permission', Integer)) from models.acl import Privilege, AclResource from libraries.acl import Permission op.bulk_insert(role_resource_table, [ { 'role_id': super_user_role_uuid, 'resource_name': '*', 'privilege': Privilege.ADD | Privilege.VIEW | Privilege.DELETE | Privilege.UPDATE, 'permission': Permission.ALLOW }, { 'role_id': owner_role_uuid, 'resource_name': '*', 'privilege': Privilege.ADD | Privilege.VIEW | Privilege.DELETE | Privilege.UPDATE, 'permission': Permission.ALLOW }, { 'role_id': user_role_uuid, 'resource_name': AclResource.PRODUCT, 'privilege': Privilege.VIEW, 'permission': Permission.ALLOW }, { 'role_id': user_role_uuid, 'resource_name': AclResource.CATEGORY, 'privilege': Privilege.VIEW, 'permission': Permission.ALLOW }, { 'role_id': user_role_uuid, 'resource_name': AclResource.USER, 'privilege': Privilege.VIEW, 'permission': Permission.ALLOW } ]) pass
def upgrade(): ### commands auto generated by Alembic - please adjust! ### # Insert roles from flask import current_app as app role_table = table('acl_role', column('id', guid()), column('name', String(255)), column('parent_id', guid())) user_role_uuid = str(uuid.uuid4()) owner_role_uuid = str(uuid.uuid4()) super_user_role_uuid = str(uuid.uuid4()) op.bulk_insert(role_table, [{ 'id': user_role_uuid, 'name': app.config.get('DEFAULT_USER_ROLE') }]) op.bulk_insert(role_table, [{ 'id': super_user_role_uuid, 'name': app.config.get('SUPER_USER_ROLE'), 'parent_id': user_role_uuid }, { 'id': owner_role_uuid, 'name': app.config.get('OWNER_USER_ROLE'), 'parent_id': user_role_uuid }]) # Create users user_uuid = str(uuid.uuid4()) super_user_uuid = str(uuid.uuid4()) from werkzeug.security import generate_password_hash import string, random user_table = table('user', column('id', guid()), column('username', String(255)), column('email', String(255)), column('password', String(255))) import os from colorama import Fore bold = '\033[1m' end_bold = '\033[0m' superuser_name = 'superuser' superuser_password = ''.join( random.choice(string.ascii_uppercase) for i in range(10)) superuser_email = os.getenv('SUPERUSER_EMAIL', '*****@*****.**') user_name = 'normal_user' user_password = ''.join( random.choice(string.ascii_uppercase) for i in range(10)) user_email = os.getenv('NORMAL_USER_EMAIL', '*****@*****.**') print '* Generating {2}{0}SUPER_USER{1}{3}...'.format( bold, # {0} end_bold, # {1} Fore.YELLOW, # {2} Fore.RESET # {3} ) print ' - {6}Username{7}: {5}{3}{0}{4}{7}\n - {6}Password{7}: {5}{3}{1}{4}{7}\n - {6}Email{7}: {5}{3}{2}{4}{7}'.format( superuser_name, superuser_password, superuser_email, bold, # {3} end_bold, # {4} Fore.GREEN, # {5} Fore.YELLOW, # {6} Fore.RESET # {7} ) print '* Generating {2}{0}NORMAL_USER{1}{3}...'.format( bold, # {0} end_bold, # {1} Fore.YELLOW, # {2} Fore.RESET # {3} ) print ' - {6}Username{7}: {5}{3}{0}{4}{7}\n - {6}Password{7}: {5}{3}{1}{4}{7}\n - {6}Email{7}: {5}{3}{2}{4}{7}'.format( user_name, user_password, user_email, bold, # {3} end_bold, # {4} Fore.GREEN, # {5} Fore.YELLOW, # {6} Fore.RESET # {7} ) op.bulk_insert(user_table, [{ 'id': super_user_uuid, 'username': superuser_name, 'email': superuser_email, 'password': generate_password_hash(superuser_password) }, { 'id': user_uuid, 'username': user_name, 'email': user_email, 'password': generate_password_hash(user_password) }]) # Create user_roles user_roles_table = table('acl_user_role', column('user_id', guid()), column('role_id', guid())) op.bulk_insert(user_roles_table, [{ 'user_id': super_user_uuid, 'role_id': super_user_role_uuid }]) # Create roles_resources role_resource_table = table('acl_role_resource', column('role_id', guid()), column('resource_name', String(255)), column('privilege', Integer), column('permission', Integer)) from models.acl import Privilege, AclResource from libraries.acl import Permission op.bulk_insert(role_resource_table, [{ 'role_id': super_user_role_uuid, 'resource_name': '*', 'privilege': Privilege.ADD | Privilege.VIEW | Privilege.DELETE | Privilege.UPDATE, 'permission': Permission.ALLOW }, { 'role_id': owner_role_uuid, 'resource_name': '*', 'privilege': Privilege.ADD | Privilege.VIEW | Privilege.DELETE | Privilege.UPDATE, 'permission': Permission.ALLOW }, { 'role_id': user_role_uuid, 'resource_name': AclResource.PRODUCT, 'privilege': Privilege.VIEW, 'permission': Permission.ALLOW }, { 'role_id': user_role_uuid, 'resource_name': AclResource.CATEGORY, 'privilege': Privilege.VIEW, 'permission': Permission.ALLOW }, { 'role_id': user_role_uuid, 'resource_name': AclResource.USER, 'privilege': Privilege.VIEW, 'permission': Permission.ALLOW }]) pass