コード例 #1
0
class Permission(db.Model, IdModel, SoftDeleteModel):
    """A set of rights granted to a role on a resource."""

    __tablename__ = 'permission'

    COLLECTION = 'collection'
    SOURCE = 'source'
    RESOURCE_TYPES = [COLLECTION, SOURCE]

    id = db.Column(db.Integer, primary_key=True)
    role_id = db.Column(db.Integer, db.ForeignKey('role.id'), index=True)
    read = db.Column(db.Boolean, default=False)
    write = db.Column(db.Boolean, default=False)
    resource_id = db.Column(db.Integer, nullable=False)
    resource_type = db.Column(db.Enum(*RESOURCE_TYPES, name='permission_type'),
                              nullable=False)

    @classmethod
    def grant_foreign(cls, resource, foreign_id, read, write):
        from aleph.model import Source, Collection, Role
        role = Role.by_foreign_id(foreign_id)
        if role is None:
            return
        if isinstance(resource, Source):
            cls.grant_resource(cls.SOURCE, resource.id, role, read, write)
        if isinstance(resource, Collection):
            cls.grant_resource(cls.COLLECTION, resource.id, role, read, write)

    @classmethod
    def grant_resource(cls, resource_type, resource_id, role, read, write):
        q = cls.all()
        q = q.filter(Permission.role_id == role.id)
        q = q.filter(Permission.resource_type == resource_type)
        q = q.filter(Permission.resource_id == resource_id)
        permission = q.first()
        if permission is None:
            permission = Permission()
            permission.role_id = role.id
            permission.resource_type = resource_type
            permission.resource_id = resource_id
        permission.read = read
        permission.write = write
        db.session.add(permission)
        return permission

    def to_dict(self):
        return {
            # 'id': self.id,
            'role': self.role_id,
            'read': self.read,
            'write': self.write,
            'resource_id': self.resource_id,
            'resource_type': self.resource_type
        }
コード例 #2
0
ファイル: role.py プロジェクト: x0rzkov/aleph
class Role(db.Model, IdModel, SoftDeleteModel):
    """A user, group or other access control subject."""
    __tablename__ = 'role'

    USER = '******'
    GROUP = 'group'
    SYSTEM = 'system'
    TYPES = [USER, GROUP, SYSTEM]

    SYSTEM_GUEST = 'guest'
    SYSTEM_USER = '******'

    #: Generates URL-safe signatures for invitations.
    SIGNATURE = URLSafeTimedSerializer(settings.SECRET_KEY)

    #: Signature maximum age, defaults to 1 day
    SIGNATURE_MAX_AGE = 60 * 60 * 24

    foreign_id = db.Column(db.Unicode(2048), nullable=False, unique=True)
    name = db.Column(db.Unicode, nullable=False)
    email = db.Column(db.Unicode, nullable=True)
    type = db.Column(db.Enum(*TYPES, name='role_type'), nullable=False)
    api_key = db.Column(db.Unicode, nullable=True)
    is_admin = db.Column(db.Boolean, nullable=False, default=False)
    is_muted = db.Column(db.Boolean, nullable=False, default=False)
    is_tester = db.Column(db.Boolean, nullable=False, default=False)
    is_blocked = db.Column(db.Boolean, nullable=False, default=False)
    password_digest = db.Column(db.Unicode, nullable=True)
    password = None
    reset_token = db.Column(db.Unicode, nullable=True)
    locale = db.Column(db.Unicode, nullable=True)

    permissions = db.relationship('Permission', backref='role')

    @property
    def has_password(self):
        return self.password_digest is not None

    @property
    def is_public(self):
        return self.id in self.public_roles()

    @property
    def is_alertable(self):
        if self.email is None:
            return False
        if self.is_muted is True:
            return False
        # TODO: ignore people that have not logged in for a certain time?
        return True

    @property
    def label(self):
        return anonymize_email(self.name, self.email)

    def update(self, data):
        self.name = data.get('name', self.name)
        self.is_muted = data.get('is_muted', self.is_muted)
        self.is_tester = data.get('is_tester', self.is_tester)
        if data.get('password'):
            self.set_password(data.get('password'))
        self.locale = stringify(data.get('locale', self.locale))
        self.updated_at = datetime.utcnow()

    def clear_roles(self):
        """Removes any existing roles from group membership."""
        self.roles = []
        self.updated_at = datetime.utcnow()
        db.session.add(self)
        db.session.flush()

    def add_role(self, role):
        """Adds an existing role as a membership of a group."""
        self.roles.append(role)
        db.session.add(role)
        db.session.add(self)
        self.updated_at = datetime.utcnow()

    def to_dict(self):
        data = self.to_dict_dates()
        data.update({
            'id': stringify(self.id),
            'type': self.type,
            'name': self.name,
            'label': self.label,
            'email': self.email,
            'locale': self.locale,
            'api_key': self.api_key,
            'is_admin': self.is_admin,
            'is_muted': self.is_muted,
            'is_tester': self.is_tester,
            'has_password': self.has_password,
            # 'notified_at': self.notified_at
        })
        return data

    @classmethod
    def by_foreign_id(cls, foreign_id):
        if foreign_id is not None:
            return cls.all().filter_by(foreign_id=foreign_id).first()

    @classmethod
    def by_email(cls, email):
        if email is None:
            return None
        q = cls.all()
        q = q.filter(func.lower(cls.email) == email.lower())
        return q.first()

    @classmethod
    def by_api_key(cls, api_key):
        if api_key is not None:
            return cls.all().filter_by(api_key=api_key).first()

    @classmethod
    def load_or_create(cls, foreign_id, type, name, email=None, is_admin=None):
        role = cls.by_foreign_id(foreign_id)

        if role is None:
            role = cls()
            role.foreign_id = foreign_id
            role.name = name or email
            role.type = type
            role.is_admin = False
            role.is_muted = False
            role.is_tester = False
            role.is_blocked = False
            role.notified_at = datetime.utcnow()

        if role.api_key is None:
            role.api_key = make_textid()

        if email is not None:
            role.email = email

        if is_admin is not None:
            role.is_admin = is_admin

        # see: https://github.com/alephdata/aleph/issues/111
        auto_admins = [a.lower() for a in settings.ADMINS]
        if email is not None and email.lower() in auto_admins:
            role.is_admin = True

        db.session.add(role)
        db.session.flush()
        return role

    @classmethod
    def load_cli_user(cls):
        return cls.load_or_create(foreign_id=settings.SYSTEM_USER,
                                  name='Aleph',
                                  type=cls.USER,
                                  is_admin=True)

    @classmethod
    def load_id(cls, foreign_id):
        """Load a role and return the ID."""
        if not hasattr(settings, '_roles'):
            settings._roles = {}
        if foreign_id not in settings._roles:
            role_id = cls.all_ids().filter_by(foreign_id=foreign_id).first()
            if role_id is not None:
                settings._roles[foreign_id] = role_id[0]
        return settings._roles.get(foreign_id)

    @classmethod
    def public_roles(cls):
        """Roles which make a collection to be considered public."""
        return set([
            cls.load_id(cls.SYSTEM_USER),
            cls.load_id(cls.SYSTEM_GUEST),
        ])

    @classmethod
    def by_prefix(cls, prefix, exclude=[]):
        """Load a list of roles matching a name, email address, or foreign_id.

        :param str pattern: Pattern to match.
        """
        query = prefix.replace('%', ' ').replace('_', ' ')
        query = '%%%s%%' % query
        q = cls.all()
        q = q.filter(Role.type == Role.USER)
        if len(exclude):
            q = q.filter(not_(Role.id.in_(exclude)))
        q = q.filter(
            or_(
                func.lower(cls.email) == prefix.lower(),
                cls.name.ilike(query)))
        q = q.order_by(Role.id.asc())
        return q

    @classmethod
    def all_groups(cls, authz):
        q = cls.all()
        q = q.filter(Role.type == Role.GROUP)
        q = q.order_by(Role.name.asc())
        q = q.order_by(Role.foreign_id.asc())
        if not authz.is_admin:
            q = q.filter(Role.id.in_(authz.roles))
        return q

    @classmethod
    def all_users(cls):
        return cls.all().filter(Role.type == Role.USER)

    @classmethod
    def all_system(cls):
        return cls.all().filter(Role.type == Role.SYSTEM)

    def set_password(self, secret):
        """Hashes and sets the role password.

        :param str secret: The password to be set.
        """
        self.password_digest = generate_password_hash(secret)

    def check_password(self, secret):
        """Checks the password if it matches the role password hash.

        :param str secret: The password to be checked.
        :rtype: bool
        """
        digest = self.password_digest or ''
        return check_password_hash(digest, secret)

    def __repr__(self):
        return '<Role(%r,%r)>' % (self.id, self.foreign_id)
