def permission_factory(obj, action): """ Get default permission factory. :param obj: An instance of :class:`invenio_files_rest.models.Bucket` or :class:`invenio_files_rest.models.ObjectVersion` or :class:`invenio_files_rest.models.MultipartObject` or ``None`` if the action is global. :param action: The required action. :raises RuntimeError: If the object is unknown. :returns: A :class:`invenio_access.permissions.DynamicPermission` instance. """ need_class = _action2need_map[action] if obj is None: return Permission(need_class(None)) # pragma: nocover arg = None if isinstance(obj, Taxonomy): arg = str(obj.code) elif isinstance(obj, TaxonomyTerm): arg = str(obj.tree_path) else: raise RuntimeError('Unknown object') # pragma: nocover return Permission(need_class(arg))
def detail_permission_factory(record=None): """Permissions factory for record detail.""" # TODO: adapt for more owners owners = _get_owners(record) if len(owners) > 0: return Permission(UserNeed(int(owners[0]))) return Permission(any_user)
def permission_factory(obj, action): """Get default permission factory. :param obj: An instance of :class:`invenio_files_rest.models.Bucket` or :class:`invenio_files_rest.models.ObjectVersion` or :class:`invenio_files_rest.models.MultipartObject` or ``None`` if the action is global. :param action: The required action. :raises RuntimeError: If the object is unknown. :returns: A :class:`invenio_access.permissions.Permission` instance. """ need_class = _action2need_map[action] if obj is None: return Permission(need_class(None)) arg = None if isinstance(obj, Bucket): arg = str(obj.id) elif isinstance(obj, ObjectVersion): arg = str(obj.bucket_id) elif isinstance(obj, MultipartObject): arg = str(obj.bucket_id) else: raise RuntimeError('Unknown object') return Permission(need_class(arg))
def owner_permission_factory(record=None): """Permissions factory for record owners.""" # TODO: adapt for more owners owners = _get_owners(record) if len(owners) > 0: return Permission(UserNeed(int(owners[0]))) return Permission(UserNeed(-1))
def user_is_term_manager(uuid, user: User): if not user or not uuid: raise PermissionDenied() identity = get_identity(user) permission = Permission(ObjectSourceTermManager(uuid)) if permission.allows(identity): return True raise PermissionDenied()
def wrapper(*args, **kwargs): permission = Permission(notification_admin_actions) current_identity = get_identity(current_user) if not permission.allows(current_identity): return iroko_json_response(IrokoResponseStatus.ERROR, 'Need to be source administrator.', None, None) else: return fn(*args, **kwargs)
def vocabulary_editor_permission_factory(obj): try: permission = Permission(vocabularies_full_editor_actions) current_identity = get_identity(current_user) if permission.allows(current_identity): return permission except Exception as e: msg = str(e) return Permission(ObjectVocabularyEditor(obj['name']))
def source_manager_permission_factory(obj): try: permission = Permission(source_full_manager_actions) current_identity = get_identity(current_user) if permission.allows(current_identity): return permission except Exception as e: pass return Permission(ObjectSourceManager(obj['uuid']))
def is_user_sources_admin(user: User): its = False permission = Permission(source_full_manager_actions) current_identity = get_identity(user) if permission.allows(current_identity): its = True # except Exception as e: # # print(str(e)) return its
def notification_viewed_permission_factory(obj): try: permission = Permission(notification_admin_actions) current_identity = get_identity(current_user) if permission.allows(current_identity): return permission except Exception as e: pass return Permission(ObjectNotificationViewed(obj['id']))
def can(self): """Return boolean if permission valid.""" identity = get_identity(self.user) return ( is_owner(self.user, self.record) or ( Permission(menrva_edit_published_record).allows(identity) and has_published(self.record) ) or Permission(menrva_edit).allows(identity) # NOTE: by default any Permission has a super-user Need )
def user_has_edit_permission(source, user: User): if not user or not source: raise PermissionDenied() try: if user_has_manager_permission(source, user): return True except PermissionDenied as err: pass identity = get_identity(user) perm = Permission(ObjectSourceEditor(source.id)) if perm.allows(identity): return True raise PermissionDenied()
def files_permission_factory(obj, action=None): """Permission for files are always based on the type of bucket. 1. Community bucket: Read access for everyone 2. Record bucket: Read access only with open and restricted access. 3. Deposit bucket: Read/update with restricted access. 4. Any other bucket is restricted to admins only. """ # Extract bucket id bucket_id = None if isinstance(obj, Bucket): bucket_id = str(obj.id) elif isinstance(obj, ObjectVersion): bucket_id = str(obj.bucket_id) elif isinstance(obj, MultipartObject): bucket_id = str(obj.bucket_id) elif isinstance(obj, FileObject): bucket_id = str(obj.bucket_id) # Retrieve record if bucket_id is not None: # Community bucket if str(bucket_id) in get_public_bucket_uuids(): return PublicBucketPermission(action) # Record or deposit bucket rb = RecordsBuckets.query.filter_by(bucket_id=bucket_id).one_or_none() if rb is not None: record = Record.get_record(rb.record_id) if is_record(record): return RecordFilesPermission.create(record, action) elif is_deposit(record): return DepositFilesPermission.create(record, action) return Permission(ActionNeed('admin-access'))
def check_and_handle_spam(community=None, deposit=None): """Checks community/deposit metadata for spam.""" try: if current_app.config.get('ZENODO_SPAM_MODEL_LOCATION'): if community: task = check_metadata_for_spam.delay(community_id=community.id) if deposit: task = check_metadata_for_spam.delay(dep_id=str(deposit.id)) spam_proba = task.get( timeout=current_app.config['ZENODO_SPAM_CHECK_TIMEOUT']) else: spam_proba = 0 if spam_proba > current_app.config['ZENODO_SPAM_THRESHOLD']: if not Permission(ActionNeed('admin-access')).can(): has_records = RecordsSearch(index='records').query( Q('query_string', query="owners:{}".format(community.id_user))).count() has_communities = Community.query.filter_by( id_user=community.id_user).count() - 1 if not (has_records or has_communities): current_app.config['ZENODO_SPAM_HANDLING_ACTIONS']( community=community, deposit=deposit) except HTTPException: raise except Exception: current_app.logger.exception(u'Could not check for spam')
def files_permission_factory(obj, action=None): """Permission for files are always based on the type of bucket. 1. Community bucket: Read access for everyone 2. Record bucket: Read access only with open and restricted access. 3. Deposit bucket: Read/update with restricted access. 4. Any other bucket is restricted to admins only. """ # Extract bucket id bucket_id = None if isinstance(obj, Bucket): bucket_id = str(obj.id) elif isinstance(obj, ObjectVersion): bucket_id = str(obj.bucket_id) elif isinstance(obj, MultipartObject): bucket_id = str(obj.bucket_id) elif isinstance(obj, FileObject): bucket_id = str(obj.bucket_id) # Retrieve record if bucket_id is not None: # Community bucket if str(bucket_id) in get_public_bucket_uuids(): return PublicBucketPermission(action) # Record or deposit bucket rbs = RecordsBuckets.query.filter_by(bucket_id=bucket_id).all() if len(rbs) >= 2: # Extra formats bucket or bad records-buckets state # Only admins should access. Users use the ".../formats" endpoints return Permission(ActionNeed('admin-access')) rb = next(iter(rbs), None) # Use first bucket if rb: record = Record.get_record(rb.record_id) # "Cache" the file's record in the request context (e.g for stats) if record and request: setattr(request, 'current_file_record', record) # Bail if extra formats bucket if str(bucket_id) == \ record.get('_buckets', {}).get('extra_formats'): return Permission(ActionNeed('admin-access')) if is_record(record): return RecordFilesPermission.create(record, action) elif is_deposit(record): return DepositFilesPermission.create(record, action) return Permission(ActionNeed('admin-access'))
def create(cls, obj, action): """Create an <Action>FilesPermission for record associated with `obj`. For now it defaults to ReadFilesPermission for all actions. Adapted from https://github.com/zenodo/zenodo """ # Extract bucket id bucket_id = None if isinstance(obj, Bucket): bucket_id = str(obj.id) elif isinstance(obj, ObjectVersion): bucket_id = str(obj.bucket_id) elif isinstance(obj, MultipartObject): bucket_id = str(obj.bucket_id) elif isinstance(obj, FileObject): bucket_id = str(obj.bucket_id) # Retrieve record if not bucket_id: # Don't think this conditional should be hit return Permission(ActionNeed('superuser-access')) # WARNING: invenio-records-files implies a one-to-one relationship # between Record and Bucket, but does not enforce it # "for better future" the invenio-records-files code says record_bucket = \ RecordsBuckets.query.filter_by(bucket_id=bucket_id).one_or_none() if not record_bucket: return Permission(ActionNeed('superuser-access')) record_metadata = record_bucket.record record = Record(record_metadata.json, model=record_metadata) # "Cache" the file's record in the request context if record and request: setattr(request, 'current_file_record', record) if record: # TODO: Differentiate between actions if action in cls.actions: if '-read' in action: return ReadFilesPermission(current_user, record) elif '-update' in action: return CreateFilesPermission(current_user, record) return Permission(ActionNeed('superuser-access'))
def user_has_editor_or_manager_permissions(obj): permission = Permission(source_full_manager_actions) current_identity = get_identity(current_user) if permission.allows(current_identity): return permission permiso = None permiso = Permission(ObjectSourceManager(obj['uuid'])) if permiso: return permiso aux = obj['terms'] terms = aux.split(',') permiso = None for term_uuid in terms: try: permiso = Permission(ObjectSourceTermManager(term_uuid)) if permiso: return permiso except Exception as e: raise e aux = obj['orgs'] orgs = aux.split(',') permiso = None for org_uuid in orgs: try: permiso = Permission(ObjectSourceTermManager(org_uuid)) if permiso: return permiso except Exception as e: raise e return Permission(ObjectSourceEditor(obj['uuid']))
def check_and_handle_spam(community=None, deposit=None, retry=True): """Checks community/deposit metadata for spam.""" try: if current_app.config.get('ZENODO_SPAM_MODEL_LOCATION'): if community: task = check_metadata_for_spam.delay(community_id=community.id) user_id = community.id_user if deposit: task = check_metadata_for_spam.delay(dep_id=str(deposit.id)) user_id = deposit['owners'][0] spam_proba = task.get( timeout=current_app.config['ZENODO_SPAM_CHECK_TIMEOUT']) else: spam_proba = 0 if spam_proba > current_app.config['ZENODO_SPAM_THRESHOLD']: if not Permission(ActionNeed('admin-access')).can(): user_records = RecordsSearch(index='records').query( Q('query_string', query="owners:{}".format(user_id))).count() user_communities = Community.query.filter_by( id_user=user_id).count() if community: # Ignore the newly created community user_communities = user_communities - 1 current_app.logger.warning( u'Found spam upload', extra={ 'depid': deposit.id if deposit else None, 'comid': community.id if community else None }) if not (user_records + user_communities > current_app.config['ZENODO_SPAM_SKIP_CHECK_NUM']): current_app.config['ZENODO_SPAM_HANDLING_ACTIONS']( community=community, deposit=deposit) except HTTPException: raise except TimeoutError: if retry: check_and_handle_spam(community=community, deposit=deposit, retry=False) else: current_app.logger.exception( u'Could not check for spam', extra={ 'depid': deposit.id if deposit else None, 'comid': community.id if community else None }) except Exception: current_app.logger.exception(u'Could not check for spam', extra={ 'depid': deposit.id if deposit else None, 'comid': community.id if community else None })
def test_cli_action_allow(app, script_info, community, authenticated_user, db): runner = CliRunner() app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv( 'SQLALCHEMY_DATABASE_URI', 'sqlite://') role = community[1].roles[0] current_datastore.add_role_to_user(authenticated_user, role) read_need = action_factory(COMMUNITY_READ, parameter=True) login_user(authenticated_user) assert not Permission(read_need(community[0])).allows(g.identity)
def owner_permission_impl(record, *args, **kwargs): f"""Record owner permission factory. * Allows access to record if current_user if record is owned by the current user. * If the record is not owned by any user, access to the record is denied. """ owner = current_oarepo_communities.get_owned_by_field(record) if owner: return Permission(UserNeed(owner)) return deny_all()
def check_user_vocabulary_editor_permission( user_id, vocabulary_id) -> Dict[str, bool]: done = False msg = '' try: if is_current_user_taxonomy_admin(): done = True else: vocabulary = Vocabulary.query.filter_by( identifier=vocabulary_id).first() user = User.query.filter_by(id=user_id) user_identity = get_identity(user) permission = Permission(ObjectVocabularyEditor( vocabulary.name)) done = permission.allows(user_identity) except Exception as e: msg = str(e) # print(str(e)) return msg, done
def test_action_needs(app, db, community, community_curator): """Test action needs creation.""" role = current_datastore.find_role('community:comtest:member') current_datastore.add_role_to_user(community_curator, role) ar = ActionRoles(action=COMMUNITY_READ, argument=community[1].id, role=role) db.session.add(ar) db.session.commit() login_user(community_curator) assert Permission(ParameterizedActionNeed(COMMUNITY_READ, community[1].id)).can()
def can(self): """Return boolean if permission valid.""" # Enforce the fact that this is only for published record. return RecordType.is_published(self.published_record) and ( is_open_access(self.published_record) or has_restricted_access(self.user, self.published_record) or is_owner(self.user, self.published_record) or Permission(menrva_view_published_record).allows( get_identity(self.user) ) # NOTE: by default any Permission has a super-user Need )
def test_owner_permissions(app, db, community, authenticated_user): """Test owner system role permissions.""" login_user(authenticated_user) assert len(g.identity.provides) == 4 assert community_record_owner in g.identity.provides permissions = require_any( # Approval is granted either by user role Permission(ParameterizedActionNeed(COMMUNITY_REQUEST_APPROVAL, community[0])), require_all( # Or user id must match and record owners must be granted the action Permission(UserNeed(authenticated_user.id)), Permission(ParameterizedActionNeed(f'owner-{COMMUNITY_REQUEST_APPROVAL}', community[0])) ) ) assert not permissions().can() db.session.add( ActionSystemRoles(action=f'owner-{COMMUNITY_REQUEST_APPROVAL}', role_name=community_record_owner.value, argument=community[0])) assert permissions().can()
def source_organization_manager_permission_factory(obj): permission = Permission(source_full_manager_actions) current_identity = get_identity(current_user) if permission.allows(current_identity): return permission permiso = None permiso = Permission(ObjectSourceManager(obj['uuid'])) if permiso: return permiso aux = obj['orgs'] orgs = aux.split(',') permiso = None for org_uuid in orgs: try: permiso = Permission(ObjectSourceOrganizationManager(org_uuid)) if permiso: return permiso except Exception as e: raise e raise PermissionDenied('No tiene permisos de gestión')
def inner(record, *args, **kwargs): if record is None: raise RuntimeError('Record is missing.') arg = None if isinstance(record, Record): arg = record.primary_community elif isinstance(record, dict): arg = current_oarepo_communities.get_primary_community_field(record) else: raise RuntimeError('Unknown or missing object') return require_all( require_action_allowed(action), Permission(ParameterizedActionNeed(action, arg)))
def test_cli_action_deny(app, community, authenticated_user, db): runner = CliRunner() script_info = ScriptInfo(create_app=lambda info: app) app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv( 'SQLALCHEMY_DATABASE_URI', 'sqlite://') role = community[1].roles[0] current_datastore.add_role_to_user(authenticated_user, role) login_user(authenticated_user) db.session.add( ActionRoles(action=COMMUNITY_READ, argument=community[0], role=role)) assert Permission(ParameterizedActionNeed(COMMUNITY_READ, community[0])).allows(g.identity)
def read_permission_factory(record, *args, **kwargs): f"""Read permission factory that takes secondary communities into account. Allows access to record in one of the following cases: * Record is PUBLISHED * Current user is the OWNER of the record * User's role has allowed READ action in one of record's communities AND: 1) User is in one of the roles of the community from the request path AND record is atleast APPROVED. OR 2) User is CURATOR in the community from the request path :param record: An instance of :class:`oarepo_communities.record.CommunityRecordMixin` or ``None`` if the action is global. :raises RuntimeError: If the object is unknown. :returns: A :class:`invenio_access.permissions.Permission` instance. """ if isinstance(record, Record): communities = [record.primary_community, *record.secondary_communities] return require_any( #: Anyone can read published records state_required(STATE_PUBLISHED), require_all( require_action_allowed(COMMUNITY_READ), require_any( #: Record AUTHOR can READ his own records owner_permission_impl, require_all( #: User's role has granted READ permissions in record's communities Permission(*[ParameterizedActionNeed(COMMUNITY_READ, x) for x in communities]), require_any( #: Community MEMBERS can READ APPROVED community records require_all( state_required(STATE_APPROVED), require_any( community_member_permission_impl, community_publisher_permission_impl ) ), #: Community CURATORS can READ ALL community records community_curator_permission_impl ) ) ) ) )(record, *args, **kwargs) else: raise RuntimeError('Unknown or missing object')
def admin_permission_factory(admin_view): """Default factory for creating a permission for an admin. It tries to load a :class:`invenio_access.permissions.Permission` instance if `invenio_access` is installed. Otherwise, it loads a :class:`flask_principal.Permission` instance. :param admin_view: Instance of administration view which is currently being protected. :returns: Permission instance. """ try: pkg_resources.get_distribution('invenio-access') from invenio_access import Permission except pkg_resources.DistributionNotFound: from flask_principal import Permission return Permission(action_admin_access)
def records_filter(): """Query ElasticSearch for a *filtered* list of Records. NOTE: Any of these queries is run in a filtered context. So ES can optimize them. """ if not has_request_context(): return Q() elif Permission(ActionNeed('admin-access')).can(): return Q() elif not current_user.is_authenticated: return (Q('term', type='published') & Q('term', permissions=RecordPermissions.ALL_VIEW)) else: return (Q('term', type='published') & (Q('term', permissions=RecordPermissions.ALL_VIEW) | Q('term', permissions=RecordPermissions.RESTRICTED_VIEW) | (Q('term', permissions=RecordPermissions.PRIVATE_VIEW) & Q('term', _deposit__owners=getattr(current_user, 'id', 0)))))