/* * Copyright 2014-2016 CyberVision, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kaaproject.kaa.server.verifiers.gplus.verifier; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.apache.http.HttpEntity; import org.apache.http.StatusLine; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.kaaproject.kaa.server.common.verifier.UserVerifierCallback; import org.kaaproject.kaa.server.verifiers.gplus.config.gen.GplusAvroConfig; import org.mockito.Mockito; import org.slf4j.Logger; import org.springframework.test.util.ReflectionTestUtils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.URI; import java.nio.charset.StandardCharsets; public class GplusUserVerifierTest extends GplusUserVerifier { private static GplusUserVerifier verifier; private static String userId = "1557997434440423"; private static GplusAvroConfig config; @BeforeClass public static void setUp() { config = mock(GplusAvroConfig.class); when(config.getMaxParallelConnections()).thenReturn(1); } @Test public void invalidUserAccessCodeTest() { verifier = new MyGplusVerifier(200, "{\n" + " \"error\": \"invalid_token\",\n" + " \"error_description\": \"Invalid Value\"\n" + "}\n"); verifier.init(null, config); verifier.start(); UserVerifierCallback callback = mock(UserVerifierCallback.class); verifier.checkAccessToken("invalidUserId", "falseUserAccessToken", callback); verify(callback, Mockito.timeout(1000).atLeastOnce()).onVerificationFailure(anyString()); } @Test public void incompatibleUserIdsTest() { verifier = new MyGplusVerifier(200, "{\n" + " \"audience\":\"8819981768.apps.googleusercontent.com\",\n" + " \"user_id\":\"" + userId + "\",\n" + " \"scope\":\"profile email\",\n" + " \"expires_in\":436\n" + "}"); verifier.init(null, config); verifier.start(); UserVerifierCallback callback = mock(UserVerifierCallback.class); verifier.checkAccessToken("invalidUserId", "falseUserAccessToken", callback); verify(callback, Mockito.timeout(1000).atLeastOnce()).onVerificationFailure(anyString()); } @Test public void badRequestTest() { verifier = new MyGplusVerifier(400); verifier.init(null, config); verifier.start(); UserVerifierCallback callback = mock(UserVerifierCallback.class); verifier.checkAccessToken("invalidUserId", "falseUserAccessToken", callback); verify(callback, Mockito.timeout(1000).atLeastOnce()).onTokenInvalid(); } @Test public void successfulVerificationTest() { String userId = "12456789123456"; verifier = new MyGplusVerifier(200, "{\n" + " \"audience\":\"8819981768.apps.googleusercontent.com\",\n" + " \"user_id\":\"" + userId + "\",\n" + " \"scope\":\"profile email\",\n" + " \"expires_in\":436\n" + "}"); verifier.init(null, config); verifier.start(); UserVerifierCallback callback = mock(UserVerifierCallback.class); verifier.checkAccessToken(userId, "someToken", callback); verify(callback, Mockito.timeout(1000).atLeastOnce()).onSuccess(); } @Test public void internalErrorIOExceptionTest() throws IOException { verifier = new GplusUserVerifier(); verifier.init(null, config); verifier.start(); CloseableHttpClient httpClientMock = mock(CloseableHttpClient.class); doThrow(new IOException()).when(httpClientMock).execute(any(HttpGet.class)); ReflectionTestUtils.setField(verifier, "httpClient", httpClientMock); UserVerifierCallback callback = mock(UserVerifierCallback.class); verifier.checkAccessToken("id", "token", callback); verifier.stop(); Mockito.verify(callback, Mockito.timeout(1000)).onInternalError(); } @Test public void internalErrorExceptionTest() throws Exception { verifier = new GplusUserVerifier(); verifier.init(null, config); verifier.start(); CloseableHttpClient httpClientMock = mock(CloseableHttpClient.class); doThrow(new NullPointerException()).when(httpClientMock).execute(any(HttpGet.class)); ReflectionTestUtils.setField(verifier, "httpClient", httpClientMock); UserVerifierCallback callback = mock(UserVerifierCallback.class); verifier.checkAccessToken("id", "token", callback); verifier.stop(); Mockito.verify(callback, Mockito.timeout(2000)).onInternalError(); } @Test public void unableToCloseHttpClientTest() throws Exception { verifier = new GplusUserVerifier(); verifier.init(null, config); verifier.start(); CloseableHttpClient httpClientMock = mock(CloseableHttpClient.class); // Throw any descendant of Exception, as the indicator of an internal error doThrow(new IOException()).when(httpClientMock).close(); ReflectionTestUtils.setField(verifier, "httpClient", httpClientMock); Logger LOG = mock(Logger.class); Field logField = GplusUserVerifier.class.getDeclaredField("LOG"); setFinalStatic(logField, LOG); UserVerifierCallback callback = mock(UserVerifierCallback.class); verifier.checkAccessToken("id", "token", callback); verifier.stop(); Mockito.verify(callback, Mockito.timeout(1000)).onInternalError(); } @Test public void getConfigurationClassTest() { verifier = new GplusUserVerifier(); Assert.assertEquals(verifier.getConfigurationClass(), GplusAvroConfig.class); } @Test public void internalErrorBadResponseCodeTest() throws IOException { verifier = new MyGplusVerifier(503, "{\n" + " \"audience\":\"8819981768.apps.googleusercontent.com\",\n" + " \"user_id\":\"" + userId + "\",\n" + " \"scope\":\"profile email\",\n" + " \"expires_in\":436\n" + "}"); verifier.init(null, config); verifier.start(); UserVerifierCallback callback = mock(UserVerifierCallback.class); verifier.checkAccessToken("invalidUserId", "falseUserAccessToken", callback); verifier.stop(); verify(callback, Mockito.timeout(1000).atLeastOnce()).onInternalError(); } @Test public void checkMalformedUri() throws Exception { verifier = new GplusUserVerifier(); verifier.init(null, config); verifier.start(); Field uriPart = GplusUserVerifier.class.getDeclaredField("GOOGLE_OAUTH"); setFinalStatic(uriPart, "\\\\\\\\\\"); UserVerifierCallback callback = mock(UserVerifierCallback.class); verifier.checkAccessToken("id", "token", callback); verifier.stop(); verify(callback, Mockito.timeout(1000).atLeastOnce()).onInternalError(); setFinalStatic(uriPart, "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token="); } private void setFinalStatic(Field field, Object newValue) throws Exception { field.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, newValue); } private static class MyGplusVerifier extends GplusUserVerifier { int responseCode; String inputStreamString = ""; MyGplusVerifier(int responseCode) { this.responseCode = responseCode; } MyGplusVerifier(int responseCode, String intputStreamString) { this.responseCode = responseCode; this.inputStreamString = intputStreamString; } @Override protected CloseableHttpResponse establishConnection(URI uri) { CloseableHttpResponse closeableHttpResponse = mock(CloseableHttpResponse.class); try { StatusLine statusLine = mock(StatusLine.class); when(statusLine.getStatusCode()).thenReturn(responseCode); HttpEntity httpEntity = mock(HttpEntity.class); when(httpEntity.getContent()).thenReturn(new ByteArrayInputStream(inputStreamString. getBytes(StandardCharsets.UTF_8))); when(closeableHttpResponse.getStatusLine()).thenReturn(statusLine); when(closeableHttpResponse.getEntity()).thenReturn(httpEntity); } catch (Exception e) { e.printStackTrace(); } return closeableHttpResponse; } } }