Exemple #1
0
def main_add_basic(args):
    """
    Add a config entry for basic auth
    """
    backend = args.backend
    username = args.username
    try_auth = args.try_auth
    config = AuthConfig()

    print("Will add basic auth config for backend URL {b!r}".format(b=backend))
    print("to config file: {c!r}".format(c=str(config.path)))

    # Find username and password
    if not username:
        username = builtins.input("Enter username and press enter: ")
    print("Using username {u!r}".format(u=username))
    password = getpass("Enter password and press enter: ") or None

    if try_auth:
        print("Trying to authenticate with {b!r}".format(b=backend))
        con = connect(backend)
        con.authenticate_basic(username, password)
        print("Successfully authenticated {u!r}".format(u=username))

    config.set_basic_auth(backend=backend,
                          username=username,
                          password=password)
    print("Saved credentials to {p!r}".format(p=str(config.path)))
Exemple #2
0
 def test_tmp_openeo_config_home(self, tmp_openeo_config_home, tmp_path):
     expected_dir = str(tmp_path)
     assert str(AuthConfig.default_path()).startswith(expected_dir)
     assert not AuthConfig.default_path().exists()
     config = AuthConfig()
     assert str(config.path).startswith(expected_dir)
     assert config.load() == {}
Exemple #3
0
def main_config_dump(args):
    """
    Dump auth config file
    """
    config = AuthConfig()
    print("### {p} ".format(p=str(config.path)).ljust(80, "#"))
    data = config.load(empty_on_file_not_found=False)
    if not args.show_secrets:
        _redact(data,
                keys_to_redact=["client_secret", "password", "refresh_token"])
    json.dump(data, fp=sys.stdout, indent=2)
    print()
Exemple #4
0
def main_paths(args):
    """
    Print paths of auth config file and refresh token cache file.
    """
    def describe(p: Path):
        if p.exists():
            return "perms: 0o{p:o}, size: {s}B".format(p=p.stat().st_mode
                                                       & 0o777,
                                                       s=p.stat().st_size)
        else:
            return "does not exist"

    config_path = AuthConfig().path
    print("openEO auth config: {p} ({d})".format(p=str(config_path),
                                                 d=describe(config_path)))
    tokens_path = RefreshTokenStore().path
    print("openEO OpenID Connect refresh token store: {p} ({d})".format(
        p=str(tokens_path), d=describe(tokens_path)))
def test_authenticate_oidc_resource_owner_password_credentials_client_from_config(
        requests_mock):
    requests_mock.get(API_URL, json={"api_version": "1.0.0"})
    client_id = "myclient"
    client_secret = "$3cr3t"
    username, password = "******", "j0hn"
    issuer = "https://oidc.test"
    oidc_discovery_url = "https://oidc.test/.well-known/openid-configuration"
    requests_mock.get(API_URL + 'credentials/oidc',
                      json={
                          "providers": [{
                              "id": "oi",
                              "issuer": issuer,
                              "title": "example",
                              "scopes": ["openid"]
                          }]
                      })
    oidc_mock = OidcMock(
        requests_mock=requests_mock,
        expected_grant_type="password",
        expected_client_id=client_id,
        expected_fields={
            "username": username,
            "password": password,
            "scope": "openid",
            "client_secret": client_secret
        },
        oidc_discovery_url=oidc_discovery_url,
    )
    AuthConfig().set_oidc_client_config(backend=API_URL,
                                        provider_id="oi",
                                        client_id=client_id,
                                        client_secret=client_secret)

    # With all this set up, kick off the openid connect flow
    refresh_token_store = mock.Mock()
    conn = Connection(API_URL, refresh_token_store=refresh_token_store)
    assert isinstance(conn.auth, NullAuth)
    conn.authenticate_oidc_resource_owner_password_credentials(
        username=username, password=password)
    assert isinstance(conn.auth, BearerAuth)
    assert conn.auth.bearer == 'oidc/oi/' + oidc_mock.state["access_token"]
    assert refresh_token_store.mock_calls == []
