Пример #1
0
def do_link_auth_flow(session_params={}, force_new_client=False):
    """
    Prompts the user with a link to authenticate with globus auth
    and authorize the CLI to act on their behalf.
    """
    # get the ConfidentialApp client object
    auth_client = internal_auth_client(
        requires_instance=True, force_new_client=force_new_client)

    # start the Confidential App Grant flow
    auth_client.oauth2_start_flow(
        redirect_uri=auth_client.base_url + 'v2/web/auth-code',
        refresh_tokens=True, requested_scopes=SCOPES)

    # prompt
    additional_params = {"prompt": "login"}
    additional_params.update(session_params)
    linkprompt = 'Please authenticate with Globus here'
    safeprint('{0}:\n{1}\n{2}\n{1}\n'
              .format(linkprompt, '-' * len(linkprompt),
                      auth_client.oauth2_get_authorize_url(
                        additional_params=additional_params)))

    # come back with auth code
    auth_code = click.prompt(
        'Enter the resulting Authorization Code here').strip()

    # finish auth flow
    exchange_code_and_store_config(auth_client, auth_code)
    return True
Пример #2
0
def check_logged_in():
    # first, try to get the refresh tokens from config
    # we can skip the access tokens and their expiration times as those are not
    # strictly necessary
    transfer_rt = lookup_option(TRANSFER_RT_OPTNAME)
    auth_rt = lookup_option(AUTH_RT_OPTNAME)

    # if either of the refresh tokens are null return False
    if (transfer_rt is None or auth_rt is None):
        return False

    # get or create the instance client
    auth_client = internal_auth_client(requires_instance=True)

    # check that tokens and client are valid
    try:
        for tok in (transfer_rt, auth_rt):
            res = auth_client.oauth2_validate_token(tok)
            if not res['active']:
                return False

    # if the instance client is invalid, an AuthAPIError will be raised
    # we then force a new client to be created before continuing
    except AuthAPIError:
        return False

    return True
Пример #3
0
def do_link_login_flow():
    """
    Prompts the user with a link to authorize the CLI to act on their behalf.
    """
    # get the NativeApp client object
    native_client = internal_auth_client()

    # start the Native App Grant flow, prefilling the
    # named grant label on the consent page if we can get a
    # hostname for the local system
    label = platform.node() or None
    native_client.oauth2_start_flow(refresh_tokens=True,
                                    prefill_named_grant=label,
                                    requested_scopes=SCOPES)

    # prompt
    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()))

    # come back with auth code
    auth_code = click.prompt(
        'Enter the resulting Authorization Code here').strip()

    # finish login flow
    exchange_code_and_store_config(native_client, auth_code)
Пример #4
0
def check_logged_in():
    # first, try to get the refresh tokens from config
    # we can skip the access tokens and their expiration times as those are not
    # strictly necessary
    transfer_rt = lookup_option(TRANSFER_RT_OPTNAME)
    auth_rt = lookup_option(AUTH_RT_OPTNAME)

    # if either of the refresh tokens are null return False
    if transfer_rt is None or auth_rt is None:
        return False

    # get or create the instance client
    auth_client = internal_auth_client(requires_instance=True)

    # check that tokens and client are valid
    try:
        for tok in (transfer_rt, auth_rt):
            res = auth_client.oauth2_validate_token(tok)
            if not res["active"]:
                return False

    # if the instance client is invalid, an AuthAPIError will be raised
    # we then force a new client to be created before continuing
    except AuthAPIError:
        return False

    return True
