Skip to content
Snippets Groups Projects
Commit 4210b0fb authored by Dmitrii Novikov (EPAM)'s avatar Dmitrii Novikov (EPAM) Committed by Riabokon Stanislav(EPAM)[GCP]
Browse files

Remove datastore tenant info provisioning from Notification service (EPAM GONRG-4242)

parent 6520034e
No related branches found
No related tags found
3 merge requests!232Update os-core-lib-azure,!231initial commit,!178Remove datastore tenant info provisioning from Notification service (EPAM GONRG-4242)
Showing with 72 additions and 231 deletions
......@@ -21,6 +21,7 @@ import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
import org.opengroup.osdu.notification.provider.gcp.config.AppProperties;
import org.opengroup.osdu.notification.provider.interfaces.IServiceAccountValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......
......@@ -17,188 +17,22 @@
package org.opengroup.osdu.notification.provider.gcp.util;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.iam.v1.Iam;
import com.google.api.services.iam.v1.IamScopes;
import com.google.api.services.iam.v1.model.SignJwtRequest;
import com.google.api.services.iam.v1.model.SignJwtResponse;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.base.Strings;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.http.HttpHeaders;
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.model.http.AppException;
import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.opengroup.osdu.core.auth.TokenProvider;
import org.opengroup.osdu.core.common.util.IServiceAccountJwtClient;
import org.opengroup.osdu.core.gcp.multitenancy.TenantFactory;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.stereotype.Component;
@Component
@Slf4j
@RequiredArgsConstructor
public class ServiceAccountJwtGcpClientImpl implements IServiceAccountJwtClient {
private AppProperties config;
private static final String JWT_AUDIENCE = "https://www.googleapis.com/oauth2/v4/token";
private static final String SERVICE_ACCOUNT_NAME_FORMAT = "projects/%s/serviceAccounts/%s";
private static final GsonFactory JSON_FACTORY = new GsonFactory();
static final String INVALID_INPUT = "Invalid inputs provided to getIdToken function";
static final String INVALID_DATA_PARTITION = "Invalid data partition id";
private static ConcurrentHashMap<String, JwtValidity> jwtCache = new ConcurrentHashMap<>();
private Iam iam;
public ServiceAccountJwtGcpClientImpl(AppProperties config) {
if (config == null) {
throw new IllegalArgumentException("AppProperties is null when initializing jwt client.");
} else {
this.config = config;
}
}
public String getIdToken(String dataPartitionId) {
String googleAudience = this.config.getGoogleAudiences();
String hostName = this.config.getRegisterAPI();
if (Strings.isNullOrEmpty(dataPartitionId) || Strings.isNullOrEmpty(googleAudience) || Strings.isNullOrEmpty(hostName)) {
throw new AppException(HttpStatus.SC_BAD_REQUEST, "data partition id, audiences or hostname are null", INVALID_INPUT);
}
try {
// Check if there is already a valid jwt
String key = dataPartitionId + googleAudience + hostName;
String jwt = checkAndGetJwtIfValid(key);
if (!Strings.isNullOrEmpty(jwt))
return jwt;
TenantInfo tenantInfo = new TenantFactory().getTenantInfo(dataPartitionId);
if (tenantInfo == null) {
throw new AppException(HttpStatus.SC_BAD_REQUEST, "data partition id is invalid", INVALID_DATA_PARTITION);
}
long currentTime = System.currentTimeMillis() / 1000;
long expiryTime = currentTime + 3600;
// get signed JWT
Map<String, Object> signJwtPayload = this.getJwtCreationPayload(tenantInfo, googleAudience, currentTime, expiryTime);
SignJwtRequest signJwtRequest = new SignJwtRequest();
signJwtRequest.setPayload(JSON_FACTORY.toString(signJwtPayload));
String serviceAccountName = String.format(SERVICE_ACCOUNT_NAME_FORMAT, tenantInfo.getProjectId(),
tenantInfo.getServiceAccount());
Iam.Projects.ServiceAccounts.SignJwt signJwt = this.getIam(hostName).projects().serviceAccounts()
.signJwt(serviceAccountName, signJwtRequest);
SignJwtResponse signJwtResponse = signJwt.execute();
String signedJwt = signJwtResponse.getSignedJwt();
// get 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")) {
throw new AppException(HttpStatus.SC_UNAUTHORIZED, "User is not authorized to perform this operation.", "Unauthorized to generate token");
}
String token = "Bearer " + jsonContent.get("id_token").getAsString();
jwtCache.put(key, new JwtValidity(token, expiryTime));
return token;
}
} catch (Exception e) {
throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Error happens when generating sauth token", "Error generating token", e);
}
}
Iam getIam(String hostName) throws GeneralSecurityException, IOException {
if (this.iam == null) {
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
GoogleCredentials credential = GoogleCredentials.getApplicationDefault();
if (credential.createScopedRequired()) {
List<String> scopes = new ArrayList<>();
scopes.add(IamScopes.CLOUD_PLATFORM);
credential = credential.createScoped(scopes);
}
this.iam = new Iam.Builder(httpTransport, JSON_FACTORY, new HttpCredentialsAdapter(credential))
.setApplicationName(hostName).build();
}
return this.iam;
}
// THIS METHOD IS ONLY TO ENABLE UNIT TESTING
boolean reduceTenantExpiry(String dataPartitionId, String googleAudience, String hostName, long keepDifference) {
JwtValidity jwtValidity = jwtCache.get(dataPartitionId + googleAudience + hostName);
if (jwtValidity == null)
return false;
long currentTime = System.currentTimeMillis() / 1000;
jwtValidity.expiryTime = currentTime + keepDifference;
return true;
}
// THIS METHOD IS ONLY TO ENABLE UNIT TESTING
void clearCache() {
jwtCache.clear();
}
private String checkAndGetJwtIfValid(String key) {
JwtValidity jwtValidity = jwtCache.get(key);
if (jwtValidity == null)
return null;
// get current time
long currentTime = System.currentTimeMillis() / 1000;
// If exipring in less than 5 minutes then need to renew the token
if (jwtValidity.expiryTime - 300 < currentTime) {
jwtCache.remove(key);
return null;
}
return jwtValidity.token;
}
private final TokenProvider tokenProvider;
private Map<String, Object> getJwtCreationPayload(TenantInfo tenantInfo, String googleAudience, long currentTime, long expiryTime) {
if (googleAudience.contains(",")) {
googleAudience = googleAudience.split(",")[0];
}
Map<String, Object> payload = new HashMap<>();
payload.put("target_audience", googleAudience);
payload.put("aud", JWT_AUDIENCE);
payload.put("exp", expiryTime);
payload.put("iat", currentTime);
payload.put("iss", tenantInfo.getServiceAccount());
return payload;
public String getIdToken(String tenantName) {
log.info("Tenant name received for auth token is: {}", tenantName);
return tokenProvider.getIdToken();
}
}
......
#
# Copyright 2022 Google LLC
# Copyright 2022 EPAM Systems, 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
#
# https://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.
#
service.token.provider=OPENID
partition-auth-enabled=false
oqmDriver=rabbitmq
\ No newline at end of file
#
# Copyright 2017-2020, 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.
#
spring.profiles.active=dev
app.entitlements=https://entitlements-dot-opendes.appspot.com/entitlements/v1
app.register=https://os-register-dot-opendes.appspot.com/api/register/v1
app.project=opendes
app.googleAudience=245464679631-ktfdfpl147m1mjpbutl00b3cmffissgq.apps.googleusercontent.com
server.port=8080
enable.appengine.log.factory=true
\ No newline at end of file
#
# Copyright 2022 Google LLC
# Copyright 2022 EPAM Systems, 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
#
# https://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.
#
service.token.provider=GCP
partition-auth-enabled=true
oqmDriver=pubsub
\ No newline at end of file
#
# Copyright 2017-2020, 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.
#
app.entitlements=http://os-entitlement-service/entitlements/v1
app.register=https://os-register-dot-opendes.appspot.com/api/register/v1
app.project=${GOOGLE_CLOUD_PROJECT}
app.googleAudience=${GOOGLE_AUDIENCES}
server.port=8080
server.servlet.contextPath=/api/notification/v1/
......@@ -14,9 +14,14 @@
# limitations under the License.
#
spring.profiles.active=local
app.entitlements=https://entitlements-dot-opendes.appspot.com/entitlements/v1
app.register=https://os-register-dot-opendes.appspot.com/api/register/v1
app.project=opendes
app.googleAudience=245464679631-ktfdfpl147m1mjpbutl00b3cmffissgq.apps.googleusercontent.com
server.port=8080
logging.level.org.springframework.web=${LOG_LEVEL:DEBUG}
app.entitlements=https://community.gcp.gnrg-osdu.projects.epam.com/entitlements/v2
app.register=https://community.gcp.gnrg-osdu.projects.epam.com/api/register/v1
app.project=nice-etching-277309
google-audiences=689762842995-pv217jo3k8j803kk6gqf52qb5amos3a9.apps.googleusercontent.com
service.token.provider=GCP
partition-auth-enabled=true
oqmDriver=pubsub
......@@ -14,14 +14,21 @@
# limitations under the License.
#
# Global defaults
LOG_PREFIX=notification
logging.level.org.springframework.web=${LOG_LEVEL:DEBUG}
logging.level.org.springframework.web=${LOG_LEVEL:INFO}
server.servlet.contextPath=/api/notification/v1
app.expireTime=300
app.maxCacheSize=10
server.error.whitelabel.enabled=false
google.audiences=${APP_AUDIENCES}
partition.api=http://localhost:8081/api/partition/v1
# External services
app.entitlements=http://entitlements/api/entitlements/v2/
app.register=http://register/api/register/v1
partition.api=http://partition/api/partition/v1/
# No profile defaults
service.token.provider=GCP
partition-auth-enabled=true
oqmDriver=pubsub
\ No newline at end of file
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