def do_native_app_authentication(client_id,
                                     redirect_uri,
                                     requested_scopes=None):
        """
        Does a Native App authentication flow and returns a
        dict of tokens keyed by service name.
        """
        client = NativeAppAuthClient(client_id=client_id)
        # pass refresh_tokens=True to request refresh tokens
        client.oauth2_start_flow(requested_scopes=requested_scopes,
                                 redirect_uri=redirect_uri,
                                 refresh_tokens=True)

        url = client.oauth2_get_authorize_url()

        print('Native App Authorization URL: \n{}'.format(url))

        if not is_remote_session():
            webbrowser.open(url, new=1)

        auth_code = input('Enter the auth code: ').strip()

        token_response = client.oauth2_exchange_code_for_tokens(auth_code)

        # return a set of tokens, organized by resource server name
        return token_response.by_resource_server
Exemple #2
0
def do_native_app_authentication(client_id,
                                 redirect_uri,
                                 requested_scopes=None):
    """
    Does a Native App authentication flow and returns a
    dict of tokens keyed by service name.
    """
    client = NativeAppAuthClient(client_id=client_id)
    # pass refresh_tokens=True to request refresh tokens
    client.oauth2_start_flow(requested_scopes=requested_scopes,
                             redirect_uri=redirect_uri,
                             refresh_tokens=True)

    url = client.oauth2_get_authorize_url()

    print('Native App Authorization URL:\n{}'.format(url))

    if not is_remote_session():
        # There was a bug in webbrowser recently that this fixes:
        # https://bugs.python.org/issue30392
        if sys.platform == 'darwin':
            webbrowser.get('safari').open(url, new=1)
        else:
            webbrowser.open(url, new=1)

    auth_code = get_input('Enter the auth code: ').strip()

    token_response = client.oauth2_exchange_code_for_tokens(auth_code)

    # return a set of tokens, organized by resource server name
    return token_response.by_resource_server
def login():
    tok_path = os.path.expanduser('~/.mdf_agent_tokens.json')

    def _read_tokfile():
        tokens = {}
        if os.path.exists(tok_path):
            with open(tok_path) as f:
                tokens = json.load(f)
        return tokens

    def _write_tokfile(new_tokens):
        # We have multiple tokens in our tokens file, but on update we only
        # get the currently updated token, so read current and update with the
        # input tokens
        cur_tokens = _read_tokfile()
        for key in new_tokens:
            cur_tokens[key] = new_tokens[key]
        # deny rwx to Group and World -- don't bother storing the returned old
        # mask value, since we'll never restore it anyway
        # do this on every call to ensure that we're always consistent about it
        os.umask(0o077)
        with open(tok_path, 'w') as f:
            f.write(json.dumps(cur_tokens))

    def _update_tokfile(tokens):
        _write_tokfile(tokens.by_resource_server['transfer.api.globus.org'])

    tokens = _read_tokfile()
    client_id = "1e162bfc-ad52-4014-8844-b82841145fc4"
    native_client = NativeAppAuthClient(client_id, app_name='MDF Agents')

    if not tokens:
        # and do the Native App Grant flow
        native_client.oauth2_start_flow(
            requested_scopes='urn:globus:auth:scope:transfer.api.globus.org:all',
            refresh_tokens=True)
        linkprompt = 'Please login to Globus here'
        print('{0}:\n{1}\n{2}\n{1}\n'
              .format(linkprompt, '-' * len(linkprompt),
                      native_client.oauth2_get_authorize_url()), flush=True)
        auth_code = input(
            'Enter the resulting Authorization Code here').strip()
        tkns = native_client.oauth2_exchange_code_for_tokens(auth_code)
        tokens = tkns.by_resource_server['transfer.api.globus.org']

        _write_tokfile(tokens)

    transfer_tokens = tokens

    transfer_authorizer = RefreshTokenAuthorizer(
        transfer_tokens['refresh_token'], native_client,
        transfer_tokens['access_token'], transfer_tokens['expires_at_seconds'],
        on_refresh=_update_tokfile)

    transfer_client = TransferClient(authorizer=transfer_authorizer)
    return transfer_client
