def get_review_recipients(deposit, host_url, config): # mail of reviewer reviewer_mail = current_user.email recipients = [ reviewer_mail, ] # mail of owner # owners = deposit.get("_deposit", {}).get("owners") owner_mail = "-" try: owner = deposit["_deposit"]["owners"][0] owner_mail = User.query.filter_by(id=owner).one().email recipients.append(owner_mail) except IndexError: pass cadi_id = deposit.get("analysis_context", {}).get("cadi_id") if cadi_id: # if cadi mail Hypernews if review from admin reviewer (stat committee) cms_stats_commitee_email = current_app.config.get( "CMS_STATS_QUESTIONNAIRE_ADMIN_ROLES") # check that current user is an admin reviewer if (cms_stats_commitee_email and Permission(RoleNeed(cms_stats_commitee_email)).can()): add_hypernews_mail_to_recipients(recipients, cadi_id) subject = create_base_subject(config, cadi_id) message = create_base_message(deposit, host_url) message += f"Submitted by {owner_mail}, and reviewed by {reviewer_mail}." return subject, message, recipients
def files_permission_factory(obj, action=None): """Permission for files are always based on the type of record. Record bucket: Read access only with open access. Deposit bucket: Read/update with restricted access. """ # 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: # 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_publication(record.model): return PublicationFilesPermission(record, action) elif is_deposit(record.model): return DepositFilesPermission(record, action) return Permission(superuser_access)
def test_entry_points(): """Test admin views discovery through entry points.""" from flask_principal import Permission app = Flask('testapp') admin_app = InvenioAdmin(app, permission_factory=lambda x: Permission(), view_class_factory=lambda x: x) # Check if model views were added by checking the labels of menu items menu_items = {str(item.name): item for item in admin_app.admin.menu()} assert 'OneAndTwo' in menu_items # Category for ModelOne and ModelTwo assert 'Four' in menu_items # Category for ModelOne and ModelTwo assert 'Model One' not in menu_items # ModelOne should go to a category assert 'Model Two' not in menu_items # ModelTwo should go to a category assert 'Model Three' in menu_items # ModelThree goes straight to menu assert isinstance(menu_items['Model Three'], flask_admin.menu.MenuView) assert isinstance(menu_items['OneAndTwo'], flask_admin.menu.MenuCategory) assert menu_items['OneAndTwo'].is_category() assert not menu_items['Model Three'].is_category() submenu_items = { str(item.name): item for item in menu_items['OneAndTwo'].get_children() } assert 'Model One' in submenu_items assert 'Model Two' in submenu_items assert not submenu_items['Model One'].is_category() assert not submenu_items['Model Two'].is_category() assert isinstance(submenu_items['Model One'], flask_admin.menu.MenuView) assert isinstance(submenu_items['Model Two'], flask_admin.menu.MenuView) four_item = menu_items['Four'].get_children()[0] assert four_item.name == 'View number Four' assert isinstance(four_item, flask_admin.menu.MenuView)
def get_review_recipients(deposit, config): # mail of owner owner = deposit["_deposit"]["owners"][0] owner_mail = User.query.filter_by(id=owner).one().email # mail of reviewer reviewer_mail = current_user.email recipients = list({owner_mail, reviewer_mail}) cadi_id = deposit.get("analysis_context", {}).get("cadi_id") cadi_regex = current_app.config.get("CADI_REGEX") # if CADI ID mail Hypernews if review from admin reviewer (stat commitee) if cadi_id: cms_stats_commitee_email = current_app.config.get( "CMS_STATS_QUESTIONNAIRE_ADMIN_ROLES") # check that current user is a aon reviewer if (cms_stats_commitee_email and Permission(RoleNeed(cms_stats_commitee_email)).can()): # mail for reviews - Hypernews # should be sent to hn-cms-<cadi-id>@cern.ch if # well-formed cadi id hypernews_mail = \ current_app.config.get("CMS_HYPERNEWS_EMAIL_FORMAT") if re.match(cadi_regex, cadi_id) and hypernews_mail: recipients.append(hypernews_mail.format(cadi_id)) message = f"Submitted by {owner_mail}, and reviewed by {reviewer_mail}." return message, recipients
def community_curation(record, user): """Generate a list of pending and accepted communities with permissions. Return a 4-tuple of lists (in order): * 'pending' communities, which can be curated by given user * 'accepted' communities, which can be curated by given user * All 'pending' communities * All 'accepted' communities """ irs = ZenodoCommunity.get_irs(record).all() pending = list(set(ir.community for ir in irs)) accepted = [Community.get(c) for c in record.get('communities', [])] # Additionally filter out community IDs that did not resolve (None) accepted = [c for c in accepted if c] # Check for global curation permission (all communities on this record). global_perm = None if user.is_anonymous: global_perm = False elif Permission(ActionNeed('admin-access')).can(): global_perm = True if global_perm: return (pending, accepted, pending, accepted) else: return ( [c for c in pending if _can_curate(c, user, record)], [c for c in accepted if _can_curate(c, user, record, accepted=True)], pending, accepted, )
def admin_permission_factory(admin_view): """Factory for creating a permission for an admin. :param admin_view: Instance of administration view which is currently being protected. :returns: Permission instance. """ return Permission(action_admin_access)
def create_permissions(*permissions): """Create permissions with given needs and excludes.""" _permissions = [] for _needs in permissions: permission = Permission() permission.explicit_needs.update(_needs.get("needs", set())) permission.explicit_excludes.update(_needs.get("excludes", set())) _permissions.append(permission) return _permissions
def admin_permission_factory(view): """Create a permission for an admin. It tries to load a :class:`invenio_access.permissions.DynamicPermission` 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. """ return Permission(superuser_access)
def _init_owners(self, identity, record, **kwargs): """If the record has no owners yet, add the current user.""" # if the given identity is that of a user, we add the # corresponding user to the owners # (record.parent.access.owners) and commit the parent # otherwise, the parent's owners stays empty is_sys_id = Permission(system_process).allows(identity) if not record.parent.access.owners and not is_sys_id: owner_dict = {"user": identity.id} record.parent.access.owners.add(owner_dict) record.parent.commit()
def action_read(): """View only allowed to open action.""" identity = g.identity actions = {} for action in access.actions.values(): actions[action.value] = Permission(action).allows(identity) message = 'You are opening a page requiring the "read" permission' return render_template("invenio_access/limited.html", message=message, actions=actions, identity=identity)
def _load_additional_permissions(self): # owners of the deposit are allowed to read the deposit. needs = set(UserNeed(owner_id) for owner_id in self.deposit['_deposit']['owners']) # add specific permission to read deposits of this community # in this publication state needs.add(read_deposit_need_factory( community=self.deposit['community'], publication_state=self.deposit['publication_state'], )) permission = Permission(*needs) self.permissions.add(permission)
def admin_permission_factory(): """Factory for creating a permission for an admin. :returns: Permission instance. """ try: pkg_resources.get_distribution('invenio-access') from invenio_access.permissions import DynamicPermission as Permission except pkg_resources.DistributionNotFound: from flask_principal import Permission return Permission(action_admin_access)
def files_permission_factory(obj, action=None): """Permission factory for deposit files.""" bucket_id = str(obj.id) if isinstance(obj, Bucket) else str(obj.bucket_id) try: bucket = RecordsBuckets.query.filter_by( bucket_id=bucket_id ).one() return DepositFilesPermission(bucket.record, action) except NoResultFound: return Permission()
def admin_permission_factory(admin_view): """Factory for creating a permission for an admin. :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.permissions import DynamicPermission as Permission except pkg_resources.DistributionNotFound: from flask_principal import Permission return Permission(action_admin_access)
def files_permission_factory(obj, action=None): """Permission factory for deposit files.""" bucket_id = str(obj.id) if isinstance(obj, Bucket) else str(obj.bucket_id) try: bucket = RecordsBuckets.query.filter_by(bucket_id=bucket_id).one() record_type = _get_record_type(bucket.record.id) return { 'recid': RecordFilesPermission(bucket.record, action), 'depid': DepositFilesPermission(bucket.record, action) }[record_type] except (NoResultFound, KeyError): return Permission()
def delete(user_id): """Delete spam.""" # Only admin can access this view if not Permission(ActionNeed('admin-access')).can(): abort(403) user = User.query.get(user_id) deleteform = DeleteSpamForm() communities = Community.query.filter_by(id_user=user.id) rs = RecordsSearch(index='records').query( Q('query_string', query="owners: {0}".format(user.id))) rec_count = rs.count() ctx = { 'user': user, 'form': deleteform, 'is_new': False, 'communities': communities, 'rec_count': rec_count, } if deleteform.validate_on_submit(): if deleteform.remove_all_communities.data: for c in communities: if not c.deleted_at: if not c.description.startswith('--SPAM--'): c.description = '--SPAM--' + c.description if c.oaiset: db.session.delete(c.oaiset) c.delete() db.session.commit() if deleteform.deactivate_user.data: _datastore.deactivate_user(user) db.session.commit() # delete_record function commits the session internally # for each deleted record if deleteform.remove_all_records.data: for r in rs.scan(): delete_record(r.meta.id, 'spam', int(current_user.get_id())) flash("Spam removed", category='success') return redirect(url_for('.delete', user_id=user.id)) else: records = islice(rs.scan(), 10) ctx.update(records=records) return render_template('zenodo_spam/delete.html', **ctx)
def admin_permission_factory(): """Factory for creating a permission for an admin `deposit-admin-access`. If `invenio-access` module is installed, it returns a :class:`invenio_access.permissions.DynamicPermission` object. Otherwise, it returns a :class:`flask_principal.Permission` object. :returns: Permission instance. """ try: pkg_resources.get_distribution('invenio-access') from invenio_access.permissions import DynamicPermission as Permission except pkg_resources.DistributionNotFound: from flask_principal import Permission return Permission(action_admin_access)
def index(): """Basic test view.""" identity = g.identity actions = {} for action in access.actions.values(): actions[action.value] = Permission(action).allows(identity) if current_user.is_anonymous: return render_template("invenio_access/open.html", actions=actions, identity=identity) else: return render_template("invenio_access/limited.html", message='', actions=actions, identity=identity)
def admin_permission_factory(admin_view): """Default factory for creating a permission for an admin. It tries to load a :class:`invenio_access.permissions.DynamicPermission` 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.permissions import DynamicPermission as Permission except pkg_resources.DistributionNotFound: from flask_principal import Permission return Permission(action_admin_access)
def test_invenio_access_permission(app): """Permission is always denied if not explicitly granted.""" fake_identity = FakeIdentity() InvenioAccess(app) with app.test_request_context(): db.session.begin(nested=True) user = User(email='*****@*****.**') permission = Permission(ActionNeed('read')) assert not permission.allows(fake_identity) db.session.add(ActionUsers(action='read', user=user)) db.session.commit() assert not permission.allows(fake_identity) fake_identity.provides.add(UserNeed(user.id)) assert permission.allows(fake_identity)
def _load_additional_permissions(self): """Create additional permission.""" if self.action in _read_actions: if self.record['open_access']: # allow everybody to see open_access records self.permissions.clear() self.permissions.add(Permission()) else: needs = set() needs.add(read_restricted_files(self.record['community'])) needs.add(read_restricted_files(None)) # all actions are granted to the owner for owner_id in self.record['_deposit']['owners']: needs.add(UserNeed(owner_id)) self.permissions.add(StrictDynamicPermission(*needs)) else: # Nobody can change the files of a published record. self.permissions.clear() self.permissions.add(DenyAllPermission)
def app(base_app): """Flask application fixture.""" base_app._internal_jsonschemas = InvenioJSONSchemas(base_app) InvenioREST(base_app) InvenioRecordsREST(base_app) InvenioRecords(base_app) InvenioPIDStore(base_app) base_app.url_map.converters['pid'] = PIDConverter SampleExt(base_app) OARepoMappingIncludesExt(base_app) LoginManager(base_app) Permission(base_app) InvenioAccess(base_app) Principal(base_app) OARepoValidate(base_app) Actions(base_app) base_app.register_blueprint( invenio_records_rest.views.create_blueprint_from_app(base_app)) login_manager = LoginManager() login_manager.init_app(base_app) login_manager.login_view = 'login' @login_manager.user_loader def basic_user_loader(user_id): user_obj = User.query.get(int(user_id)) return user_obj @base_app.route('/test/login/<int:id>', methods=['GET', 'POST']) def test_login(id): print("test: logging user with id", id) response = make_response() user = User.query.get(id) login_user(user) set_identity(user) return response app_loaded.send(None, app=base_app) with base_app.app_context(): yield base_app
# -*- coding: utf-8 -*- # # This file is part of INSPIRE. # Copyright (C) 2014-2017 CERN. # # INSPIRE is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # INSPIRE is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with INSPIRE. If not, see <http://www.gnu.org/licenses/>. # # In applying this license, CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. from __future__ import absolute_import, division, print_function from invenio_access.permissions import (Permission, ParameterizedActionNeed) action_editor_manage_tickets = ParameterizedActionNeed('editor_manage_tickets', argument=None) editor_manage_tickets_permission = Permission(action_editor_manage_tickets)
# the Free Software Foundation, version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """Invenio-SIP2 permissions.""" from flask import abort from flask_login import current_user from invenio_access.permissions import Permission, SystemRoleNeed admin_user = Permission(SystemRoleNeed('admin')) def deny_all(): """Deny all permission.""" return type('Deny', (), {'can': lambda self: False})() def check_permission(permission): """Abort if permission is not allowed. :param permission: The permission to check. """ if permission is not None and not permission.can(): if not current_user.is_authenticated: abort(401)
def custom_views_permissions_factory(action): if action == "circulation-loan-force-checkout": # fake permission for a specific user return Permission(UserNeed(librarian2.id)) else: return default_factory(action)
# # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """Permissions for this module.""" from functools import wraps from flask import abort, current_app, redirect, url_for from flask_login import current_user from flask_principal import RoleNeed from flask_security import login_required, roles_required from invenio_access.permissions import Permission from .modules.patrons.api import Patron request_item_permission = Permission(RoleNeed('patron')) librarian_permission = Permission(RoleNeed('librarian'), RoleNeed('system_librarian')) admin_permission = Permission(RoleNeed('admin')) editor_permission = Permission(RoleNeed('editor'), RoleNeed('admin')) def staffer_is_authenticated(user=None): """Checks if user (librarian or system_librarian) is authenticated. :return: patron records if user is logged in and authenticated and has librarian or system_librarian role. :return False otherwise. """ if not user: user = current_user
def backoffice_permission(*args, **kwargs): """Return permission to allow only librarians and admins.""" return Permission(backoffice_access_action)
def circulation_permission(patron_pid): """Return circulation status permission for a patron.""" return Permission(UserNeed(int(patron_pid)), backoffice_access_action)
def authenticated_user_permission(*args, **kwargs): """Return an object that evaluates if the current user is authenticated.""" return Permission(authenticated_user)
def exp_permission_factory(experiment): """Experiment permission factory.""" return Permission( ActionNeed('{}-access'.format(experiment.lower())) )