Пример #5
0
def logout_command():
    # try to get the user's preferred username from userinfo
    # if an API error is raised, they probably are not logged in
    try:
        username = get_auth_client().oauth2_userinfo()["preferred_username"]
    except AuthAPIError:
        safeprint(("Unable to lookup username. You may not be logged in. "
                   "Attempting logout anyway...\n"))
        username = None
    safeprint(
        u'Logging out of Globus{}\n'.format(u' as ' +
                                            username if username else ''))

    # build the NativeApp client object
    native_client = internal_auth_client()

    # remove tokens from config and revoke them
    # also, track whether or not we should print the rescind help
    print_rescind_help = False
    for token_opt in (TRANSFER_RT_OPTNAME, TRANSFER_AT_OPTNAME,
                      AUTH_RT_OPTNAME, AUTH_AT_OPTNAME):
        # first lookup the token -- if not found we'll continue
        token = lookup_option(token_opt)
        if not token:
            safeprint(('Warning: Found no token named "{}"! '
                       'Recommend rescinding consent').format(token_opt))
            print_rescind_help = True
            continue
        # token was found, so try to revoke it
        try:
            native_client.oauth2_revoke_token(token)
        # if we network error, revocation failed -- print message and abort so
        # that we can revoke later when the network is working
        except globus_sdk.NetworkError:
            safeprint(('Failed to reach Globus to revoke tokens. '
                       'Because we cannot revoke these tokens, cancelling '
                       'logout'))
            click.get_current_context().exit(1)
        # finally, we revoked, so it's safe to remove the token
        remove_option(token_opt)

    # remove expiration times, just for cleanliness
    for expires_opt in (TRANSFER_AT_EXPIRES_OPTNAME, AUTH_AT_EXPIRES_OPTNAME):
        remove_option(expires_opt)

    # if print_rescind_help is true, we printed warnings above
    # so, jam out an extra newline as a separator
    safeprint(("\n" if print_rescind_help else "") + _LOGOUT_EPILOG)

    # if some token wasn't found in the config, it means its possible that the
    # config file was removed without logout
    # in that case, the user should rescind the CLI consent to invalidate any
    # potentially leaked refresh tokens, so print the help on that
    if print_rescind_help:
        safeprint(_RESCIND_HELP)
Пример #6
0
def session_show():
    """List all identities in your current CLI auth session.

    Lists identities that are in the session tied to the CLI's access tokens along with
    the time the user authenticated with that identity.
    """
    # get a token to introspect, refreshing if neccecary
    auth_client = internal_auth_client()
    try:
        auth_client.authorizer._check_expiration_time()
    except AttributeError:  # if we have no RefreshTokenAuthorizor
        pass
    access_token = lookup_option(AUTH_AT_OPTNAME)

    # only instance clients can introspect tokens
    if isinstance(auth_client, globus_sdk.ConfidentialAppAuthClient):
        res = auth_client.oauth2_token_introspect(access_token,
                                                  include="session_info")

        session_info = res.get("session_info", {})
        authentications = session_info.get("authentications") or {}

    # empty session if still using Native App Client
    else:
        session_info = {}
        authentications = {}

    # resolve ids to human readable usernames
    resolved_ids = globus_sdk.IdentityMap(get_auth_client(),
                                          list(authentications))

    # put the nested dicts in a format table output can work with
    # while also converting vals into human readable formats
    list_data = [{
        "id":
        key,
        "username":
        resolved_ids.get(key, {}).get("username"),
        "auth_time":
        time.strftime("%Y-%m-%d %H:%M %Z", time.localtime(vals["auth_time"])),
    } for key, vals in authentications.items()]

    print_command_hint(
        "For information on your primary identity or full identity set see\n"
        "  globus whoami\n")

    formatted_print(
        list_data,
        json_converter=lambda x: session_info,
        fields=[("Username", "username"), ("ID", "id"),
                ("Auth Time", "auth_time")],
    )
Пример #7
0
def get_auth_client():
    tokens = get_auth_tokens()
    authorizer = None

    # if there's a refresh token, use it to build the authorizer
    if tokens['refresh_token'] is not None:
        authorizer = RefreshTokenAuthorizer(tokens['refresh_token'],
                                            internal_auth_client(),
                                            tokens['access_token'],
                                            tokens['access_token_expires'],
                                            on_refresh=_update_access_tokens)

    client = AuthClient(authorizer=authorizer, app_name=version.app_name)
    return client
