Exemplo n.º 1
0
def whoami_command(linked_identities):
    """
    Executor for `globus whoami`
    """
    client = get_auth_client()

    # get userinfo from auth.
    # if we get back an error the user likely needs to log in again
    try:
        res = client.oauth2_userinfo()
    except AuthAPIError:
        safeprint(
            "Unable to get user information. Please try " "logging in again.",
            write_to_stderr=True,
        )
        click.get_current_context().exit(1)

    print_command_hint(
        "For information on which identities are in session see\n"
        "  globus session show\n"
    )

    # --linked-identities either displays all usernames or a table if verbose
    if linked_identities:
        try:
            formatted_print(
                res["identity_set"],
                fields=[
                    ("Username", "username"),
                    ("Name", "name"),
                    ("ID", "sub"),
                    ("Email", "email"),
                ],
                simple_text=(
                    None
                    if is_verbose()
                    else "\n".join([x["username"] for x in res["identity_set"]])
                ),
            )
        except KeyError:
            safeprint(
                "Your current login does not have the consents required "
                "to view your full identity set. Please log in again "
                "to agree to the required consents.",
                write_to_stderr=True,
            )

    # Default output is the top level data
    else:
        formatted_print(
            res,
            text_format=FORMAT_TEXT_RECORD,
            fields=[
                ("Username", "preferred_username"),
                ("Name", "name"),
                ("ID", "sub"),
                ("Email", "email"),
            ],
            simple_text=(None if is_verbose() else res["preferred_username"]),
        )
Exemplo n.º 2
0
def role_list(endpoint_id):
    """
    List the assigned roles on an endpoint.

    You must have sufficient privileges to see the roles on the endpoint.
    """
    client = get_client()
    roles = client.endpoint_role_list(endpoint_id)

    resolved_ids = IdentityMap(
        get_auth_client(),
        (x["principal"] for x in roles if x["principal_type"] == "identity"),
    )

    def principal_str(role):
        principal = role["principal"]
        if role["principal_type"] == "identity":
            try:
                return resolved_ids[principal]["username"]
            except KeyError:
                return principal
        if role["principal_type"] == "group":
            return (u"https://app.globus.org/groups/{}").format(principal)
        return principal

    formatted_print(
        roles,
        fields=[
            ("Principal Type", "principal_type"),
            ("Role ID", "id"),
            ("Principal", principal_str),
            ("Role", "role"),
        ],
    )
Exemplo n.º 3
0
def list_command(endpoint_id):
    """List all rules in an endpoint's access control list."""
    client = get_client()

    rules = client.endpoint_acl_list(endpoint_id)

    resolved_ids = IdentityMap(
        get_auth_client(),
        (x["principal"] for x in rules if x["principal_type"] == "identity"),
    )

    def principal_str(rule):
        principal = rule["principal"]
        if rule["principal_type"] == "identity":
            try:
                return resolved_ids[principal]["username"]
            except KeyError:
                return principal
        if rule["principal_type"] == "group":
            return (u"https://app.globus.org/groups/{}").format(principal)
        return rule["principal_type"]

    formatted_print(
        rules,
        fields=[
            ("Rule ID", "id"),
            ("Permissions", "permissions"),
            ("Shared With", principal_str),
            ("Path", "path"),
        ],
    )
Exemplo n.º 4
0
def get_identities_command(values):
    """
    Executor for `globus get-identities`
    """
    client = get_auth_client()

    resolved_values = [_try_b32_decode(v) or v for v in values]

    # since API doesn't accept mixed ids and usernames,
    # split input values into separate lists
    ids = []
    usernames = []
    for val in resolved_values:
        try:
            uuid.UUID(val)
            ids.append(val)
        except ValueError:
            usernames.append(val)

    # make two calls to get_identities with ids and usernames
    # then combine the calls into one response
    results = []
    if len(ids):
        results += client.get_identities(ids=ids)["identities"]
    if len(usernames):
        results += client.get_identities(usernames=usernames)["identities"]
    res = GlobusResponse({"identities": results})

    def _custom_text_format(identities):
        """
        Non-verbose text output is customized
        """
        def resolve_identity(value):
            """
            helper to deal with variable inputs and uncertain response order
            """
            for identity in identities:
                if identity["id"] == value:
                    return identity["username"]
                if identity["username"] == value:
                    return identity["id"]
            return "NO_SUCH_IDENTITY"

        # standard output is one resolved identity per line in the same order
        # as the inputs. A resolved identity is either a username if given a
        # UUID vice versa, or "NO_SUCH_IDENTITY" if the identity could not be
        # found
        for val in resolved_values:
            safeprint(resolve_identity(val))

    formatted_print(
        res,
        response_key='identities',
        fields=[('ID', 'id'), ('Username', 'username'), ('Full Name', 'name'),
                ('Organization', 'organization'), ('Email Address', 'email')],
        # verbose output is a table. Order not guaranteed, may contain
        # duplicates
        text_format=(FORMAT_TEXT_TABLE
                     if is_verbose() else _custom_text_format))
