Пример #1
0
    def resolve_visibility(self, info):
        app_config = Configuration().config
        default_remote = app_config['git']['default_remote']
        admin_service = None
        for remote in Configuration().config['git']['remotes']:
            if default_remote == remote:
                admin_service = app_config['git']['remotes'][remote]['admin_service']
                break

        # Extract valid Bearer token
        if "HTTP_AUTHORIZATION" in info.context.headers.environ:
            token = parse_token(info.context.headers.environ["HTTP_AUTHORIZATION"])
        else:
            raise ValueError("Authorization header not provided. Must have a valid session to query for collaborators")

        # Get collaborators from remote service
        mgr = GitLabManager(default_remote, admin_service, token)
        try:
            d = mgr.repo_details(namespace=self.owner, repository_name=self.name)
            return d.get('visibility')
        except ValueError:
            return "unknown"
Пример #2
0
    def mutate_and_get_payload(cls,
                               root,
                               info,
                               owner,
                               labbook_name,
                               username,
                               client_mutation_id=None):
        logged_in_username = get_logged_in_username()
        lb = InventoryManager().load_labbook(logged_in_username,
                                             owner,
                                             labbook_name,
                                             author=get_logged_in_author())

        # TODO: Future work will look up remote in LabBook data, allowing user to select remote.
        default_remote = lb.client_config.config['git']['default_remote']
        admin_service = None
        for remote in lb.client_config.config['git']['remotes']:
            if default_remote == remote:
                admin_service = lb.client_config.config['git']['remotes'][
                    remote]['admin_service']
                break

        # Extract valid Bearer token
        if "HTTP_AUTHORIZATION" in info.context.headers.environ:
            token = parse_token(
                info.context.headers.environ["HTTP_AUTHORIZATION"])
        else:
            raise ValueError(
                "Authorization header not provided. Must have a valid session to query for collaborators"
            )

        # Add collaborator to remote service
        mgr = GitLabManager(default_remote, admin_service, token)
        mgr.delete_collaborator(owner, labbook_name, username)

        create_data = {"owner": owner, "name": labbook_name}

        return DeleteLabbookCollaborator(updated_labbook=LabbookObject(
            **create_data))
Пример #3
0
    def _fetch_collaborators(self, labbook, info):
        """Helper method to fetch this labbook's collaborators

        Args:
            info: The graphene info object for this requests

        """
        # TODO: Future work will look up remote in LabBook data, allowing user to select remote.
        default_remote = labbook.client_config.config['git']['default_remote']
        admin_service = None
        for remote in labbook.client_config.config['git']['remotes']:
            if default_remote == remote:
                admin_service = labbook.client_config.config['git']['remotes'][
                    remote]['admin_service']
                break

        # Extract valid Bearer token
        if "HTTP_AUTHORIZATION" in info.context.headers.environ:
            token = parse_token(
                info.context.headers.environ["HTTP_AUTHORIZATION"])
        else:
            raise ValueError(
                "Authorization header not provided. Must have a valid session to query for collaborators"
            )

        # Get collaborators from remote service
        mgr = GitLabManager(default_remote, admin_service, token)
        try:
            self._collaborators = [
                Collaborator(owner=self.owner,
                             name=self.name,
                             collaborator_username=c[1],
                             permission=ProjectPermissions(c[2]).name)
                for c in mgr.get_collaborators(self.owner, self.name)
            ]
        except ValueError:
            # If ValueError Raised, assume repo doesn't exist yet
            self._collaborators = []
Пример #4
0
    def mutate_and_get_payload(cls,
                               root,
                               info,
                               owner,
                               labbook_name,
                               set_public=False,
                               client_mutation_id=None):
        # Load LabBook
        username = get_logged_in_username()
        lb = InventoryManager().load_labbook(username,
                                             owner,
                                             labbook_name,
                                             author=get_logged_in_author())
        # Extract valid Bearer token
        if "HTTP_AUTHORIZATION" in info.context.headers.environ:
            token = parse_token(
                info.context.headers.environ["HTTP_AUTHORIZATION"])
        else:
            raise ValueError(
                "Authorization header not provided. Must have a valid session to query for collaborators"
            )

        job_metadata = {'method': 'publish_labbook', 'labbook': lb.key}
        job_kwargs = {
            'repository': lb,
            'username': username,
            'access_token': token,
            'public': set_public
        }
        dispatcher = Dispatcher()
        job_key = dispatcher.dispatch_task(jobs.publish_repository,
                                           kwargs=job_kwargs,
                                           metadata=job_metadata)
        logger.info(
            f"Publishing LabBook {lb.root_dir} in background job with key {job_key.key_str}"
        )

        return PublishLabbook(job_key=job_key.key_str)
