Skip to content
Snippets Groups Projects
Commit 8a4c7c1c authored by Rustam Lotsmanenko (EPAM)'s avatar Rustam Lotsmanenko (EPAM)
Browse files

fix ServiceAccountJwtGcpClientImpl , bump-up google-cloud-logging to resolve...

fix ServiceAccountJwtGcpClientImpl , bump-up google-cloud-logging to resolve java.lang.AbstractMethodError: com.google.api.gax.grpc.InstantiatingGrpcChannelProvider
parent 6d8041bd
No related branches found
No related tags found
2 merge requests!183Locking down maven central,!163Gcp iam migration(GONRG-2518)
Pipeline #48650 canceled
......@@ -32,7 +32,11 @@
<groupId>org.opengroup.osdu</groupId>
<artifactId>os-core-common</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-core</artifactId>
<version>1.38.1</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-datastore</artifactId>
......@@ -41,7 +45,7 @@
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-logging</artifactId>
<version>1.72.0</version>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
......@@ -70,11 +74,6 @@
<artifactId>google-api-client</artifactId>
<version>1.30.11</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-core</artifactId>
<version>1.38.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
......
......@@ -15,26 +15,12 @@
package org.opengroup.osdu.indexer.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.cloud.iam.credentials.v1.GenerateIdTokenResponse;
import com.google.cloud.iam.credentials.v1.IamCredentialsClient;
import com.google.cloud.iam.credentials.v1.ServiceAccountName;
import com.google.cloud.iam.credentials.v1.SignJwtRequest;
import com.google.cloud.iam.credentials.v1.SignJwtResponse;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.http.HttpHeaders;
import java.util.Collections;
import javax.inject.Inject;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.opengroup.osdu.core.common.model.http.DpsHeaders;
......@@ -43,28 +29,24 @@ import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
import org.opengroup.osdu.core.common.provider.interfaces.IJwtCache;
import org.opengroup.osdu.core.common.provider.interfaces.ITenantFactory;
import org.opengroup.osdu.core.common.util.IServiceAccountJwtClient;
import org.opengroup.osdu.core.gcp.multitenancy.credentials.IamCredentialsProvider;
import org.opengroup.osdu.indexer.config.IndexerConfigurationProperties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Primary
@Component
@RequestScope
public class ServiceAccountJwtGcpClientImpl implements IServiceAccountJwtClient {
private static final String JWT_AUDIENCE = "https://www.googleapis.com/oauth2/v4/token";
private static final String SERVICE_ACCOUNT_NAME_FORMAT = "projects/-/serviceAccounts/%s";
private final JsonFactory jsonFactory = new JacksonFactory();
private final IamCredentialsProvider iamCredentialsProvider = new IamCredentialsProvider();
private IamCredentialsClient iam;
@Value("GOOGLE_AUDIENCES")
private String audiences;
@Inject
private ITenantFactory tenantInfoServiceProvider;
......@@ -94,76 +76,18 @@ public class ServiceAccountJwtGcpClientImpl implements IServiceAccountJwtClient
return cachedToken.getTokenValue();
}
// Getting signed JWT
Map<String, Object> signJwtPayload = this.getJWTCreationPayload(tenant);
ServiceAccountName name = ServiceAccountName.parse(String.format(SERVICE_ACCOUNT_NAME_FORMAT,
tenant.getServiceAccount()));
List<String> delegates = new ArrayList<>();
delegates.add(tenant.getServiceAccount());
SignJwtRequest request = SignJwtRequest.newBuilder()
.setName(name.toString())
.addAllDelegates(delegates)
.setPayload(jsonFactory.toString(signJwtPayload))
.build();
SignJwtResponse signJwtResponse = this.getIamCredentialsClient().signJwt(request);
String signedJwt = signJwtResponse.getSignedJwt();
// Getting id token
List<NameValuePair> postParameters = new ArrayList<>();
postParameters.add(new BasicNameValuePair("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"));
postParameters.add(new BasicNameValuePair("assertion", signedJwt));
HttpPost post = new HttpPost(JWT_AUDIENCE);
post.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
post.setEntity(new UrlEncodedFormEntity(postParameters, "UTF-8"));
try(CloseableHttpClient httpclient = HttpClients.createDefault();
CloseableHttpResponse httpResponse = httpclient.execute(post)) {
JsonObject jsonContent = new JsonParser().parse(EntityUtils.toString(httpResponse.getEntity())).getAsJsonObject();
if (!jsonContent.has("id_token")) {
log.error(String.format("Google IAM response: %s", jsonContent.toString()));
throw new AppException(HttpStatus.SC_FORBIDDEN, "Access denied", "The user is not authorized to perform this action");
}
String token = jsonContent.get("id_token").getAsString();
try (IamCredentialsClient iamCredentialsClient = iamCredentialsProvider.getIamCredentialsClient()) {
ServiceAccountName serviceAccountName = ServiceAccountName.parse(String.format(SERVICE_ACCOUNT_NAME_FORMAT, tenant.getServiceAccount()));
GenerateIdTokenResponse idTokenResponse = iamCredentialsClient.generateIdToken(serviceAccountName, Collections.emptyList(), audiences, true);
String token = idTokenResponse.getToken();
IdToken idToken = IdToken.builder().tokenValue(token).expirationTimeMillis(JWT.decode(token).getExpiresAt().getTime()).build();
this.cacheService.put(tenant.getServiceAccount(), idToken);
return token;
}
} catch (JWTDecodeException e) {
throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Persistence error", "Invalid token, error decoding", e);
} catch (AppException e) {
throw e;
} catch (Exception e) {
throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Persistence error", "Error generating token", e);
}
}
public IamCredentialsClient getIamCredentialsClient() throws Exception {
if (this.iam == null) {
this.iam = IamCredentialsClient.create();
}
return this.iam;
}
private Map<String, Object> getJWTCreationPayload(TenantInfo tenantInfo) {
Map<String, Object> payload = new HashMap<>();
String googleAudience = properties.getGoogleAudiences();
if (googleAudience.contains(",")) {
googleAudience = googleAudience.split(",")[0];
}
payload.put("target_audience", googleAudience);
payload.put("exp", System.currentTimeMillis() / 1000 + 3600);
payload.put("iat", System.currentTimeMillis() / 1000);
payload.put("iss", tenantInfo.getServiceAccount());
payload.put("aud", JWT_AUDIENCE);
return payload;
}
}
// Copyright 2017-2019, Schlumberger
//
// 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.opengroup.osdu.indexer.util;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.gax.rpc.UnaryCallable;
import com.google.cloud.iam.credentials.v1.IamCredentialsClient;
import com.google.cloud.iam.credentials.v1.SignJwtRequest;
import com.google.cloud.iam.credentials.v1.SignJwtResponse;
import com.google.cloud.iam.credentials.v1.stub.IamCredentialsStub;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
import org.opengroup.osdu.core.common.model.search.DeploymentEnvironment;
import org.opengroup.osdu.core.common.model.search.IdToken;
import org.opengroup.osdu.core.common.provider.interfaces.IJwtCache;
import org.opengroup.osdu.indexer.config.IndexerConfigurationProperties;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.reflect.Whitebox;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.powermock.api.mockito.PowerMockito.when;
@Ignore
@RunWith(SpringRunner.class)
@PrepareForTest({GoogleNetHttpTransport.class, GoogleCredential.class, NetHttpTransport.class, SignJwtResponse.class, HttpClients.class, EntityUtils.class, IndexerConfigurationProperties.class})
public class ServiceAccountJwtGcpClientImplTest {
private static final String JWT_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik1UVXlPREE0TXpFd09BPT0ifQ.eyJzdWIiOiJtemh1OUBzbGIuY29tIiwiaXNzIjoic2F1dGgtcHJldmlldy5zbGIuY29tIiwiYXVkIjoidGVzdC1zbGJkZXYtZGV2cG9ydGFsLnNsYmFwcC5jb20iLCJpYXQiOjE1MjgxNDg5MTUsImV4cCI6MTUyODIzNTMxNSwicHJvdmlkZXIiOiJzbGIuY29tIiwiY2xpZW50IjoidGVzdC1zbGJkZXYtZGV2cG9ydGFsLnNsYmFwcC5jb20iLCJ1c2VyaWQiOiJtemh1OUBzbGIuY29tIiwiZW1haWwiOiJtemh1OUBzbGIuY29tIiwiYXV0aHoiOiJ7XCJhY2NvdW50Q291bnRyeVwiOntcImNvZGVcIjpcInVzXCIsXCJpZFwiOjU3MTU5OTkxMDE4MTI3MzYsXCJuYW1lXCI6XCJVbml0ZWQgU3RhdGVzIG9mIEFtZXJpY2FcIn0sXCJhY2NvdW50SWRcIjo1NjkxODc4ODMzOTEzODU2LFwiYWNjb3VudE5hbWVcIjpcIlNJUyBJbnRlcm5hbCBIUVwiLFwiY3JlYXRlZFwiOlwiMjAxOC0wNS0wM1QxNzoyNTo1NS40NDNaXCIsXCJkZXBhcnRtZW50TWFuYWdlclwiOm51bGwsXCJzdWJzY3JpcHRpb25zXCI6W3tcImFjY291bnRJZFwiOjU2OTE4Nzg4MzM5MTM4NTYsXCJjb250cmFjdElkXCI6NTc1MTcwMDIxMjE1NDM2OCxcImNyZWF0ZWRcIjpcIjIwMTgtMDUtMDNUMTc6MzM6MDkuNTczWlwiLFwiY3JtQ29udHJhY3RJZFwiOlwiU0lTLUlOVEVSTkFMLUhRLVFBXCIsXCJjcm1Db250cmFjdEl0ZW1JZFwiOlwiZGV2bGlcIixcImV4cGlyYXRpb25cIjpcIjE5NzAtMDEtMDFUMDA6MDA6MDAuMDAwWlwiLFwiaWRcIjo1MDc5Mjg4NTA0MTIzMzkyLFwicHJvZHVjdFwiOntcImNvZGVcIjpcImRldmVsb3Blci1saWdodFwiLFwiY29tY2F0TmFtZVwiOlwiTm90IGluIENvbUNhdFwiLFwiZmVhdHVyZVNldHNcIjpbe1wiYXBwbGljYXRpb25cIjp7XCJjb2RlXCI6XCJhcGlkZXZlbG9wZXJwb3J0YWxcIixcImlkXCI6NTE2ODkzMDY5NTkzODA0OCxcIm5hbWVcIjpcIkFQSSBEZXZlbG9wZXIgUG9ydGFsXCIsXCJ0eXBlXCI6XCJXZWJBcHBcIn0sXCJjbGFpbXNcIjpudWxsLFwiaWRcIjo1MTkxNTcyMjg3MTI3NTUyLFwibmFtZVwiOlwiRGV2ZWxvcGVyXCIsXCJ0eXBlXCI6XCJCQVNFXCJ9XSxcImlkXCI6NTE1MDczMDE1MTI2NDI1NixcIm5hbWVcIjpcIkRldmVsb3BlciBQb3J0YWxcIixcInBhcnROdW1iZXJcIjpcIlNERUwtUEItU1VCVVwifX1dLFwidXNlckVtYWlsXCI6XCJtemh1OUBzbGIuY29tXCIsXCJ1c2VyTmFtZVwiOlwiTWluZ3lhbmcgWmh1XCJ9XG4iLCJsYXN0bmFtZSI6IlpodSIsImZpcnN0bmFtZSI6Ik1pbmd5YW5nIiwiY291bnRyeSI6IiIsImNvbXBhbnkiOiIiLCJqb2J0aXRsZSI6IiIsInN1YmlkIjoiNDE3YjczMjktYmMwNy00OTFmLWJiYzQtZTQ1YjRhMWFiYjVjLVd3U0c0dyIsImlkcCI6ImNvcnAyIiwiaGQiOiJzbGIuY29tIn0.WQfGr1Xu-6IdaXdoJ9Fwzx8O2el1UkFPWo1vk_ujiAfdOjAR46UG5SrBC7mzC7gYRyK3a4fimBmbv3uRVJjTNXdxXRLZDw0SvXUMIOqjUGLom491ESbrtka_Xz7vGO-tWyDcEQDTfFzQ91LaVN7XdzL18_EDTXZoPhKb-zquyk9WLQxP9Mw-3Yh-UrbvC9nl1-GRn1IVbzp568kqkpOVUFM9alYSGw-oMGDZNt1DIYOJnpGaw2RB5B3AKvNivZH_Xdac7ZTzQbsDOt8B8DL2BphuxcJ9jshCJkM2SHQ15uErv8sfnzMwdF08e_0QcC_30I8eX9l8yOu6TnwwqlXunw";
@Mock
private IndexerConfigurationProperties indexerConfigurationProperties;
@Mock
private JaxRsDpsLog log;
@Mock
private GoogleCredential credential;
@Mock
private NetHttpTransport httpTransport;
@Mock
private IamCredentialsClient iam;
@Mock
private IamCredentialsStub iamCredentialsStub;
@Mock
private UnaryCallable<SignJwtRequest, SignJwtResponse> unaryCallable;
@Mock
private CloseableHttpClient httpClient;
@Mock
private CloseableHttpResponse httpResponse;
// @InjectMocks
// private TenantInfoServiceImpl tenantInfoServiceProvider;
// @Mock
// private TenantInfoServiceImpl tenantInfoService;
@Mock
private IJwtCache cacheService;
@InjectMocks @Spy
private ServiceAccountJwtGcpClientImpl sut;
@Before
public void setup() throws Exception {
initMocks(this);
// mockStatic(GoogleNetHttpTransport.class);
// mockStatic(GoogleCredential.class);
// mockStatic(HttpClients.class);
// mockStatic(EntityUtils.class);
// mockStatic(Config.class);
when(GoogleNetHttpTransport.newTrustedTransport()).thenReturn(httpTransport);
when(GoogleCredential.getApplicationDefault()).thenReturn(credential);
when(credential.createScopedRequired()).thenReturn(true);
when(credential.createScoped(any())).thenReturn(credential);
when(HttpClients.createDefault()).thenReturn(httpClient);
when(httpClient.execute(any())).thenReturn(httpResponse);
when(indexerConfigurationProperties.getDeploymentEnvironment()).thenReturn(DeploymentEnvironment.LOCAL);
when(indexerConfigurationProperties.getGoogleAudiences()).thenReturn("aud");
// when(this.tenantInfoServiceProvider).thenReturn(this.tenantInfoService);
TenantInfo tenantInfo = new TenantInfo();
tenantInfo.setServiceAccount("tenant");
// when(this.tenantInfoService.getTenantInfo()).thenReturn(tenantInfo);
when(this.sut.getIamCredentialsClient()).thenReturn(iam);
Whitebox.setInternalState(iam, "stub", iamCredentialsStub);
SignJwtResponse signJwtResponse = SignJwtResponse.getDefaultInstance();
SignJwtRequest signJwtRequest = SignJwtRequest.newBuilder().build();
when(iamCredentialsStub.signJwtCallable()).thenReturn(unaryCallable);
when(unaryCallable.call(any())).thenReturn(signJwtResponse);
when(iam.signJwt(signJwtRequest)).thenReturn(signJwtResponse);
}
@Test
public void should_returnCachedToken_givenCachedToken_getIdTokenTest() {
String tokenValue = "tokenValue";
IdToken idToken = IdToken.builder().tokenValue(tokenValue).expirationTimeMillis(System.currentTimeMillis() + 10000000L).build();
when(this.cacheService.get(any())).thenReturn(idToken);
String returnedIdToken = this.sut.getIdToken(tokenValue);
Assert.assertEquals(tokenValue, returnedIdToken);
}
@Test
public void should_returnValidToken_getIdTokenTest() throws Exception {
when(EntityUtils.toString(any())).thenReturn(String.format("{\"id_token\":\"%s\"}", JWT_TOKEN));
String returnedToken = this.sut.getIdToken("tenant");
Assert.assertEquals(JWT_TOKEN, returnedToken);
}
@Test
public void should_return500_given_invalidJWTResponse_getIdTokenException() {
try {
when(EntityUtils.toString(any())).thenReturn(String.format("{\"id_token\":\"%s\"}", "invalid jwt"));
this.sut.getIdToken("tenant");
fail("Should throw exception");
} catch (AppException e) {
Assert.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getError().getCode());
Assert.assertEquals("Invalid token, error decoding", e.getError().getMessage());
} catch (Exception e) {
fail("Should not throw this exception" + e.getMessage());
}
}
@Test
public void should_return403_given_missingIdTokenResponse_getIdTokenException() {
try {
when(EntityUtils.toString(any())).thenReturn("{}");
this.sut.getIdToken("tenant");
fail("Should throw exception");
} catch (AppException e) {
Assert.assertEquals(HttpStatus.SC_FORBIDDEN, e.getError().getCode());
Assert.assertEquals("The user is not authorized to perform this action", e.getError().getMessage());
} catch (Exception e) {
fail("Should not throw this exception" + e.getMessage());
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment