def resolve_guid(guid, suffix=None): """Resolve GUID to corresponding URL and return result of appropriate view function. This effectively yields a redirect without changing the displayed URL of the page. :param guid: GUID value (not the object) :param suffix: String to append to GUID route :return: Werkzeug response """ # Get prefix; handles API routes prefix = request.path.split(guid)[0].rstrip('/') # Look up GUID guid_object = Guid.load(guid) if guid_object: # verify that the object is a GuidStoredObject descendant. If a model # was once a descendant but that relationship has changed, it's # possible to have referents that are instances of classes that don't # have a redirect_mode attribute or otherwise don't behave as # expected. if not isinstance(guid_object.referent, GuidStoredObject): sentry.log_message( 'Guid `{}` resolved to non-guid object'.format(guid) ) raise HTTPError(http.NOT_FOUND) referent = guid_object.referent if referent is None: logger.error('Referent of GUID {0} not found'.format(guid)) raise HTTPError(http.NOT_FOUND) mode = referent.redirect_mode if mode is None: raise HTTPError(http.NOT_FOUND) url = referent.deep_url if mode == 'proxy' else referent.url url = _build_guid_url(url, prefix, suffix) # Always redirect API URLs; URL should identify endpoint being called if prefix or mode == 'redirect': if request.query_string: url += '?' + request.query_string return redirect(url) return proxy_url(url) # GUID not found; try lower-cased and redirect if exists guid_object_lower = Guid.load(guid.lower()) if guid_object_lower: return redirect( _build_guid_url( guid.lower(), prefix, suffix ) ) # GUID not found raise HTTPError(http.NOT_FOUND)
def resolve_guid(guid, suffix=None): """Load GUID by primary key, look up the corresponding view function in the routing table, and return the return value of the view function without changing the URL. :param str guid: GUID primary key :param str suffix: Remainder of URL after the GUID :return: Return value of proxied view function """ try: # Look up guid_object = Guid.load(guid) except KeyError as e: if e.message == 'osfstorageguidfile': # Used when an old detached OsfStorageGuidFile object is accessed raise HTTPError(http.NOT_FOUND) else: raise e if guid_object: # verify that the object implements a GuidStoredObject-like interface. If a model # was once GuidStoredObject-like but that relationship has changed, it's # possible to have referents that are instances of classes that don't # have a deep_url attribute or otherwise don't behave as # expected. if not hasattr(guid_object.referent, 'deep_url'): sentry.log_message( 'Guid `{}` resolved to an object with no deep_url'.format(guid) ) raise HTTPError(http.NOT_FOUND) referent = guid_object.referent if referent is None: logger.error('Referent of GUID {0} not found'.format(guid)) raise HTTPError(http.NOT_FOUND) if not referent.deep_url: raise HTTPError(http.NOT_FOUND) if isinstance(referent, PreprintService): return send_from_directory( os.path.abspath(os.path.join(os.getcwd(), EXTERNAL_EMBER_APPS['preprints']['path'])), 'index.html' ) url = _build_guid_url(urllib.unquote(referent.deep_url), suffix) return proxy_url(url) # GUID not found; try lower-cased and redirect if exists guid_object_lower = Guid.load(guid.lower()) if guid_object_lower: return redirect( _build_guid_url(guid.lower(), suffix) ) # GUID not found raise HTTPError(http.NOT_FOUND)
def resolve_guid(guid, suffix=None): """Resolve GUID to corresponding URL and return result of appropriate view function. This effectively yields a redirect without changing the displayed URL of the page. :param guid: GUID value (not the object) :param suffix: String to append to GUID route :return: Werkzeug response """ # Get prefix; handles API routes prefix = request.path.split(guid)[0].rstrip('/') # Look up GUID guid_object = Guid.load(guid) if guid_object: # verify that the object is a GuidStoredObject descendant. If a model # was once a descendant but that relationship has changed, it's # possible to have referents that are instances of classes that don't # have a redirect_mode attribute or otherwise don't behave as # expected. if not isinstance(guid_object.referent, GuidStoredObject): sentry.log_message( 'Guid `{}` resolved to non-guid object'.format(guid)) raise HTTPError(http.NOT_FOUND) referent = guid_object.referent if referent is None: logger.error('Referent of GUID {0} not found'.format(guid)) raise HTTPError(http.NOT_FOUND) mode = referent.redirect_mode if mode is None: raise HTTPError(http.NOT_FOUND) url = referent.deep_url if mode == 'proxy' else referent.url url = _build_guid_url(url, prefix, suffix) # Always redirect API URLs; URL should identify endpoint being called if prefix or mode == 'redirect': if request.query_string: url += '?' + request.query_string return redirect(url) return proxy_url(url) # GUID not found; try lower-cased and redirect if exists guid_object_lower = Guid.load(guid.lower()) if guid_object_lower: return redirect(_build_guid_url(guid.lower(), prefix, suffix)) # GUID not found raise HTTPError(http.NOT_FOUND)
def migrate(targets): collections = targets.pop('collections') assert len(targets), 'Must specify object to create new guid for' assert len( targets) == 1, 'Can only create new guid for one object at a time' old_id = targets.values()[0] node = Node.load(old_id) new_guid = Guid.generate(referent=node) logger.info('* Created new guid {} for node {}'.format( new_guid._id, old_id)) logger.info('* Preparing to set references.') fix_backrefs(node) node.reload() node._id = new_guid._id node.save() new_guid.referent = node new_guid.save() for collection, _id_list in collections.iteritems(): assert type( _id_list ) == list, 'Expected type list for collection {} ids, got {}'.format( collection, type(_id_list)) for _id in _id_list: logger.info('** Updating {} ({})'.format(_id, collection)) doc = clean_dict(database[collection].find_one({'_id': _id})) replacement = json.loads( re.sub(r'\b{}\b'.format(old_id), new_guid._id, json.dumps(doc))) database[collection].find_and_modify({'_id': _id}, {'$set': replacement}) logger.info('*** Updated {} ({}): \n{}\n'.format( _id, collection, replacement)) logger.info('Successfully migrate {} to {}'.format(old_id, new_guid._id))
def test_restore_file(self): root = models.StoredFileNode( path='root', name='rootfolder', is_file=False, node=self.node, provider='test', materialized_path='/long/path/to', ).wrapped() root.save() fn = models.StoredFileNode( parent=root._id, path='afile', name='name', is_file=True, node=self.node, provider='test', materialized_path='/long/path/to/name', ).wrapped() guid = Guid.generate(fn) before = fn.to_storage() trashed = fn.delete(user=self.user) restored = trashed.restore() assert_equal(restored.to_storage(), before) assert_equal(models.TrashedFileNode.load(trashed._id), None) # Guid is repointed guid.reload() assert_equal(guid.referent, restored)
def resolve_target(node, guid): if not guid: return node target = Guid.load(guid) if target is None: raise HTTPError(http.BAD_REQUEST) return target.referent
def test_migrate(self): # Sanity check for obj in self.legacy_objs: guid_obj = Guid.load(obj._id) assert_equal(guid_obj.referent, obj) nobjs = OsfStorageGuidFile.find().count() main(dry_run=False) Guid._clear_caches() for obj in self.legacy_objs: guid_obj = Guid.load(obj._id) assert_not_equal(guid_obj.referent, obj) assert_true(isinstance(guid_obj.referent, OsfStorageGuidFile)) assert_equal(guid_obj.referent.node, self.project) assert_equal(guid_obj.referent.path, obj.name) assert_equal(guid_obj.referent._id, obj._id) assert_equal(OsfStorageGuidFile.find().count(), nobjs + 3) # Test idempotence main(dry_run=False) assert_equal(OsfStorageGuidFile.find().count(), nobjs + 3)
def resolve_guid(guid, suffix=None): """Load GUID by primary key, look up the corresponding view function in the routing table, and return the return value of the view function without changing the URL. :param str guid: GUID primary key :param str suffix: Remainder of URL after the GUID :return: Return value of proxied view function """ # Look up GUID guid_object = Guid.load(guid) if guid_object: # verify that the object is a GuidStoredObject descendant. If a model # was once a descendant but that relationship has changed, it's # possible to have referents that are instances of classes that don't # have a redirect_mode attribute or otherwise don't behave as # expected. if not isinstance(guid_object.referent, GuidStoredObject): sentry.log_message( 'Guid `{}` resolved to non-guid object'.format(guid) ) raise HTTPError(http.NOT_FOUND) referent = guid_object.referent if referent is None: logger.error('Referent of GUID {0} not found'.format(guid)) raise HTTPError(http.NOT_FOUND) if not referent.deep_url: raise HTTPError(http.NOT_FOUND) url = _build_guid_url(referent.deep_url, suffix) return proxy_url(url) # GUID not found; try lower-cased and redirect if exists guid_object_lower = Guid.load(guid.lower()) if guid_object_lower: return redirect( _build_guid_url(guid.lower(), suffix) ) # GUID not found raise HTTPError(http.NOT_FOUND)
def resolve_guid(guid, suffix=None): """Load GUID by primary key, look up the corresponding view function in the routing table, and return the return value of the view function without changing the URL. :param str guid: GUID primary key :param str suffix: Remainder of URL after the GUID :return: Return value of proxied view function """ # Look up GUID guid_object = Guid.load(guid) if guid_object: # verify that the object implements a GuidStoredObject-like interface. If a model # was once GuidStoredObject-like but that relationship has changed, it's # possible to have referents that are instances of classes that don't # have a deep_url attribute or otherwise don't behave as # expected. if not hasattr(guid_object.referent, 'deep_url'): sentry.log_message( 'Guid `{}` resolved to an object with no deep_url'.format(guid) ) raise HTTPError(http.NOT_FOUND) referent = guid_object.referent if referent is None: logger.error('Referent of GUID {0} not found'.format(guid)) raise HTTPError(http.NOT_FOUND) if not referent.deep_url: raise HTTPError(http.NOT_FOUND) url = _build_guid_url(urllib.unquote(referent.deep_url), suffix) return proxy_url(url) # GUID not found; try lower-cased and redirect if exists guid_object_lower = Guid.load(guid.lower()) if guid_object_lower: return redirect( _build_guid_url(guid.lower(), suffix) ) # GUID not found raise HTTPError(http.NOT_FOUND)
def migrate(): for target in targets: guid = Guid.load(target['guid']) good_sfn = StoredFileNode.load(target['good']) bad_sfn = StoredFileNode.load(target['bad']) logger.info('Repointing Guid {} referent to StoredFileNode {}'.format(target['guid'], target['good'])) guid.referent = good_sfn guid.save() logger.info('Removing StoredFileNode {}'.format(target['bad'])) StoredFileNode.remove_one(bad_sfn)
def restore_file(guid): guid_obj = Guid.load(guid) trashed_file_node = guid_obj.referent assert isinstance(trashed_file_node, TrashedFileNode), 'Guid does not point to a trashedfilenode' logger.info('Loaded trashedfilenode {}'.format(trashed_file_node._id)) try: logger.info('Calling restore()') trashed_file_node.restore() except KeyExistsException: # File with same name exists; user most likely re-uploaded file logger.warn('File with name {} exists. Renaming...'.format(trashed_file_node.name)) rename_file(trashed_file_node) logger.info('Calling restore()') trashed_file_node.restore() return True
def migrate_legacy_obj(legacy_guid_file): """Create `OsfStorageGuidFile` object corresponding to provided `OsfGuidFile` object, then set the `referent` of the `Guid` object to the newly created record. """ logger.info('Migrating legacy Guid {0}'.format(legacy_guid_file._id)) storage_obj = get_or_create_storage_file( legacy_guid_file.node, legacy_guid_file.name, _id=legacy_guid_file._id, ) guid_obj = Guid.load(legacy_guid_file._id) guid_obj.referent = storage_obj guid_obj.save() return storage_obj
def test_restore_file(self): root = models.StoredFileNode( path='root', name='rootfolder', is_file=False, node=self.node, provider='test', materialized_path='/long/path/to', ).wrapped() root.save() fn = models.StoredFileNode( parent=root._id, path='afile', name='name', is_file=True, node=self.node, provider='test', materialized_path='/long/path/to/name', ).wrapped() guid = Guid.generate(fn) before = fn.to_storage() trashed = fn.delete(user=self.user) restored = trashed.restore() assert_equal( restored.to_storage(), before ) assert_equal(models.TrashedFileNode.load(trashed._id), None) # Guid is repointed guid.reload() assert_equal(guid.referent, restored)
def resolve_guid(guid, suffix=None): """Load GUID by primary key, look up the corresponding view function in the routing table, and return the return value of the view function without changing the URL. :param str guid: GUID primary key :param str suffix: Remainder of URL after the GUID :return: Return value of proxied view function """ try: # Look up guid_object = Guid.load(guid) except KeyError as e: if e.message == 'osfstorageguidfile': # Used when an old detached OsfStorageGuidFile object is accessed raise HTTPError(http.NOT_FOUND) else: raise e if guid_object: # verify that the object implements a GuidStoredObject-like interface. If a model # was once GuidStoredObject-like but that relationship has changed, it's # possible to have referents that are instances of classes that don't # have a deep_url attribute or otherwise don't behave as # expected. if not hasattr(guid_object.referent, 'deep_url'): sentry.log_message( 'Guid `{}` resolved to an object with no deep_url'.format( guid)) raise HTTPError(http.NOT_FOUND) referent = guid_object.referent if referent is None: logger.error('Referent of GUID {0} not found'.format(guid)) raise HTTPError(http.NOT_FOUND) if not referent.deep_url: raise HTTPError(http.NOT_FOUND) # Handle file `/download` shortcut with supported types. if suffix and suffix.rstrip('/').lower() == 'download': file_referent = None if isinstance(referent, PreprintService) and referent.primary_file: if not referent.is_published: # TODO: Ideally, permissions wouldn't be checked here. # This is necessary to prevent a logical inconsistency with # the routing scheme - if a preprint is not published, only # admins should be able to know it exists. auth = Auth.from_kwargs(request.args.to_dict(), {}) if not referent.node.has_permission( auth.user, permissions.ADMIN): raise HTTPError(http.NOT_FOUND) file_referent = referent.primary_file elif isinstance(referent, BaseFileNode) and referent.is_file: file_referent = referent if file_referent: # Extend `request.args` adding `action=download`. request.args = request.args.copy() request.args.update({'action': 'download'}) # Do not include the `download` suffix in the url rebuild. url = _build_guid_url(urllib.unquote(file_referent.deep_url)) return proxy_url(url) # Handle Ember Applications if isinstance(referent, PreprintService): if referent.provider.domain_redirect_enabled: # This route should always be intercepted by nginx for the branded domain, # w/ the exception of `<guid>/download` handled above. return redirect(referent.absolute_url, http.MOVED_PERMANENTLY) return send_from_directory(preprints_dir, 'index.html') url = _build_guid_url(urllib.unquote(referent.deep_url), suffix) return proxy_url(url) # GUID not found; try lower-cased and redirect if exists guid_object_lower = Guid.load(guid.lower()) if guid_object_lower: return redirect(_build_guid_url(guid.lower(), suffix)) # GUID not found raise HTTPError(http.NOT_FOUND)