Пример #5
0
    def helper_resolve_visibility(dataset, info):
        # TODO: Future work will look up remote in Dataset data, allowing user to select remote.
        default_remote = dataset.client_config.config['git']['default_remote']
        admin_service = None
        for remote in dataset.client_config.config['git']['remotes']:
            if default_remote == remote:
                admin_service = dataset.client_config.config['git']['remotes'][remote]['admin_service']
                break

        # Extract valid Bearer token
        if "HTTP_AUTHORIZATION" in info.context.headers.environ:
            token = parse_token(info.context.headers.environ["HTTP_AUTHORIZATION"])
        else:
            raise ValueError("Authorization header not provided. Must have a valid session to query for collaborators")

        # Get collaborators from remote service
        mgr = GitLabManager(default_remote, admin_service, token)
        try:
            owner = InventoryManager().query_owner(dataset)
            d = mgr.repo_details(namespace=owner, repository_name=dataset.name)
            return d.get('visibility')
        except ValueError:
            return "local"
Пример #6
0
    def resolve(self, next, root, info, **args):
        if not self.identity_mgr:
            self.identity_mgr = get_identity_manager_instance()

        # On first field processed in request, authenticate
        if not hasattr(info.context, "auth_middleware_complete"):
            # Pull the token out of the header if available
            token = None
            if "Authorization" in info.context.headers:
                token = parse_token(info.context.headers["Authorization"])

            # Save token to the request context for future use (e.g. look up a user's profile information if needed)
            flask.g.access_token = token

            # Check if you are authenticated
            try:
                self.identity_mgr.is_authenticated(token)
            except AuthenticationError:
                raise AuthenticationError("User not authenticated", 401)

            info.context.auth_middleware_complete = True

        return next(root, info, **args)
Пример #7
0
    def mutate_and_get_payload(cls,
                               root,
                               info,
                               owner,
                               dataset_name,
                               pull_only=False,
                               override_method="abort",
                               client_mutation_id=None):
        # Load Dataset
        username = get_logged_in_username()
        ds = InventoryManager().load_dataset(username,
                                             owner,
                                             dataset_name,
                                             author=get_logged_in_author())

        # Extract valid Bearer token
        token = None
        if hasattr(info.context.headers, 'environ'):
            if "HTTP_AUTHORIZATION" in info.context.headers.environ:
                token = parse_token(
                    info.context.headers.environ["HTTP_AUTHORIZATION"])

        if not token:
            raise ValueError(
                "Authorization header not provided. Must have a valid session to query for collaborators"
            )

        default_remote = ds.client_config.config['git']['default_remote']
        admin_service = None
        for remote in ds.client_config.config['git']['remotes']:
            if default_remote == remote:
                admin_service = ds.client_config.config['git']['remotes'][
                    remote]['admin_service']
                break

        if not admin_service:
            raise ValueError('admin_service could not be found')

        # Configure git creds
        mgr = GitLabManager(default_remote, admin_service, access_token=token)
        mgr.configure_git_credentials(default_remote, username)

        override = MergeOverride(override_method)
        job_metadata = {'method': 'sync_dataset', 'dataset': ds.key}
        job_kwargs = {
            'repository': ds,
            'username': username,
            'pull_only': pull_only,
            'override': override,
            'access_token': token,
            'id_token': flask.g.id_token
        }

        dispatcher = Dispatcher()
        job_key = dispatcher.dispatch_task(jobs.sync_repository,
                                           kwargs=job_kwargs,
                                           metadata=job_metadata)
        logger.info(
            f"Syncing Dataset {ds.root_dir} in background job with key {job_key.key_str}"
        )

        return SyncDataset(job_key=job_key.key_str)