Пример #8
0
def get_client():
    tokens = get_transfer_tokens()
    authorizer = None

    # if there's a refresh token, use it to build the authorizer
    if tokens['refresh_token'] is not None:
        authorizer = RefreshTokenAuthorizer(tokens['refresh_token'],
                                            internal_auth_client(),
                                            tokens['access_token'],
                                            tokens['access_token_expires'],
                                            on_refresh=_update_access_tokens)

    return RetryingTransferClient(tries=10,
                                  authorizer=authorizer,
                                  app_name=version.app_name)
Пример #9
0
def get_auth_client():
    tokens = get_auth_tokens()
    authorizer = None

    # if there's a refresh token, use it to build the authorizer
    if tokens["refresh_token"] is not None:
        authorizer = RefreshTokenAuthorizer(
            tokens["refresh_token"],
            internal_auth_client(),
            tokens["access_token"],
            tokens["access_token_expires"],
            on_refresh=_update_access_tokens,
        )

    client = AuthClient(authorizer=authorizer, app_name=version.app_name)
    return client
Пример #10
0
def session_show():
    # get a token to introspect, refreshing if neccecary
    auth_client = internal_auth_client()
    try:
        auth_client.authorizer._check_expiration_time()
    except AttributeError:  # if we have no RefreshTokenAuthorizor
        pass
    access_token = lookup_option(AUTH_AT_OPTNAME)

    # only instance clients can introspect tokens
    if isinstance(auth_client, globus_sdk.ConfidentialAppAuthClient):
        res = auth_client.oauth2_token_introspect(access_token, include="session_info")

        session_info = res.get("session_info", {})
        authentications = session_info.get("authentications") or {}

    # empty session if still using Native App Client
    else:
        session_info = {}
        authentications = {}

    # resolve ids to human readable usernames
    resolved_ids = LazyIdentityMap(list(authentications))

    # put the nested dicts in a format table output can work with
    # while also converting vals into human readable formats
    list_data = [
        {
            "id": key,
            "username": resolved_ids.get(key),
            "auth_time": time.strftime(
                "%Y-%m-%d %H:%M %Z", time.localtime(vals["auth_time"])
            ),
        }
        for key, vals in authentications.items()
    ]

    print_command_hint(
        "For information on your primary identity or full identity set see\n"
        "  globus whoami\n"
    )

    formatted_print(
        list_data,
        json_converter=lambda x: session_info,
        fields=[("Username", "username"), ("ID", "id"), ("Auth Time", "auth_time")],
    )
Пример #11
0
def get_client():
    tokens = get_transfer_tokens()
    authorizer = None

    # if there's a refresh token, use it to build the authorizer
    if tokens["refresh_token"] is not None:
        authorizer = RefreshTokenAuthorizer(
            tokens["refresh_token"],
            internal_auth_client(),
            tokens["access_token"],
            tokens["access_token_expires"],
            on_refresh=_update_access_tokens,
        )

    return RetryingTransferClient(
        tries=10, authorizer=authorizer, app_name=version.app_name
    )
Пример #12
0
def check_logged_in():
    # first, try to get the refresh tokens from config
    # we can skip the access tokens and their expiration times as those are not
    # strictly necessary
    transfer_rt = lookup_option(TRANSFER_RT_OPTNAME)
    auth_rt = lookup_option(AUTH_RT_OPTNAME)

    # if either of the refresh tokens are null return False
    if (transfer_rt is None or auth_rt is None):
        return False

    # check that tokens are valid
    native_client = internal_auth_client()
    for tok in (transfer_rt, auth_rt):
        res = native_client.oauth2_validate_token(tok)
        if not res['active']:
            return False

    return True
