Skip to content

Commit

Permalink
feat: Adds Pluggable Auth support (WIF) (googleapis#908)
Browse files Browse the repository at this point in the history
* feat: Adds Pluggable Auth support to ADC  (googleapis#895)

* chore(deps): update dependency com.google.http-client:google-http-client-bom to v1.41.5 (googleapis#896)

[![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [com.google.http-client:google-http-client-bom](https://togithub.com/googleapis/google-http-java-client) | `1.41.4` -> `1.41.5` | [![age](https://badges.renovateapi.com/packages/maven/com.google.http-client:google-http-client-bom/1.41.5/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.http-client:google-http-client-bom/1.41.5/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.http-client:google-http-client-bom/1.41.5/compatibility-slim/1.41.4)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.http-client:google-http-client-bom/1.41.5/confidence-slim/1.41.4)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>googleapis/google-http-java-client</summary>

### [`v1.41.5`](https://togithub.com/googleapis/google-http-java-client/blob/HEAD/CHANGELOG.md#&#8203;1415-httpsgithubcomgoogleapisgoogle-http-java-clientcomparev1414v1415-2022-03-21)

[Compare Source](https://togithub.com/googleapis/google-http-java-client/compare/v1.41.4...v1.41.5)

</details>

---

### Configuration

📅 **Schedule**: At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox.

---

This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/google-auth-library-java).

* feat: Add ability to provide PrivateKey as Pkcs8 encoded string googleapis#883 (googleapis#889)

* feat: Add ability to provide PrivateKey as Pkcs8 encoded string googleapis#883

This change adds a new method `setPrivateKeyString` in `ServiceAccountCredentials.Builder` to accept Pkcs8 encoded string representation of private keys.

Co-authored-by: Timur Sadykov <stim@google.com>

* chore: fix downstream check (googleapis#898)

* fix: update branding in ExternalAccountCredentials (googleapis#893)

These changes align the Javadoc comments with the branding that Google uses externally:

+ STS -> Security Token Service
+ GCP -> Google Cloud
+ Remove references to a Google-internal token type

Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
- [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/google-auth-library-java/issues/new/choose) before writing your code!  That way we can discuss the change, evaluate designs, and agree on the general idea
- [ ] Ensure the tests and linter pass: Tests are failing, but I don't think that was caused by the changes in this PR
- [ ] Code coverage does not decrease (if any source code was changed): n/a
- [ ] Appropriate docs were updated (if necessary): n/a

* feat: Adds the ExecutableHandler interface for Pluggable Auth

* feat: Adds a Pluggable Auth specific exception

* feat: Adds new PluggableAuthCredentials class that plug into ADC

* feat: Adds unit tests for PluggableAuthCredentials and ExternalAccountCredentials

* Add units tests for GoogleCredentials

* fix: update javadoc/comments

* fix: A concrete ExecutableOptions implementation is not needed

* review: javadoc changes + constants

Co-authored-by: WhiteSource Renovate <bot@renovateapp.com>
Co-authored-by: Navina Ramesh <navi.trinity@gmail.com>
Co-authored-by: Timur Sadykov <stim@google.com>
Co-authored-by: Neenu Shaji <Neenu1995@users.noreply.github.com>
Co-authored-by: Jeff Williams <jeffrey.l.williams@gmail.com>

* feat: finalizes PluggableAuth implementation (googleapis#906)

* Adds ExecutableResponse class

* Adds unit tests for ExecutableResponse

* Adds 3rd party executable handler

* Adds unit tests for PluggableAuthHandler

* Fix build issues

* don't fail on javadoc errors

* feat: Improve Pluggable Auth error handling (googleapis#912)

* feat: improves pluggable auth error handling

* cleanup

* fix: consume input stream immediately for Pluggable Auth (googleapis#915)

* feat: improves pluggable auth error handling

* cleanup

* fix: consume input stream immediately so that the spawned process will not hang if the STDOUT buffer is filled.

* fix: fix merge

* fix: review comments

* fix: refactor to keep ImpersonatedCredentials final (googleapis#917)

* fix: adds more documentation for InternalProcessBuilder and moves it to the bottom of the file

* fix: keep ImpersonatedCredentials final

* fix: make sure executor is shutdown

Co-authored-by: WhiteSource Renovate <bot@renovateapp.com>
Co-authored-by: Navina Ramesh <navi.trinity@gmail.com>
Co-authored-by: Timur Sadykov <stim@google.com>
Co-authored-by: Neenu Shaji <Neenu1995@users.noreply.github.com>
Co-authored-by: Jeff Williams <jeffrey.l.williams@gmail.com>
Co-authored-by: Emily Ball <emilyball@google.com>
  • Loading branch information
7 people committed Jun 24, 2022
1 parent f269756 commit c3e8d16
Show file tree
Hide file tree
Showing 15 changed files with 2,779 additions and 7 deletions.
67 changes: 67 additions & 0 deletions oauth2_http/java/com/google/auth/oauth2/ExecutableHandler.java
@@ -0,0 +1,67 @@
/*
* Copyright 2022 Google LLC
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Google LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package com.google.auth.oauth2;

import java.io.IOException;
import java.util.Map;
import javax.annotation.Nullable;

/** An interface for 3rd party executable handling. */
interface ExecutableHandler {

/** An interface for required fields needed to call 3rd party executables. */
interface ExecutableOptions {

/** An absolute path to the command used to retrieve 3rd party tokens. */
String getExecutableCommand();

/** A set of process-local environment variable mappings to be set for the script to execute. */
Map<String, String> getEnvironmentMap();

/** A timeout for waiting for the executable to finish, in milliseconds. */
int getExecutableTimeoutMs();

/**
* An output file path which points to the 3rd party credentials generated by the executable.
*/
@Nullable
String getOutputFilePath();
}

/**
* Handles executing the 3rd party script and parsing the token from the response.
*
* @param options A set executable options for handling the executable.
* @return A 3rd party token.
*/
String retrieveTokenFromExecutable(ExecutableOptions options) throws IOException;
}
206 changes: 206 additions & 0 deletions oauth2_http/java/com/google/auth/oauth2/ExecutableResponse.java
@@ -0,0 +1,206 @@
/*
* Copyright 2022 Google LLC
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Google LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package com.google.auth.oauth2;

import com.google.api.client.json.GenericJson;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.Instant;
import javax.annotation.Nullable;

/**
* Encapsulates response values for the 3rd party executable response (e.g. OIDC, SAML, error
* responses).
*/
class ExecutableResponse {

private static final String SAML_SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:saml2";

private final int version;
private final boolean success;

@Nullable private Long expirationTime;
@Nullable private String tokenType;
@Nullable private String subjectToken;
@Nullable private String errorCode;
@Nullable private String errorMessage;

ExecutableResponse(GenericJson json) throws IOException {
if (!json.containsKey("version")) {
throw new PluggableAuthException(
"INVALID_EXECUTABLE_RESPONSE", "The executable response is missing the `version` field.");
}

if (!json.containsKey("success")) {
throw new PluggableAuthException(
"INVALID_EXECUTABLE_RESPONSE", "The executable response is missing the `success` field.");
}

this.version = parseIntField(json.get("version"));
this.success = (boolean) json.get("success");

if (success) {
if (!json.containsKey("token_type")) {
throw new PluggableAuthException(
"INVALID_EXECUTABLE_RESPONSE",
"The executable response is missing the `token_type` field.");
}

if (!json.containsKey("expiration_time")) {
throw new PluggableAuthException(
"INVALID_EXECUTABLE_RESPONSE",
"The executable response is missing the `expiration_time` field.");
}

this.tokenType = (String) json.get("token_type");
this.expirationTime = parseLongField(json.get("expiration_time"));

if (SAML_SUBJECT_TOKEN_TYPE.equals(tokenType)) {
this.subjectToken = (String) json.get("saml_response");
} else {
this.subjectToken = (String) json.get("id_token");
}
if (subjectToken == null || subjectToken.isEmpty()) {
throw new PluggableAuthException(
"INVALID_EXECUTABLE_RESPONSE",
"The executable response does not contain a valid token.");
}
} else {
// Error response must contain both an error code and message.
this.errorCode = (String) json.get("code");
this.errorMessage = (String) json.get("message");
if (errorCode == null
|| errorCode.isEmpty()
|| errorMessage == null
|| errorMessage.isEmpty()) {
throw new PluggableAuthException(
"INVALID_EXECUTABLE_RESPONSE",
"The executable response must contain `error` and `message` fields when unsuccessful.");
}
}
}

/**
* Returns the version of the executable output. Only version `1` is currently supported. This is
* useful for future changes to the expected output format.
*
* @return The version of the JSON output.
*/
int getVersion() {
return this.version;
}

/**
* Returns the status of the response.
*
* <p>When this is true, the response will contain the 3rd party token for a sign in / refresh
* operation. When this is false, the response should contain an additional error code and
* message.
*
* @return Whether the `success` field in the executable response is true.
*/
boolean isSuccessful() {
return this.success;
}

/** Returns true if the subject token is expired or not present, false otherwise. */
boolean isExpired() {
return this.expirationTime == null || this.expirationTime <= Instant.now().getEpochSecond();
}

/** Returns whether the execution was successful and returned an unexpired token. */
boolean isValid() {
return isSuccessful() && !isExpired();
}

/** Returns the subject token expiration time in seconds (Unix epoch time). */
@Nullable
Long getExpirationTime() {
return this.expirationTime;
}

/**
* Returns the 3rd party subject token type.
*
* <p>Possible valid values:
*
* <ul>
* <li>urn:ietf:params:oauth:token-type:id_token
* <li>urn:ietf:params:oauth:token-type:jwt
* <li>urn:ietf:params:oauth:token-type:saml2
* </ul>
*
* @return The 3rd party subject token type for success responses, null otherwise.
*/
@Nullable
String getTokenType() {
return this.tokenType;
}

/** Returns the subject token if the execution was successful, null otherwise. */
@Nullable
String getSubjectToken() {
return this.subjectToken;
}

/** Returns the error code if the execution was unsuccessful, null otherwise. */
@Nullable
String getErrorCode() {
return this.errorCode;
}

/** Returns the error message if the execution was unsuccessful, null otherwise. */
@Nullable
String getErrorMessage() {
return this.errorMessage;
}

private static int parseIntField(Object field) {
if (field instanceof String) {
return Integer.parseInt((String) field);
}
if (field instanceof BigDecimal) {
return ((BigDecimal) field).intValue();
}
return (int) field;
}

private static long parseLongField(Object field) {
if (field instanceof String) {
return Long.parseLong((String) field);
}
if (field instanceof BigDecimal) {
return ((BigDecimal) field).longValue();
}
return (long) field;
}
}

0 comments on commit c3e8d16

Please sign in to comment.