Exemplo n.º 5
0
 def setUpClass(self):
     """
     Gets a TransferClient and AuthClient for direct sdk calls
     Cleans any old sharing data created by previous test runs
     """
     self.tc = get_client()
     self.ac = get_auth_client()
     clean_sharing()
Exemplo n.º 6
0
 def setUpClass(self):
     """
     Gets a TransferClient and AuthClient for direct sdk calls
     Cleans any old sharing data created by previous test runs
     """
     self.tc = get_client()
     self.ac = get_auth_client()
     clean_sharing()
Exemplo n.º 7
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)
Exemplo n.º 8
0
def whoami_command(linked_identities):
    """
    Executor for `globus whoami`
    """
    client = get_auth_client()

    # get userinfo from auth.
    # if we get back an error the user likely needs to log in again
    try:
        res = client.oauth2_userinfo()
    except AuthAPIError:
        click.echo(
            "Unable to get user information. Please try logging in again.",
            err=True)
        click.get_current_context().exit(1)

    print_command_hint(
        "For information on which identities are in session see\n"
        "  globus session show\n")

    # --linked-identities either displays all usernames or a table if verbose
    if linked_identities:
        try:
            formatted_print(
                res["identity_set"],
                fields=[
                    ("Username", "username"),
                    ("Name", "name"),
                    ("ID", "sub"),
                    ("Email", "email"),
                ],
                simple_text=(None if is_verbose() else "\n".join(
                    [x["username"] for x in res["identity_set"]])),
            )
        except KeyError:
            click.echo(
                "Your current login does not have the consents required "
                "to view your full identity set. Please log in again "
                "to agree to the required consents.",
                err=True,
            )

    # Default output is the top level data
    else:
        formatted_print(
            res,
            text_format=FORMAT_TEXT_RECORD,
            fields=[
                ("Username", "preferred_username"),
                ("Name", "name"),
                ("ID", "sub"),
                ("Email", "email"),
            ],
            simple_text=(None if is_verbose() else res["preferred_username"]),
        )
Exemplo n.º 9
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")],
    )
Exemplo n.º 10
0
def session_update(identities, no_local_server, all):

    if (not (identities or all)) or (identities and all):
        raise click.UsageError("Either give one or more IDENTITIES or use --all")
    auth_client = get_auth_client()

    # if --all use every identity id in the user's identity set
    if all:
        res = auth_client.oauth2_userinfo()
        try:
            identity_ids = [user["sub"] for user in res["identity_set"]]
        except KeyError:
            click.echo(
                "Your current login does not have the consents required "
                "to view your full identity set. Please log in again "
                "to agree to the required consents.",
                err=True,
            )
            click.get_current_context().exit(1)

    # otherwise try to resolve any non uuid values to identity ids
    else:
        identity_ids = []
        identity_names = []

        for val in identities:
            try:
                uuid.UUID(val)
                identity_ids.append(val)
            except ValueError:
                identity_names.append(val)

        if identity_names:
            res = auth_client.get_identities(usernames=identity_names)["identities"]

            for name in identity_names:
                for identity in res:
                    if identity["username"] == name:
                        identity_ids.append(identity["id"])
                        break
                else:
                    click.echo("No such identity {}".format(val), err=True)
                    click.get_current_context().exit(1)

    # create session params once we have all identity ids
    session_params = {
        "session_required_identities": ",".join(identity_ids),
        "session_message": "Authenticate to update your CLI session.",
    }

    # use a link login if remote session or user requested
    if no_local_server or is_remote_session():
        do_link_auth_flow(session_params=session_params)

    # otherwise default to a local server login flow
    else:
        click.echo(
            "You are running 'globus session update', "
            "which should automatically open a browser window for you to "
            "authenticate with specific identities.\n"
            "If this fails or you experience difficulty, try "
            "'globus session update --no-local-server'"
            "\n---"
        )
        do_local_server_auth_flow(session_params=session_params)

    click.echo(
        "\nYou have successfully updated your CLI session.\n"
        "Use 'globus session show' to see the updated session."
    )