Пример #13
0
def do_local_server_auth_flow(session_params=None, force_new_client=False):
    """
    Starts a local http server, opens a browser to have the user authenticate,
    and gets the code redirected to the server (no copy and pasting required)
    """
    session_params = session_params or {}

    # start local server and create matching redirect_uri
    with start_local_server(listen=("127.0.0.1", 0)) as server:
        _, port = server.socket.getsockname()
        redirect_uri = "http://localhost:{}".format(port)

        # get the ConfidentialApp client object and start a flow
        auth_client = internal_auth_client(requires_instance=True,
                                           force_new_client=force_new_client)
        auth_client.oauth2_start_flow(refresh_tokens=True,
                                      redirect_uri=redirect_uri,
                                      requested_scopes=SCOPES)
        additional_params = {"prompt": "login"}
        additional_params.update(session_params)
        url = auth_client.oauth2_get_authorize_url(
            additional_params=additional_params)

        # open web-browser for user to log in, get auth code
        webbrowser.open(url, new=1)
        auth_code = server.wait_for_code()

    if isinstance(auth_code, LocalServerError):
        click.echo("Authorization failed: {}".format(auth_code), err=True)
        click.get_current_context().exit(1)
    elif isinstance(auth_code, Exception):
        click.echo(
            "Authorization failed with unexpected error:\n{}".format(
                auth_code),
            err=True,
        )
        click.get_current_context().exit(1)

    # finish auth flow and return true
    exchange_code_and_store_config(auth_client, auth_code)
    return True
Пример #14
0
def do_local_server_login_flow():
    """
    Starts a local http server, opens a browser to have the user login,
    and gets the code redirected to the server (no copy and pasting required)
    """
    safeprint(
        "You are running 'globus login', which should automatically open "
        "a browser window for you to login.\n"
        "If this fails or you experience difficulty, try "
        "'globus login --no-local-server'"
        "\n---")
    # start local server and create matching redirect_uri
    with start_local_server(listen=('127.0.0.1', 0)) as server:
        _, port = server.socket.getsockname()
        redirect_uri = 'http://localhost:{}'.format(port)

        # get the NativeApp client object and start a flow
        # if available, use the system-name to prefill the grant
        label = platform.node() or None
        native_client = internal_auth_client()
        native_client.oauth2_start_flow(refresh_tokens=True,
                                        prefill_named_grant=label,
                                        redirect_uri=redirect_uri,
                                        requested_scopes=SCOPES)
        url = native_client.oauth2_get_authorize_url()

        # open web-browser for user to log in, get auth code
        webbrowser.open(url, new=1)
        auth_code = server.wait_for_code()

    if isinstance(auth_code, LocalServerError):
        safeprint('Login failed: {}'.format(auth_code), write_to_stderr=True)
        click.get_current_context().exit(1)
    elif isinstance(auth_code, Exception):
        safeprint('Login failed with unexpected error:\n{}'.format(auth_code),
                  write_to_stderr=True)
        click.get_current_context().exit(1)

    # finish login flow
    exchange_code_and_store_config(native_client, auth_code)
Пример #15
0
def do_local_server_auth_flow(session_params=None, force_new_client=False):
    """
    Starts a local http server, opens a browser to have the user authenticate,
    and gets the code redirected to the server (no copy and pasting required)
    """
    session_params = session_params or {}

    # start local server and create matching redirect_uri
    with start_local_server(listen=("127.0.0.1", 0)) as server:
        _, port = server.socket.getsockname()
        redirect_uri = "http://localhost:{}".format(port)

        # get the ConfidentialApp client object and start a flow
        auth_client = internal_auth_client(
            requires_instance=True, force_new_client=force_new_client
        )
        auth_client.oauth2_start_flow(
            refresh_tokens=True, redirect_uri=redirect_uri, requested_scopes=SCOPES
        )
        additional_params = {"prompt": "login"}
        additional_params.update(session_params)
        url = auth_client.oauth2_get_authorize_url(additional_params=additional_params)

        # open web-browser for user to log in, get auth code
        webbrowser.open(url, new=1)
        auth_code = server.wait_for_code()

    if isinstance(auth_code, LocalServerError):
        safeprint("Authorization failed: {}".format(auth_code), write_to_stderr=True)
        click.get_current_context().exit(1)
    elif isinstance(auth_code, Exception):
        safeprint(
            "Authorization failed with unexpected error:\n{}".format(auth_code),
            write_to_stderr=True,
        )
        click.get_current_context().exit(1)

    # finish auth flow and return true
    exchange_code_and_store_config(auth_client, auth_code)
    return True
