Skip to content

Commit 7621caa

Browse files
committed
feat: Add integration test for the client side cab
1 parent 2c7f7a2 commit 7621caa

File tree

2 files changed

+154
-1
lines changed

2 files changed

+154
-1
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright 2025, 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.credentialaccessboundary;
33+
34+
import static org.junit.Assert.assertEquals;
35+
import static org.junit.Assert.assertThrows;
36+
import static org.junit.Assert.assertTrue;
37+
38+
import com.google.api.client.http.GenericUrl;
39+
import com.google.api.client.http.HttpRequest;
40+
import com.google.api.client.http.HttpRequestFactory;
41+
import com.google.api.client.http.HttpResponse;
42+
import com.google.api.client.http.HttpResponseException;
43+
import com.google.api.client.http.javanet.NetHttpTransport;
44+
import com.google.api.client.json.JsonObjectParser;
45+
import com.google.api.client.json.gson.GsonFactory;
46+
import com.google.auth.Credentials;
47+
import com.google.auth.http.HttpCredentialsAdapter;
48+
import com.google.auth.oauth2.CredentialAccessBoundary;
49+
import com.google.auth.oauth2.GoogleCredentials;
50+
import com.google.auth.oauth2.OAuth2CredentialsWithRefresh;
51+
import com.google.auth.oauth2.ServiceAccountCredentials;
52+
import dev.cel.common.CelValidationException;
53+
import java.io.IOException;
54+
import java.security.GeneralSecurityException;
55+
import org.junit.Test;
56+
57+
/**
58+
* Integration tests for the Client Side Credential Access Boundary Factory.
59+
*
60+
* <p>The only requirements for this test suite to run is to set the environment variable
61+
* GOOGLE_APPLICATION_CREDENTIALS to point to the same service account configured in the setup
62+
* script (downscoping-with-cab-setup.sh).
63+
*/
64+
public final class ITClientSideCredentialAccessBoundaryTest {
65+
66+
// Output copied from the setup script (downscoping-with-cab-setup.sh).
67+
private static final String GCS_BUCKET_NAME = "cab-int-bucket-cbi3qrv5";
68+
private static final String GCS_OBJECT_NAME_WITH_PERMISSION = "cab-first-cbi3qrv5.txt";
69+
private static final String GCS_OBJECT_NAME_WITHOUT_PERMISSION = "cab-second-cbi3qrv5.txt";
70+
71+
// This Credential Access Boundary enables the objectViewer permission to the specified object in
72+
// the specified bucket.
73+
private static final CredentialAccessBoundary CREDENTIAL_ACCESS_BOUNDARY =
74+
CredentialAccessBoundary.newBuilder()
75+
.addRule(
76+
CredentialAccessBoundary.AccessBoundaryRule.newBuilder()
77+
.setAvailableResource(
78+
String.format(
79+
"//storage.googleapis.com/projects/_/buckets/%s", GCS_BUCKET_NAME))
80+
.addAvailablePermission("inRole:roles/storage.objectViewer")
81+
.setAvailabilityCondition(
82+
CredentialAccessBoundary.AccessBoundaryRule.AvailabilityCondition.newBuilder()
83+
.setExpression(
84+
String.format(
85+
"resource.name.startsWith('projects/_/buckets/%s/objects/%s')",
86+
GCS_BUCKET_NAME, GCS_OBJECT_NAME_WITH_PERMISSION))
87+
.build())
88+
.build())
89+
.build();
90+
91+
/**
92+
* A downscoped credential is obtained using ClientSideCredentialAccessBoundaryFactory with
93+
* permissions to access an object in the GCS bucket configured. We should only have access to
94+
* retrieve this object.
95+
*
96+
* <p>We confirm this by: 1. Validating that we can successfully retrieve this object with the
97+
* downscoped token. 2. Validating that we do not have permission to retrieve a different object
98+
* in the same bucket.
99+
*/
100+
@Test
101+
public void clientSideCredentialAccessBoundary_serviceAccountSource() throws IOException {
102+
OAuth2CredentialsWithRefresh.OAuth2RefreshHandler refreshHandler =
103+
() -> {
104+
ServiceAccountCredentials sourceCredentials =
105+
(ServiceAccountCredentials)
106+
GoogleCredentials.getApplicationDefault()
107+
.createScoped("https://www.googleapis.com/auth/cloud-platform");
108+
109+
ClientSideCredentialAccessBoundaryFactory factory =
110+
ClientSideCredentialAccessBoundaryFactory.newBuilder()
111+
.setSourceCredential(sourceCredentials)
112+
.build();
113+
114+
try {
115+
return factory.generateToken(CREDENTIAL_ACCESS_BOUNDARY);
116+
} catch (CelValidationException | GeneralSecurityException e) {
117+
throw new RuntimeException(e);
118+
}
119+
};
120+
121+
OAuth2CredentialsWithRefresh credentials =
122+
OAuth2CredentialsWithRefresh.newBuilder().setRefreshHandler(refreshHandler).build();
123+
124+
// Attempt to retrieve the object that the downscoped token has access to.
125+
retrieveObjectFromGcs(credentials, GCS_OBJECT_NAME_WITH_PERMISSION);
126+
127+
// Attempt to retrieve the object that the downscoped token does not have access to. This should
128+
// fail.
129+
HttpResponseException exception =
130+
assertThrows(
131+
HttpResponseException.class,
132+
() -> retrieveObjectFromGcs(credentials, GCS_OBJECT_NAME_WITHOUT_PERMISSION));
133+
assertEquals(403, exception.getStatusCode());
134+
}
135+
136+
private void retrieveObjectFromGcs(Credentials credentials, String objectName)
137+
throws IOException {
138+
String url =
139+
String.format(
140+
"https://storage.googleapis.com/storage/v1/b/%s/o/%s", GCS_BUCKET_NAME, objectName);
141+
142+
HttpCredentialsAdapter credentialsAdapter = new HttpCredentialsAdapter(credentials);
143+
HttpRequestFactory requestFactory =
144+
new NetHttpTransport().createRequestFactory(credentialsAdapter);
145+
HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(url));
146+
147+
JsonObjectParser parser = new JsonObjectParser(GsonFactory.getDefaultInstance());
148+
request.setParser(parser);
149+
150+
HttpResponse response = request.execute();
151+
assertTrue(response.isSuccessStatusCode());
152+
}
153+
}

cab-token-generator/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>com.google.auth</groupId>
88
<artifactId>google-auth-library-parent</artifactId>
9-
<version>1.30.2-SNAPSHOT
9+
<version>1.31.0
1010
</version><!-- {x-version-update:google-auth-library-parent:current} -->
1111
</parent>
1212

0 commit comments

Comments
 (0)