1 /* 2 * Copyright 2016, 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 static org.junit.Assert.assertArrayEquals; 35 import static org.junit.Assert.assertEquals; 36 import static org.junit.Assert.assertFalse; 37 import static org.junit.Assert.assertNotSame; 38 import static org.junit.Assert.assertTrue; 39 import static org.junit.Assert.fail; 40 41 import com.google.common.collect.ImmutableMap; 42 import java.io.IOException; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.Collection; 46 import java.util.Collections; 47 import java.util.Date; 48 import java.util.List; 49 import java.util.Map; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.junit.runners.JUnit4; 53 54 @RunWith(JUnit4.class) 55 public class AppEngineCredentialsTest extends BaseSerializationTest { 56 57 private static final String EXPECTED_ACCESS_TOKEN = "ExpectedAccessToken"; 58 private static final Date EXPECTED_EXPIRATION_DATE = 59 new Date(System.currentTimeMillis() + 60L * 60L * 100L); 60 private static final byte[] EXPECTED_SIGNATURE = {0xD, 0xE, 0xA, 0xD}; 61 private static final String EXPECTED_ACCOUNT = "serviceAccount"; 62 63 private static final Collection<String> SCOPES = 64 Collections.unmodifiableCollection(Arrays.asList("scope1", "scope2")); 65 private static final Collection<String> DEFAULT_SCOPES = 66 Collections.unmodifiableCollection(Arrays.asList("scope3")); 67 68 @Test constructor_usesAppIdentityService()69 public void constructor_usesAppIdentityService() throws IOException { 70 Collection<String> scopes = Collections.singleton("SomeScope"); 71 TestAppEngineCredentials credentials = new TestAppEngineCredentials(scopes); 72 List<String> forNameArgs = credentials.getForNameArgs(); 73 assertEquals(4, forNameArgs.size()); 74 assertEquals(AppEngineCredentials.APP_IDENTITY_SERVICE_FACTORY_CLASS, forNameArgs.get(0)); 75 assertEquals(AppEngineCredentials.APP_IDENTITY_SERVICE_CLASS, forNameArgs.get(1)); 76 assertEquals(AppEngineCredentials.GET_ACCESS_TOKEN_RESULT_CLASS, forNameArgs.get(2)); 77 assertEquals(AppEngineCredentials.SIGNING_RESULT_CLASS, forNameArgs.get(3)); 78 } 79 80 @Test constructor_noAppEngineRuntime_throwsHelpfulLoadError()81 public void constructor_noAppEngineRuntime_throwsHelpfulLoadError() throws IOException { 82 try { 83 new TestAppEngineCredentialsNoSdk(); 84 fail("Credential expected to fail to load if credential class not present."); 85 } catch (IOException e) { 86 String message = e.getMessage(); 87 assertTrue(message.contains("Check that the App Engine SDK is deployed.")); 88 assertTrue(e.getCause() instanceof ClassNotFoundException); 89 assertTrue( 90 e.getCause() 91 .getMessage() 92 .contains(AppEngineCredentials.APP_IDENTITY_SERVICE_FACTORY_CLASS)); 93 } 94 } 95 96 @Test refreshAccessToken_sameAs()97 public void refreshAccessToken_sameAs() throws IOException { 98 TestAppEngineCredentials credentials = new TestAppEngineCredentials(SCOPES); 99 AccessToken accessToken = credentials.refreshAccessToken(); 100 assertEquals(EXPECTED_ACCESS_TOKEN, accessToken.getTokenValue()); 101 assertEquals(EXPECTED_EXPIRATION_DATE, accessToken.getExpirationTime()); 102 } 103 104 @Test getAccount_sameAs()105 public void getAccount_sameAs() throws IOException { 106 TestAppEngineCredentials credentials = new TestAppEngineCredentials(SCOPES); 107 assertEquals(EXPECTED_ACCOUNT, credentials.getAccount()); 108 } 109 110 @Test sign_sameAs()111 public void sign_sameAs() throws IOException { 112 TestAppEngineCredentials credentials = new TestAppEngineCredentials(SCOPES); 113 assertArrayEquals(EXPECTED_SIGNATURE, credentials.sign("Bytes to sign".getBytes())); 114 } 115 116 @Test createScoped_clonesWithScopes()117 public void createScoped_clonesWithScopes() throws IOException { 118 TestAppEngineCredentials credentials = new TestAppEngineCredentials(null); 119 assertTrue(credentials.createScopedRequired()); 120 try { 121 credentials.refreshAccessToken(); 122 fail("Should not be able to use credential without scopes."); 123 } catch (Exception expected) { 124 // Expected 125 } 126 127 GoogleCredentials scopedCredentials = credentials.createScoped(SCOPES); 128 assertNotSame(credentials, scopedCredentials); 129 130 AccessToken accessToken = scopedCredentials.refreshAccessToken(); 131 assertEquals(EXPECTED_ACCESS_TOKEN, accessToken.getTokenValue()); 132 assertEquals(EXPECTED_EXPIRATION_DATE, accessToken.getExpirationTime()); 133 } 134 135 @Test createScoped_defaultScopes()136 public void createScoped_defaultScopes() throws IOException { 137 TestAppEngineCredentials credentials = new TestAppEngineCredentials(null); 138 assertTrue(credentials.createScopedRequired()); 139 140 GoogleCredentials newCredentials = credentials.createScoped(null, DEFAULT_SCOPES); 141 assertFalse(newCredentials.createScopedRequired()); 142 143 newCredentials = credentials.createScoped(SCOPES, null); 144 assertFalse(newCredentials.createScopedRequired()); 145 146 newCredentials = credentials.createScoped(SCOPES, DEFAULT_SCOPES); 147 assertFalse(newCredentials.createScopedRequired()); 148 149 AccessToken accessToken = newCredentials.refreshAccessToken(); 150 assertEquals(EXPECTED_ACCESS_TOKEN, accessToken.getTokenValue()); 151 assertEquals(EXPECTED_EXPIRATION_DATE, accessToken.getExpirationTime()); 152 } 153 154 @Test equals_true()155 public void equals_true() throws IOException { 156 GoogleCredentials credentials = new TestAppEngineCredentials(SCOPES); 157 GoogleCredentials otherCredentials = new TestAppEngineCredentials(SCOPES); 158 assertTrue(credentials.equals(credentials)); 159 assertTrue(credentials.equals(otherCredentials)); 160 assertTrue(otherCredentials.equals(credentials)); 161 } 162 163 @Test equals_false_scopes()164 public void equals_false_scopes() throws IOException { 165 final Collection<String> emptyScopes = Collections.emptyList(); 166 Collection<String> scopes = Collections.singleton("SomeScope"); 167 AppEngineCredentials credentials = new TestAppEngineCredentials(emptyScopes); 168 AppEngineCredentials otherCredentials = new TestAppEngineCredentials(scopes); 169 assertFalse(credentials.equals(otherCredentials)); 170 assertFalse(otherCredentials.equals(credentials)); 171 } 172 173 @Test toString_containsFields()174 public void toString_containsFields() throws IOException { 175 String expectedToString = 176 String.format( 177 "TestAppEngineCredentials{scopes=[%s], scopesRequired=%b}", "SomeScope", false); 178 Collection<String> scopes = Collections.singleton("SomeScope"); 179 AppEngineCredentials credentials = new TestAppEngineCredentials(scopes); 180 assertEquals(expectedToString, credentials.toString()); 181 } 182 183 @Test hashCode_equals()184 public void hashCode_equals() throws IOException { 185 AppEngineCredentials credentials = new TestAppEngineCredentials(SCOPES); 186 assertEquals(credentials.hashCode(), credentials.hashCode()); 187 } 188 189 @Test serialize()190 public void serialize() throws IOException, ClassNotFoundException { 191 Collection<String> scopes = Collections.singleton("SomeScope"); 192 AppEngineCredentials credentials = new TestAppEngineCredentials(scopes); 193 GoogleCredentials deserializedCredentials = serializeAndDeserialize(credentials); 194 assertEquals(credentials, deserializedCredentials); 195 assertEquals(credentials.hashCode(), deserializedCredentials.hashCode()); 196 assertEquals(credentials.toString(), deserializedCredentials.toString()); 197 } 198 199 private static class TestSigningResult { 200 201 private final byte[] signature; 202 TestSigningResult(byte[] signature)203 TestSigningResult(byte[] signature) { 204 this.signature = signature; 205 } 206 getSignature()207 public byte[] getSignature() { 208 return this.signature; 209 } 210 } 211 212 private static class TestAppIdentityServiceFactory { 213 getAppIdentityService()214 public static TestAppIdentityService getAppIdentityService() { 215 return new TestAppIdentityService(); 216 } 217 } 218 219 private static class TestAppIdentityService { 220 getAccessToken(Iterable<String> scopes)221 public TestGetAccessTokenResult getAccessToken(Iterable<String> scopes) { 222 return new TestGetAccessTokenResult(EXPECTED_ACCESS_TOKEN, EXPECTED_EXPIRATION_DATE); 223 } 224 getServiceAccountName()225 public String getServiceAccountName() { 226 return EXPECTED_ACCOUNT; 227 } 228 signForApp(byte[] toSign)229 public TestSigningResult signForApp(byte[] toSign) { 230 return new TestSigningResult(EXPECTED_SIGNATURE); 231 } 232 } 233 234 private static class TestGetAccessTokenResult { 235 236 private final String accessToken; 237 private final Date expirationTime; 238 TestGetAccessTokenResult(String accessToken, Date expirationTime)239 TestGetAccessTokenResult(String accessToken, Date expirationTime) { 240 this.accessToken = accessToken; 241 this.expirationTime = expirationTime; 242 } 243 getAccessToken()244 public String getAccessToken() { 245 return this.accessToken; 246 } 247 getExpirationTime()248 public Date getExpirationTime() { 249 return this.expirationTime; 250 } 251 } 252 253 private static class TestAppEngineCredentials extends AppEngineCredentials { 254 255 private static final long serialVersionUID = -5191475572296306231L; 256 257 private static final Map<String, Class<?>> TYPES = 258 ImmutableMap.of( 259 AppEngineCredentials.APP_IDENTITY_SERVICE_FACTORY_CLASS, 260 TestAppIdentityServiceFactory.class, 261 AppEngineCredentials.APP_IDENTITY_SERVICE_CLASS, 262 TestAppIdentityService.class, 263 AppEngineCredentials.GET_ACCESS_TOKEN_RESULT_CLASS, 264 TestGetAccessTokenResult.class, 265 AppEngineCredentials.SIGNING_RESULT_CLASS, 266 TestSigningResult.class); 267 private List<String> forNameArgs; 268 TestAppEngineCredentials(Collection<String> scopes)269 TestAppEngineCredentials(Collection<String> scopes) throws IOException { 270 super(scopes, null); 271 } 272 TestAppEngineCredentials(Collection<String> scopes, Collection<String> defaultScopes)273 TestAppEngineCredentials(Collection<String> scopes, Collection<String> defaultScopes) 274 throws IOException { 275 super(scopes, defaultScopes); 276 } 277 278 @Override forName(String className)279 Class<?> forName(String className) throws ClassNotFoundException { 280 if (forNameArgs == null) { 281 forNameArgs = new ArrayList<>(); 282 } 283 forNameArgs.add(className); 284 Class<?> lookup = TYPES.get(className); 285 if (lookup != null) { 286 return lookup; 287 } 288 throw new ClassNotFoundException(className); 289 } 290 getForNameArgs()291 List<String> getForNameArgs() { 292 return forNameArgs; 293 } 294 } 295 296 private static class TestAppEngineCredentialsNoSdk extends AppEngineCredentials { 297 298 private static final long serialVersionUID = -8987103249265111274L; 299 TestAppEngineCredentialsNoSdk()300 TestAppEngineCredentialsNoSdk() throws IOException { 301 super(null, null); 302 } 303 304 @Override forName(String className)305 Class<?> forName(String className) throws ClassNotFoundException { 306 throw new ClassNotFoundException(className); 307 } 308 } 309 } 310