コード例 #3
0
ファイル: entityset.py プロジェクト: rmallof/aleph
class EntitySetItem(db.Model, SoftDeleteModel):
    __tablename__ = "entityset_item"

    id = db.Column(db.Integer, primary_key=True)
    entityset_id = db.Column(db.String(ENTITY_ID_LEN),
                             db.ForeignKey("entityset.id"),
                             index=True)
    entity_id = db.Column(db.String(ENTITY_ID_LEN), index=True)
    collection_id = db.Column(db.Integer,
                              db.ForeignKey("collection.id"),
                              index=True)

    compared_to_entity_id = db.Column(db.String(ENTITY_ID_LEN))
    added_by_id = db.Column(db.Integer, db.ForeignKey("role.id"))
    judgement = db.Column(db.Enum(Judgement))

    entityset = db.relationship(EntitySet)
    collection = db.relationship(Collection)
    added_by = db.relationship(Role)

    @classmethod
    def by_entity_id(cls, entityset, entity_id):
        q = cls.all()
        q = q.filter(cls.entityset_id == entityset.id)
        q = q.filter(cls.entity_id == entity_id)
        q = q.order_by(cls.created_at.desc())
        return q.first()

    @classmethod
    def save(cls,
             entityset,
             entity_id,
             judgement=None,
             collection_id=None,
             **data):
        if judgement is None:
            judgement = Judgement.POSITIVE
        else:
            judgement = Judgement(judgement)
        existing = cls.by_entity_id(entityset, entity_id)
        if existing is not None:
            if existing.judgement == judgement:
                return existing
            existing.delete()
        if judgement == Judgement.NO_JUDGEMENT:
            return
        item = cls(
            entityset_id=entityset.id,
            entity_id=entity_id,
            judgement=judgement,
            compared_to_entity_id=data.get("compared_to_entity_id"),
            collection_id=collection_id or entityset.collection_id,
            added_by_id=data.get("added_by_id"),
        )
        db.session.add(item)
        return item

    @classmethod
    def delete_by_collection(cls, collection_id):
        pq = db.session.query(cls)
        pq = pq.filter(cls.collection_id == collection_id)
        pq.delete(synchronize_session=False)

        pq = db.session.query(cls)
        pq = pq.filter(EntitySet.collection_id == collection_id)
        pq = pq.filter(EntitySet.id == cls.entityset_id)
        pq.delete(synchronize_session=False)

    @classmethod
    def delete_by_entity(cls, entity_id):
        pq = db.session.query(cls)
        pq = pq.filter(cls.entity_id == entity_id)
        pq.delete(synchronize_session=False)

    def to_dict(self):
        data = self.to_dict_dates()
        data.update({
            "entityset_id": self.entityset_id,
            "entity_id": self.entity_id,
            "collection_id": self.collection_id,
            "added_by_id": self.added_by_id,
            "compared_to_entity_id": self.compared_to_entity_id,
        })
        if self.judgement:
            data["judgement"] = self.judgement.value
        return data

    def __repr__(self):
        return "<EntitySetItem(%r, %r)>" % (self.entityset_id, self.entity_id)