Пример #16
0
def do_link_auth_flow(session_params=None, force_new_client=False):
    """
    Prompts the user with a link to authenticate with globus auth
    and authorize the CLI to act on their behalf.
    """
    session_params = session_params or {}

    # get the ConfidentialApp client object
    auth_client = internal_auth_client(
        requires_instance=True, force_new_client=force_new_client
    )

    # start the Confidential App Grant flow
    auth_client.oauth2_start_flow(
        redirect_uri=auth_client.base_url + "v2/web/auth-code",
        refresh_tokens=True,
        requested_scopes=SCOPES,
    )

    # prompt
    additional_params = {"prompt": "login"}
    additional_params.update(session_params)
    linkprompt = "Please authenticate with Globus here"
    safeprint(
        "{0}:\n{1}\n{2}\n{1}\n".format(
            linkprompt,
            "-" * len(linkprompt),
            auth_client.oauth2_get_authorize_url(additional_params=additional_params),
        )
    )

    # come back with auth code
    auth_code = click.prompt("Enter the resulting Authorization Code here").strip()

    # finish auth flow
    exchange_code_and_store_config(auth_client, auth_code)
    return True
Пример #17
0
def logout_command():
    # try to get the user's preferred username from userinfo
    # if an API error is raised, they probably are not logged in
    try:
        username = get_auth_client().oauth2_userinfo()["preferred_username"]
    except AuthAPIError:
        click.echo(("Unable to lookup username. You may not be logged in. "
                    "Attempting logout anyway...\n"))
        username = None
    click.echo(
        u"Logging out of Globus{}\n".format(u" as " +
                                            username if username else ""))

    # we use a Native Client to prevent an invalid instance client
    # from preventing token revocation
    native_client = internal_native_client()

    # remove tokens from config and revoke them
    # also, track whether or not we should print the rescind help
    print_rescind_help = False
    for token_opt in (
            TRANSFER_RT_OPTNAME,
            TRANSFER_AT_OPTNAME,
            AUTH_RT_OPTNAME,
            AUTH_AT_OPTNAME,
    ):
        # first lookup the token -- if not found we'll continue
        token = lookup_option(token_opt)
        if not token:
            click.echo(('Warning: Found no token named "{}"! '
                        "Recommend rescinding consent").format(token_opt))
            print_rescind_help = True
            continue
        # token was found, so try to revoke it
        try:
            native_client.oauth2_revoke_token(token)
        # if we network error, revocation failed -- print message and abort so
        # that we can revoke later when the network is working
        except globus_sdk.NetworkError:
            click.echo(("Failed to reach Globus to revoke tokens. "
                        "Because we cannot revoke these tokens, cancelling "
                        "logout"))
            click.get_current_context().exit(1)
        # finally, we revoked, so it's safe to remove the token
        remove_option(token_opt)

    # delete the instance client if one exists
    client_id = lookup_option(CLIENT_ID_OPTNAME)

    if client_id:
        instance_client = internal_auth_client()
        try:
            instance_client.delete("/v2/api/clients/{}".format(client_id))

        # if the client secret has been invalidated or the client has
        # already been removed, we continue on
        except AuthAPIError:
            pass

    # remove deleted client values and expiration times
    for opt in (
            CLIENT_ID_OPTNAME,
            CLIENT_SECRET_OPTNAME,
            TRANSFER_AT_EXPIRES_OPTNAME,
            AUTH_AT_EXPIRES_OPTNAME,
    ):
        remove_option(opt)

    # if print_rescind_help is true, we printed warnings above
    # so, jam out an extra newline as a separator
    click.echo(("\n" if print_rescind_help else "") + _LOGOUT_EPILOG)

    # if some token wasn't found in the config, it means its possible that the
    # config file was removed without logout
    # in that case, the user should rescind the CLI consent to invalidate any
    # potentially leaked refresh tokens, so print the help on that
    if print_rescind_help:
        click.echo(_RESCIND_HELP)