Source code for magpie.models

from magpie.api.exception import evaluate_call
from magpie.definitions.pyramid_definitions import ALLOW, ALL_PERMISSIONS, HTTPInternalServerError
from magpie.definitions.sqlalchemy_definitions import sa, declared_attr, relationship, declarative_base
from magpie.definitions.ziggurat_definitions import (
    get_db_session,
    permission_to_pyramid_acls,
    ziggurat_model_init,
    BaseModel,
    ExternalIdentityMixin,
    GroupMixin,
    GroupPermissionMixin,
    GroupResourcePermissionMixin,
    ResourceMixin,
    ResourceTreeService,
    ResourceTreeServicePostgreSQL,
    UserGroupMixin,
    UserMixin,
    UserPermissionMixin,
    UserResourcePermissionMixin,
    UserService,
    BaseService,
)
from magpie.permissions import Permission
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from magpie.definitions.typedefs import Str  # noqa: F401

[docs]Base = declarative_base()
[docs]def get_session_callable(request): return request.db
[docs]class Group(GroupMixin, Base):
[docs] def get_member_count(self, db_session=None): return BaseService.all(UserGroup, db_session=db_session).filter(UserGroup.group_id == self.id).count()
[docs]class GroupPermission(GroupPermissionMixin, Base): pass
[docs]class UserGroup(UserGroupMixin, Base): pass
[docs]class GroupResourcePermission(GroupResourcePermissionMixin, Base): pass
[docs]class Resource(ResourceMixin, Base): # required resource type identifier (unique)
[docs] resource_type_name = None # type: Str
[docs] child_resource_allowed = True
[docs] resource_display_name = sa.Column(sa.Unicode(100), nullable=True)
# reference to top-most service under which the resource is nested # if the resource is the service, id is None (NULL) @declared_attr
[docs] def root_service_id(self): return sa.Column(sa.Integer, sa.ForeignKey("services.resource_id", onupdate="CASCADE", ondelete="SET NULL"), index=True)
@property
[docs] def __acl__(self): acl = [] if self.owner_user_id: acl.extend([(ALLOW, self.owner_user_id, ALL_PERMISSIONS,), ]) if self.owner_group_id: acl.extend([(ALLOW, "group:%s" % self.owner_group_id, ALL_PERMISSIONS,), ]) return acl
[docs]class UserPermission(UserPermissionMixin, Base): pass
[docs]class UserResourcePermission(UserResourcePermissionMixin, Base): pass
[docs]class User(UserMixin, Base):
[docs] def __str__(self): return "<User: %s, %s>" % (self.id, self.user_name)
[docs]class ExternalIdentity(ExternalIdentityMixin, Base): pass
[docs]class RootFactory(object): def __init__(self, request): self.__acl__ = [] if request.user: permissions = UserService.permissions(request.user, request.db) self.__acl__.extend(permission_to_pyramid_acls(permissions))
[docs]class Service(Resource): """ Resource of `service` type. """
[docs] __tablename__ = u"services"
[docs] resource_id = sa.Column(sa.Integer(), sa.ForeignKey("resources.resource_id", onupdate="CASCADE", ondelete="CASCADE", ), primary_key=True, )
[docs] resource_type_name = u"service"
[docs] __mapper_args__ = {u"polymorphic_identity": resource_type_name, u"inherit_condition": resource_id == Resource.resource_id}
@property
[docs] def permissions(self): raise TypeError("Service permissions must be accessed by 'magpie.services.ServiceInterface' "
"instead of 'magpie.models.Service'.") @declared_attr
[docs] def url(self): # http://localhost:8083 return sa.Column(sa.UnicodeText(), unique=True)
@declared_attr
[docs] def type(self): """ Identifier matching ``magpie.services.ServiceInterface.service_type``. """ # wps, wms, thredds,... return sa.Column(sa.UnicodeText())
@declared_attr
[docs] def sync_type(self): """ Identifier matching ``magpie.helpers.SyncServiceInterface.sync_type``. """ # project-api, geoserver-api,... return sa.Column(sa.UnicodeText(), nullable=True)
@staticmethod
[docs] def by_service_name(service_name, db_session): db = get_db_session(db_session) service = db.query(Service).filter(Resource.resource_name == service_name).first() return service
[docs]class PathBase(object):
[docs] permissions = [ Permission.READ, Permission.WRITE, Permission.GET_CAPABILITIES, Permission.GET_MAP, Permission.GET_FEATURE_INFO, Permission.GET_LEGEND_GRAPHIC, Permission.GET_METADATA,
]
[docs]class File(Resource, PathBase):
[docs] child_resource_allowed = False
[docs] resource_type_name = u"file"
[docs] __mapper_args__ = {u"polymorphic_identity": resource_type_name}
[docs]class Directory(Resource, PathBase):
[docs] resource_type_name = u"directory"
[docs] __mapper_args__ = {u"polymorphic_identity": resource_type_name}
[docs]class Workspace(Resource):
[docs] resource_type_name = u"workspace"
[docs] __mapper_args__ = {u"polymorphic_identity": resource_type_name}
[docs] permissions = [ Permission.GET_CAPABILITIES, Permission.GET_MAP, Permission.GET_FEATURE_INFO, Permission.GET_LEGEND_GRAPHIC, Permission.GET_METADATA, Permission.GET_FEATURE, Permission.DESCRIBE_FEATURE_TYPE, Permission.LOCK_FEATURE, Permission.TRANSACTION,
]
[docs]class Route(Resource):
[docs] resource_type_name = u"route"
[docs] __mapper_args__ = {u"polymorphic_identity": resource_type_name}
[docs] permissions = [ Permission.READ, # access with inheritance (this route and all under it) Permission.WRITE, # access with inheritance (this route and all under it) Permission.READ_MATCH, # access without inheritance (only on this specific route) Permission.WRITE_MATCH, # access without inheritance (only on this specific route)
]
[docs]class RemoteResource(BaseModel, Base):
[docs] __tablename__ = "remote_resources"
[docs] __possible_permissions__ = ()
[docs] _ziggurat_services = [ResourceTreeService]
[docs] resource_id = sa.Column(sa.Integer(), primary_key=True, nullable=False, autoincrement=True)
[docs] service_id = sa.Column(sa.Integer(), sa.ForeignKey("services.resource_id", onupdate="CASCADE", ondelete="CASCADE"), index=True, nullable=False)
[docs] parent_id = sa.Column(sa.Integer(), sa.ForeignKey("remote_resources.resource_id", onupdate="CASCADE", ondelete="SET NULL"), nullable=True)
[docs] ordering = sa.Column(sa.Integer(), default=0, nullable=False)
[docs] resource_name = sa.Column(sa.Unicode(100), nullable=False)
[docs] resource_display_name = sa.Column(sa.Unicode(100), nullable=True)
[docs] resource_type = sa.Column(sa.Unicode(30), nullable=False)
[docs] def __repr__(self): info = self.resource_type, self.resource_name, self.resource_id, self.ordering, self.parent_id return "<RemoteResource: %s, %s, id: %s position: %s, parent_id: %s>" % info
[docs]class RemoteResourcesSyncInfo(BaseModel, Base):
[docs] __tablename__ = "remote_resources_sync_info"
[docs] id = sa.Column(sa.Integer(), primary_key=True, nullable=False, autoincrement=True)
[docs] service_id = sa.Column(sa.Integer(), sa.ForeignKey("services.resource_id", onupdate="CASCADE", ondelete="CASCADE"), index=True, nullable=False)
[docs] service = relationship("Service", foreign_keys=[service_id])
[docs] remote_resource_id = sa.Column(sa.Integer(), sa.ForeignKey("remote_resources.resource_id", onupdate="CASCADE", ondelete="CASCADE"))
[docs] last_sync = sa.Column(sa.DateTime(), nullable=True)
@staticmethod
[docs] def by_service_id(service_id, session): condition = RemoteResourcesSyncInfo.service_id == service_id service_info = session.query(RemoteResourcesSyncInfo).filter(condition).first() return service_info
[docs] def __repr__(self): last_modified = self.last_sync.strftime("%Y-%m-%dT%H:%M:%S") if self.last_sync else None info = self.service_id, last_modified, self.id return "<RemoteResourcesSyncInfo service_id: %s, last_sync: %s, id: %s>" % info
[docs]class RemoteResourceTreeService(ResourceTreeService): def __init__(self, service_cls): self.model = RemoteResource super(RemoteResourceTreeService, self).__init__(service_cls)
[docs]class RemoteResourceTreeServicePostgresSQL(ResourceTreeServicePostgreSQL): """ This is necessary, because ResourceTreeServicePostgresSQL.model is the Resource class. If we want to change it for a RemoteResource, we need this class. The ResourceTreeService.__init__ call sets the model. """ pass
ziggurat_model_init(User, Group, UserGroup, GroupPermission, UserPermission, UserResourcePermission, GroupResourcePermission, Resource, ExternalIdentity, passwordmanager=None)
[docs]resource_tree_service = ResourceTreeService(ResourceTreeServicePostgreSQL)
[docs]remote_resource_tree_service = RemoteResourceTreeService(RemoteResourceTreeServicePostgresSQL)
[docs]RESOURCE_TYPE_DICT = dict()
for res in [Service, Directory, File, Workspace, Route]: if res.resource_type_name in RESOURCE_TYPE_DICT: raise KeyError("Duplicate resource type identifiers not allowed") RESOURCE_TYPE_DICT[res.resource_type_name] = res
[docs]def resource_factory(**kwargs): resource_type = evaluate_call(lambda: kwargs["resource_type"], httpError=HTTPInternalServerError, msgOnFail="kwargs do not contain required 'resource_type'", content={u"kwargs": repr(kwargs)}) return evaluate_call(lambda: RESOURCE_TYPE_DICT[resource_type](**kwargs), httpError=HTTPInternalServerError, msgOnFail="kwargs unpacking failed from specified 'resource_type' and 'RESOURCE_TYPE_DICT'", content={u"kwargs": repr(kwargs), u"RESOURCE_TYPE_DICT": repr(RESOURCE_TYPE_DICT)})
[docs]def find_children_by_name(child_name, parent_id, db_session): tree_struct = resource_tree_service.from_parent_deeper(parent_id=parent_id, limit_depth=1, db_session=db_session) tree_level_entries = [node for node in tree_struct] tree_level_filtered = [node.Resource for node in tree_level_entries if node.Resource.resource_name.lower() == child_name.lower()] return tree_level_filtered.pop() if len(tree_level_filtered) else None