Example #1
0
 def test_get_auth_providers(self):
     provider = {
         "id": "aca35ba3-a44a-47c2-8b3b-afe43a88360d",
         "service_id": "cfde039d-f550-47e7-833c-9ebc4e257847",
         "name": "Duke Authentication Service",
         "is_deprecated": True,
         "is_default": True,
         "login_initiation_url": "https://someurl"
     }
     json_results = {
         "results": [
             provider
         ]
     }
     mock_requests = MagicMock()
     mock_requests.get.side_effect = [
         fake_response_with_pages(status_code=200, json_return_value=json_results, num_pages=1)
     ]
     api = DataServiceApi(auth=self.create_mock_auth(config_page_size=100), url="something.com/v1",
                          http=mock_requests)
     result = api.get_auth_providers()
     self.assertEqual(200, result.status_code)
     self.assertEqual(json_results, result.json())
     self.assertEqual('something.com/v1/auth_providers', mock_requests.get.call_args_list[0][0][0])
Example #2
0
 def test_get_auth_providers(self):
     provider = {
         "id": "aca35ba3-a44a-47c2-8b3b-afe43a88360d",
         "service_id": "cfde039d-f550-47e7-833c-9ebc4e257847",
         "name": "Duke Authentication Service",
         "is_deprecated": True,
         "is_default": True,
         "login_initiation_url": "https://someurl"
     }
     json_results = {"results": [provider]}
     mock_requests = MagicMock()
     mock_requests.get.side_effect = [
         fake_response_with_pages(status_code=200,
                                  json_return_value=json_results,
                                  num_pages=1)
     ]
     api = DataServiceApi(auth=None,
                          url="something.com/v1",
                          http=mock_requests)
     result = api.get_auth_providers()
     self.assertEqual(200, result.status_code)
     self.assertEqual(json_results, result.json())
     self.assertEqual('something.com/v1/auth_providers',
                      mock_requests.get.call_args_list[0][0][0])