Exemple #6
0
 def test_oidc_backend_normalization(self, tmp_path, to_set, to_get):
     config = AuthConfig(path=tmp_path)
     with mock.patch.object(openeo.rest.auth.config,
                            "utcnow_rfc3339",
                            return_value="2020-06-08T11:18:27Z"):
         config.set_oidc_client_config(to_set,
                                       "default",
                                       client_id="client123",
                                       client_secret="$6cr67")
     for backend in [to_set, to_get]:
         assert config.get_oidc_client_configs(backend,
                                               "default") == ("client123",
                                                              "$6cr67")
         assert config.get_oidc_provider_configs(backend) == {
             "default": {
                 "date": "2020-06-08T11:18:27Z",
                 "client_id": "client123",
                 "client_secret": "$6cr67"
             }
         }
def test_authenticate_basic_from_config(requests_mock, api_version):
    user, pwd = "john281", "J0hndo3"
    requests_mock.get(API_URL, json={"api_version": api_version})

    def text_callback(request, context):
        assert request.headers[
            "Authorization"] == requests.auth._basic_auth_str(username=user,
                                                              password=pwd)
        return '{"access_token":"w3lc0m3"}'

    requests_mock.get(API_URL + 'credentials/basic', text=text_callback)
    AuthConfig().set_basic_auth(backend=API_URL, username=user, password=pwd)

    conn = Connection(API_URL)
    assert isinstance(conn.auth, NullAuth)
    conn.authenticate_basic()
    assert isinstance(conn.auth, BearerAuth)
    if ComparableVersion(api_version).at_least("1.0.0"):
        assert conn.auth.bearer == "basic//w3lc0m3"
    else:
        assert conn.auth.bearer == "w3lc0m3"
Exemple #8
0
 def test_basic_auth(self, tmp_path):
     config = AuthConfig(path=tmp_path)
     with mock.patch.object(openeo.rest.auth.config,
                            "utcnow_rfc3339",
                            return_value="2020-06-08T11:18:27Z"):
         config.set_basic_auth("oeo.test", "John", "j0hn123")
     assert config.path.exists()
     assert [p.name
             for p in tmp_path.iterdir()] == [AuthConfig.DEFAULT_FILENAME]
     with config.path.open("r") as f:
         data = json.load(f)
     assert data["backends"] == {
         "oeo.test": {
             "basic": {
                 "date": "2020-06-08T11:18:27Z",
                 "username": "******",
                 "password": "******"
             }
         }
     }
     assert config.get_basic_auth("oeo.test") == ("John", "j0hn123")
     assert config.get_basic_auth("oeo.test") == ("John", "j0hn123")
def test_authenticate_oidc_device_flow_client_from_config(requests_mock):
    requests_mock.get(API_URL, json={"api_version": "1.0.0"})
    client_id = "myclient"
    client_secret = "$3cr3t"
    issuer = "https://oidc.test"
    oidc_discovery_url = "https://oidc.test/.well-known/openid-configuration"
    requests_mock.get(API_URL + 'credentials/oidc',
                      json={
                          "providers": [{
                              "id": "oi",
                              "issuer": issuer,
                              "title": "example",
                              "scopes": ["openid"]
                          }]
                      })
    oidc_mock = OidcMock(
        requests_mock=requests_mock,
        expected_grant_type="urn:ietf:params:oauth:grant-type:device_code",
        expected_client_id=client_id,
        expected_fields={
            "scope": "openid",
            "client_secret": client_secret
        },
        oidc_discovery_url=oidc_discovery_url,
    )
    AuthConfig().set_oidc_client_config(backend=API_URL,
                                        provider_id="oi",
                                        client_id=client_id,
                                        client_secret=client_secret)

    # With all this set up, kick off the openid connect flow
    refresh_token_store = mock.Mock()
    conn = Connection(API_URL, refresh_token_store=refresh_token_store)
    assert isinstance(conn.auth, NullAuth)
    oidc_mock.state["device_code_callback_timeline"] = ["great success"]
    conn.authenticate_oidc_device()
    assert isinstance(conn.auth, BearerAuth)
    assert conn.auth.bearer == 'oidc/oi/' + oidc_mock.state["access_token"]
    assert refresh_token_store.mock_calls == []
