Still very initial scaffolding
This commit is contained in:
parent
9000ecdce5
commit
0c6601576f
@ -11,12 +11,8 @@ pyramid.debug_authorization = false
|
|||||||
pyramid.debug_notfound = false
|
pyramid.debug_notfound = false
|
||||||
pyramid.debug_routematch = false
|
pyramid.debug_routematch = false
|
||||||
pyramid.default_locale_name = en
|
pyramid.default_locale_name = en
|
||||||
pyramid.includes =
|
|
||||||
pyramid_debugtoolbar
|
|
||||||
|
|
||||||
# By default, the toolbar only appears for clients from IP addresses
|
sqlalchemy.url = postgresql://postgres:123456@localhost:5432/pics
|
||||||
# '127.0.0.1' and '::1'.
|
|
||||||
# debugtoolbar.hosts = 127.0.0.1 ::1
|
|
||||||
|
|
||||||
###
|
###
|
||||||
# wsgi server configuration
|
# wsgi server configuration
|
||||||
|
5
setup.py
5
setup.py
@ -10,9 +10,10 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
|
|||||||
|
|
||||||
requires = [
|
requires = [
|
||||||
'pyramid',
|
'pyramid',
|
||||||
'pyramid_chameleon',
|
|
||||||
'pyramid_debugtoolbar',
|
|
||||||
'waitress',
|
'waitress',
|
||||||
|
'transaction',
|
||||||
|
'zope.sqlalchemy',
|
||||||
|
'SQLAlchemy',
|
||||||
]
|
]
|
||||||
|
|
||||||
setup(name='Soter',
|
setup(name='Soter',
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
from pyramid.config import Configurator
|
from pyramid.config import Configurator
|
||||||
|
from sqlalchemy import engine_from_config
|
||||||
|
from soter.models import initialize_sql
|
||||||
|
|
||||||
|
|
||||||
def main(global_config, **settings):
|
def main(global_config, **settings):
|
||||||
""" This function returns a Pyramid WSGI application.
|
""" This function returns a Pyramid WSGI application.
|
||||||
"""
|
"""
|
||||||
|
engine = engine_from_config(settings, 'sqlalchemy.')
|
||||||
|
initialize_sql(engine)
|
||||||
|
|
||||||
config = Configurator(settings=settings)
|
config = Configurator(settings=settings)
|
||||||
config.include('pyramid_chameleon')
|
|
||||||
config.add_static_view('static', 'static', cache_max_age=3600)
|
config.add_static_view('static', 'static', cache_max_age=3600)
|
||||||
config.add_route('home', '/')
|
config.add_route('home', '/')
|
||||||
|
|
||||||
|
52
soter/models/__init__.py
Normal file
52
soter/models/__init__.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||||
|
from zope.sqlalchemy import ZopeTransactionExtension
|
||||||
|
|
||||||
|
|
||||||
|
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), expire_on_commit=False))
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_sql(engine):
|
||||||
|
DBSession.configure(bind=engine)
|
||||||
|
Base.metadata.bind = engine
|
||||||
|
if not schema_exists(engine):
|
||||||
|
fixtures(engine)
|
||||||
|
|
||||||
|
|
||||||
|
def schema_exists(engine):
|
||||||
|
from soter.models.master import DbSetting
|
||||||
|
|
||||||
|
with engine.connect() as connection:
|
||||||
|
return engine.dialect.has_table(connection, DbSetting.__tablename__)
|
||||||
|
|
||||||
|
|
||||||
|
def fixtures(engine):
|
||||||
|
import transaction
|
||||||
|
import uuid
|
||||||
|
from soter.models.master import Album, DbSetting, Picture, Tag, picture_tag
|
||||||
|
from soter.models.auth import Permission, Role, User, role_permission, role_user
|
||||||
|
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
|
user = User('Admin', 'Administrator', 'soter@tanshu.com', '123456', False,
|
||||||
|
uuid.UUID('8de98592-76d9-c74d-bb3f-d6184d388b5a'))
|
||||||
|
DBSession.add(user)
|
||||||
|
|
||||||
|
roles = [Role('Owner', uuid.UUID('52e08c0c-048a-784f-be10-6e129ad4b5d4'))]
|
||||||
|
|
||||||
|
for role in roles:
|
||||||
|
DBSession.add(role)
|
||||||
|
user.roles.append(role)
|
||||||
|
|
||||||
|
permissions = [Permission('Attendance', uuid.UUID('09d05434-a09a-fa45-963b-769a2e3fc667')),
|
||||||
|
Permission('Trial Balance', uuid.UUID('3b099fec-ddc5-4243-b30e-afb78d9ca14a')),
|
||||||
|
Permission('Cash Flow', uuid.UUID('c4d3ae29-420b-ea4c-ae90-00a356263fd9')),
|
||||||
|
Permission('Cost Centers', uuid.UUID('6fcc1a20-6aec-e840-b334-1632b34aeab8'))]
|
||||||
|
|
||||||
|
for permission in permissions:
|
||||||
|
DBSession.add(permission)
|
||||||
|
roles[0].permissions.append(permission)
|
||||||
|
|
||||||
|
transaction.commit()
|
||||||
|
|
149
soter/models/auth.py
Normal file
149
soter/models/auth.py
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
from hashlib import sha256
|
||||||
|
import uuid
|
||||||
|
from sqlalchemy import Column, Unicode, Boolean, Table, ForeignKey, UniqueConstraint
|
||||||
|
from sqlalchemy.orm import relationship, synonym
|
||||||
|
from soter.models import Base, DBSession
|
||||||
|
from soter.models.guidtype import GUID
|
||||||
|
|
||||||
|
|
||||||
|
def encrypt(val):
|
||||||
|
return sha256(val.encode('utf-8') + "Salt".encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
role_user = Table(
|
||||||
|
'role_users', Base.metadata,
|
||||||
|
Column('id', GUID(), primary_key=True, default=uuid.uuid4),
|
||||||
|
Column('user_id', GUID(), ForeignKey('users.id')),
|
||||||
|
Column('role_id', GUID(), ForeignKey('roles.id')),
|
||||||
|
UniqueConstraint('user_id', 'role_id')
|
||||||
|
)
|
||||||
|
|
||||||
|
role_permission = Table(
|
||||||
|
'role_permissions', Base.metadata,
|
||||||
|
Column('id', GUID(), primary_key=True, default=uuid.uuid4),
|
||||||
|
Column('permission_id', GUID(), ForeignKey('permissions.id')),
|
||||||
|
Column('role_id', GUID(), ForeignKey('roles.id')),
|
||||||
|
UniqueConstraint('permission_id', 'role_id')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = 'users'
|
||||||
|
|
||||||
|
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||||
|
name = Column('name', Unicode(255), unique=True)
|
||||||
|
display_name = Column('display_name', Unicode(255))
|
||||||
|
email = Column('email', Unicode(255), unique=True)
|
||||||
|
_password = Column('password', Unicode(64))
|
||||||
|
locked_out = Column('locked_out', Boolean)
|
||||||
|
|
||||||
|
roles = relationship("Role", secondary=role_user)
|
||||||
|
|
||||||
|
def _get_password(self):
|
||||||
|
return self._password
|
||||||
|
|
||||||
|
def _set_password(self, password):
|
||||||
|
self._password = encrypt(password)
|
||||||
|
|
||||||
|
password = property(_get_password, _set_password)
|
||||||
|
password = synonym('_password', descriptor=password)
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __name__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __init__(self, name=None, display_name=None, email=None, password=None, locked_out=None, id=None):
|
||||||
|
self.name = name
|
||||||
|
self.display_name = display_name
|
||||||
|
self.email = email
|
||||||
|
self.password = password
|
||||||
|
self.locked_out = locked_out
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_name(cls, name):
|
||||||
|
if not name:
|
||||||
|
return None
|
||||||
|
return DBSession.query(cls).filter(cls.name.ilike(name)).first()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_email(cls, email):
|
||||||
|
if not email:
|
||||||
|
return None
|
||||||
|
return DBSession.query(cls).filter(cls.email.ilike(email)).first()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_id(cls, id):
|
||||||
|
if not isinstance(id, uuid.UUID):
|
||||||
|
id = uuid.UUID(id)
|
||||||
|
return DBSession.query(cls).filter(cls.id == id).one()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def auth(cls, name, password):
|
||||||
|
if password is None:
|
||||||
|
return False, None
|
||||||
|
user = cls.by_name(name)
|
||||||
|
if not user:
|
||||||
|
return False, None
|
||||||
|
if user.password != encrypt(password) or user.locked_out:
|
||||||
|
return False, None
|
||||||
|
else:
|
||||||
|
return True, user
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls):
|
||||||
|
return DBSession.query(cls).order_by(cls.name).all()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def query(cls):
|
||||||
|
return DBSession.query(cls)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filtered_list(cls, name):
|
||||||
|
query = DBSession.query(cls)
|
||||||
|
for item in name.split():
|
||||||
|
query = query.filter(cls.name.ilike('%' + item + '%'))
|
||||||
|
return query.order_by(cls.name)
|
||||||
|
|
||||||
|
|
||||||
|
class Role(Base):
|
||||||
|
__tablename__ = 'roles'
|
||||||
|
|
||||||
|
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||||
|
name = Column('name', Unicode(255), unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, name=None, id=None):
|
||||||
|
self.name = name
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_id(cls, id):
|
||||||
|
return DBSession.query(cls).filter(cls.id == id).one()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls):
|
||||||
|
return DBSession.query(cls).order_by(cls.name).all()
|
||||||
|
|
||||||
|
|
||||||
|
class Permission(Base):
|
||||||
|
__tablename__ = 'permissions'
|
||||||
|
|
||||||
|
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||||
|
name = Column('name', Unicode(255), unique=True)
|
||||||
|
|
||||||
|
roles = relationship("Role", secondary=role_permission, backref="permissions")
|
||||||
|
|
||||||
|
def __init__(self, name=None, id=None):
|
||||||
|
self.name = name
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls):
|
||||||
|
return DBSession.query(cls).order_by(cls.name).all()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_id(cls, id):
|
||||||
|
return DBSession.query(cls).filter(cls.id == id).one()
|
||||||
|
|
56
soter/models/guidtype.py
Normal file
56
soter/models/guidtype.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
from sqlalchemy.types import TypeDecorator, CHAR, Binary
|
||||||
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
|
from sqlalchemy.dialects.sqlite import BLOB
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
class GUID(TypeDecorator):
|
||||||
|
"""Platform-independent GUID type.
|
||||||
|
|
||||||
|
Uses Postgresql's UUID type, otherwise uses
|
||||||
|
CHAR(32), storing as stringified hex values.
|
||||||
|
|
||||||
|
"""
|
||||||
|
impl = Binary
|
||||||
|
|
||||||
|
# if dialect.value == 'postgresql':
|
||||||
|
# impl = CHAR
|
||||||
|
# elif dialect.value == 'mysql':
|
||||||
|
# impl = MSBinary
|
||||||
|
# elif dialect.valie == 'sqlite':
|
||||||
|
# impl = Binary
|
||||||
|
# else:
|
||||||
|
# impl = Binary
|
||||||
|
|
||||||
|
def load_dialect_impl(self, dialect):
|
||||||
|
if dialect.name == 'postgresql':
|
||||||
|
return dialect.type_descriptor(UUID())
|
||||||
|
elif dialect.name == 'sqlite':
|
||||||
|
return dialect.type_descriptor(BLOB())
|
||||||
|
else:
|
||||||
|
return dialect.type_descriptor(CHAR(32))
|
||||||
|
|
||||||
|
def process_bind_param(self, value, dialect):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
elif dialect.name == 'postgresql':
|
||||||
|
return str(value)
|
||||||
|
elif not isinstance(value,uuid.UUID):
|
||||||
|
raise ValueError('value %s is not a valid uuid.UUID' % value)
|
||||||
|
else:
|
||||||
|
return value.bytes
|
||||||
|
# if not isinstance(value, uuid.UUID):
|
||||||
|
# return "%.32x" % uuid.UUID(value)
|
||||||
|
# else:
|
||||||
|
# # hexstring
|
||||||
|
# return "%.32x" % value
|
||||||
|
|
||||||
|
def process_result_value(self,value,dialect=None):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
elif isinstance(value, bytes):
|
||||||
|
return uuid.UUID(bytes=value)
|
||||||
|
else:
|
||||||
|
return uuid.UUID(value)
|
||||||
|
|
||||||
|
def is_mutable(self):
|
||||||
|
return False
|
135
soter/models/master.py
Normal file
135
soter/models/master.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import uuid
|
||||||
|
from sqlalchemy import Column, PickleType, Unicode, ForeignKey, Boolean, Table, UniqueConstraint
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
from soter.models import Base, DBSession
|
||||||
|
from soter.models.guidtype import GUID
|
||||||
|
|
||||||
|
|
||||||
|
class DbSetting(Base):
|
||||||
|
__tablename__ = 'settings'
|
||||||
|
|
||||||
|
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||||
|
name = Column('name', Unicode(255), unique=True, nullable=False)
|
||||||
|
data = Column('data', PickleType)
|
||||||
|
|
||||||
|
def __init__(self, id=None, name=None, data=None):
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_id(cls, id):
|
||||||
|
if not isinstance(id, uuid.UUID):
|
||||||
|
id = uuid.UUID(id)
|
||||||
|
return DBSession.query(cls).filter(cls.id == id).first()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_name(cls, name):
|
||||||
|
return DBSession.query(cls).filter(cls.name == name).first()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls):
|
||||||
|
return DBSession.query(cls).order_by(cls.name).all()
|
||||||
|
|
||||||
|
|
||||||
|
picture_tag = Table(
|
||||||
|
'picture_tags', Base.metadata,
|
||||||
|
Column('id', GUID(), primary_key=True, default=uuid.uuid4),
|
||||||
|
Column('picture_id', GUID(), ForeignKey('pictures.id'), nullable=False),
|
||||||
|
Column('tag_id', GUID(), ForeignKey('tags.id'), nullable=False),
|
||||||
|
UniqueConstraint('picture_id', 'tag_id')
|
||||||
|
)
|
||||||
|
|
||||||
|
class Album(Base):
|
||||||
|
__tablename__ = 'albums'
|
||||||
|
|
||||||
|
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||||
|
name = Column('name', Unicode(255), unique=True)
|
||||||
|
user_id = Column('user_id', GUID(), ForeignKey('users.id'), nullable=False)
|
||||||
|
is_fixture = Column('is_fixture', Boolean, nullable=False)
|
||||||
|
is_public = Column('is_public', Boolean, nullable=False)
|
||||||
|
|
||||||
|
user = relationship('User', primaryjoin="User.id==Album.user_id", cascade=None)
|
||||||
|
|
||||||
|
pictures = relationship('Picture', backref='album')
|
||||||
|
|
||||||
|
def __init__(self, name, user_id, is_public, id=None, is_fixture=False):
|
||||||
|
self.name = name
|
||||||
|
self.user_id = user_id
|
||||||
|
self.is_public = is_public
|
||||||
|
self.id = id
|
||||||
|
self.is_fixture = is_fixture
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls):
|
||||||
|
return DBSession.query(cls).order_by(cls.name)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_id(cls, id):
|
||||||
|
return DBSession.query(cls).filter(cls.id == id).first()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def menu_item(cls):
|
||||||
|
return uuid.UUID('dad46805-f577-4e5b-8073-9b788e0173fc')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def semi(cls):
|
||||||
|
return uuid.UUID('e6bf81b9-1e9b-499f-81d5-ab5662e9d9b1')
|
||||||
|
|
||||||
|
|
||||||
|
class Picture(Base):
|
||||||
|
__tablename__ = 'pictures'
|
||||||
|
|
||||||
|
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||||
|
name = Column('name', Unicode(255), unique=True)
|
||||||
|
user_id = Column('user_id', GUID(), ForeignKey('users.id'), nullable=False)
|
||||||
|
album_id = Column('album_id', GUID(), ForeignKey('albums.id'), nullable=False)
|
||||||
|
is_public = Column('is_public', Boolean, nullable=False)
|
||||||
|
|
||||||
|
user = relationship('User', primaryjoin="User.id==Picture.user_id", cascade=None)
|
||||||
|
|
||||||
|
def __init__(self, name, user_id, is_public, id=None, is_fixture=False):
|
||||||
|
self.name = name
|
||||||
|
self.user_id = user_id
|
||||||
|
self.is_public = is_public
|
||||||
|
self.id = id
|
||||||
|
self.is_fixture = is_fixture
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls):
|
||||||
|
return DBSession.query(cls).order_by(cls.name)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_id(cls, id):
|
||||||
|
return DBSession.query(cls).filter(cls.id == id).first()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def menu_item(cls):
|
||||||
|
return uuid.UUID('dad46805-f577-4e5b-8073-9b788e0173fc')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def semi(cls):
|
||||||
|
return uuid.UUID('e6bf81b9-1e9b-499f-81d5-ab5662e9d9b1')
|
||||||
|
|
||||||
|
|
||||||
|
class Tag(Base):
|
||||||
|
__tablename__ = 'tags'
|
||||||
|
|
||||||
|
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||||
|
name = Column('name', Unicode(100), unique=True, nullable=False)
|
||||||
|
|
||||||
|
def __init__(self, name=None):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_id(cls, id):
|
||||||
|
return DBSession.query(cls).filter(cls.id == id).first()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_name(cls, name):
|
||||||
|
return DBSession.query(cls).filter(cls.name == name).first()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls):
|
||||||
|
return DBSession.query(cls).all()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user