コード例 #4
0
class Role(db.Model, IdModel, SoftDeleteModel):
    """A user, group or other access control subject."""
    __tablename__ = 'role'

    USER = '******'
    GROUP = 'group'
    SYSTEM = 'system'
    TYPES = [USER, GROUP, SYSTEM]

    SYSTEM_GUEST = 'guest'
    SYSTEM_USER = '******'

    #: Generates URL-safe signatures for invitations.
    SIGNATURE = URLSafeTimedSerializer(settings.SECRET_KEY)

    #: Signature maximum age, defaults to 1 day
    SIGNATURE_MAX_AGE = 60 * 60 * 24

    #: Password minimum length
    PASSWORD_MIN_LENGTH = 6

    foreign_id = db.Column(db.Unicode(2048), nullable=False, unique=True)
    name = db.Column(db.Unicode, nullable=False)
    email = db.Column(db.Unicode, nullable=True)
    api_key = db.Column(db.Unicode, nullable=True)
    is_admin = db.Column(db.Boolean, nullable=False, default=False)
    type = db.Column(db.Enum(*TYPES, name='role_type'), nullable=False)
    password_digest = db.Column(db.Unicode, nullable=True)
    password = None
    reset_token = db.Column(db.Unicode, nullable=True)
    permissions = db.relationship('Permission', backref='role')

    @property
    def has_password(self):
        return self.password_digest is not None

    def update(self, data):
        self.name = data.get('name', self.name)
        if data.get('password'):
            self.set_password(data.get('password'))

    def clear_roles(self):
        """Removes any existing roles from group membership."""
        self.roles = []
        db.session.add(self)

    def add_role(self, role):
        """Adds an existing role as a membership of a group."""
        self.roles.append(role)
        db.session.add(role)
        db.session.add(self)

    @classmethod
    def notifiable(cls):
        return cls.all_ids().filter(cls.email != None)  # noqa

    @classmethod
    def by_foreign_id(cls, foreign_id):
        if foreign_id is not None:
            return cls.all().filter_by(foreign_id=foreign_id).first()

    @classmethod
    def by_email(cls, email):
        if email:
            return cls.all().filter_by(email=email)

    @classmethod
    def by_api_key(cls, api_key):
        if api_key is not None:
            return cls.all().filter_by(api_key=api_key).first()

    @classmethod
    def load_or_create(cls, foreign_id, type, name, email=None, is_admin=None):
        role = cls.by_foreign_id(foreign_id)

        if role is None:
            role = cls()
            role.foreign_id = foreign_id
            role.name = name
            role.type = type
            role.is_admin = False

        if role.api_key is None:
            role.api_key = make_textid()

        role.email = email
        if is_admin is not None:
            role.is_admin = is_admin

        # see: https://github.com/alephdata/aleph/issues/111
        auto_admins = [a.lower() for a in settings.ADMINS]
        if email is not None and email.lower() in auto_admins:
            role.is_admin = True

        db.session.add(role)
        db.session.flush()

        return role

    @classmethod
    def load_id(cls, foreign_id, type=None, name=None):
        """Load a role and return the ID.

        If type is given and no role is found, a new role will be created.
        """
        if not hasattr(current_app, '_authz_roles'):
            current_app._authz_roles = {}
        if foreign_id not in current_app._authz_roles:
            role = cls.by_foreign_id(foreign_id)
            if role is None:
                if type is None:
                    return
                name = name or foreign_id
                role = cls.load_or_create(foreign_id, type, name)
            current_app._authz_roles[foreign_id] = role.id
        return current_app._authz_roles[foreign_id]

    @classmethod
    def public_roles(cls):
        """Roles which make a collection to be considered public."""
        return set([
            cls.load_id(cls.SYSTEM_USER),
            cls.load_id(cls.SYSTEM_GUEST),
        ])

    @classmethod
    def by_prefix(cls, prefix):
        """Load a list of roles matching a name, email address, or foreign_id.

        :param str pattern: Pattern to match.
        """
        q = cls.all()
        q = q.filter(Role.type == Role.USER)
        q = q.filter(
            or_(cls.foreign_id.ilike('%' + prefix + '%'),
                cls.email.ilike('%' + prefix + '%'),
                cls.name.ilike('%' + prefix + '%')))
        return q

    @classmethod
    def all_groups(cls):
        return cls.all().filter(Role.type != Role.USER)

    def set_password(self, secret):
        """Hashes and sets the role password.

        :param str secret: The password to be set.
        """
        self.password_digest = generate_password_hash(secret)

    def check_password(self, secret):
        """Checks the password if it matches the role password hash.

        :param str secret: The password to be checked.
        :rtype: bool
        """
        return check_password_hash(self.password_digest or '', secret)

    def __repr__(self):
        return '<Role(%r,%r)>' % (self.id, self.foreign_id)