Exemple #4
0
    def register_agent(self):
        """
        Manage the login with Globus and register
        this agent with the cloud service under this
        user's name. If it is a new user they will have
        account created for them by the service.
        """
        scopes = ("urn:globus:auth:scope:transfer.api.globus.org:all "
                  "urn:globus:auth:scope:auth.globus.org:view_identities "
                  "openid email profile")

        redirect_uri = 'https://auth.globus.org/v2/web/auth-code'

        client = NativeAppAuthClient(client_id=self.client_id)
        client.oauth2_start_flow(requested_scopes=scopes,
                                 redirect_uri=redirect_uri,
                                 refresh_tokens=True)

        url = client.oauth2_get_authorize_url()

        print('Globus Authorization URL: \n{}'.format(url))

        auth_code = input('Enter the auth code: ').strip()

        token_response = client.oauth2_exchange_code_for_tokens(auth_code)
        id_data = token_response.decode_id_token(client)
        user_email = id_data.get('preferred_username', '')
        transfer_tokens = token_response.by_resource_server[
            'transfer.api.globus.org']

        print('Authenticated as: %s' % user_email)

        # Get a user friendly name for this agent
        agent_name = input(
            'Enter a name for this agent (e.g. laptop): ').strip()

        payload = {
            "access_token": transfer_tokens['access_token'],
            "username": user_email,
            "agent_id": self.endpoint_id,
            "refresh_token": transfer_tokens['refresh_token'],
            "expires_at_seconds": transfer_tokens['expires_at_seconds'],
            "agent_name": agent_name
        }

        # register this data with the service
        r = requests.post(self.agent_path, json=payload)
        logger.info("Response: %s %s" % (r.status_code, r.reason))

        # Check it was successfully registered, otherwise try again
        # Note: this returns 500 at the moment as I haven't configured
        # a response integration with SNS.
        if r.status_code == requests.codes.ok or r.status_code == 500:
            logger.info("Successfully registered agent.")
Exemple #5
0
class OAuth2(object):
    """Base class for OAuth2 model
    """

    def __init__(self):
        """Initiate an OAuth2() object.

        Initiate OAuth2 flow with Globus credentaials to obtain access tokens. 
        Refresh the tokens automatically so another login is not required.

        Examples
        --------
        Create an OAuth2 object:
            >>> from archeion.models import OAuth2
            >>> authorizer = OAuth2()

        """
        self.client = NativeAppAuthClient(CLIENT_ID)
        self.client.oauth2_start_flow(refresh_tokens=True)

        logger.info("Opening browser window for Globus Authentication")
        webbrowser.open_new(self.client.oauth2_get_authorize_url())

        get_input = getattr(__builtins__, "raw_input", input)
        auth_code = get_input(
            "Please enter the code you get after login here: "
        ).strip()
        logger.debug("User has input authentication code")
        token_response = self.client.oauth2_exchange_code_for_tokens(auth_code)

        self.access_token = token_response.by_resource_server["auth.globus.org"][
            "access_token"
        ]
        transfer_response = token_response.by_resource_server["transfer.api.globus.org"]
        self.transfer_token = transfer_response["access_token"]
        self.transfer_refresh_token = transfer_response["refresh_token"]
        self.transfer_expiry_seconds = transfer_response["expires_at_seconds"]

        authorizer = RefreshTokenAuthorizer(
            self.transfer_refresh_token,
            self.client,
            access_token=self.transfer_token,
            expires_at=self.transfer_expiry_seconds,
        )
        self.transfer_client = TransferClient(
            AccessTokenAuthorizer(self.transfer_token)
        )
        self.authorisation_client = AuthClient(authorizer=authorizer)
Exemple #6
0
def _do_login_for_scopes(native_client: NativeAppAuthClient,
                         scopes: List[str]) -> OAuthTokenResponse:
    label = CLIENT_NAME
    host = platform.node()
    if host:
        label = label + f" on {host}"
    native_client.oauth2_start_flow(
        requested_scopes=scopes,
        refresh_tokens=True,
        prefill_named_grant=label,
    )
    linkprompt = "Please log into Globus here"
    safeprint("{0}:\n{1}\n{2}\n{1}\n".format(
        linkprompt, "-" * len(linkprompt),
        native_client.oauth2_get_authorize_url()))
    auth_code = click.prompt(
        "Enter the resulting Authorization Code here").strip()
    return native_client.oauth2_exchange_code_for_tokens(auth_code)