Exemple #10
0
 def test_oidc(self, tmp_path):
     config = AuthConfig(path=tmp_path)
     with mock.patch.object(openeo.rest.auth.config,
                            "utcnow_rfc3339",
                            return_value="2020-06-08T11:18:27Z"):
         config.set_oidc_client_config("oeo.test",
                                       "default",
                                       client_id="client123",
                                       client_secret="$6cr67")
     assert config.path.exists()
     assert [p.name
             for p in tmp_path.iterdir()] == [AuthConfig.DEFAULT_FILENAME]
     with config.path.open("r") as f:
         data = json.load(f)
     assert data["backends"] == {
         "oeo.test": {
             "oidc": {
                 "providers": {
                     "default": {
                         "date": "2020-06-08T11:18:27Z",
                         "client_id": "client123",
                         "client_secret": "$6cr67"
                     }
                 }
             }
         }
     }
     assert config.get_oidc_client_configs("oeo.test",
                                           "default") == ("client123",
                                                          "$6cr67")
     assert config.get_oidc_provider_configs("oeo.test") == {
         "default": {
             "date": "2020-06-08T11:18:27Z",
             "client_id": "client123",
             "client_secret": "$6cr67"
         }
     }
def test_authenticate_oidc_auth_code_pkce_flow_client_from_config(
        requests_mock):
    requests_mock.get(API_URL, json={"api_version": "1.0.0"})
    client_id = "myclient"
    issuer = "https://oidc.test"
    oidc_discovery_url = "https://oidc.test/.well-known/openid-configuration"
    requests_mock.get(API_URL + 'credentials/oidc',
                      json={
                          "providers": [{
                              "id": "oi",
                              "issuer": issuer,
                              "title": "example",
                              "scopes": ["openid"]
                          }]
                      })
    oidc_mock = OidcMock(
        requests_mock=requests_mock,
        expected_grant_type="authorization_code",
        expected_client_id=client_id,
        expected_fields={"scope": "openid"},
        oidc_discovery_url=oidc_discovery_url,
        scopes_supported=["openid"],
    )
    AuthConfig().set_oidc_client_config(backend=API_URL,
                                        provider_id="oi",
                                        client_id=client_id)

    # With all this set up, kick off the openid connect flow
    refresh_token_store = mock.Mock()
    conn = Connection(API_URL, refresh_token_store=refresh_token_store)
    assert isinstance(conn.auth, NullAuth)
    conn.authenticate_oidc_authorization_code(
        webbrowser_open=oidc_mock.webbrowser_open)
    assert isinstance(conn.auth, BearerAuth)
    assert conn.auth.bearer == 'oidc/oi/' + oidc_mock.state["access_token"]
    assert refresh_token_store.mock_calls == []