コード例 #5
0
class Role(db.Model, IdModel, SoftDeleteModel):
    """A user, group or other access control subject."""

    _schema = 'role.json#'
    __tablename__ = 'role'

    USER = '******'
    GROUP = 'group'
    SYSTEM = 'system'
    TYPES = [USER, GROUP, SYSTEM]

    SYSTEM_GUEST = 'guest'
    SYSTEM_USER = '******'

    foreign_id = db.Column(db.Unicode(2048), nullable=False, unique=True)
    name = db.Column(db.Unicode, nullable=False)
    email = db.Column(db.Unicode, nullable=True)
    api_key = db.Column(db.Unicode, nullable=True)
    is_admin = db.Column(db.Boolean, nullable=False, default=False)
    type = db.Column(db.Enum(*TYPES, name='role_type'), nullable=False)
    permissions = db.relationship("Permission", backref="role")

    def update(self, data):
        validate(data, self._schema)
        self.name = data.get('name', self.name)
        self.email = data.get('email', self.email)

    def clear_roles(self):
        self.roles = []
        db.session.add(self)

    def add_role(self, role):
        self.roles.append(role)
        db.session.add(role)
        db.session.add(self)

    @classmethod
    def notifiable(cls):
        return cls.all_ids().filter(cls.email != None)  # noqa

    @classmethod
    def by_foreign_id(cls, foreign_id):
        if foreign_id is not None:
            return cls.all().filter_by(foreign_id=foreign_id).first()

    @classmethod
    def by_api_key(cls, api_key):
        if api_key is not None:
            return cls.all().filter_by(api_key=api_key).first()

    @classmethod
    def load_or_create(cls, foreign_id, type, name, email=None, is_admin=None):
        role = cls.by_foreign_id(foreign_id)
        if role is None:
            role = cls()
            role.foreign_id = foreign_id
            role.name = name
            role.type = type
            role.is_admin = False

        if role.api_key is None:
            role.api_key = uuid4().hex

        role.email = email
        if is_admin is not None:
            role.is_admin = is_admin

        # see: https://github.com/pudo/aleph/issues/111
        auto_admins = get_config('AUTHZ_ADMINS') or ''
        auto_admins = [a.lower() for a in auto_admins.split(',')]
        if email is not None and email.lower() in auto_admins:
            role.is_admin = True

        db.session.add(role)
        db.session.flush()
        return role

    @classmethod
    def load_id(cls, foreign_id, type=None, name=None):
        """Load a role and return the ID.

        If type is given and no role is found, a new role will be created.
        """
        if not hasattr(current_app, '_authz_roles'):
            current_app._authz_roles = {}
        if foreign_id not in current_app._authz_roles:
            role = cls.by_foreign_id(foreign_id)
            if role is None:
                if type is None:
                    return
                name = name or foreign_id
                role = cls.load_or_create(foreign_id, type, name)
            current_app._authz_roles[foreign_id] = role.id
        return current_app._authz_roles[foreign_id]

    def __repr__(self):
        return '<Role(%r,%r)>' % (self.id, self.foreign_id)

    def __unicode__(self):
        return self.name

    def to_dict(self):
        data = super(Role, self).to_dict()
        data.update({
            'api_url': url_for('roles_api.view', id=self.id),
            'foreign_id': self.foreign_id,
            'is_admin': self.is_admin,
            'email': self.email,
            'name': self.name,
            'type': self.type
        })
        return data
コード例 #6
0
class Role(db.Model, IdModel, SoftDeleteModel):
    """A user, group or other access control subject."""

    _schema = 'role.json#'
    __tablename__ = 'role'

    USER = '******'
    GROUP = 'group'
    SYSTEM = 'system'
    TYPES = [USER, GROUP, SYSTEM]

    SYSTEM_GUEST = 'guest'
    SYSTEM_USER = '******'

    #: Generates URL-safe signatures for invitations.
    SIGNATURE_SERIALIZER = URLSafeTimedSerializer(secret_key)

    #: Signature maximum age, defaults to 1 day
    SIGNATURE_MAX_AGE = 60 * 60 * 24

    #: Password minimum length
    PASSWORD_MIN_LENGTH = 6

    foreign_id = db.Column(db.Unicode(2048), nullable=False, unique=True)
    name = db.Column(db.Unicode, nullable=False)
    email = db.Column(db.Unicode, nullable=True)
    api_key = db.Column(db.Unicode, nullable=True)
    is_admin = db.Column(db.Boolean, nullable=False, default=False)
    type = db.Column(db.Enum(*TYPES, name='role_type'), nullable=False)
    password_digest = db.Column(db.Unicode, nullable=True)
    reset_token = db.Column(db.Unicode, nullable=True)
    permissions = db.relationship('Permission', backref='role')

    def update(self, data):
        validate(data, self._schema)
        self.name = data.get('name', self.name)
        self.email = data.get('email', self.email)

    def clear_roles(self):
        """Removes any existing roles from group membership."""
        self.roles = []
        db.session.add(self)

    def add_role(self, role):
        """Adds an existing role as a membership of a group."""
        self.roles.append(role)
        db.session.add(role)
        db.session.add(self)

    @classmethod
    def notifiable(cls):
        return cls.all_ids().filter(cls.email != None)  # noqa

    @classmethod
    def by_foreign_id(cls, foreign_id):
        if foreign_id is not None:
            return cls.all().filter_by(foreign_id=foreign_id).first()

    @classmethod
    def by_email(cls, email):
        if email:
            return cls.all().filter_by(email=email)

    @classmethod
    def by_api_key(cls, api_key):
        if api_key is not None:
            return cls.all().filter_by(api_key=api_key).first()

    @classmethod
    def load_or_create(cls, foreign_id, type, name, email=None, is_admin=None):
        role = cls.by_foreign_id(foreign_id)

        if role is None:
            role = cls()
            role.foreign_id = foreign_id
            role.name = name
            role.type = type
            role.is_admin = False

        if role.api_key is None:
            role.api_key = make_textid()

        role.email = email
        if is_admin is not None:
            role.is_admin = is_admin

        # see: https://github.com/pudo/aleph/issues/111
        auto_admins = [a.lower() for a in get_config('AUTHZ_ADMINS')]
        if email is not None and email.lower() in auto_admins:
            role.is_admin = True

        db.session.add(role)
        db.session.flush()

        return role

    @classmethod
    def load_id(cls, foreign_id, type=None, name=None):
        """Load a role and return the ID.

        If type is given and no role is found, a new role will be created.
        """
        if not hasattr(current_app, '_authz_roles'):
            current_app._authz_roles = {}
        if foreign_id not in current_app._authz_roles:
            role = cls.by_foreign_id(foreign_id)
            if role is None:
                if type is None:
                    return
                name = name or foreign_id
                role = cls.load_or_create(foreign_id, type, name)
            current_app._authz_roles[foreign_id] = role.id
        return current_app._authz_roles[foreign_id]

    def set_password(self, secret):
        """Hashes and sets the role password.

        :param str secret: The password to be set.
        """
        self.password_digest = generate_password_hash(secret)

    def check_password(self, secret):
        """Checks the password if it matches the role password hash.

        :param str secret: The password to be checked.
        :rtype: bool
        """
        return check_password_hash(self.password_digest or '', secret)

    def __repr__(self):
        return '<Role(%r,%r)>' % (self.id, self.foreign_id)

    def __unicode__(self):
        return self.name

    def to_dict(self):
        data = super(Role, self).to_dict()
        data.update({
            'api_url': url_for('roles_api.view', id=self.id),
            'foreign_id': self.foreign_id,
            'is_admin': self.is_admin,
            'email': self.email,
            'name': self.name,
            'type': self.type
        })
        return data