Exemple #7
0
def do_native_app_authentication(client_id=config['CLIENT_ID'],
                                 redirect_uri=config['REDIRECT_URI'],
                                 requested_scopes=config['SCOPES']):
    """
    Does a Native App authentication flow and returns a
    dict of tokens keyed by service name.
    """
    client = NativeAppAuthClient(client_id=client_id)
    client.oauth2_start_flow(requested_scopes=config['SCOPES'],
                             redirect_uri=redirect_uri)
    url = client.oauth2_get_authorize_url()

    server = start_local_server(listen=config['SERVER_ADDRESS'])

    if not is_remote_session():
        webbrowser.open(url, new=1)

    auth_code = server.wait_for_code()
    token_response = client.oauth2_exchange_code_for_tokens(auth_code)

    server.shutdown()

    # return a set of tokens, organized by resource server name
    return token_response.by_resource_server
Exemple #8
0
class GlobusStorageManager:
    # https://globus-sdk-python.readthedocs.io/en/stable/clients/transfer/

    app_id = 'b2fe5703-edb0-4f7f-80a6-2147c8ae35f0'  # map transfer app id

    class GlobusQueue:
        ''' placeholder for globus async helpers '''
        pass

    def __init__(self):

        self.auth_client = NativeAppAuthClient(self.app_id)
        self.auth_client.oauth2_start_flow(refresh_tokens=True)
        self.xfer_client = None

        custom = dj.config.get('custom', None)
        if custom and 'globus.token' in custom:
            self.refresh()
        else:
            self.login()

    # authentication methods

    def login(self):
        ''' fetch refresh token, store in dj.config['globus.token'] '''

        auth_client = self.auth_client

        print('Please login via: {}'.format(
            auth_client.oauth2_get_authorize_url()))

        code = input('and enter code:').strip()
        tokens = auth_client.oauth2_exchange_code_for_tokens(code)

        xfer_auth_cfg = tokens.by_resource_server['transfer.api.globus.org']
        xfer_rt = xfer_auth_cfg['refresh_token']
        xfer_at = xfer_auth_cfg['access_token']
        xfer_exp = xfer_auth_cfg['expires_at_seconds']

        xfer_auth = RefreshTokenAuthorizer(
            xfer_rt, auth_client, access_token=xfer_at, expires_at=xfer_exp)

        self.xfer_client = TransferClient(authorizer=xfer_auth)

        custom = dj.config.get('custom', {})
        custom['globus.token'] = xfer_rt
        dj.config['custom'] = custom

    def refresh(self):
        ''' use refresh token to refresh access token '''
        auth_client = self.auth_client

        xfer_auth = RefreshTokenAuthorizer(
            dj.config['custom']['globus.token'], auth_client,
            access_token=None, expires_at=None)

        self.xfer_client = TransferClient(authorizer=xfer_auth)

    # endpoint managment / utility methods

    @classmethod
    def ep_parts(cls, endpoint_path):
        # split endpoint:/path to endpoint, path
        epsplit = endpoint_path.split(':')
        return epsplit[0], ':'.join(epsplit[1:])

    def activate_endpoint(self, endpoint):
        ''' activate an endpoint '''
        tc = self.xfer_client

        r = tc.endpoint_autoactivate(endpoint, if_expires_in=3600)

        log.debug('activate_endpoint() code: {}'.format(r['code']))

        if r['code'] == 'AutoActivationFailed':
            print('Endpoint({}) Not Active! Error! Source message: {}'
                  .format(endpoint, r['message']))
            raise Exception('globus endpoint activation failure')

        knownok = any(('AutoActivated' in r['code'],
                       'AlreadyActivated' in r['code']))

        if not knownok:
            log.debug('activate_endpoint(): not knownok response')

    def _wait(self, task, timeout=10, polling_interval=10):
        ''' tranfer client common wait wrapper '''
        return self.xfer_client.task_wait(task, timeout, polling_interval)

    def _tasks(self):
        '''
        >>> tl = tc.task_list(num_results=25, filter="type:TRANSFER,DELETE")
        >>> _ = [print(t["task_id"], t["type"], t["status"]) for t in tl]
        '''
        pass

    def _task_info(self):
        '''
        >>> for event in tc.task_event_list(task_id):
        >>>     print("Event on Task({}) at {}:\n{}".format(
        >>>         task_id, event["time"], event["description"])
        or
        get_task
        '''
        pass

    # transfer methods

    def ls(self, endpoint_path):
        '''
        returns:
            {
              "DATA": [
                {
                  "DATA_TYPE": "file",
                  "group": "staff",
                  "last_modified": "2018-05-22 18:49:19+00:00",
                  "link_group": null,
                  "link_last_modified": null,
                  "link_size": null,
                  "link_target": null,
                  "link_user": null,
                  "name": "map",
                  "permissions": "0755",
                  "size": 102,
                  "type": "dir",
                  "user": "******"
                },
              ],
              "DATA_TYPE": "file_list",
              "absolute_path": null,
              "endpoint": "aa4e5f9c-05f3-11e8-a6ad-0a448319c2f8",
              "length": 2,
              "path": "/~/Globus/",
              "rename_supported": true,
              "symlink_supported": false,
              "total": 2
            }
        '''
        ep, path = self.ep_parts(endpoint_path)
        return self.xfer_client.operation_ls(ep, path=path)

    def mkdir(self, ep_path):
        ''' create a directory at ep_path '''
        ep, path = self.ep_parts(ep_path)
        return self.xfer_client.operation_mkdir(ep, path=path)

    def rmdir(self, ep_path, recursive=False):
        ''' remove a directory at ep_path '''
        tc = self.xfer_client
        ep, path = self.ep_parts(ep_path)
        ddata = DeleteData(tc, ep, recursive=recursive)
        ddata.add_item(path)
        task_id = tc.submit_delete(ddata)['task_id']
        return self._wait(task_id)

    def cp(self, src_ep_path, dst_ep_path, recursive=False):
        '''
        copy file/path
        todo: support label, sync_level, etc?
        sync_level: ["exists", "size", "mtime", "checksum"]
        '''
        tc = self.xfer_client
        sep, spath = self.ep_parts(src_ep_path)
        dep, dpath = self.ep_parts(dst_ep_path)

        td = TransferData(tc, sep, dep)
        td.add_item(spath, dpath, recursive=recursive)

        task_id = tc.submit_transfer(td)['task_id']
        return self._wait(task_id)

    def rename(self, src_ep_path, dst_ep_path):
        ''' rename a file/path '''
        tc = self.xfer_client
        sep, spath = self.ep_parts(src_ep_path)
        dep, dpath = self.ep_parts(dst_ep_path)

        if sep != dep:
            raise Exception('rename between two different endpoints')

        return tc.operation_rename(sep, spath, dpath)