Exemplo n.º 11
0
def ac():
    with patch_config():
        return get_auth_client()
Exemplo n.º 12
0
def get_identities_command(values):
    """
    Executor for `globus get-identities`
    """
    client = get_auth_client()

    resolved_values = [_try_b32_decode(v) or v for v in values]

    # since API doesn't accept mixed ids and usernames,
    # split input values into separate lists
    ids = []
    usernames = []
    for val in resolved_values:
        try:
            uuid.UUID(val)
            ids.append(val)
        except ValueError:
            usernames.append(val)

    # make two calls to get_identities with ids and usernames
    # then combine the calls into one response
    results = []
    if len(ids):
        results += client.get_identities(ids=ids)["identities"]
    if len(usernames):
        results += client.get_identities(usernames=usernames)["identities"]
    res = GlobusResponse({"identities": results})

    def _custom_text_format(identities):
        """
        Non-verbose text output is customized
        """

        def resolve_identity(value):
            """
            helper to deal with variable inputs and uncertain response order
            """
            for identity in identities:
                if identity["id"] == value:
                    return identity["username"]
                if identity["username"] == value:
                    return identity["id"]
            return "NO_SUCH_IDENTITY"

        # standard output is one resolved identity per line in the same order
        # as the inputs. A resolved identity is either a username if given a
        # UUID vice versa, or "NO_SUCH_IDENTITY" if the identity could not be
        # found
        for val in resolved_values:
            safeprint(resolve_identity(val))

    formatted_print(
        res,
        response_key="identities",
        fields=[
            ("ID", "id"),
            ("Username", "username"),
            ("Full Name", "name"),
            ("Organization", "organization"),
            ("Email Address", "email"),
        ],
        # verbose output is a table. Order not guaranteed, may contain
        # duplicates
        text_format=(FORMAT_TEXT_TABLE if is_verbose() else _custom_text_format),
    )
Exemplo n.º 13
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)
Exemplo n.º 14
0
def get_identities_command(values, provision):
    """
    Lookup Globus Auth Identities given one or more uuids
    and/or usernames.

    Default output resolves each UUID to a username and each username to a UUID,
    with one output per line in the same order as the inputs.
    If a particular input had no corresponding identity in Globus Auth,
    "NO_SUCH_IDENTITY" is printed instead.

    If more fields are desired, --verbose will give tabular output, but does not
    guarantee order and ignores inputs with no corresponding Globus Auth identity.
    """
    client = get_auth_client()

    resolved_values = [_try_b32_decode(v) or v for v in values]

    # since API doesn't accept mixed ids and usernames,
    # split input values into separate lists
    ids = []
    usernames = []
    for val in resolved_values:
        try:
            uuid.UUID(val)
            ids.append(val)
        except ValueError:
            usernames.append(val)

    # make two calls to get_identities with ids and usernames
    # then combine the calls into one response
    results = []
    if len(ids):
        results += client.get_identities(ids=ids,
                                         provision=provision)["identities"]
    if len(usernames):
        results += client.get_identities(usernames=usernames,
                                         provision=provision)["identities"]
    res = GlobusResponse({"identities": results})

    def _custom_text_format(identities):
        """
        Non-verbose text output is customized
        """
        def resolve_identity(value):
            """
            helper to deal with variable inputs and uncertain response order
            """
            for identity in identities:
                if identity["id"] == value:
                    return identity["username"]
                if identity["username"] == value:
                    return identity["id"]
            return "NO_SUCH_IDENTITY"

        # standard output is one resolved identity per line in the same order
        # as the inputs. A resolved identity is either a username if given a
        # UUID vice versa, or "NO_SUCH_IDENTITY" if the identity could not be
        # found
        for val in resolved_values:
            click.echo(resolve_identity(val))

    formatted_print(
        res,
        response_key="identities",
        fields=[
            ("ID", "id"),
            ("Username", "username"),
            ("Full Name", "name"),
            ("Organization", "organization"),
            ("Email Address", "email"),
        ],
        # verbose output is a table. Order not guaranteed, may contain
        # duplicates
        text_format=(FORMAT_TEXT_TABLE
                     if is_verbose() else _custom_text_format),
    )