Exemple #12
0
def main_add_oidc(args):
    """
    Add a config entry for OIDC auth
    """
    backend = args.backend
    provider_id = args.provider_id
    client_id = args.client_id
    config = AuthConfig()

    print("Will add OpenID Connect auth config for backend URL {b!r}".format(
        b=backend))
    print("to config file: {c!r}".format(c=str(config.path)))

    con = connect(backend)
    api_version = con.capabilities().api_version_check
    if api_version < "1.0.0":
        raise CliToolException(
            "Backend API version is too low: {v} < 1.0.0".format(
                v=api_version))
    # Find provider ID
    oidc_info = con.get("/credentials/oidc", expected_status=200).json()
    providers = OrderedDict([(p["id"], p) for p in oidc_info["providers"]])
    if not providers:
        raise CliToolException(
            "No OpenID Connect providers listed by backend {b!r}.".format(
                b=backend))
    if not provider_id:
        if len(providers) == 1:
            provider_id = list(providers.keys())[0]
        else:
            provider_id = _interactive_choice(
                title="Backend {b!r} has multiple OpenID Connect providers.".
                format(b=backend),
                options=[(p["id"], "{t} (issuer {s})".format(t=p["title"],
                                                             s=p["issuer"]))
                         for p in providers.values()])
    if provider_id not in providers:
        raise CliToolException(
            "Invalid provider ID {p!r}. Should be one of {o}.".format(
                p=provider_id, o=list(providers.keys())))
    issuer = providers[provider_id]["issuer"]
    print("Using provider ID {p!r} (issuer {i!r})".format(p=provider_id,
                                                          i=issuer))

    # Get client_id and client_secret
    # Find username and password
    if not client_id:
        client_id = builtins.input("Enter client_id and press enter: ")
    print("Using client ID {u!r}".format(u=client_id))
    if not client_id:
        show_warning("Given client ID was empty.")
    client_secret = getpass("Enter client_secret and press enter: ")
    if not client_secret:
        show_warning("Given client secret was empty.")

    config.set_oidc_client_config(backend=backend,
                                  provider_id=provider_id,
                                  client_id=client_id,
                                  client_secret=client_secret,
                                  issuer=issuer)
    print("Saved client information to {p!r}".format(p=str(config.path)))
Exemple #13
0
def main_add_oidc(args):
    """
    Add a config entry for OIDC auth
    """
    backend = args.backend
    provider_id = args.provider_id
    client_id = args.client_id
    ask_client_secret = args.ask_client_secret
    use_default_client = args.use_default_client
    config = AuthConfig()

    print("Will add OpenID Connect auth config for backend URL {b!r}".format(
        b=backend))
    print("to config file: {c!r}".format(c=str(config.path)))

    con = connect(backend)
    api_version = con.capabilities().api_version_check
    if api_version < "1.0.0":
        raise CliToolException(
            "Backend API version is too low: {v} < 1.0.0".format(
                v=api_version))
    # Find provider ID
    oidc_info = con.get("/credentials/oidc", expected_status=200).json()
    providers = OrderedDict((p["id"], OidcProviderInfo.from_dict(p))
                            for p in oidc_info["providers"])

    if not providers:
        raise CliToolException(
            "No OpenID Connect providers listed by backend {b!r}.".format(
                b=backend))
    if not provider_id:
        if len(providers) == 1:
            provider_id = list(providers.keys())[0]
        else:
            provider_id = _interactive_choice(
                title="Backend {b!r} has multiple OpenID Connect providers.".
                format(b=backend),
                options=[(p.id, "{t} (issuer {s})".format(t=p.title,
                                                          s=p.issuer))
                         for p in providers.values()])
    if provider_id not in providers:
        raise CliToolException(
            "Invalid provider ID {p!r}. Should be one of {o}.".format(
                p=provider_id, o=list(providers.keys())))
    provider = providers[provider_id]
    print("Using provider ID {p!r} (issuer {i!r})".format(p=provider_id,
                                                          i=provider.issuer))

    # Get client_id and client_secret (if necessary)
    if use_default_client:
        if not provider.default_clients:
            show_warning(
                "No default clients declared for provider {p!r}".format(
                    p=provider_id))
        client_id, client_secret = None, None
    else:
        if not client_id:
            if provider.default_clients:
                client_prompt = "Enter client_id or leave empty to use default client, and press enter: "
            else:
                client_prompt = "Enter client_id and press enter: "
            client_id = builtins.input(client_prompt).strip() or None
        print("Using client ID {u!r}".format(u=client_id))
        if not client_id and not provider.default_clients:
            show_warning("Given client ID was empty.")

        if client_id and ask_client_secret:
            client_secret = getpass(
                "Enter client_secret or leave empty to not use a secret, and press enter: "
            ) or None
        else:
            client_secret = None

    config.set_oidc_client_config(backend=backend,
                                  provider_id=provider_id,
                                  client_id=client_id,
                                  client_secret=client_secret,
                                  issuer=provider.issuer)
    print("Saved client information to {p!r}".format(p=str(config.path)))
