1 /* 2 * Copyright 2018, Google Inc. All rights reserved. 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 Inc. 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.http.HttpStatusCodes; 35 import com.google.api.client.http.LowLevelHttpRequest; 36 import com.google.api.client.http.LowLevelHttpResponse; 37 import com.google.api.client.json.GenericJson; 38 import com.google.api.client.json.Json; 39 import com.google.api.client.testing.http.MockHttpTransport; 40 import com.google.api.client.testing.http.MockLowLevelHttpRequest; 41 import com.google.api.client.testing.http.MockLowLevelHttpResponse; 42 import com.google.auth.TestUtils; 43 import com.google.common.io.BaseEncoding; 44 import java.io.IOException; 45 46 /** Transport that simulates the IAMCredentials server for access tokens. */ 47 public class MockIAMCredentialsServiceTransport extends MockHttpTransport { 48 49 private static final String DEFAULT_IAM_ACCESS_TOKEN_ENDPOINT = 50 "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken"; 51 private static final String IAM_ID_TOKEN_ENDPOINT = 52 "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateIdToken"; 53 private static final String IAM_SIGN_ENDPOINT = 54 "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:signBlob"; 55 private Integer tokenResponseErrorCode; 56 private String tokenResponseErrorContent; 57 private String targetPrincipal; 58 private byte[] signedBlob; 59 private int responseCode = HttpStatusCodes.STATUS_CODE_OK; 60 private String errorMessage; 61 private String iamAccessTokenEndpoint; 62 63 private String accessToken; 64 private String expireTime; 65 66 private String idToken; 67 68 private MockLowLevelHttpRequest request; 69 MockIAMCredentialsServiceTransport()70 public MockIAMCredentialsServiceTransport() {} 71 setTokenResponseErrorCode(Integer tokenResponseErrorCode)72 public void setTokenResponseErrorCode(Integer tokenResponseErrorCode) { 73 this.tokenResponseErrorCode = tokenResponseErrorCode; 74 } 75 setTokenResponseErrorContent(String tokenResponseErrorContent)76 public void setTokenResponseErrorContent(String tokenResponseErrorContent) { 77 this.tokenResponseErrorContent = tokenResponseErrorContent; 78 } 79 setTargetPrincipal(String targetPrincipal)80 public void setTargetPrincipal(String targetPrincipal) { 81 this.targetPrincipal = targetPrincipal; 82 } 83 setAccessToken(String accessToken)84 public void setAccessToken(String accessToken) { 85 this.accessToken = accessToken; 86 } 87 setExpireTime(String expireTime)88 public void setExpireTime(String expireTime) { 89 this.expireTime = expireTime; 90 } 91 setSignedBlob(byte[] signedBlob)92 public void setSignedBlob(byte[] signedBlob) { 93 this.signedBlob = signedBlob; 94 } 95 setErrorResponseCodeAndMessage(int responseCode, String errorMessage)96 public void setErrorResponseCodeAndMessage(int responseCode, String errorMessage) { 97 this.responseCode = responseCode; 98 this.errorMessage = errorMessage; 99 } 100 setIdToken(String idToken)101 public void setIdToken(String idToken) { 102 this.idToken = idToken; 103 } 104 setAccessTokenEndpoint(String accessTokenEndpoint)105 public void setAccessTokenEndpoint(String accessTokenEndpoint) { 106 this.iamAccessTokenEndpoint = accessTokenEndpoint; 107 } 108 getRequest()109 public MockLowLevelHttpRequest getRequest() { 110 return request; 111 } 112 113 @Override buildRequest(String method, String url)114 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 115 116 String iamAccesssTokenformattedUrl = 117 iamAccessTokenEndpoint != null 118 ? iamAccessTokenEndpoint 119 : String.format(DEFAULT_IAM_ACCESS_TOKEN_ENDPOINT, this.targetPrincipal); 120 String iamSignBlobformattedUrl = String.format(IAM_SIGN_ENDPOINT, this.targetPrincipal); 121 String iamIdTokenformattedUrl = String.format(IAM_ID_TOKEN_ENDPOINT, this.targetPrincipal); 122 if (url.equals(iamAccesssTokenformattedUrl)) { 123 this.request = 124 new MockLowLevelHttpRequest(url) { 125 @Override 126 public LowLevelHttpResponse execute() throws IOException { 127 128 if (tokenResponseErrorCode != null) { 129 return new MockLowLevelHttpResponse() 130 .setStatusCode(tokenResponseErrorCode) 131 .setContentType(Json.MEDIA_TYPE) 132 .setContent(tokenResponseErrorContent); 133 } 134 135 // Create the JSON response 136 GenericJson refreshContents = new GenericJson(); 137 refreshContents.setFactory(OAuth2Utils.JSON_FACTORY); 138 refreshContents.put("accessToken", accessToken); 139 refreshContents.put("expireTime", expireTime); 140 String refreshText = refreshContents.toPrettyString(); 141 return new MockLowLevelHttpResponse() 142 .setContentType(Json.MEDIA_TYPE) 143 .setContent(refreshText); 144 } 145 }; 146 } else if (url.equals(iamSignBlobformattedUrl) 147 && responseCode != HttpStatusCodes.STATUS_CODE_OK) { 148 this.request = 149 new MockLowLevelHttpRequest(url) { 150 @Override 151 public LowLevelHttpResponse execute() throws IOException { 152 153 if (tokenResponseErrorCode != null) { 154 return new MockLowLevelHttpResponse() 155 .setStatusCode(tokenResponseErrorCode) 156 .setContentType(Json.MEDIA_TYPE) 157 .setContent(tokenResponseErrorContent); 158 } 159 160 BaseEncoding base64 = BaseEncoding.base64(); 161 GenericJson refreshContents = new GenericJson(); 162 refreshContents.setFactory(OAuth2Utils.JSON_FACTORY); 163 refreshContents.put("signedBlob", base64.encode(signedBlob)); 164 return new MockLowLevelHttpResponse() 165 .setStatusCode(responseCode) 166 .setContent(TestUtils.errorJson(errorMessage)); 167 } 168 }; 169 } else if (url.equals(iamSignBlobformattedUrl)) { 170 this.request = 171 new MockLowLevelHttpRequest(url) { 172 @Override 173 public LowLevelHttpResponse execute() throws IOException { 174 175 if (tokenResponseErrorCode != null) { 176 return new MockLowLevelHttpResponse() 177 .setStatusCode(tokenResponseErrorCode) 178 .setContentType(Json.MEDIA_TYPE) 179 .setContent(tokenResponseErrorContent); 180 } 181 182 BaseEncoding base64 = BaseEncoding.base64(); 183 GenericJson refreshContents = new GenericJson(); 184 refreshContents.setFactory(OAuth2Utils.JSON_FACTORY); 185 refreshContents.put("signedBlob", base64.encode(signedBlob)); 186 String refreshText = refreshContents.toPrettyString(); 187 return new MockLowLevelHttpResponse() 188 .setContentType(Json.MEDIA_TYPE) 189 .setContent(refreshText); 190 } 191 }; 192 } else if (url.equals(iamIdTokenformattedUrl)) { 193 this.request = 194 new MockLowLevelHttpRequest(url) { 195 @Override 196 public LowLevelHttpResponse execute() throws IOException { 197 198 if (responseCode != HttpStatusCodes.STATUS_CODE_OK) { 199 return new MockLowLevelHttpResponse() 200 .setStatusCode(responseCode) 201 .setContentType(Json.MEDIA_TYPE) 202 .setContent(errorMessage); 203 } 204 205 GenericJson refreshContents = new GenericJson(); 206 refreshContents.setFactory(OAuth2Utils.JSON_FACTORY); 207 refreshContents.put("token", idToken); 208 String tokenContent = refreshContents.toPrettyString(); 209 return new MockLowLevelHttpResponse() 210 .setContentType(Json.MEDIA_TYPE) 211 .setContent(tokenContent); 212 } 213 }; 214 } else { 215 return super.buildRequest(method, url); 216 } 217 218 return this.request; 219 } 220 } 221