def test_delete_draft_registration_non_admin(self): assert_equal(1, DraftRegistration.find().count()) url = self.node.api_url_for('delete_draft_registration', draft_id=self.draft._id) res = self.app.delete(url, auth=self.non_admin.auth, expect_errors=True) assert_equal(res.status_code, http.FORBIDDEN) assert_equal(1, DraftRegistration.find().count())
def test_affiliated_institutions_are_copied_from_user( self, app, user, url_draft_registrations, payload): project = ProjectFactory(is_public=True, creator=user) InstitutionFactory() payload['data']['relationships']['branched_from']['data'][ 'id'] = project._id res = app.post_json_api(url_draft_registrations, payload, auth=user.auth, expect_errors=True) assert res.status_code == 201 draft_registration = DraftRegistration.load(res.json['data']['id']) assert not draft_registration.affiliated_institutions.exists() project = ProjectFactory(is_public=True, creator=user) payload['data']['relationships']['branched_from']['data'][ 'id'] = project._id user.affiliated_institutions.set(Institution.objects.filter(id__lt=3)) res = app.post_json_api(url_draft_registrations, payload, auth=user.auth, expect_errors=True) assert res.status_code == 201 draft_registration = DraftRegistration.load(res.json['data']['id']) assert not draft_registration.affiliated_institutions.all( ) == user.affiliated_institutions.all()
def test_delete_draft_registration(self): assert_equal(1, DraftRegistration.find().count()) url = self.node.api_url_for('delete_draft_registration', draft_id=self.draft._id) res = self.app.delete(url, auth=self.user.auth) assert_equal(res.status_code, http.NO_CONTENT) assert_equal(0, DraftRegistration.find().count())
def test_only_admin_can_delete_registration(self): non_admin = AuthUserFactory() assert_equal(1, DraftRegistration.find().count()) url = self.node.api_url_for('delete_draft_registration', draft_id=self.draft._id) res = self.app.delete(url, auth=non_admin.auth, expect_errors=True) assert_equal(res.status_code, http.FORBIDDEN) assert_equal(1, DraftRegistration.find().count())
def test_delete_draft_registration_approved_and_registration_deleted(self, mock_register_draft): self.draft.register(auth=self.auth, save=True) self.draft.registered_node.is_deleted = True self.draft.registered_node.save() assert_equal(1, DraftRegistration.find().count()) url = self.node.api_url_for('delete_draft_registration', draft_id=self.draft._id) res = self.app.delete(url, auth=self.user.auth) assert_equal(res.status_code, http.NO_CONTENT) assert_equal(0, DraftRegistration.find().count())
def test_branched_from_must_be_a_node_or_draft_node(self): with pytest.raises(DraftRegistrationStateError): DraftRegistration.create_from_node( user=user, node=factories.RegistrationFactory(), schema=factories.get_default_metaschema()) with pytest.raises(DraftRegistrationStateError): DraftRegistration.create_from_node( user=user, node=factories.CollectionFactory(), schema=factories.get_default_metaschema())
def test_delete_draft_registration_approved_and_registration_deleted( self, mock_register_draft): self.draft.register(auth=self.auth, save=True) self.draft.registered_node.is_deleted = True self.draft.registered_node.save() assert_equal(1, DraftRegistration.find().count()) url = self.node.api_url_for('delete_draft_registration', draft_id=self.draft._id) res = self.app.delete(url, auth=self.user.auth) assert_equal(res.status_code, http.NO_CONTENT) assert_equal(0, DraftRegistration.find().count())
def delete_draft_registration(auth, node, draft, *args, **kwargs): """Permanently delete a draft registration :return: None :rtype: NoneType """ if draft.registered_node and not draft.registered_node.is_deleted: raise HTTPError( http.FORBIDDEN, data={ 'message_short': 'Can\'t delete draft', 'message_long': 'This draft has already been registered and cannot be deleted.' } ) DraftRegistration.remove_one(draft) return None, http.NO_CONTENT
def __init__(self, registration, missing_files, *args, **kwargs): super(ArchivedFileNotFound, self).__init__(*args, **kwargs) self.draft_registration = DraftRegistration.find_one( Q('registered_node', 'eq', registration) ) self.missing_files = missing_files
def post(self, request, *args, **kwargs): try: data = json.loads(request.body).get('schema_data', {}) draft = DraftRegistration.load(self.kwargs.get('draft_pk')) draft.update_metadata(data) draft.save() log_message = list() for key, value in data.iteritems(): comments = data.get(key, {}).get('comments', []) for comment in comments: log_message.append('{}: {}'.format(key, comment['value'])) update_admin_log( user_id=request.user.id, object_id=draft._id, object_repr='Draft Registration', message='Comments: <p>{}</p>'.format('</p><p>'.join(log_message)), action_flag=COMMENT_PREREG ) return JsonResponse(serializers.serialize_draft_registration(draft)) except AttributeError: raise Http404('{} with id "{}" not found.'.format( self.context_object_name.title(), self.kwargs.get('draft_pk') )) except NodeStateError as e: return bad_request(request, e)
def new_draft_registration(auth, node, *args, **kwargs): """Create a new draft registration for the node :return: Redirect to the new draft's edit page :rtype: flask.redirect :raises: HTTPError """ if node.is_registration: raise HTTPError(http.FORBIDDEN, data={ 'message_short': "Can't create draft", 'message_long': 'Creating draft registrations on registered projects is not allowed.' }) data = request.values schema_name = data.get('schema_name') if not schema_name: raise HTTPError( http.BAD_REQUEST, data={ 'message_short': 'Must specify a schema_name', 'message_long': 'Please specify a schema_name' } ) schema_version = data.get('schema_version', 2) meta_schema = get_schema_or_fail(Q(name=schema_name, schema_version=int(schema_version))) draft = DraftRegistration.create_from_node( node, user=auth.user, schema=meta_schema, data={} ) return redirect(node.web_url_for('edit_draft_registration_page', draft_id=draft._id))
def make_draft_registration(node=None): draft_registration = DraftRegistration.create_from_node( user=user, schema=get_default_metaschema(), data={}, node=node if node else None ) user.affiliated_institutions.add(institution) draft_registration.set_title(title, Auth(user)) draft_registration.set_description(description, Auth(user)) draft_registration.category = category draft_registration.add_contributor(write_contrib, permissions=WRITE) draft_registration.set_node_license( { 'id': license.license_id, 'year': NEW_YEAR, 'copyrightHolders': COPYLEFT_HOLDERS }, auth=Auth(user), save=True ) draft_registration.add_tag('savanna', Auth(user)) draft_registration.add_tag('taxonomy', Auth(user)) draft_registration.set_subjects([[subject._id]], auth=Auth(draft_registration.creator)) draft_registration.affiliated_institutions.add(institution) draft_registration.save() return draft_registration
def test_can_view_property(self, user): project = factories.ProjectFactory(creator=user) write_contrib = factories.UserFactory() read_contrib = factories.UserFactory() non_contrib = factories.UserFactory() draft = DraftRegistration.create_from_node( user=user, node=project, schema=factories.get_default_metaschema() ) project.add_contributor(non_contrib, ADMIN, save=True) draft.add_contributor(write_contrib, WRITE, save=True) draft.add_contributor(read_contrib, READ, save=True) assert draft.get_permissions(user) == [READ, WRITE, ADMIN] assert draft.get_permissions(write_contrib) == [READ, WRITE] assert draft.get_permissions(read_contrib) == [READ] assert draft.can_view(Auth(user)) is True assert draft.can_view(Auth(write_contrib)) is True assert draft.can_view(Auth(read_contrib)) is True assert draft.can_view(Auth(non_contrib)) is False
def create(self, validated_data): node = validated_data.pop('node') initiator = validated_data.pop('initiator') metadata = validated_data.pop('registration_metadata', None) schema_id = validated_data.pop('registration_schema').get('_id') schema = get_object_or_error(MetaSchema, schema_id, self.context['request']) if schema.schema_version != LATEST_SCHEMA_VERSION or not schema.active: raise exceptions.ValidationError( 'Registration supplement must be an active schema.') draft = DraftRegistration.create_from_node(node=node, user=initiator, schema=schema) reviewer = is_prereg_admin_not_project_admin(self.context['request'], draft) if metadata: try: # Required fields are only required when creating the actual registration, not updating the draft. draft.validate_metadata(metadata=metadata, reviewer=reviewer, required_fields=False) except ValidationError as e: raise exceptions.ValidationError(e.message) draft.update_metadata(metadata) draft.save() return draft
def new_draft_registration(auth, node, *args, **kwargs): """Create a new draft registration for the node :return: Redirect to the new draft's edit page :rtype: flask.redirect :raises: HTTPError """ if node.is_registration: raise HTTPError(http.FORBIDDEN, data={ 'message_short': "Can't create draft", 'message_long': 'Creating draft registrations on registered projects is not allowed.' }) data = request.values schema_name = data.get('schema_name') if not schema_name: raise HTTPError( http.BAD_REQUEST, data={ 'message_short': 'Must specify a schema_name', 'message_long': 'Please specify a schema_name' } ) schema_version = data.get('schema_version', 2) meta_schema = get_schema_or_fail(schema_name, int(schema_version)) draft = DraftRegistration.create_from_node( node, user=auth.user, schema=meta_schema, data={} ) return redirect(node.web_url_for('edit_draft_registration_page', draft_id=draft._id, _guid=True))
def dispatch(self, request, *args, **kwargs): self.draft = DraftRegistration.load(self.kwargs.get('draft_pk')) if self.draft is None: raise Http404('{} with id "{}" not found.'.format( self.context_object_name.title(), self.kwargs.get('draft_pk') )) return super(DraftFormView, self).dispatch(request, *args, **kwargs)
def test_create_draft_registration_without_node(self, user): data = {'some': 'data'} draft = DraftRegistration.create_from_node( user=user, schema=get_default_metaschema(), data=data, ) assert draft.title == 'Untitled' assert draft.branched_from.title == 'Untitled' assert draft.branched_from.type == 'osf.draftnode' assert draft.branched_from.creator == user assert len(draft.logs.all()) == 0
def test_new_draft_registration_POST(self): target = NodeFactory(creator=self.user) payload = { 'schema_name': self.meta_schema.name, 'schema_version': self.meta_schema.schema_version } url = target.web_url_for('new_draft_registration') res = self.app.post(url, payload, auth=self.user.auth) assert_equal(res.status_code, http.FOUND) target.reload() draft = DraftRegistration.find_one(Q('branched_from', 'eq', target)) assert_equal(draft.registration_schema, self.meta_schema)
def test_create_draft_with_provider(self, app, user, url_draft_registrations, non_default_provider, payload_with_non_default_provider): res = app.post_json_api(url_draft_registrations, payload_with_non_default_provider, auth=user.auth) assert res.status_code == 201 data = res.json['data'] assert data['relationships']['provider']['links']['related']['href'] == \ f'{settings.API_DOMAIN}v2/providers/registrations/{non_default_provider._id}/' draft = DraftRegistration.load(data['id']) assert draft.provider == non_default_provider
def test_create_from_node(self): proj = NodeFactory() user = proj.creator schema = MetaSchema.objects.first() data = {'some': 'data'} draft = DraftRegistration.create_from_node( proj, user=user, schema=schema, data=data, ) assert user == draft.initiator assert schema == draft.registration_schema assert data == draft.registration_metadata assert proj == draft.branched_from
def test_admin_can_create_draft( self, app, user, project_public, url_draft_registrations, payload, metaschema_open_ended): url = '{}embed=branched_from&embed=initiator'.format(url_draft_registrations) res = app.post_json_api(url, payload, auth=user.auth) assert res.status_code == 201 data = res.json['data'] assert metaschema_open_ended._id in data['relationships']['registration_schema']['links']['related']['href'] assert data['attributes']['registration_metadata'] == {} assert data['embeds']['branched_from']['data']['id'] == DraftRegistration.objects.get(_id=data['id']).branched_from._id assert data['embeds']['initiator']['data']['id'] == user._id draft = DraftRegistration.load(data['id']) assert draft.creator == user assert draft.has_permission(user, ADMIN) is True
def bulk_upload_create_draft_registration(auth, initiator, schema, node, data, provider, row, title, external_id): """Create a draft registration from one registration row. """ draft = None try: draft = DraftRegistration.create_from_node( initiator, schema, node=node, data=data, provider=provider, ) # Remove all contributors except the initiator if created from an existing node if node: # Temporarily make initiator contributor visible so that removal of the others can succeed. initiator_contributor = draft.contributor_set.get(user=initiator) if not initiator_contributor.visible: initiator_contributor.visible = True initiator_contributor.save() contributor_set = draft.contributor_set.all() for contributor in contributor_set: if initiator != contributor.user: is_removed = draft.remove_contributor(contributor, auth) assert is_removed, 'Removal of an non-initiator contributor from the draft has failed' draft.save() assert len(draft.contributor_set.all( )) == 1, 'Draft should only have one contributor upon creation.' # Remove the initiator from the citation list initiator_contributor = draft.contributor_set.get(user=initiator) initiator_contributor.visible = False initiator_contributor.save() # Relate the draft to the row row.draft_registration = draft row.save() except Exception as e: # If the draft has been created already but failure happens before it is related to the registration row, # provide the draft id to the exception object for the caller to delete it after the exception is caught. draft_id = draft.id if draft else None raise RegistrationBulkCreationRowError( row.upload.id, row.id, title, external_id, draft_id=draft_id, error=repr(e), ) return draft
def prereg_reminder(email): """ Check make sure the draft still exists, has not already received a reminder, and has not been submitted. :param email: QueuedMail object, with the 'draft_id' in its data field :return: boolean based on whether the email should be sent """ # In line import to prevent circular importing from osf.models.queued_mail import QueuedMail from osf.models import DraftRegistration draft_id = email.data['draft_id'] draft = DraftRegistration.load(draft_id) reminders_sent = QueuedMail.objects.filter(data__draft_id=draft_id).exclude(sent_at=None).exists() return draft and not draft.deleted and not reminders_sent and not draft.registered_node
def prereg_reminder(email): """ Check make sure the draft still exists, has not already received a reminder, and has not been submitted. :param email: QueuedMail object, with the 'draft_id' in its data field :return: boolean based on whether the email should be sent """ # In line import to prevent circular importing from osf.models.queued_mail import QueuedMail from osf.models import DraftRegistration draft_id = email.data['draft_id'] draft = DraftRegistration.load(draft_id) reminders_sent = QueuedMail.objects.filter( data__draft_id=draft_id).exclude(sent_at=None).exists() return draft and not draft.deleted and not reminders_sent and not draft.registered_node
def test_create_no_project_draft_emails_initiator(self, app, user, url_draft_registrations, payload): post_url = url_draft_registrations + 'embed=branched_from&embed=initiator' # Intercepting the send_mail call from website.project.views.contributor.notify_added_contributor with mock.patch.object(mails, 'send_mail') as mock_send_mail: resp = app.post_json_api(post_url, payload, auth=user.auth) assert mock_send_mail.called # Python 3.6 does not support mock.call_args.args/kwargs # Instead, mock.call_args[0] is positional args, mock.call_args[1] is kwargs # (note, this is compatible with later versions) mock_send_kwargs = mock_send_mail.call_args[1] assert mock_send_kwargs[ 'mail'] == mails.CONTRIBUTOR_ADDED_DRAFT_REGISTRATION assert mock_send_kwargs['user'] == user assert mock_send_kwargs['node'] == DraftRegistration.load( resp.json['data']['id'])
def test_create_from_node_draft_node(self, user): draft = DraftRegistration.create_from_node( user=user, schema=factories.get_default_metaschema(), ) assert draft.title == 'Untitled' assert draft.description == '' assert draft.category == '' assert user in draft.contributors.all() assert len(draft.contributors.all()) == 1 assert draft.get_permissions(user) == [READ, WRITE, ADMIN] assert draft.node_license is None draft_tags = draft.tags.values_list('name', flat=True) assert len(draft_tags) == 0 assert draft.subjects.count() == 0 assert draft.affiliated_institutions.count() == 0
def _create(cls, *args, **kwargs): branched_from = kwargs.get('branched_from') initiator = kwargs.get('initiator') registration_schema = kwargs.get('registration_schema') registration_metadata = kwargs.get('registration_metadata') if not branched_from: project_params = {} if initiator: project_params['creator'] = initiator branched_from = ProjectFactory(**project_params) initiator = branched_from.creator registration_schema = registration_schema or MetaSchema.find()[0] registration_metadata = registration_metadata or {} draft = DraftRegistration.create_from_node( branched_from, user=initiator, schema=registration_schema, data=registration_metadata, ) return draft
def create(self, validated_data): node = validated_data.pop('node') initiator = validated_data.pop('initiator') metadata = validated_data.pop('registration_metadata', None) schema_id = validated_data.pop('registration_schema').get('_id') schema = get_object_or_error(MetaSchema, schema_id) if schema.schema_version != LATEST_SCHEMA_VERSION or not schema.active: raise exceptions.ValidationError('Registration supplement must be an active schema.') draft = DraftRegistration.create_from_node(node=node, user=initiator, schema=schema) reviewer = is_prereg_admin_not_project_admin(self.context['request'], draft) if metadata: try: # Required fields are only required when creating the actual registration, not updating the draft. draft.validate_metadata(metadata=metadata, reviewer=reviewer, required_fields=False) except ValidationError as e: raise exceptions.ValidationError(e.message) draft.update_metadata(metadata) draft.save() return draft
def addon_view_or_download_file(auth, path, provider, **kwargs): extras = request.args.to_dict() extras.pop('_', None) # Clean up our url params a bit action = extras.get('action', 'view') guid = kwargs.get('guid') guid_target = getattr(Guid.load(guid), 'referent', None) target = guid_target or kwargs.get('node') or kwargs['project'] provider_safe = markupsafe.escape(provider) path_safe = markupsafe.escape(path) if not path: raise HTTPError(httplib.BAD_REQUEST) if hasattr(target, 'get_addon'): node_addon = target.get_addon(provider) if not isinstance(node_addon, BaseStorageAddon): object_text = markupsafe.escape(getattr(target, 'project_or_component', 'this object')) raise HTTPError(httplib.BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer connected to {}.'.format(provider_safe, path_safe, object_text) }) if not node_addon.has_auth: raise HTTPError(httplib.UNAUTHORIZED, data={ 'message_short': 'Unauthorized', 'message_long': 'The {} add-on containing {} is no longer authorized.'.format(provider_safe, path_safe) }) if not node_addon.complete: raise HTTPError(httplib.BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer configured.'.format(provider_safe, path_safe) }) savepoint_id = transaction.savepoint() file_node = BaseFileNode.resolve_class(provider, BaseFileNode.FILE).get_or_create(target, path) # Note: Cookie is provided for authentication to waterbutler # it is overriden to force authentication as the current user # the auth header is also pass to support basic auth version = file_node.touch( request.headers.get('Authorization'), **dict( extras, cookie=request.cookies.get(settings.COOKIE_NAME) ) ) if version is None: # File is either deleted or unable to be found in the provider location # Rollback the insertion of the file_node transaction.savepoint_rollback(savepoint_id) if not file_node.pk: file_node = BaseFileNode.load(path) if file_node.kind == 'folder': raise HTTPError(httplib.BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'You cannot request a folder from this endpoint.' }) # Allow osfstorage to redirect if the deep url can be used to find a valid file_node if file_node and file_node.provider == 'osfstorage' and not file_node.is_deleted: return redirect( file_node.target.web_url_for('addon_view_or_download_file', path=file_node._id, provider=file_node.provider) ) return addon_deleted_file(target=target, file_node=file_node, path=path, **kwargs) else: transaction.savepoint_commit(savepoint_id) # TODO clean up these urls and unify what is used as a version identifier if request.method == 'HEAD': return make_response(('', httplib.FOUND, { 'Location': file_node.generate_waterbutler_url(**dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render')) })) if action == 'download': format = extras.get('format') _, extension = os.path.splitext(file_node.name) # avoid rendering files with the same format type. if format and '.{}'.format(format.lower()) != extension.lower(): return redirect('{}/export?format={}&url={}'.format(get_mfr_url(target, provider), format, urllib.quote(file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render') )))) return redirect(file_node.generate_waterbutler_url(**dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render'))) if action == 'get_guid': draft_id = extras.get('draft') draft = DraftRegistration.load(draft_id) if draft is None or draft.is_approved: raise HTTPError(httplib.BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'File not associated with required object.' }) guid = file_node.get_guid(create=True) guid.referent.save() return dict(guid=guid._id) if len(request.path.strip('/').split('/')) > 1: guid = file_node.get_guid(create=True) return redirect(furl.furl('/{}/'.format(guid._id)).set(args=extras).url) if isinstance(target, Preprint): # Redirecting preprint file guids to the preprint detail page return redirect('/{}/'.format(target._id)) return addon_view_file(auth, target, file_node, version)
def main(guid, creator_username): egap_schema = ensure_egap_schema() creator, creator_auth = get_creator_auth_header(creator_username) egap_assets_path = get_egap_assets(guid, creator_auth) # __MACOSX is a hidden file created by the os when zipping directory_list = [ directory for directory in os.listdir(egap_assets_path) if directory not in ('egap_assets.zip', '__MACOSX') and not directory.startswith('.') ] for egap_project_dir in directory_list: logger.info('Attempting to import the follow directory: {}'.format( egap_project_dir)) # Node Creation try: node = create_node_from_project_json(egap_assets_path, egap_project_dir, creator=creator) except Exception as err: logger.error( 'There was an error attempting to create a node from the ' '{} directory. Attempting to rollback node and contributor creation' .format(egap_project_dir)) logger.error(str(err)) try: rollback_node_from_project_json(egap_assets_path, egap_project_dir, creator=creator) except Exception as err: logger.error(str(err)) continue # Node File Upload non_anon_files = os.path.join(egap_assets_path, egap_project_dir, 'data', 'nonanonymous') non_anon_metadata = recursive_upload(creator_auth, node, non_anon_files) anon_files = os.path.join(egap_assets_path, egap_project_dir, 'data', 'anonymous') if os.path.isdir(anon_files): anon_metadata = recursive_upload(creator_auth, node, anon_files) else: anon_metadata = {} # DraftRegistration Metadata Handling with open( os.path.join(egap_assets_path, egap_project_dir, 'registration-schema.json'), 'r') as fp: registration_metadata = json.load(fp) # add selectedFileName Just so filenames are listed in the UI for data in non_anon_metadata: data['selectedFileName'] = data['data']['attributes']['name'] for data in anon_metadata: data['selectedFileName'] = data['data']['attributes']['name'] non_anon_titles = ', '.join( [data['data']['attributes']['name'] for data in non_anon_metadata]) registration_metadata['q37'] = { 'comments': [], 'extra': non_anon_metadata, 'value': non_anon_titles } anon_titles = ', '.join( [data['data']['attributes']['name'] for data in anon_metadata]) registration_metadata['q38'] = { 'comments': [], 'extra': anon_metadata, 'value': anon_titles } # DraftRegistration Creation draft_registration = DraftRegistration.create_from_node( node, user=creator, schema=egap_schema, data=registration_metadata, ) # Registration Creation logger.info( 'Attempting to create a Registration for Project {}'.format( node._id)) # Retrieve EGAP registration date and potential embargo go-public date if registration_metadata.get('q4'): egap_registration_date_string = registration_metadata['q4'][ 'value'] egap_registration_date = dt.strptime( egap_registration_date_string, '%m/%d/%Y - %H:%M').replace(tzinfo=pytz.UTC) else: logger.error( 'DraftRegistration associated with Project {} ' 'does not have a valid registration date in registration_metadata' .format(node._id)) continue if registration_metadata.get('q12'): if bool(registration_metadata['q12'].get('value')): egap_embargo_public_date_string = registration_metadata['q12'][ 'value'] egap_embargo_public_date = dt.strptime( egap_embargo_public_date_string, '%m/%d/%y').replace(tzinfo=pytz.UTC) else: egap_embargo_public_date = None else: egap_embargo_public_date = None sanction_type = 'RegistrationApproval' if egap_embargo_public_date and (egap_embargo_public_date > dt.today().replace(tzinfo=pytz.UTC)): sanction_type = 'Embargo' logger.info('Attempting to register {} silently'.format(node._id)) try: register_silently(draft_registration, Auth(creator), sanction_type, egap_registration_date, egap_embargo_public_date) except Exception as err: logger.error( 'Unexpected error raised when attempting to silently register ' 'project {}. Continuing...'.format(node._id)) logger.info(str(err)) continue # Update contributors on project to Admin contributors = node.contributor_set.all() for contributor in contributors: if contributor.user == creator: pass else: node.update_contributor(contributor.user, permission=ADMIN, visible=True, auth=Auth(creator), save=True) shutil.rmtree(egap_assets_path)
def get_object(self, *args, **kwargs): return DraftRegistration.load(self.kwargs.get('draft_pk'))
def draft_reg_util(): DraftRegistration.remove() return MetaSchema.objects.get(name='Prereg Challenge', schema_version=2)
def test_create_from_node_existing(self, user): node = factories.ProjectFactory(creator=user) member = factories.AuthUserFactory() osf_group = factories.OSFGroupFactory(creator=user) osf_group.make_member(member, auth=Auth(user)) node.add_osf_group(osf_group, ADMIN) write_contrib = factories.AuthUserFactory() subject = factories.SubjectFactory() institution = factories.InstitutionFactory() user.affiliated_institutions.add(institution) title = 'A Study of Elephants' description = 'Loxodonta africana' category = 'Methods and Materials' node.set_title(title, Auth(user)) node.set_description(description, Auth(user)) node.category = category node.add_contributor(write_contrib, permissions=WRITE) GPL3 = NodeLicense.objects.get(license_id='GPL3') NEW_YEAR = '2014' COPYLEFT_HOLDERS = ['Richard Stallman'] node.set_node_license( { 'id': GPL3.license_id, 'year': NEW_YEAR, 'copyrightHolders': COPYLEFT_HOLDERS }, auth=Auth(user), save=True ) node.add_tag('savanna', Auth(user)) node.add_tag('taxonomy', Auth(user)) node.set_subjects([[subject._id]], auth=Auth(node.creator)) node.affiliated_institutions.add(institution) node.save() draft = DraftRegistration.create_from_node( node=node, user=user, schema=factories.get_default_metaschema(), ) # Assert existing metadata-like node attributes are copied to the draft assert draft.title == title assert draft.description == description assert draft.category == category assert user in draft.contributors.all() assert write_contrib in draft.contributors.all() assert member not in draft.contributors.all() assert not draft.has_permission(member, 'read') assert draft.get_permissions(user) == [READ, WRITE, ADMIN] assert draft.get_permissions(write_contrib) == [READ, WRITE] assert draft.node_license.license_id == GPL3.license_id assert draft.node_license.name == GPL3.name assert draft.node_license.copyright_holders == COPYLEFT_HOLDERS draft_tags = draft.tags.values_list('name', flat=True) assert 'savanna' in draft_tags assert 'taxonomy' in draft_tags assert subject in draft.subjects.all() assert institution in draft.affiliated_institutions.all() assert draft.branched_from == node
def check_access(node, auth, action, cas_resp): """Verify that user can perform requested action on resource. Raise appropriate error code if action cannot proceed. """ permission = permission_map.get(action, None) if permission is None: raise HTTPError(httplib.BAD_REQUEST) if cas_resp: if permission == 'read': if node.is_public: return True required_scope = oauth_scopes.CoreScopes.NODE_FILE_READ else: required_scope = oauth_scopes.CoreScopes.NODE_FILE_WRITE if not cas_resp.authenticated \ or required_scope not in oauth_scopes.normalize_scopes(cas_resp.attributes['accessTokenScope']): raise HTTPError(httplib.FORBIDDEN) if permission == 'read': if node.can_view(auth): return True # The user may have admin privileges on a parent node, in which # case they should have read permissions if node.is_registration and node.registered_from.can_view(auth): return True if permission == 'write' and node.can_edit(auth): return True # Users attempting to register projects with components might not have # `write` permissions for all components. This will result in a 403 for # all `copyto` actions as well as `copyfrom` actions if the component # in question is not public. To get around this, we have to recursively # check the node's parent node to determine if they have `write` # permissions up the stack. # TODO(hrybacki): is there a way to tell if this is for a registration? # All nodes being registered that receive the `copyto` action will have # `node.is_registration` == True. However, we have no way of telling if # `copyfrom` actions are originating from a node being registered. # TODO This is raise UNAUTHORIZED for registrations that have not been archived yet if action == 'copyfrom' or (action == 'copyto' and node.is_registration): parent = node.parent_node while parent: if parent.can_edit(auth): return True parent = parent.parent_node # Users with the PREREG_ADMIN_TAG should be allowed to download files # from prereg challenge draft registrations. try: prereg_schema = MetaSchema.find_one( Q('name', 'eq', 'Prereg Challenge') & Q('schema_version', 'eq', 2) ) allowed_nodes = [node] + node.parents prereg_draft_registration = DraftRegistration.find( Q('branched_from', 'in', [n for n in allowed_nodes]) & Q('registration_schema', 'eq', prereg_schema) ) if action == 'download' and \ auth.user is not None and \ prereg_draft_registration.count() > 0 and \ settings.PREREG_ADMIN_TAG in auth.user.system_tags: return True except NoResultsFound: pass raise HTTPError(httplib.FORBIDDEN if auth.user else httplib.UNAUTHORIZED)
def draft_reg_util(): DraftRegistration.remove() return MetaSchema.find_one( Q('name', 'eq', 'Prereg Challenge') & Q('schema_version', 'eq', 2))
def main(guid, creator_username): egap_schema = ensure_egap_schema() creator, creator_auth = get_creator_auth_header(creator_username) egap_assets_path = get_egap_assets(guid, creator_auth) # __MACOSX is a hidden file created by the os when zipping directory_list = [ directory for directory in os.listdir(egap_assets_path) if directory not in ('egap_assets.zip', '__MACOSX') ] for epag_project_dir in directory_list: node = create_node_from_project_json(egap_assets_path, epag_project_dir, creator=creator) non_anon_files = os.path.join(egap_assets_path, epag_project_dir, 'data', 'nonanonymous') non_anon_metadata = recursive_upload(creator_auth, node, non_anon_files) anon_files = os.path.join(egap_assets_path, epag_project_dir, 'data', 'anonymous') if os.path.isdir(anon_files): anon_metadata = recursive_upload(creator_auth, node, anon_files) else: anon_metadata = {} with open( os.path.join(egap_assets_path, epag_project_dir, 'registration-schema.json'), 'r') as fp: registration_metadata = json.load(fp) # add selectedFileName Just so filenames are listed in the UI for data in non_anon_metadata: data['selectedFileName'] = data['data']['attributes']['name'] for data in anon_metadata: data['selectedFileName'] = data['data']['attributes']['name'] non_anon_titles = ', '.join( [data['data']['attributes']['name'] for data in non_anon_metadata]) registration_metadata['q37'] = { 'comments': [], 'extra': non_anon_metadata, 'value': non_anon_titles } anon_titles = ', '.join( [data['data']['attributes']['name'] for data in anon_metadata]) registration_metadata['q38'] = { 'comments': [], 'extra': anon_metadata, 'value': anon_titles } DraftRegistration.create_from_node( node, user=creator, schema=egap_schema, data=registration_metadata, ) shutil.rmtree(egap_assets_path)
def addon_view_or_download_file(auth, path, provider, **kwargs): extras = request.args.to_dict() extras.pop('_', None) # Clean up our url params a bit action = extras.get('action', 'view') guid = kwargs.get('guid') guid_target = getattr(Guid.load(guid), 'referent', None) target = guid_target or kwargs.get('node') or kwargs['project'] provider_safe = markupsafe.escape(provider) path_safe = markupsafe.escape(path) if not path: raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if hasattr(target, 'get_addon'): node_addon = target.get_addon(provider) if not isinstance(node_addon, BaseStorageAddon): object_text = markupsafe.escape( getattr(target, 'project_or_component', 'this object')) raise HTTPError( http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer connected to {}.' .format(provider_safe, path_safe, object_text) }) if not node_addon.has_auth: raise HTTPError( http_status.HTTP_401_UNAUTHORIZED, data={ 'message_short': 'Unauthorized', 'message_long': 'The {} add-on containing {} is no longer authorized.'. format(provider_safe, path_safe) }) if not node_addon.complete: raise HTTPError( http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer configured.'. format(provider_safe, path_safe) }) savepoint_id = transaction.savepoint() file_node = BaseFileNode.resolve_class(provider, BaseFileNode.FILE).get_or_create( target, path) # Note: Cookie is provided for authentication to waterbutler # it is overriden to force authentication as the current user # the auth header is also pass to support basic auth version = file_node.touch( request.headers.get('Authorization'), **dict(extras, cookie=request.cookies.get(settings.COOKIE_NAME))) if version is None: # File is either deleted or unable to be found in the provider location # Rollback the insertion of the file_node transaction.savepoint_rollback(savepoint_id) if not file_node.pk: file_node = BaseFileNode.load(path) if not file_node: raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'File Not Found', 'message_long': 'The requested file could not be found.' }) if file_node.kind == 'folder': raise HTTPError( http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'You cannot request a folder from this endpoint.' }) # Allow osfstorage to redirect if the deep url can be used to find a valid file_node if file_node.provider == 'osfstorage' and not file_node.is_deleted: return redirect( file_node.target.web_url_for('addon_view_or_download_file', path=file_node._id, provider=file_node.provider)) return addon_deleted_file(target=target, file_node=file_node, path=path, **kwargs) else: transaction.savepoint_commit(savepoint_id) # TODO clean up these urls and unify what is used as a version identifier if request.method == 'HEAD': return make_response(('', http_status.HTTP_302_FOUND, { 'Location': file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render')) })) if action == 'download': format = extras.get('format') _, extension = os.path.splitext(file_node.name) # avoid rendering files with the same format type. if format and '.{}'.format(format.lower()) != extension.lower(): return redirect('{}/export?format={}&url={}'.format( get_mfr_url(target, provider), format, quote( file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render'))))) return redirect( file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render'))) if action == 'get_guid': draft_id = extras.get('draft') draft = DraftRegistration.load(draft_id) if draft is None or draft.is_approved: raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'File not associated with required object.' }) guid = file_node.get_guid(create=True) guid.referent.save() return dict(guid=guid._id) if len(request.path.strip('/').split('/')) > 1: guid = file_node.get_guid(create=True) return redirect( furl.furl('/{}/'.format(guid._id)).set(args=extras).url) if isinstance(target, Preprint): # Redirecting preprint file guids to the preprint detail page return redirect('/{}/'.format(target._id)) return addon_view_file(auth, target, file_node, version)
def draft_reg_util(): DraftRegistration.remove() return MetaSchema.find_one( Q('name', 'eq', 'Prereg Challenge') & Q('schema_version', 'eq', 2) )