Exemple #9
0
#!/usr/bin/env python

from globus_sdk import NativeAppAuthClient

CLIENT_ID = "d0f1d9b0-bd81-4108-be74-ea981664453a"
SCOPES = ("openid profile email "
          "urn:globus:auth:scope:transfer.api.globus.org:all")

get_input = getattr(__builtins__, 'raw_input', input)

client = NativeAppAuthClient(client_id=CLIENT_ID)
client.oauth2_start_flow(requested_scopes=SCOPES, refresh_tokens=True)
url = client.oauth2_get_authorize_url()

print("Native App Authorization URL: \n{}".format(url))
auth_code = get_input("Enter the auth code: ").strip()

token_res = client.oauth2_exchange_code_for_tokens(auth_code)
tokens = token_res.by_resource_server

transfer_token = tokens["transfer.api.globus.org"]["refresh_token"]
auth_token = tokens["auth.globus.org"]["refresh_token"]
id_token = token_res["id_token"]
access_token = token_res["access_token"]

print("Transfer Refresh Token: {}".format(transfer_token))
print("Auth Refresh Token    : {}".format(auth_token))
print("Openid id_token       : {}".format(id_token))
print("Access Token          : {}".format(access_token))
Exemple #10
0
class NativeClient(object):
    """
    The Native Client serves as another small layer on top of the Globus SDK
    to automatically handle token storage and provide a customizable
    Local Server. It can be used both by simple scripts to simplify the
    auth flow, or by full command line clients that may extend various pieces
    and tailor them to its own needs.
    """

    TOKEN_STORAGE_ATTRS = {'write_tokens', 'read_tokens', 'clear_tokens'}

    def __init__(self,
                 token_storage=MultiClientTokenStorage(),
                 local_server_code_handler=LocalServerCodeHandler(),
                 secondary_code_handler=InputCodeHandler(),
                 default_scopes=None,
                 *args,
                 **kwargs):
        self.client = NativeAppAuthClient(*args, **kwargs)
        self.token_storage = token_storage
        if token_storage is not None:
            self.verify_token_storage(self.token_storage)
        self.app_name = kwargs.get('app_name') or 'My App'
        self.local_server_code_handler = local_server_code_handler
        self.local_server_code_handler.set_app_name(self.app_name)
        self.secondary_code_handler = secondary_code_handler
        if isinstance(self.token_storage, MultiClientTokenStorage):
            self.token_storage.set_client_id(kwargs.get('client_id'))
        self.default_scopes = default_scopes

    def login(self,
              no_local_server=False,
              no_browser=False,
              requested_scopes=(),
              refresh_tokens=None,
              prefill_named_grant=None,
              additional_params=None,
              force=False):
        """
        Do a Native App Auth Flow to get tokens for requested scopes. This
        first attempts to load tokens and will simply return those if they are
        valid, and will automatically attempt to save tokens on login success
        (token_storage must be set for automatic load/save functionality).
        :param no_local_server: Don't use the local server. This may be because
        of permissions issues of standing up a server on the clients machine.
        :param no_browser: Don't automatically open a browser, and instead
        instruct the user to manually follow the URL to login. This is useful
        on remote servers which don't have native browsers for clients to use.
        :param requested_scopes: Globus Scopes to request on the users behalf.
        :param refresh_tokens: Use refresh tokens to extend login time
        :param prefill_named_grant: Named Grant to use on Consent Page
        :param additional_params: Additional Params to supply, such as for
        using Globus Sessions
        :param force: Force a login flow, even if loaded tokens are valid.
        :return:
        """
        if force is False:
            try:
                return self.load_tokens(requested_scopes=requested_scopes)
            except (LoadError, Exception):
                pass

        grant_name = prefill_named_grant or '{} Login'.format(self.app_name)
        code_handler = (self.secondary_code_handler
                        if no_local_server else self.local_server_code_handler)

        with code_handler.start():
            self.client.oauth2_start_flow(
                requested_scopes=requested_scopes or self.default_scopes,
                refresh_tokens=refresh_tokens,
                prefill_named_grant=grant_name,
                redirect_uri=code_handler.get_redirect_uri())
            auth_url = self.client.oauth2_get_authorize_url(
                additional_params=additional_params)
            auth_code = code_handler.authenticate(url=auth_url,
                                                  no_browser=no_browser)
        token_response = self.client.oauth2_exchange_code_for_tokens(auth_code)
        try:
            self.save_tokens(token_response.by_resource_server)
        except LoadError:
            pass
        return token_response.by_resource_server

    def verify_token_storage(self, obj):
        for attr in self.TOKEN_STORAGE_ATTRS:
            if getattr(obj, attr, None) is None:
                raise AttributeError('token_storage requires object "{}" to '
                                     'have the {} attribute'.format(obj, attr))

    def save_tokens(self, tokens):
        """
        Save tokens if token_storage is set. Typically this is called
        automatically in a successful login().
        :param tokens: globus_sdk.auth.token_response.OAuthTokenResponse.
        :return: None
        """
        if self.token_storage is not None:
            return self.token_storage.write_tokens(tokens)
        raise LoadError('No token_storage set on client.')

    def _load_raw_tokens(self):
        """
        Loads tokens without checking them
        :return: tokens by resource server, or an exception if that fails
        """
        if self.token_storage is not None:
            return self.token_storage.read_tokens()
        raise LoadError('No token_storage set on client.')

    def load_tokens(self, requested_scopes=None):
        """
        Load tokens from the set token_storage object if one exists.
        :param requested_scopes: Check that the loaded scopes match these
        requested scopes. Raises ScopesMismatch if there is a discrepancy.
        :return: Loaded tokens, or a LoadError if loading fails.
        """
        tokens = self._load_raw_tokens()

        if not tokens:
            raise LoadError('No Tokens loaded')

        if requested_scopes not in [None, ()]:
            check_scopes(tokens, requested_scopes)
        try:
            check_expired(tokens)
        except TokensExpired as te:
            expired = {rs: tokens[rs] for rs in te.resource_servers}
            if not self._refreshable(expired):
                raise
            tokens.update(self.refresh_tokens(expired))
            self.save_tokens(tokens)
        return tokens

    def _refreshable(self, tokens):
        return all([bool(ts['refresh_token']) for ts in tokens.values()])

    def refresh_tokens(self, tokens):
        if not self._refreshable(tokens):
            raise TokensExpired('No Refresh Token, cannot refresh tokens: ',
                                resource_servers=tokens.keys())

        for rs, token_dict in tokens.items():
            authorizer = RefreshTokenAuthorizer(
                token_dict['refresh_token'],
                self.client,
                access_token=token_dict['access_token'],
                expires_at=token_dict['expires_at_seconds'],
            )
            authorizer.check_expiration_time()
            token_dict['access_token'] = authorizer.access_token
            token_dict['expires_at_seconds'] = authorizer.expires_at
        return tokens

    def get_authorizers(self):
        authorizers = {}
        for resource_server, token_dict in self.load_tokens().items():
            if token_dict.get('refresh_token') is not None:
                authorizers[resource_server] = RefreshTokenAuthorizer(
                    token_dict['refresh_token'],
                    self.client,
                    access_token=token_dict['access_token'],
                    expires_at=token_dict['expires_at_seconds'],
                    on_refresh=self.on_refresh,
                )
            else:
                authorizers[resource_server] = AccessTokenAuthorizer(
                    token_dict['access_token'])
        return authorizers

    def on_refresh(self, token_response):
        loaded_tokens = self._load_raw_tokens()
        loaded_tokens.update(token_response.by_resource_server)
        self.save_tokens(loaded_tokens)

    def logout(self):
        """
        Revoke saved tokens and clear them from storage
        """
        self.revoke_token_set(self._load_raw_tokens())
        self.token_storage.clear_tokens()

    def revoke_token_set(self, tokens):
        for rs, tok_set in tokens.items():
            self.client.oauth2_revoke_token(tok_set.get('access_token'))
            self.client.oauth2_revoke_token(tok_set.get('refresh_token'))
    def initiate_connection(self):
        """ Initiate the connection
        """
        tokens = None
        try:
            # if we already have tokens, load and use them
            tokens = self.load_tokens_from_file()
        except:
            pass

        if not tokens:
            # if we need to get tokens, start the Native App authentication process
            client = NativeAppAuthClient(
                client_id=self.CLIENT_ID)  #self.config['client_id'])
            # pass refresh_tokens=True to request refresh tokens
            client.oauth2_start_flow(
                requested_scopes=self.
                SCOPES,  #self.config['requested_scopes'],
                redirect_uri=self.REDIRECT_URI,  #self.config['redirect_uri'],
                refresh_tokens=True)

            url = client.oauth2_get_authorize_url()

            print 'Native App Authorization URL: \n{}'.format(url)

            auth_code = raw_input('Enter the auth code: ').strip()

            token_response = client.oauth2_exchange_code_for_tokens(auth_code)

            # return a set of tokens, organized by resource server name
            tokens = token_response.by_resource_server

            try:
                self.save_tokens_to_file(tokens)
            except:
                pass

        transfer_tokens = tokens['transfer.api.globus.org']

        auth_client = NativeAppAuthClient(client_id=self.config['client_id'])

        authorizer = RefreshTokenAuthorizer(
            transfer_tokens['refresh_token'],
            auth_client,
            #access_token=transfer_tokens['access_token'],
            #expires_at=transfer_tokens['expires_at_seconds'],
            on_refresh=self.update_tokens_file_on_refresh)

        self.client = TransferClient(authorizer=authorizer)

        # print out a directory listing from an endpoint
        try:
            #print 'ACTIVATE'
            #print 'DEST',self.config['dest_ep']
            self.client.endpoint_autoactivate(self.config['dest_ep'])
            ac = self.client.endpoint_get_activation_requirements(
                self.config['dest_ep'])
            #print ac
            self.client.endpoint_autoactivate(self.config['src_ep'])
            ac2 = self.client.endpoint_get_activation_requirements(
                self.config['src_ep'])
            #print ac2
        except GlobusAPIError as ex:
            self.logger.error('Error in endpoint activation %s', str(ex))

            if ex.http_status == 401:
                sys.exit('Refresh token has expired. '
                         'Please delete refresh-tokens.json and try again.')
            else:
                raise ex