コード例 #7
0
ファイル: role.py プロジェクト: tomjie/aleph
class Role(db.Model, IdModel, SoftDeleteModel, SchemaModel):
    """A user, group or other access control subject."""

    _schema = 'role.json#'
    __tablename__ = 'role'

    USER = '******'
    GROUP = 'group'
    SYSTEM = 'system'
    TYPES = [USER, GROUP, SYSTEM]

    SYSTEM_GUEST = 'guest'
    SYSTEM_USER = '******'

    foreign_id = db.Column(db.Unicode(2048), nullable=False, unique=True)
    name = db.Column(db.Unicode, nullable=False)
    email = db.Column(db.Unicode, nullable=True)
    api_key = db.Column(db.Unicode, nullable=True)
    is_admin = db.Column(db.Boolean, nullable=False, default=False)
    type = db.Column(db.Enum(*TYPES, name='role_type'), nullable=False)
    permissions = db.relationship("Permission", backref="role")

    def update(self, data):
        self.schema_update(data)

    @classmethod
    def notifiable(cls):
        return cls.all_ids().filter(cls.email != None)  # noqa

    @classmethod
    def by_foreign_id(cls, foreign_id):
        if foreign_id is not None:
            return cls.all().filter_by(foreign_id=foreign_id).first()

    @classmethod
    def by_api_key(cls, api_key):
        if api_key is not None:
            return cls.all().filter_by(api_key=api_key).first()

    @classmethod
    def load_or_create(cls, foreign_id, type, name, email=None, is_admin=None):
        role = cls.by_foreign_id(foreign_id)
        if role is None:
            role = cls()
            role.foreign_id = foreign_id
            role.type = type
            role.is_admin = False

        if role.api_key is None:
            role.api_key = uuid4().hex
        role.name = name
        role.email = email

        if is_admin is not None:
            role.is_admin = is_admin

        db.session.add(role)
        db.session.flush()
        return role

    @classmethod
    def system(cls, foreign_id):
        if not hasattr(current_app, '_authz_roles'):
            current_app._authz_roles = {}
        if foreign_id not in current_app._authz_roles:
            role = cls.by_foreign_id(foreign_id)
            if role is None:
                return
            current_app._authz_roles[foreign_id] = role.id
        return current_app._authz_roles[foreign_id]

    def __repr__(self):
        return '<Role(%r,%r)>' % (self.id, self.foreign_id)

    def __unicode__(self):
        return self.name

    def to_dict(self):
        data = super(Role, self).to_dict()
        data['api_url'] = url_for('roles_api.view', id=self.id)
        data['foreign_id'] = self.foreign_id
        data['is_admin'] = self.is_admin
        data['email'] = self.email
        data['type'] = self.type
        return data
コード例 #8
0
class EntitySetItem(db.Model, SoftDeleteModel):
    __tablename__ = "entityset_item"

    id = db.Column(db.Integer, primary_key=True)
    entityset_id = db.Column(db.String(ENTITY_ID_LEN),
                             db.ForeignKey("entityset.id"),
                             index=True)
    entity_id = db.Column(db.String(ENTITY_ID_LEN), index=True)
    collection_id = db.Column(db.Integer,
                              db.ForeignKey("collection.id"),
                              index=True)

    compared_to_entity_id = db.Column(db.String(ENTITY_ID_LEN))
    added_by_id = db.Column(db.Integer, db.ForeignKey("role.id"))
    judgement = db.Column(db.Enum(Judgement))

    entityset = db.relationship(EntitySet)
    collection = db.relationship(Collection)
    added_by = db.relationship(Role)

    @classmethod
    def by_entity_id(cls, entityset, entity_id):
        q = cls.all()
        q = q.filter(cls.entityset_id == entityset.id)
        q = q.filter(cls.entity_id == entity_id)
        q = q.order_by(cls.created_at.desc())
        return q.first()

    @classmethod
    def save(cls,
             entityset,
             entity_id,
             judgement=None,
             collection_id=None,
             **data):
        if judgement is None:
            judgement = Judgement.POSITIVE
        else:
            judgement = Judgement(judgement)

        # Special case for profiles: an entity can only be part of one profile
        # in any given collection. An attempt to add it to a second profile must
        # result in a merging of both profiles.
        if entityset.type == EntitySet.PROFILE and judgement == Judgement.POSITIVE:
            for existing in EntitySet.by_entity_id(
                    entity_id,
                    collection_ids=[entityset.collection_id],
                    judgements=[Judgement.POSITIVE],
                    types=[EntitySet.PROFILE],
            ):
                entityset = entityset.merge(existing, entity_id)

        # Check if there is an existing relationship between the entity and the
        # entity set. If the judgement matches, no-op - otherwise delete the
        # previous relationship item.
        existing = cls.by_entity_id(entityset, entity_id)
        if existing is not None:
            if existing.judgement == judgement:
                return existing
            existing.delete()

        # There is no judgement information to be stored, so let's keep that out
        # of the database:
        if judgement == Judgement.NO_JUDGEMENT:
            return

        item = cls(
            entityset=entityset,
            entity_id=entity_id,
            judgement=judgement,
            compared_to_entity_id=data.get("compared_to_entity_id"),
            collection_id=collection_id or entityset.collection_id,
            added_by_id=data.get("added_by_id"),
        )
        db.session.add(item)
        return item

    @classmethod
    def delete_by_collection(cls, collection_id):
        pq = db.session.query(cls)
        pq = pq.filter(cls.collection_id == collection_id)
        pq.delete(synchronize_session=False)

        pq = db.session.query(cls)
        pq = pq.filter(EntitySet.collection_id == collection_id)
        pq = pq.filter(EntitySet.id == cls.entityset_id)
        pq.delete(synchronize_session=False)

    @classmethod
    def delete_by_entity(cls, entity_id):
        pq = db.session.query(cls)
        pq = pq.filter(cls.entity_id == entity_id)
        pq.delete(synchronize_session=False)

    def to_dict(self, entityset=None):
        data = {
            "id": "$".join((self.entityset_id, self.entity_id)),
            "entity_id": self.entity_id,
            "collection_id": self.collection_id,
            "added_by_id": self.added_by_id,
            "judgement": self.judgement,
            "compared_to_entity_id": self.compared_to_entity_id,
        }
        entityset = entityset or self.entityset
        data["entityset_collection_id"] = entityset.collection_id
        data["entityset_id"] = entityset.id
        data.update(self.to_dict_dates())
        return data

    def __repr__(self):
        return "<EntitySetItem(%r, %r)>" % (self.entityset_id, self.entity_id)