Пример #8
0
    def mutate_and_get_payload(cls,
                               root,
                               info,
                               owner,
                               dataset_name,
                               username,
                               permissions,
                               client_mutation_id=None):
        # Here "username" refers to the intended recipient username.
        # Todo: it should probably be renamed here and in the frontend to "collaboratorUsername"
        logged_in_username = get_logged_in_username()
        lb = InventoryManager().load_dataset(logged_in_username,
                                             owner,
                                             dataset_name,
                                             author=get_logged_in_author())

        # TODO: Future work will look up remote in LabBook data, allowing user to select remote.
        default_remote = lb.client_config.config['git']['default_remote']
        admin_service = None
        for remote in lb.client_config.config['git']['remotes']:
            if default_remote == remote:
                admin_service = lb.client_config.config['git']['remotes'][
                    remote]['admin_service']
                break

        # Extract valid Bearer token
        if "HTTP_AUTHORIZATION" in info.context.headers.environ:
            token = parse_token(
                info.context.headers.environ["HTTP_AUTHORIZATION"])
        else:
            raise ValueError(
                "Authorization header not provided. "
                "Must have a valid session to query for collaborators")

        if permissions == 'readonly':
            perm = ProjectPermissions.READ_ONLY
        elif permissions == 'readwrite':
            perm = ProjectPermissions.READ_WRITE
        elif permissions == 'owner':
            perm = ProjectPermissions.OWNER
        else:
            raise ValueError(f"Unknown permission set: {permissions}")

        # Add collaborator to remote service
        mgr = GitLabManager(default_remote, admin_service, token)

        existing_collabs = mgr.get_collaborators(owner, dataset_name)

        if username not in [n[1] for n in existing_collabs]:
            logger.info(f"Adding user {username} to {owner}/{dataset_name}"
                        f"with permission {perm}")
            mgr.add_collaborator(owner, dataset_name, username, perm)
        else:
            logger.warning(f"Changing permission of {username} on"
                           f"{owner}/{dataset_name} to {perm}")
            mgr.delete_collaborator(owner, dataset_name, username)
            mgr.add_collaborator(owner, dataset_name, username, perm)
        create_data = {"owner": owner, "name": dataset_name}

        return AddDatasetCollaborator(updated_dataset=DatasetObject(
            **create_data))
Пример #9
0
    def mutate_and_get_payload(cls,
                               root,
                               info,
                               owner,
                               labbook_name,
                               confirm,
                               client_mutation_id=None):
        if confirm is True:
            # Load config data
            configuration = Configuration().config

            # Extract valid Bearer token
            token = None
            if hasattr(info.context.headers, 'environ'):
                if "HTTP_AUTHORIZATION" in info.context.headers.environ:
                    token = parse_token(
                        info.context.headers.environ["HTTP_AUTHORIZATION"])
            if not token:
                raise ValueError(
                    "Authorization header not provided. Cannot perform remote delete operation."
                )

            # Get remote server configuration
            default_remote = configuration['git']['default_remote']
            admin_service = None
            for remote in configuration['git']['remotes']:
                if default_remote == remote:
                    admin_service = configuration['git']['remotes'][remote][
                        'admin_service']
                    index_service = configuration['git']['remotes'][remote][
                        'index_service']
                    break

            if not admin_service:
                raise ValueError('admin_service could not be found')

            # Perform delete operation
            mgr = GitLabManager(default_remote,
                                admin_service,
                                access_token=token)
            mgr.remove_repository(owner, labbook_name)
            logger.info(
                f"Deleted {owner}/{labbook_name} from the remote repository {default_remote}"
            )

            # Call Index service to remove project from cloud index and search
            # Don't raise an exception if the index delete fails, since this can be handled relatively gracefully
            # for now, but do return success=false
            success = True
            access_token = flask.g.get('access_token', None)
            id_token = flask.g.get('id_token', None)
            repo_id = mgr.get_repository_id(owner, labbook_name)
            response = requests.delete(
                f"https://{index_service}/index/{repo_id}",
                headers={
                    "Authorization": f"Bearer {access_token}",
                    "Identity": id_token
                },
                timeout=10)

            if response.status_code != 204:
                logger.error(f"Failed to remove project from cloud index. "
                             f"Status Code: {response.status_code}")
                logger.error(response.json())
            else:
                logger.info(
                    f"Deleted remote repository {owner}/{labbook_name} from cloud index"
                )

            # Remove locally any references to that cloud repo that's just been deleted.
            try:
                username = get_logged_in_username()
                lb = InventoryManager().load_labbook(
                    username,
                    owner,
                    labbook_name,
                    author=get_logged_in_author())
                lb.remove_remote()
                lb.remove_lfs_remotes()
            except GigantumException as e:
                logger.warning(e)

            return DeleteLabbook(success=True)
        else:
            logger.info(
                f"Dry run deleting {labbook_name} from remote repository -- not deleted."
            )
            return DeleteLabbook(success=False)