class Transfer:
    '''
    Modified Transfer, add an option to pass refresh token to avoid the web login
    '''
    def __init__(self,
                 src_endpoint_name,
                 dst_endpoint_name,
                 transfer_rt=None,
                 log_lv=logging.INFO):
        log_format = '%(asctime)-15s %(levelname)s:\t  class:%(name)s %(message)s'
        logging.basicConfig(format=log_format)
        self.logger = logging.getLogger(self.__class__.__name__)
        self.logger.setLevel(log_lv)
        self.logger.debug('CLIENT_ID: {0}'.format(CLIENT_ID))
        self.client = NativeAppAuthClient(CLIENT_ID)
        self.client.oauth2_start_flow(refresh_tokens=True)

        if transfer_rt is not None:
            self.authorizer = RefreshTokenAuthorizer(transfer_rt, self.client)
        else:
            authorize_url = self.client.oauth2_get_authorize_url()
            print('Please go to this URL and login: {0}'.format(authorize_url))

            get_input = getattr(__builtins__, 'raw_input', input)
            auth_code = get_input(
                'Please enter the code you get after login here: ').strip()
            token_response = self.client.oauth2_exchange_code_for_tokens(
                auth_code)
            self.globus_auth_data = token_response.by_resource_server[
                'auth.globus.org']
            self.globus_transfer_data = token_response.by_resource_server[
                'transfer.api.globus.org']
            auth_token = self.globus_auth_data['access_token']
            transfer_token = self.globus_transfer_data['access_token']
            transfer_rt = self.globus_transfer_data['refresh_token']
            transfer_at = self.globus_transfer_data['access_token']
            expires_at_s = self.globus_transfer_data['expires_at_seconds']
            self.authorizer = RefreshTokenAuthorizer(transfer_rt,
                                                     self.client,
                                                     access_token=transfer_at,
                                                     expires_at=expires_at_s)
        self.transferClient = TransferClient(authorizer=self.authorizer)
        self.src_endpoint = None
        self.dst_endpoint = None
        for ep in self.transferClient.endpoint_search(
                filter_scope="shared-with-me"):
            if ep["display_name"] == src_endpoint_name:
                self.src_endpoint = ep
                self.logger.info('Source endpoint: [{0}] {1}'.format(
                    self.src_endpoint['id'],
                    self.src_endpoint['display_name']))
        if self.src_endpoint is None:
            self.logger.error(
                'No endpoint shared with you with name: {0}'.format(
                    src_endpoint_name))
            raise LookupError
        for ep in self.transferClient.endpoint_search(
                filter_scope="my-endpoints"):
            if ep['display_name'] == dst_endpoint_name:
                self.dst_endpoint = ep
                self.logger.info('Destination endpoint: [{0}] {1}'.format(
                    self.dst_endpoint['id'],
                    self.dst_endpoint['display_name']))
        if self.dst_endpoint is None:
            self.logger.error('You don\'t have endpoint named: {0}'.format(
                dst_endpoint_name))
            raise LookupError

    def transfer_dir(self, src_dir, dst_dir):
        transfer_data = TransferData(self.transferClient,
                                     self.src_endpoint['id'],
                                     self.dst_endpoint['id'])
        transfer_data.add_item(src_dir, dst_dir, recursive=True)
        result = self.transferClient.submit_transfer(transfer_data)
        self.logger.info('task [{0}] {1}'.format(result['task_id'],
                                                 result['code']))
        return result

    def transfer_file(self, src_file, dst_file):
        transfer_data = TransferData(self.transferClient,
                                     self.src_endpoint['id'],
                                     self.dst_endpoint['id'])
        transfer_data.add_item(src_file, dst_file)
        result = self.transferClient.submit_transfer(transfer_data)
        self.logger.info('task_id [{0}] {1}'.format(result['task_id'],
                                                    result['code']))
        return result

    def ls_src_dir(self, path, ls_filter=''):
        # using iteration to get every entry from result
        # an entry contain two keys: 'name' and 'type'
        # type define the entry is a file or folder
        result = self.transferClient.operation_ls(self.src_endpoint['id'],
                                                  path=path,
                                                  filter=ls_filter)
        for entry in result:
            self.logger.debug('name: {0}\ttype: {1}'.format(
                entry["name"], entry["type"]))
        return result

    def task_list(self, num_results=10):
        result = self.transferClient.task_list(num_results=num_results)
        for task in result:
            self.logger.debug('task_id: [{0}]\t status: {1}'.format(
                task['task_id'], task['status']))

        result = self.transferClient.task_list(num_results=num_results)
        return result

    def get_task(self, task_id):
        return self.transferClient.get_task(task_id)