Exemple #14
0
def main_oidc_auth(args):
    """
    Do OIDC auth flow and store refresh tokens.
    """
    backend = args.backend
    oidc_flow = args.flow
    provider_id = args.provider_id
    timeout = args.timeout

    config = AuthConfig()

    print("Will do OpenID Connect flow to authenticate with backend {b!r}.".
          format(b=backend))
    print("Using config {c!r}.".format(c=str(config.path)))

    # Determine provider
    provider_configs = config.get_oidc_provider_configs(backend=backend)
    _log.debug("Provider configs: {c!r}".format(c=provider_configs))
    if not provider_id:
        if len(provider_configs) == 0:
            print("Will try to use default provider_id.")
            provider_id = None
        elif len(provider_configs) == 1:
            provider_id = list(provider_configs.keys())[0]
        else:
            provider_id = _interactive_choice(
                title=
                "Multiple OpenID Connect providers available for backend {b!r}"
                .format(b=backend),
                options=sorted(
                    (k,
                     "{k}: issuer {s}".format(k=k, s=v.get("issuer", "n/a")))
                    for k, v in provider_configs.items()))
    if not (provider_id is None or provider_id in provider_configs):
        raise CliToolException(
            "Invalid provider ID {p!r}. Should be `None` or one of {o}.".
            format(p=provider_id, o=list(provider_configs.keys())))
    print("Using provider ID {p!r}.".format(p=provider_id))

    # Get client id and secret
    client_id, client_secret = config.get_oidc_client_configs(
        backend=backend, provider_id=provider_id)
    if client_id:
        print("Using client ID {c!r}.".format(c=client_id))
    else:
        print("Will try to use default client.")

    refresh_token_store = RefreshTokenStore()
    con = Connection(backend, refresh_token_store=refresh_token_store)
    if oidc_flow == "auth-code":
        print("Starting OpenID Connect authorization code flow:")
        print(
            "a browser window should open allowing you to log in with the identity provider\n"
            "and grant access to the client {c!r} (timeout: {t}s).".format(
                c=client_id, t=timeout))
        con.authenticate_oidc_authorization_code(
            client_id=client_id,
            client_secret=client_secret,
            provider_id=provider_id,
            timeout=timeout,
            store_refresh_token=True,
            webbrowser_open=_webbrowser_open)
        print("The OpenID Connect authorization code flow was successful.")
    elif oidc_flow == "device":
        print("Starting OpenID Connect device flow.")
        con.authenticate_oidc_device(client_id=client_id,
                                     client_secret=client_secret,
                                     provider_id=provider_id,
                                     store_refresh_token=True)
        print("The OpenID Connect device flow was successful.")
    else:
        raise CliToolException("Invalid flow {f!r}".format(f=oidc_flow))

    print("Stored refresh token in {p!r}".format(
        p=str(refresh_token_store.path)))
 def _get_auth_config(self) -> AuthConfig:
     if self._auth_config is None:
         self._auth_config = AuthConfig()
     return self._auth_config
def auth_config(tmp_openeo_config) -> AuthConfig:
    return AuthConfig(tmp_openeo_config)
Exemple #17
0
 def test_start_empty(self, tmp_path):
     config = AuthConfig(path=tmp_path)
     assert config.get_basic_auth("foo") == (None, None)
     assert config.get_oidc_client_configs("oeo.test",
                                           "default") == (None, None)
Exemple #18
0
 def test_basic_auth_url_normalization(self, tmp_path, to_set, to_get):
     config = AuthConfig(path=tmp_path)
     config.set_basic_auth(to_set, "John", "j0hn123")
     assert config.get_basic_auth(to_set) == ("John", "j0hn123")
     assert config.get_basic_auth(to_get) == ("John", "j0hn123")
Exemple #19
0
def auth_config(tmp_openeo_config_home) -> AuthConfig:
    """Make sure we start with emtpy AuthConfig."""
    config = AuthConfig(tmp_openeo_config_home)
    assert not config.path.exists()
    return config