コード例 #9
0
class Role(db.Model, IdModel, SoftDeleteModel, SchemaModel):
    """A user, group or other access control subject."""

    _schema = 'role.json#'
    __tablename__ = 'role'

    USER = '******'
    GROUP = 'group'
    SYSTEM = 'system'
    TYPES = [USER, GROUP, SYSTEM]

    SYSTEM_GUEST = 'guest'
    SYSTEM_USER = '******'

    foreign_id = db.Column(db.Unicode(2048), nullable=False, unique=True)
    name = db.Column(db.Unicode, nullable=False)
    email = db.Column(db.Unicode, nullable=False, unique=True)
    api_key = db.Column(db.Unicode, nullable=True)
    is_admin = db.Column(db.Boolean, nullable=False, default=False)
    type = db.Column(db.Enum(*TYPES, name='role_type'), nullable=False)
    permissions = db.relationship("Permission", backref="role")

    confirmed_at = db.Column(db.DateTime())
    password = db.Column(db.String(255), nullable=False, server_default='')
    reset_password_token = db.Column(db.String(100),
                                     nullable=False,
                                     server_default='')

    mailing_list = db.Column(db.Boolean, default=False)

    def update(self, data):
        print('---role update---')
        print(data)
        self.schema_update(data)

    def check_pw(self, pw):
        return pwd_context.verify(pw, self.password)

    @classmethod
    def notifiable(cls):
        return cls.all_ids().filter(cls.email != None)  # noqa

    @classmethod
    def by_foreign_id(cls, foreign_id):
        if foreign_id is not None:
            return cls.all().filter_by(foreign_id=foreign_id).first()

    @classmethod
    def by_api_key(cls, api_key):
        if api_key is not None:
            return cls.all().filter_by(api_key=api_key).first()

    @classmethod
    def load_or_create(cls,
                       foreign_id,
                       type,
                       name,
                       email=None,
                       is_admin=None,
                       mailing_list=False):
        role = cls.by_foreign_id(foreign_id)
        if role is None:
            role = cls()
            role.foreign_id = foreign_id
            role.type = type
            role.is_admin = False
            role.mailing_list = mailing_list

        if role.api_key is None:
            role.api_key = uuid4().hex
        role.name = name
        role.email = email

        if is_admin is not None:
            role.is_admin = is_admin

        db.session.add(role)
        db.session.flush()
        return role

    @classmethod
    def system(cls, foreign_id):
        if not hasattr(current_app, '_authz_roles'):
            current_app._authz_roles = {}
        if foreign_id not in current_app._authz_roles:
            role = cls.by_foreign_id(foreign_id)
            if role is None:
                return
            current_app._authz_roles[foreign_id] = role.id
        return current_app._authz_roles[foreign_id]

    @classmethod
    def by_email(cls, email):
        q = db.session.query(cls).filter_by(email=email)
        return q.first()

    @classmethod
    def create_by_email(cls, email, pw, mailing_list=None):
        src = cls(
            email=email,
            name=email,  # XXX ask for this in the registration form
            password=pwd_context.encrypt(pw),
            type=cls.USER,
            mailing_list=mailing_list,
            foreign_id="openoil:%s" % email)  # XXX do we really need this?
        db.session.add(src)
        db.session.commit()
        send_welcome_mail(src)
        return src

    @classmethod
    def create(cls, data, user=None):
        src = cls()
        data = SourceCreateForm().deserialize(data)
        src.slug = data.get('slug')
        src.crawler = data.get('crawler')
        src.update_data(data, user)
        db.session.add(src)
        return src

    def __repr__(self):
        return '<Role(%r,%r)>' % (self.id, self.foreign_id)

    def __unicode__(self):
        return self.name

    def to_dict(self):
        data = super(Role, self).to_dict()
        data['api_url'] = url_for('roles_api.view', id=self.id)
        data['foreign_id'] = self.foreign_id
        data['is_admin'] = self.is_admin
        data['email'] = self.email
        data['mailing_list'] = self.mailing_list
        data['type'] = self.type
        return data