Пример #10
0
    def mutate_and_get_payload(cls,
                               root,
                               info,
                               labbook_owner,
                               labbook_name,
                               dataset_owner,
                               dataset_name,
                               action,
                               dataset_url=None,
                               client_mutation_id=None):
        logged_in_username = get_logged_in_username()
        im = InventoryManager()
        lb = im.load_labbook(logged_in_username,
                             labbook_owner,
                             labbook_name,
                             author=get_logged_in_author())

        with lb.lock():
            if action == 'link':
                if dataset_url:
                    remote_domain = cls._get_remote_domain(
                        dataset_url, dataset_owner, dataset_name)

                    if remote_domain:
                        # Make sure git creds are configured for the remote
                        admin_service = None
                        for remote in lb.client_config.config['git'][
                                'remotes']:
                            if remote_domain == remote:
                                admin_service = lb.client_config.config['git'][
                                    'remotes'][remote]['admin_service']
                                break
                        if "HTTP_AUTHORIZATION" in info.context.headers.environ:
                            token = parse_token(info.context.headers.
                                                environ["HTTP_AUTHORIZATION"])
                        else:
                            raise ValueError(
                                "Authorization header not provided."
                                " Must have a valid session to query for collaborators"
                            )
                        mgr = GitLabManager(remote_domain, admin_service,
                                            token)
                        mgr.configure_git_credentials(remote_domain,
                                                      logged_in_username)
                else:
                    # Link to local dataset
                    ds = im.load_dataset(logged_in_username, dataset_owner,
                                         dataset_name)
                    dataset_url = f"{ds.root_dir}/.git"

                # Link the dataset to the labbook
                ds = im.link_dataset_to_labbook(dataset_url, dataset_owner,
                                                dataset_name, lb)
                ds.namespace = dataset_owner

                # Preload the dataloader
                info.context.dataset_loader.prime(
                    f"{get_logged_in_username()}&{dataset_owner}&{dataset_name}",
                    ds)

                # Relink the revision
                m = Manifest(ds, logged_in_username)
                m.link_revision()
            elif action == 'unlink':
                im.unlink_dataset_from_labbook(dataset_owner, dataset_name, lb)
            elif action == 'update':
                ds = im.update_linked_dataset_reference(
                    dataset_owner, dataset_name, lb)
                m = Manifest(ds, logged_in_username)
                m.force_reload()

                info.context.dataset_loader.prime(
                    f"{get_logged_in_username()}&{dataset_owner}&{dataset_name}",
                    ds)
            else:
                raise ValueError(
                    "Unsupported action. Use `link`, `unlink`, or `update`")

            info.context.labbook_loader.prime(
                f"{get_logged_in_username()}&{labbook_owner}&{labbook_name}",
                lb)
            edge = LabbookConnection.Edge(node=Labbook(owner=labbook_owner,
                                                       name=labbook_name),
                                          cursor=base64.b64encode(
                                              f"{0}".encode('utf-8')))

        return ModifyDatasetLink(new_labbook_edge=edge)
    def resolve_remote_labbooks(self, info, sort: str, reverse: bool,
                                **kwargs):
        """Method to return a all RemoteLabbook instances for the logged in user

        This is a remote call, so should be fetched on its own and only when needed. The user must have a valid
        session for data to be returned.

        It is recommended to use large page size (e.g. 50-100). This is due to how the remote server returns all the
        available data at once, so it is more efficient to load a lot of records at a time.

        Args:
            sort(sort_mode): String specifying how labbooks should be sorted
            reverse(bool): Reverse sorting if True

        Supported sorting modes:
            - az: naturally sort
            - created_on: sort by creation date, newest first
            - modified_on: sort by modification date, newest first

        Returns:
            list(Labbook)
        """
        # Load config data
        configuration = Configuration().config

        # Extract valid Bearer token
        token = None
        if hasattr(info.context.headers, 'environ'):
            if "HTTP_AUTHORIZATION" in info.context.headers.environ:
                token = parse_token(
                    info.context.headers.environ["HTTP_AUTHORIZATION"])
        if not token:
            raise ValueError(
                "Authorization header not provided. Cannot list remote LabBooks."
            )

        # Get remote server configuration
        default_remote = configuration['git']['default_remote']
        admin_service = None
        for remote in configuration['git']['remotes']:
            if default_remote == remote:
                admin_service = configuration['git']['remotes'][remote][
                    'admin_service']
                break

        if not admin_service:
            raise ValueError('admin_service could not be found')

        # Query backend for data
        mgr = GitLabManager(default_remote, admin_service, access_token=token)
        edges = mgr.list_labbooks(sort_mode=sort, reverse=reverse)
        cursors = [
            base64.b64encode("{}".format(cnt).encode("UTF-8")).decode("UTF-8")
            for cnt, x in enumerate(edges)
        ]

        # Process slicing and cursor args
        lbc = ListBasedConnection(edges, cursors, kwargs)
        lbc.apply()

        # Get Labbook instances
        edge_objs = []
        for edge, cursor in zip(lbc.edges, lbc.cursors):
            create_data = {
                "id": "{}&{}".format(edge["namespace"], edge["labbook_name"]),
                "name": edge["labbook_name"],
                "owner": edge["namespace"],
                "description": edge["description"],
                "creation_date_utc": edge["created_on"],
                "modified_date_utc": edge["modified_on"]
            }

            edge_objs.append(
                RemoteLabbookConnection.Edge(node=RemoteLabbook(**create_data),
                                             cursor=cursor))

        return RemoteLabbookConnection(edges=edge_objs,
                                       page_info=lbc.page_info)
