Skip to content

Commit c3e8d16

Browse files
lsiracrenovate-botnavinaTimurSadykovNeenu1995
authored
feat: Adds Pluggable Auth support (WIF) (googleapis#908)
* 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://github.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://github.com/googleapis/google-http-java-client/blob/HEAD/CHANGELOG.md#&#8203;1415-httpsgithubcomgoogleapisgoogle-http-java-clientcomparev1414v1415-2022-03-21) [Compare Source](https://github.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 <[email protected]> * 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 <[email protected]> Co-authored-by: Navina Ramesh <[email protected]> Co-authored-by: Timur Sadykov <[email protected]> Co-authored-by: Neenu Shaji <[email protected]> Co-authored-by: Jeff Williams <[email protected]> * 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 <[email protected]> Co-authored-by: Navina Ramesh <[email protected]> Co-authored-by: Timur Sadykov <[email protected]> Co-authored-by: Neenu Shaji <[email protected]> Co-authored-by: Jeff Williams <[email protected]> Co-authored-by: Emily Ball <[email protected]>
1 parent f269756 commit c3e8d16

15 files changed

+2779
-7
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
*
15+
* * Neither the name of Google LLC nor the names of its
16+
* contributors may be used to endorse or promote products derived from
17+
* this software without specific prior written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
*/
31+
32+
package com.google.auth.oauth2;
33+
34+
import java.io.IOException;
35+
import java.util.Map;
36+
import javax.annotation.Nullable;
37+
38+
/** An interface for 3rd party executable handling. */
39+
interface ExecutableHandler {
40+
41+
/** An interface for required fields needed to call 3rd party executables. */
42+
interface ExecutableOptions {
43+
44+
/** An absolute path to the command used to retrieve 3rd party tokens. */
45+
String getExecutableCommand();
46+
47+
/** A set of process-local environment variable mappings to be set for the script to execute. */
48+
Map<String, String> getEnvironmentMap();
49+
50+
/** A timeout for waiting for the executable to finish, in milliseconds. */
51+
int getExecutableTimeoutMs();
52+
53+
/**
54+
* An output file path which points to the 3rd party credentials generated by the executable.
55+
*/
56+
@Nullable
57+
String getOutputFilePath();
58+
}
59+
60+
/**
61+
* Handles executing the 3rd party script and parsing the token from the response.
62+
*
63+
* @param options A set executable options for handling the executable.
64+
* @return A 3rd party token.
65+
*/
66+
String retrieveTokenFromExecutable(ExecutableOptions options) throws IOException;
67+
}
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
*
15+
* * Neither the name of Google LLC nor the names of its
16+
* contributors may be used to endorse or promote products derived from
17+
* this software without specific prior written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
*/
31+
32+
package com.google.auth.oauth2;
33+
34+
import com.google.api.client.json.GenericJson;
35+
import java.io.IOException;
36+
import java.math.BigDecimal;
37+
import java.time.Instant;
38+
import javax.annotation.Nullable;
39+
40+
/**
41+
* Encapsulates response values for the 3rd party executable response (e.g. OIDC, SAML, error
42+
* responses).
43+
*/
44+
class ExecutableResponse {
45+
46+
private static final String SAML_SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:saml2";
47+
48+
private final int version;
49+
private final boolean success;
50+
51+
@Nullable private Long expirationTime;
52+
@Nullable private String tokenType;
53+
@Nullable private String subjectToken;
54+
@Nullable private String errorCode;
55+
@Nullable private String errorMessage;
56+
57+
ExecutableResponse(GenericJson json) throws IOException {
58+
if (!json.containsKey("version")) {
59+
throw new PluggableAuthException(
60+
"INVALID_EXECUTABLE_RESPONSE", "The executable response is missing the `version` field.");
61+
}
62+
63+
if (!json.containsKey("success")) {
64+
throw new PluggableAuthException(
65+
"INVALID_EXECUTABLE_RESPONSE", "The executable response is missing the `success` field.");
66+
}
67+
68+
this.version = parseIntField(json.get("version"));
69+
this.success = (boolean) json.get("success");
70+
71+
if (success) {
72+
if (!json.containsKey("token_type")) {
73+
throw new PluggableAuthException(
74+
"INVALID_EXECUTABLE_RESPONSE",
75+
"The executable response is missing the `token_type` field.");
76+
}
77+
78+
if (!json.containsKey("expiration_time")) {
79+
throw new PluggableAuthException(
80+
"INVALID_EXECUTABLE_RESPONSE",
81+
"The executable response is missing the `expiration_time` field.");
82+
}
83+
84+
this.tokenType = (String) json.get("token_type");
85+
this.expirationTime = parseLongField(json.get("expiration_time"));
86+
87+
if (SAML_SUBJECT_TOKEN_TYPE.equals(tokenType)) {
88+
this.subjectToken = (String) json.get("saml_response");
89+
} else {
90+
this.subjectToken = (String) json.get("id_token");
91+
}
92+
if (subjectToken == null || subjectToken.isEmpty()) {
93+
throw new PluggableAuthException(
94+
"INVALID_EXECUTABLE_RESPONSE",
95+
"The executable response does not contain a valid token.");
96+
}
97+
} else {
98+
// Error response must contain both an error code and message.
99+
this.errorCode = (String) json.get("code");
100+
this.errorMessage = (String) json.get("message");
101+
if (errorCode == null
102+
|| errorCode.isEmpty()
103+
|| errorMessage == null
104+
|| errorMessage.isEmpty()) {
105+
throw new PluggableAuthException(
106+
"INVALID_EXECUTABLE_RESPONSE",
107+
"The executable response must contain `error` and `message` fields when unsuccessful.");
108+
}
109+
}
110+
}
111+
112+
/**
113+
* Returns the version of the executable output. Only version `1` is currently supported. This is
114+
* useful for future changes to the expected output format.
115+
*
116+
* @return The version of the JSON output.
117+
*/
118+
int getVersion() {
119+
return this.version;
120+
}
121+
122+
/**
123+
* Returns the status of the response.
124+
*
125+
* <p>When this is true, the response will contain the 3rd party token for a sign in / refresh
126+
* operation. When this is false, the response should contain an additional error code and
127+
* message.
128+
*
129+
* @return Whether the `success` field in the executable response is true.
130+
*/
131+
boolean isSuccessful() {
132+
return this.success;
133+
}
134+
135+
/** Returns true if the subject token is expired or not present, false otherwise. */
136+
boolean isExpired() {
137+
return this.expirationTime == null || this.expirationTime <= Instant.now().getEpochSecond();
138+
}
139+
140+
/** Returns whether the execution was successful and returned an unexpired token. */
141+
boolean isValid() {
142+
return isSuccessful() && !isExpired();
143+
}
144+
145+
/** Returns the subject token expiration time in seconds (Unix epoch time). */
146+
@Nullable
147+
Long getExpirationTime() {
148+
return this.expirationTime;
149+
}
150+
151+
/**
152+
* Returns the 3rd party subject token type.
153+
*
154+
* <p>Possible valid values:
155+
*
156+
* <ul>
157+
* <li>urn:ietf:params:oauth:token-type:id_token
158+
* <li>urn:ietf:params:oauth:token-type:jwt
159+
* <li>urn:ietf:params:oauth:token-type:saml2
160+
* </ul>
161+
*
162+
* @return The 3rd party subject token type for success responses, null otherwise.
163+
*/
164+
@Nullable
165+
String getTokenType() {
166+
return this.tokenType;
167+
}
168+
169+
/** Returns the subject token if the execution was successful, null otherwise. */
170+
@Nullable
171+
String getSubjectToken() {
172+
return this.subjectToken;
173+
}
174+
175+
/** Returns the error code if the execution was unsuccessful, null otherwise. */
176+
@Nullable
177+
String getErrorCode() {
178+
return this.errorCode;
179+
}
180+
181+
/** Returns the error message if the execution was unsuccessful, null otherwise. */
182+
@Nullable
183+
String getErrorMessage() {
184+
return this.errorMessage;
185+
}
186+
187+
private static int parseIntField(Object field) {
188+
if (field instanceof String) {
189+
return Integer.parseInt((String) field);
190+
}
191+
if (field instanceof BigDecimal) {
192+
return ((BigDecimal) field).intValue();
193+
}
194+
return (int) field;
195+
}
196+
197+
private static long parseLongField(Object field) {
198+
if (field instanceof String) {
199+
return Long.parseLong((String) field);
200+
}
201+
if (field instanceof BigDecimal) {
202+
return ((BigDecimal) field).longValue();
203+
}
204+
return (long) field;
205+
}
206+
}

0 commit comments

Comments
 (0)