コード例 #10
0
ファイル: role.py プロジェクト: sunu/aleph
class Role(db.Model, IdModel, SoftDeleteModel):
    """A user, group or other access control subject."""

    __tablename__ = "role"

    USER = "******"
    GROUP = "group"
    SYSTEM = "system"
    TYPES = [USER, GROUP, SYSTEM]

    SYSTEM_GUEST = "guest"
    SYSTEM_USER = "******"

    #: Generates URL-safe signatures for invitations.
    SIGNATURE = URLSafeTimedSerializer(settings.SECRET_KEY)

    #: Signature maximum age, defaults to 1 day
    SIGNATURE_MAX_AGE = 60 * 60 * 24

    foreign_id = db.Column(db.Unicode(2048), nullable=False, unique=True)
    name = db.Column(db.Unicode, nullable=False)
    email = db.Column(db.Unicode, nullable=True)
    type = db.Column(db.Enum(*TYPES, name="role_type"), nullable=False)
    api_key = db.Column(db.Unicode, nullable=True)
    is_admin = db.Column(db.Boolean, nullable=False, default=False)
    is_muted = db.Column(db.Boolean, nullable=False, default=False)
    is_tester = db.Column(db.Boolean, nullable=False, default=False)
    is_blocked = db.Column(db.Boolean, nullable=False, default=False)
    password_digest = db.Column(db.Unicode, nullable=True)
    password = None
    reset_token = db.Column(db.Unicode, nullable=True)
    locale = db.Column(db.Unicode, nullable=True)

    permissions = db.relationship("Permission", backref="role")

    @property
    def has_password(self):
        return self.password_digest is not None

    @property
    def is_public(self):
        return self.id in self.public_roles()

    @property
    def is_actor(self):
        if self.type != self.USER:
            return False
        if self.is_blocked or self.deleted_at is not None:
            return False
        return True

    @property
    def is_alertable(self):
        if self.email is None or not self.is_actor:
            return False
        if self.is_muted:
            return False
        if self.updated_at < (datetime.utcnow() - settings.ROLE_INACTIVE):
            # Disable sending notifications to roles that haven't been
            # logged in for a set amount of time.
            return False
        return True

    @property
    def label(self):
        return anonymize_email(self.name, self.email)

    def update(self, data):
        self.name = data.get("name", self.name)
        if self.name is None:
            self.name = self.email or self.foreign_id
        self.is_muted = data.get("is_muted", self.is_muted)
        self.is_tester = data.get("is_tester", self.is_tester)
        if data.get("password"):
            self.set_password(data.get("password"))
        self.locale = stringify(data.get("locale", self.locale))
        self.touch()

    def touch(self):
        self.updated_at = datetime.utcnow()
        db.session.add(self)

    def clear_roles(self):
        """Removes any existing roles from group membership."""
        self.roles = []
        self.touch()
        db.session.flush()

    def add_role(self, role):
        """Adds an existing role as a membership of a group."""
        self.roles.append(role)
        db.session.add(role)
        db.session.add(self)
        self.updated_at = datetime.utcnow()

    def set_password(self, secret):
        """Hashes and sets the role password.

        :param str secret: The password to be set.
        """
        self.password_digest = generate_password_hash(secret)

    def check_password(self, secret):
        """Checks the password if it matches the role password hash.

        :param str secret: The password to be checked.
        :rtype: bool
        """
        digest = self.password_digest or ""
        return check_password_hash(digest, secret)

    def to_dict(self):
        data = self.to_dict_dates()
        data.update({
            "id": stringify(self.id),
            "type": self.type,
            "name": self.name,
            "label": self.label,
            "email": self.email,
            "locale": self.locale,
            "api_key": self.api_key,
            "is_admin": self.is_admin,
            "is_muted": self.is_muted,
            "is_tester": self.is_tester,
            "has_password": self.has_password,
            # 'notified_at': self.notified_at
        })
        return data

    @classmethod
    def by_foreign_id(cls, foreign_id, deleted=False):
        if foreign_id is not None:
            q = cls.all(deleted=deleted)
            q = q.filter(cls.foreign_id == foreign_id)
            return q.first()

    @classmethod
    def by_email(cls, email):
        if email is None:
            return None
        q = cls.all()
        q = q.filter(func.lower(cls.email) == email.lower())
        q = q.filter(cls.type == cls.USER)
        return q.first()

    @classmethod
    def by_api_key(cls, api_key):
        if api_key is None:
            return None
        q = cls.all()
        q = q.filter_by(api_key=api_key)
        q = q.filter(cls.type == cls.USER)
        q = q.filter(cls.is_blocked == False)  # noqa
        return q.first()

    @classmethod
    def load_or_create(cls,
                       foreign_id,
                       type_,
                       name,
                       email=None,
                       is_admin=None):
        role = cls.by_foreign_id(foreign_id)

        if role is None:
            role = cls()
            role.foreign_id = foreign_id
            role.name = name or email
            role.type = type_
            role.is_admin = False
            role.is_muted = False
            role.is_tester = False
            role.is_blocked = False
            role.notified_at = datetime.utcnow()

        if role.api_key is None:
            role.api_key = make_token()

        if email is not None:
            role.email = email

        if is_admin is not None:
            role.is_admin = is_admin

        # see: https://github.com/alephdata/aleph/issues/111
        auto_admins = [a.lower() for a in settings.ADMINS]
        if email is not None and email.lower() in auto_admins:
            role.is_admin = True

        db.session.add(role)
        db.session.flush()
        return role

    @classmethod
    def load_cli_user(cls):
        return cls.load_or_create(settings.SYSTEM_USER,
                                  cls.USER,
                                  "Aleph",
                                  is_admin=True)

    @classmethod
    def load_id(cls, foreign_id):
        """Load a role and return the ID."""
        if not hasattr(settings, "_roles"):
            settings._roles = {}
        if foreign_id not in settings._roles:
            role_id = cls.all_ids().filter_by(foreign_id=foreign_id).first()
            if role_id is not None:
                settings._roles[foreign_id] = role_id[0]
        return settings._roles.get(foreign_id)

    @classmethod
    def public_roles(cls):
        """Roles which make a collection to be considered public."""
        return set(
            [cls.load_id(cls.SYSTEM_USER),
             cls.load_id(cls.SYSTEM_GUEST)])

    @classmethod
    def by_prefix(cls, prefix, exclude=[]):
        """Load a list of roles matching a name, email address, or foreign_id.

        :param str pattern: Pattern to match.
        """
        q = cls.all_users()
        if len(exclude):
            q = q.filter(not_(Role.id.in_(exclude)))
        q = q.filter(
            or_(
                func.lower(cls.email) == prefix.lower(),
                query_like(cls.name, prefix)))
        q = q.order_by(Role.id.asc())
        return q

    @classmethod
    def all_groups(cls, authz):
        q = cls.all()
        q = q.filter(Role.type == Role.GROUP)
        q = q.order_by(Role.name.asc())
        q = q.order_by(Role.foreign_id.asc())
        if not authz.is_admin:
            q = q.filter(Role.id.in_(authz.roles))
        return q

    @classmethod
    def all_users(cls):
        q = cls.all().filter(Role.type == Role.USER)
        q = q.filter(cls.is_blocked == False)  # noqa
        return q

    @classmethod
    def all_system(cls):
        return cls.all().filter(Role.type == Role.SYSTEM)

    @classmethod
    def login(cls, email, password):
        """Attempt to log a user in via an email/password method."""
        role = cls.by_email(email)
        if role is None or not role.is_actor or not role.has_password:
            return
        if role.check_password(password):
            return role

    def __repr__(self):
        return "<Role(%r,%r)>" % (self.id, self.foreign_id)