Пример #12
0
    def resolve_remote_labbooks(self, info, order_by: str, sort: str, **kwargs):
        """Method to return a all RemoteLabbook instances for the logged in user

        This is a remote call, so should be fetched on its own and only when needed. The user must have a valid
        session for data to be returned.

        Args:
            order_by(str): String specifying how labbooks should be sorted
            sort(str): 'desc' for descending (default) 'asc' for ascending

        Supported order_by modes:
            - name: naturally sort on the name
            - created_on: sort by creation date
            - modified_on: sort by modification date

        Returns:
            list(Labbook)
        """
        # Load config data
        configuration = Configuration().config

        # Extract valid Bearer token
        token = None
        if hasattr(info.context.headers, 'environ'):
            if "HTTP_AUTHORIZATION" in info.context.headers.environ:
                token = parse_token(info.context.headers.environ["HTTP_AUTHORIZATION"])
        if not token:
            raise ValueError("Authorization header not provided. Cannot list remote LabBooks.")

        # Get remote server configuration
        default_remote = configuration['git']['default_remote']
        index_service = None
        for remote in configuration['git']['remotes']:
            if default_remote == remote:
                index_service = configuration['git']['remotes'][remote]['index_service']
                break

        if not index_service:
            raise ValueError('index_service could not be found')

        # Prep arguments
        if "first" in kwargs:
            per_page = int(kwargs['first'])
        elif "last" in kwargs:
            raise ValueError("Cannot page in reverse direction, must provide first/after parameters instead")
        else:
            per_page = 20

        # use version=2 to page through both projects and datasets returned by gitlab but only return projects.
        # omitting the version parameter will return projects and datasets together, but avoids breaking functionality
        # in any gigantum instances not using the latest client code
        url = f"https://{index_service}/projects?version=2&first={per_page}"

        # Add optional arguments
        if kwargs.get("after"):
            url = f"{url}&after={kwargs.get('after')}"
        elif kwargs.get("before"):
            raise ValueError("Cannot page in reverse direction, must provide first/after parameters instead")

        if order_by is not None:
            if order_by not in ['name', 'created_on', 'modified_on']:
                raise ValueError(f"Unsupported order_by: {order_by}. Use `name`, `created_on`, `modified_on`")
            url = f"{url}&order_by={order_by}"

        if sort is not None:
            if sort not in ['desc', 'asc']:
                raise ValueError(f"Unsupported sort: {sort}. Use `desc`, `asc`")
            url = f"{url}&sort={sort}"

        # Query SaaS index service for data
        access_token = flask.g.get('access_token', None)
        id_token = flask.g.get('id_token', None)
        response = requests.get(url, headers={"Authorization": f"Bearer {access_token}",
                                              "Identity": id_token})

        if response.status_code != 200:
            raise IOError("Failed to retrieve Project listing from remote server")
        edges = response.json()

        # Get Labbook instances
        edge_objs = []
        for edge in edges:
            create_data = {"id": "{}&{}".format(edge["namespace"], edge["project"]),
                           "name": edge["project"],
                           "owner": edge["namespace"],
                           "description": edge["description"],
                           "creation_date_utc": edge["created_at"],
                           "modified_date_utc": edge["modified_at"],
                           "visibility": "public" if edge.get("visibility") == "public_project" else "private"}

            edge_objs.append(RemoteLabbookConnection.Edge(node=RemoteLabbook(**create_data),
                                                          cursor=edge['cursor']))

        # Create Page Info instance
        has_previous_page = True if kwargs.get("after") else False
        has_next_page = len(edges) == per_page

        page_info = graphene.relay.PageInfo(has_next_page=has_next_page, has_previous_page=has_previous_page,
                                            start_cursor=edges[0]['cursor'] if edges else None,
                                            end_cursor=edges[-1]['cursor'] if edges else None)

        return RemoteLabbookConnection(edges=edge_objs, page_info=page_info)