/* * Copyright 2015, Google Inc. All rights reserved. * * 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 Inc. 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.http.LowLevelHttpRequest; import com.google.api.client.http.LowLevelHttpResponse; import com.google.api.client.json.GenericJson; import com.google.api.client.json.Json; import com.google.api.client.testing.http.MockHttpTransport; import com.google.api.client.testing.http.MockLowLevelHttpRequest; import com.google.api.client.testing.http.MockLowLevelHttpResponse; import com.google.common.io.BaseEncoding; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; /** Transport that simulates the GCE metadata server for access tokens. */ public class MockMetadataServerTransport extends MockHttpTransport { private String accessToken; private Integer requestStatusCode; private String serviceAccountEmail; private String idToken; private byte[] signature; public MockMetadataServerTransport() {} public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public void setRequestStatusCode(Integer requestStatusCode) { this.requestStatusCode = requestStatusCode; } public void setServiceAccountEmail(String serviceAccountEmail) { this.serviceAccountEmail = serviceAccountEmail; } public void setSignature(byte[] signature) { this.signature = signature; } public void setIdToken(String idToken) { this.idToken = idToken; } @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { if (url.equals(ComputeEngineCredentials.getTokenServerEncodedUrl())) { return getMockRequestForTokenEndpoint(url); } else if (isGetServiceAccountsUrl(url)) { return getMockRequestForServiceAccount(url); } else if (isSignRequestUrl(url)) { return getMockRequestForSign(url); } else if (isIdentityDocumentUrl(url)) { return getMockRequestForIdentityDocument(url); } return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() { if (requestStatusCode != null) { return new MockLowLevelHttpResponse() .setStatusCode(requestStatusCode) .setContent("Metadata Error"); } MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); response.addHeader("Metadata-Flavor", "Google"); return response; } }; } private MockLowLevelHttpRequest getMockRequestForSign(String url) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { // Create the JSON response GenericJson signContents = new GenericJson(); signContents.setFactory(OAuth2Utils.JSON_FACTORY); signContents.put("signedBlob", BaseEncoding.base64().encode(signature)); String signature = signContents.toPrettyString(); return new MockLowLevelHttpResponse().setContentType(Json.MEDIA_TYPE).setContent(signature); } }; } private MockLowLevelHttpRequest getMockRequestForServiceAccount(String url) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { // Create the JSON response GenericJson serviceAccountsContents = new GenericJson(); serviceAccountsContents.setFactory(OAuth2Utils.JSON_FACTORY); GenericJson defaultAccount = new GenericJson(); defaultAccount.put("email", serviceAccountEmail); serviceAccountsContents.put("default", defaultAccount); String serviceAccounts = serviceAccountsContents.toPrettyString(); return new MockLowLevelHttpResponse() .setContentType(Json.MEDIA_TYPE) .setContent(serviceAccounts); } }; } private MockLowLevelHttpRequest getMockRequestForTokenEndpoint(String url) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { if (requestStatusCode != null) { return new MockLowLevelHttpResponse() .setStatusCode(requestStatusCode) .setContent("Token Fetch Error"); } String metadataRequestHeader = getFirstHeaderValue("Metadata-Flavor"); if (!"Google".equals(metadataRequestHeader)) { throw new IOException("Metadata request header not found."); } // Create the JSON response GenericJson refreshContents = new GenericJson(); refreshContents.setFactory(OAuth2Utils.JSON_FACTORY); refreshContents.put("access_token", accessToken); refreshContents.put("expires_in", 3600000); refreshContents.put("token_type", "Bearer"); String refreshText = refreshContents.toPrettyString(); return new MockLowLevelHttpResponse() .setContentType(Json.MEDIA_TYPE) .setContent(refreshText); } }; } private MockLowLevelHttpRequest getMockRequestForIdentityDocument(String url) throws MalformedURLException, UnsupportedEncodingException { if (idToken != null) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse().setContent(idToken); } }; } // https://cloud.google.com/compute/docs/instances/verifying-instance-identity#token_format Map queryPairs = new HashMap(); String query = (new URL(url)).getQuery(); String[] pairs = query.split("&"); for (String pair : pairs) { int idx = pair.indexOf("="); queryPairs.put( URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); } if (queryPairs.containsKey("format")) { if (((String) queryPairs.get("format")).equals("full")) { // return license only if format=full is set if (queryPairs.containsKey("license")) { if (((String) queryPairs.get("license")).equals("TRUE")) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setContent(ComputeEngineCredentialsTest.FULL_ID_TOKEN_WITH_LICENSE); } }; } } // otherwise return full format return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setContent(ComputeEngineCredentialsTest.FULL_ID_TOKEN); } }; } } // Return default format if nothing is set return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setContent(ComputeEngineCredentialsTest.STANDARD_ID_TOKEN); } }; } protected boolean isGetServiceAccountsUrl(String url) { return url.equals(ComputeEngineCredentials.getServiceAccountsUrl()); } protected boolean isSignRequestUrl(String url) { return serviceAccountEmail != null && url.equals( String.format(ComputeEngineCredentials.SIGN_BLOB_URL_FORMAT, serviceAccountEmail)); } protected boolean isIdentityDocumentUrl(String url) { return url.startsWith(String.format(ComputeEngineCredentials.getIdentityDocumentUrl())); } }