|
| 1 | +/* |
| 2 | + * Copyright 2021 Google LLC |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | + |
| 17 | +package com.google.cloud.auth.samples; |
| 18 | + |
| 19 | +import com.google.auth.oauth2.AccessToken; |
| 20 | +import com.google.auth.oauth2.CredentialAccessBoundary; |
| 21 | +import com.google.auth.oauth2.DownscopedCredentials; |
| 22 | +import com.google.auth.oauth2.GoogleCredentials; |
| 23 | +import com.google.auth.oauth2.OAuth2CredentialsWithRefresh; |
| 24 | +import com.google.cloud.storage.Blob; |
| 25 | +import com.google.cloud.storage.Storage; |
| 26 | +import com.google.cloud.storage.StorageOptions; |
| 27 | +import java.io.IOException; |
| 28 | + |
| 29 | +/** Demonstrates how to use Downscoping with Credential Access Boundaries. */ |
| 30 | +public class DownscopingExample { |
| 31 | + |
| 32 | + /** |
| 33 | + * Tests the downscoping functionality. |
| 34 | + * |
| 35 | + * <p>This will generate a downscoped token with readonly access to the specified GCS bucket, |
| 36 | + * inject them into a storage instance and then test print the contents of the specified object. |
| 37 | + */ |
| 38 | + public static void main(String[] args) throws IOException { |
| 39 | + // TODO(developer): Replace these variables before running the sample. |
| 40 | + // The Cloud Storage bucket name. |
| 41 | + String bucketName = "your-gcs-bucket-name"; |
| 42 | + // The Cloud Storage object name that resides in the specified bucket. |
| 43 | + String objectName = "your-gcs-object-name"; |
| 44 | + |
| 45 | + tokenConsumer(bucketName, objectName); |
| 46 | + } |
| 47 | + |
| 48 | + /** Simulates token broker generating downscoped tokens for specified bucket. */ |
| 49 | + // [START auth_downscoping_token_broker] |
| 50 | + public static AccessToken getTokenFromBroker(String bucketName, String objectPrefix) |
| 51 | + throws IOException { |
| 52 | + // Retrieve the source credentials from ADC. |
| 53 | + GoogleCredentials sourceCredentials = |
| 54 | + GoogleCredentials.getApplicationDefault() |
| 55 | + .createScoped("https://www.googleapis.com/auth/cloud-platform"); |
| 56 | + |
| 57 | + // [START auth_downscoping_rules] |
| 58 | + // Initialize the Credential Access Boundary rules. |
| 59 | + String availableResource = "//storage.googleapis.com/projects/_/buckets/" + bucketName; |
| 60 | + |
| 61 | + // Downscoped credentials will have readonly access to the resource. |
| 62 | + String availablePermission = "inRole:roles/storage.objectViewer"; |
| 63 | + |
| 64 | + // Only objects starting with the specified prefix string in the object name will be allowed |
| 65 | + // read access. |
| 66 | + String expression = |
| 67 | + "resource.name.startsWith('projects/_/buckets/" |
| 68 | + + bucketName |
| 69 | + + "/objects/" |
| 70 | + + objectPrefix |
| 71 | + + "')"; |
| 72 | + |
| 73 | + // Build the AvailabilityCondition. |
| 74 | + CredentialAccessBoundary.AccessBoundaryRule.AvailabilityCondition availabilityCondition = |
| 75 | + CredentialAccessBoundary.AccessBoundaryRule.AvailabilityCondition.newBuilder() |
| 76 | + .setExpression(expression) |
| 77 | + .build(); |
| 78 | + |
| 79 | + // Define the single access boundary rule using the above properties. |
| 80 | + CredentialAccessBoundary.AccessBoundaryRule rule = |
| 81 | + CredentialAccessBoundary.AccessBoundaryRule.newBuilder() |
| 82 | + .setAvailableResource(availableResource) |
| 83 | + .addAvailablePermission(availablePermission) |
| 84 | + .setAvailabilityCondition(availabilityCondition) |
| 85 | + .build(); |
| 86 | + |
| 87 | + // Define the Credential Access Boundary with all the relevant rules. |
| 88 | + CredentialAccessBoundary credentialAccessBoundary = |
| 89 | + CredentialAccessBoundary.newBuilder().addRule(rule).build(); |
| 90 | + // [END auth_downscoping_rules] |
| 91 | + |
| 92 | + // [START auth_downscoping_initialize_downscoped_cred] |
| 93 | + // Create the downscoped credentials. |
| 94 | + DownscopedCredentials downscopedCredentials = |
| 95 | + DownscopedCredentials.newBuilder() |
| 96 | + .setSourceCredential(sourceCredentials) |
| 97 | + .setCredentialAccessBoundary(credentialAccessBoundary) |
| 98 | + .build(); |
| 99 | + |
| 100 | + // Retrieve the token. |
| 101 | + // This will need to be passed to the Token Consumer. |
| 102 | + AccessToken accessToken = downscopedCredentials.refreshAccessToken(); |
| 103 | + // [END auth_downscoping_initialize_downscoped_cred] |
| 104 | + return accessToken; |
| 105 | + } |
| 106 | + // [END auth_downscoping_token_broker] |
| 107 | + |
| 108 | + /** Simulates token consumer readonly access to the specified object. */ |
| 109 | + // [START auth_downscoping_token_consumer] |
| 110 | + public static void tokenConsumer(final String bucketName, final String objectName) |
| 111 | + throws IOException { |
| 112 | + // You can pass an `OAuth2RefreshHandler` to `OAuth2CredentialsWithRefresh` which will allow the |
| 113 | + // library to seamlessly handle downscoped token refreshes on expiration. |
| 114 | + OAuth2CredentialsWithRefresh.OAuth2RefreshHandler handler = |
| 115 | + new OAuth2CredentialsWithRefresh.OAuth2RefreshHandler() { |
| 116 | + @Override |
| 117 | + public AccessToken refreshAccessToken() throws IOException { |
| 118 | + // The common pattern of usage is to have a token broker pass the downscoped short-lived |
| 119 | + // access tokens to a token consumer via some secure authenticated channel. |
| 120 | + // For illustration purposes, we are generating the downscoped token locally. |
| 121 | + // We want to test the ability to limit access to objects with a certain prefix string |
| 122 | + // in the resource bucket. objectName.substring(0, 3) is the prefix here. This field is |
| 123 | + // not required if access to all bucket resources are allowed. If access to limited |
| 124 | + // resources in the bucket is needed, this mechanism can be used. |
| 125 | + return getTokenFromBroker(bucketName, objectName.substring(0, 3)); |
| 126 | + } |
| 127 | + }; |
| 128 | + |
| 129 | + // Downscoped token retrieved from token broker. |
| 130 | + AccessToken downscopedToken = handler.refreshAccessToken(); |
| 131 | + |
| 132 | + // Create the OAuth2CredentialsWithRefresh from the downscoped token and pass a refresh handler |
| 133 | + // which will handle token expiration. |
| 134 | + // This will allow the consumer to seamlessly obtain new downscoped tokens on demand every time |
| 135 | + // token expires. |
| 136 | + OAuth2CredentialsWithRefresh credentials = |
| 137 | + OAuth2CredentialsWithRefresh.newBuilder() |
| 138 | + .setAccessToken(downscopedToken) |
| 139 | + .setRefreshHandler(handler) |
| 140 | + .build(); |
| 141 | + |
| 142 | + // Use the credentials with the Cloud Storage SDK. |
| 143 | + StorageOptions options = StorageOptions.newBuilder().setCredentials(credentials).build(); |
| 144 | + Storage storage = options.getService(); |
| 145 | + |
| 146 | + // Call Cloud Storage APIs. |
| 147 | + Blob blob = storage.get(bucketName, objectName); |
| 148 | + String content = new String(blob.getContent()); |
| 149 | + System.out.println( |
| 150 | + "Retrieved object, " |
| 151 | + + objectName |
| 152 | + + ", from bucket," |
| 153 | + + bucketName |
| 154 | + + ", with content: " |
| 155 | + + content); |
| 156 | + } |
| 157 | + // [END auth_downscoping_token_consumer] |
| 158 | +} |
0 commit comments