Example #3
0
class RemoteStore(object):
    """
    Fetches project tree data from remote store.
    """
    def __init__(self, config):
        """
        Setup to allow fetching project tree.
        :param config: ddsc.config.Config settings to use for connecting to the dataservice.
        """
        self.config = config
        auth = DataServiceAuth(self.config)
        self.data_service = DataServiceApi(auth, self.config.url)

    def fetch_remote_project(self,
                             project_name,
                             must_exist=False,
                             include_children=True):
        """
        Retrieve the project via project_name.
        :param project_name: str name of the project to try and download
        :param must_exist: should we error if the project doesn't exist
        :param include_children: should we read children(folders/files)
        :return: RemoteProject project requested or None if not found(and must_exist=False)
        """
        project = self._get_my_project(project_name)
        if project:
            if include_children:
                self._add_project_children(project)
        else:
            if must_exist:
                raise NotFoundError(
                    u'There is no project with the name {}'.format(
                        project_name).encode('utf-8'))
        return project

    def fetch_remote_project_by_id(self, id):
        """
        Retrieves project from via id
        :param id: str id of project from data service
        :return: RemoteProject we downloaded
        """
        response = self.data_service.get_project_by_id(id).json()
        return RemoteProject(response)

    def _get_my_project(self, project_name):
        """
        Return project tree root for project_name.
        :param project_name: str name of the project to download
        :return: RemoteProject project we found or None
        """
        response = self.data_service.get_projects().json()
        for project in response['results']:
            if project['name'] == project_name:
                return RemoteProject(project)
        return None

    def _add_project_children(self, project):
        """
        Add the rest of the project tree from the remote store to the project object.
        :param project: RemoteProject root of the project tree to add children too
        """
        response = self.data_service.get_project_children(project.id,
                                                          '').json()
        project_children = RemoteProjectChildren(project.id,
                                                 response['results'])
        for child in project_children.get_tree():
            project.add_child(child)

    def lookup_or_register_user_by_email_or_username(self, email, username):
        """
        Lookup user by email or username. Only fill in one field.
        For username it will try to register if not found.
        :param email: str: email address of the user
        :param username: netid of the user to find
        :return: RemoteUser
        """
        if username:
            return self.get_or_register_user_by_username(username)
        else:
            # API doesn't support registering user by email address yet
            return self.lookup_user_by_email(email)

    def lookup_user_by_name(self, full_name):
        """
        Query remote store for a single user with the name full_name or raise error.
        :param full_name: str Users full name separated by a space.
        :return: RemoteUser user info for single user with full_name
        """
        res = self.data_service.get_users_by_full_name(full_name)
        json_data = res.json()
        results = json_data['results']
        found_cnt = len(results)
        if found_cnt == 0:
            raise NotFoundError("User not found:" + full_name)
        elif found_cnt > 1:
            raise ValueError("Multiple users with name:" + full_name)
        user = RemoteUser(results[0])
        if user.full_name.lower() != full_name.lower():
            raise NotFoundError("User not found:" + full_name)
        return user

    def lookup_user_by_username(self, username):
        """
        Finds the single user who has this username or raises ValueError.
        :param username: str username we are looking for
        :return: RemoteUser: user we found
        """
        matches = [
            user for user in self.fetch_all_users()
            if user.username == username
        ]
        if not matches:
            raise NotFoundError('Username not found: {}.'.format(username))
        if len(matches) > 1:
            raise ValueError(
                'Multiple users with same username found: {}.'.format(
                    username))
        return matches[0]

    def get_or_register_user_by_username(self, username):
        """
        Try to lookup user by username. If not found try registering the user.
        :param username: str: username to lookup
        :return: RemoteUser: user we found
        """
        try:
            return self.lookup_user_by_username(username)
        except NotFoundError:
            return self.register_user_by_username(username)

    def register_user_by_username(self, username):
        """
        Tries to register user with the first non-deprecated auth provider.
        Raises ValueError if the data service doesn't have any non-deprecated providers.
        :param username: str: netid of the user we are trying to register
        :return: RemoteUser: user that was created for our netid
        """
        current_providers = [
            prov.id for prov in self.get_auth_providers()
            if not prov.is_deprecated
        ]
        if not current_providers:
            raise ValueError(
                "Unable to register user: no non-deprecated providers found!")
        auth_provider_id = current_providers[0]
        return self._register_user_by_username(auth_provider_id, username)

    def _register_user_by_username(self, auth_provider_id, username):
        """
        Tries to register a user who has a valid netid but isn't registered with DukeDS yet under auth_provider_id.
        :param auth_provider_id: str: id from RemoteAuthProvider to use for registering
        :param username: str: netid of the user we are trying to register
        :return: RemoteUser: user that was created for our netid
        """
        user_json = self.data_service.auth_provider_add_user(
            auth_provider_id, username).json()
        return RemoteUser(user_json)

    def get_auth_providers(self):
        """
        Return the list of authorization providers.
        :return: [RemoteAuthProvider]: list of remote auth providers
        """
        providers = []
        response = self.data_service.get_auth_providers().json()
        for data in response['results']:
            providers.append(RemoteAuthProvider(data))
        return providers

    def lookup_user_by_email(self, email):
        """
        Finds the single user who has this email or raises ValueError.
        :param email: str email we are looking for
        :return: RemoteUser user we found
        """
        matches = [
            user for user in self.fetch_all_users() if user.email == email
        ]
        if not matches:
            raise ValueError('Email not found: {}.'.format(email))
        if len(matches) > 1:
            raise ValueError(
                'Multiple users with same email found: {}.'.format(email))
        return matches[0]

    def get_current_user(self):
        """
        Fetch info about the current user
        :return: RemoteUser user who we are logged in as(auth determines this).
        """
        response = self.data_service.get_current_user().json()
        return RemoteUser(response)

    def fetch_all_users(self):
        """
        Retrieves all users from data service.
        :return: [RemoteUser] list of all users we downloaded
        """
        users = []
        result = self.data_service.get_all_users()
        user_list_json = result.json()
        for user_json in user_list_json['results']:
            users.append(RemoteUser(user_json))
        return users

    def fetch_user(self, id):
        """
        Retrieves user from data service having a specific id
        :param id: str id of user from data service
        :return: RemoteUser user we downloaded
        """
        response = self.data_service.get_user_by_id(id).json()
        return RemoteUser(response)

    def set_user_project_permission(self, project, user, auth_role):
        """
        Update remote store for user giving auth_role permissions on project.
        :param project: RemoteProject project to give permissions to
        :param user: RemoteUser user who we are giving permissions to
        :param auth_role: str type of authorization to give user(project_admin)
        """
        self.data_service.set_user_project_permission(project.id, user.id,
                                                      auth_role)

    def revoke_user_project_permission(self, project, user):
        """
        Update remote store for user removing auth_role permissions on project.
        :param project: RemoteProject project to remove permissions from
        :param user: RemoteUser user who we are removing permissions from
        """
        # Server errors out with 500 if a user isn't found.
        try:
            self.data_service.get_user_project_permission(project.id, user.id)
            self.data_service.revoke_user_project_permission(
                project.id, user.id)
        except DataServiceError as e:
            if e.status_code != 404:
                raise

    def download_file(self, remote_file, path, watcher):
        """
        Download a remote file associated with the remote uuid(file_id) into local path.
        :param remote_file: RemoteFile file to retrieve
        :param path: str file system path to save the contents to.
        :param watcher: object implementing send_item(item, increment_amt) that updates UI
        """
        url_json = self.data_service.get_file_url(remote_file.id).json()
        http_verb = url_json['http_verb']
        host = url_json['host']
        url = url_json['url']
        http_headers = url_json['http_headers']
        response = self.data_service.receive_external(http_verb, host, url,
                                                      http_headers)
        with open(path, 'wb') as f:
            for chunk in response.iter_content(
                    chunk_size=DOWNLOAD_FILE_CHUNK_SIZE):
                if chunk:  # filter out keep-alive new chunks
                    f.write(chunk)
                    watcher.transferring_item(remote_file,
                                              increment_amt=len(chunk))

    def get_project_names(self):
        """
        Return a list of names of the remote projects owned by this user.
        :return: [str]: the list of project names
        """
        names = []
        response = self.data_service.get_projects().json()
        for project in response['results']:
            names.append(project['name'])
        return names

    def delete_project_by_name(self, project_name):
        """
        Find the project named project_name and delete it raise error if not found.
        :param project_name: str: Name of the project we want to be deleted
        """
        project = self._get_my_project(project_name)
        if project:
            self.data_service.delete_project(project.id)
        else:
            raise ValueError(
                "No project named '{}' found.\n".format(project_name))

    def get_active_auth_roles(self, context):
        """
        Retrieve non-deprecated authorization roles based on a context.
        Context should be RemoteAuthRole.PROJECT_CONTEXT or RemoteAuthRole.SYSTEM_CONTEXT.
        :param context: str: context for which auth roles to retrieve
        :return: [RemoteAuthRole]: list of active auth_role objects
        """
        response = self.data_service.get_auth_roles(context).json()
        return self.get_active_auth_roles_from_json(response)

    @staticmethod
    def get_active_auth_roles_from_json(json_data):
        """
        Given a json blob response containing a list of authorization roles return the active ones
        in an array of RemoteAuthRole objects.
        :param json_data: list of dictionaries - data from dds in auth_role format
        :return: [RemoteAuthRole] list of active auth_role objects
        """
        result = []
        for auth_role_properties in json_data['results']:
            auth_role = RemoteAuthRole(auth_role_properties)
            if not auth_role.is_deprecated:
                result.append(auth_role)
        return result