コード例 #11
0
ファイル: entity.py プロジェクト: vied12/aleph
class Entity(db.Model):
    id = db.Column(db.Unicode(50), primary_key=True, default=make_textid)
    label = db.Column(db.Unicode)
    category = db.Column(db.Enum(*CATEGORIES, name='entity_categories'),
                         nullable=False)

    creator_id = db.Column(db.Integer(), db.ForeignKey('user.id'))
    creator = db.relationship(User,
                              backref=db.backref('entities',
                                                 lazy='dynamic',
                                                 cascade='all, delete-orphan'))

    list_id = db.Column(db.Integer(), db.ForeignKey('list.id'))
    list = db.relationship('List',
                           backref=db.backref('entities',
                                              lazy='dynamic',
                                              cascade='all, delete-orphan'))

    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime,
                           default=datetime.utcnow,
                           onupdate=datetime.utcnow)

    def to_dict(self):
        return {
            'id': self.id,
            'api_url': url_for('entities.view', id=self.id),
            'label': self.label,
            'category': self.category,
            'creator_id': self.creator_id,
            'selectors': [s.text for s in self.selectors],
            'list': self.list_id,
            'created_at': self.created_at,
            'updated_at': self.updated_at
        }

    def has_selector(self, text):
        normalized = Selector.normalize(text)
        for selector in self.selectors:
            if selector.normalized == normalized:
                return True
        return False

    def delete(self):
        db.session.delete(self)

    @classmethod
    def create(cls, data, user):
        ent = cls()
        ent.update(data)
        ent.creator = user
        db.session.add(ent)
        return ent

    def update(self, data):
        data = EntityForm().deserialize(data)
        self.label = data.get('label')
        self.list = data.get('list')
        self.category = data.get('category')

        selectors = set(data.get('selectors'))
        selectors.add(self.label)
        existing = list(self.selectors)
        for sel in list(existing):
            if sel.text in selectors:
                selectors.remove(sel.text)
                existing.remove(sel)
        for sel in existing:
            db.session.delete(sel)
        for text in selectors:
            sel = Selector()
            sel.entity = self
            sel.text = text
            db.session.add(sel)

    @classmethod
    def by_normalized_label(cls, label, lst):
        q = db.session.query(cls)
        q = q.filter_by(list=lst)
        q = q.filter(db_compare(cls.label, label))
        return q.first()

    @classmethod
    def by_id(cls, id):
        q = db.session.query(cls).filter_by(id=id)
        return q.first()

    @classmethod
    def by_lists(cls, lists, prefix=None):
        q = db.session.query(cls)
        q = q.filter(cls.list_id.in_(lists))
        if prefix is not None and len(prefix):
            q = q.join(Selector, cls.id == Selector.entity_id)
            q = cls.apply_filter(q, Selector.normalized, prefix)
        q = q.order_by(cls.label.asc())
        return q

    @classmethod
    def by_id_set(cls, ids):
        if not len(ids):
            return {}
        q = db.session.query(cls)
        q = q.filter(cls.id.in_(ids))
        entities = {}
        for ent in q:
            entities[ent.id] = ent
        return entities

    @classmethod
    def apply_filter(cls, q, col, prefix):
        prefix = Selector.normalize(prefix)
        return q.filter(
            or_(col.like('%s%%' % prefix), col.like('%% %s%%' % prefix)))

    @classmethod
    def suggest_prefix(cls, prefix, lists, limit=10):
        from aleph.model import EntityTag
        ent = aliased(Entity)
        sel = aliased(Selector)
        tag = aliased(EntityTag)
        q = db.session.query(ent.id, ent.label, ent.category)
        q = q.join(sel, ent.id == sel.entity_id)
        q = q.join(tag, ent.id == tag.entity_id)
        q = q.filter(ent.list_id.in_(lists))
        if prefix is None or not len(prefix):
            return []
        q = cls.apply_filter(q, sel.normalized, prefix)
        q = q.order_by(ent.label.asc())
        q = q.limit(limit)
        q = q.distinct()
        suggestions = []
        for entity_id, label, category in q.all():
            suggestions.append({
                'id': entity_id,
                'label': label,
                'category': category
            })
        return suggestions

    @property
    def terms(self):
        return set([s.normalized for s in self.selectors])

    def __repr__(self):
        return '<Entity(%r, %r)>' % (self.id, self.label)

    def __unicode__(self):
        return self.label