From 0f10985e1a55fc662d788a9be47c68d178639759 Mon Sep 17 00:00:00 2001 From: helayoty <hebaelayoty@gmail.com> Date: Thu, 3 Oct 2019 02:03:34 -0700 Subject: [PATCH] apply modification in headerInfo and RequestInfo --- indexer-service-azure/pom.xml | 10 -- .../indexer/azure/api/AADController.java | 20 +-- .../org/opendes/indexer/azure/api/Hello.java | 24 ---- .../security/BasicAuthSecurityConfig.java | 52 ++++++++ .../azure/security/WhoamiController.java | 39 ++++++ .../azure/util/HeadersInfoAzureImpl.java | 4 +- .../indexer/azure/util/RequestInfoImpl.java | 28 +++-- .../util/ServiceAccountJwtClientImpl.java | 118 +++++------------- 8 files changed, 143 insertions(+), 152 deletions(-) delete mode 100644 indexer-service-azure/src/main/java/org/opendes/indexer/azure/api/Hello.java create mode 100644 indexer-service-azure/src/main/java/org/opendes/indexer/azure/security/BasicAuthSecurityConfig.java create mode 100644 indexer-service-azure/src/main/java/org/opendes/indexer/azure/security/WhoamiController.java diff --git a/indexer-service-azure/pom.xml b/indexer-service-azure/pom.xml index 96ffaea82..8684f39a6 100644 --- a/indexer-service-azure/pom.xml +++ b/indexer-service-azure/pom.xml @@ -85,16 +85,6 @@ <artifactId>msal4j</artifactId> <version>0.5.0-preview</version> </dependency> -<!-- <dependency>--> -<!-- <groupId>org.json</groupId>--> -<!-- <artifactId>json</artifactId>--> -<!-- <version>20090211</version>--> -<!-- </dependency>--> - <!-- Spring 3 dependencies --> -<!-- <dependency>--> -<!-- <groupId>org.springframework.boot</groupId>--> -<!-- <artifactId>spring-boot-starter-thymeleaf</artifactId>--> -<!-- </dependency>--> </dependencies> <build> diff --git a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/api/AADController.java b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/api/AADController.java index 7e380a6d0..e91874ed2 100644 --- a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/api/AADController.java +++ b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/api/AADController.java @@ -23,25 +23,7 @@ public class AADController { String token = service.getIdToken("common"); - String oboApiCallRes = callOboService(token); - - return new ResponseEntity<>(HttpStatus.OK); - } - - private String callOboService(String accessToken){ - RestTemplate restTemplate = new RestTemplate(); - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - - headers.set("Authorization", "Bearer " + accessToken); - - HttpEntity<String> entity = new HttpEntity<>(null, headers); - - String result = restTemplate.exchange("http://localhost:8081/api", HttpMethod.GET, - entity, String.class).getBody(); - - return result; + return new ResponseEntity<String>(token, HttpStatus.OK); } } diff --git a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/api/Hello.java b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/api/Hello.java deleted file mode 100644 index a76de3480..000000000 --- a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/api/Hello.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.opendes.indexer.azure.api; - -import org.opendes.core.util.IServiceAccountJwtClient; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.security.PermitAll; - -@RestController -@RequestMapping -public class Hello { - - @GetMapping("/hello") - @PermitAll - public String Default() { - return "Hello Azure Indexer!!!"; - } - -} diff --git a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/security/BasicAuthSecurityConfig.java b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/security/BasicAuthSecurityConfig.java new file mode 100644 index 000000000..69f11d5e3 --- /dev/null +++ b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/security/BasicAuthSecurityConfig.java @@ -0,0 +1,52 @@ +// Copyright © Microsoft Corporation +// +// 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.opendes.indexer.azure.security; + +import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class BasicAuthSecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + private AADAppRoleStatelessAuthenticationFilter appRoleAuthFilter; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .csrf().disable() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) + .and() + .authorizeRequests() + .antMatchers("/", "/index.html","/obo_api", + "/index-worker", "/_dps/task-handlers", "/_dps/task-handlers/**", + "/reindex", + "/v2/api-docs", + "/swagger-resources/**", + "/configuration/security", + "/swagger", + "/swagger-ui.html", + "/webjars/**").permitAll() + .anyRequest().authenticated() + .and() + .addFilterBefore(appRoleAuthFilter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/security/WhoamiController.java b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/security/WhoamiController.java new file mode 100644 index 000000000..d682b70a6 --- /dev/null +++ b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/security/WhoamiController.java @@ -0,0 +1,39 @@ +// Copyright © Microsoft Corporation +// +// 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.opendes.indexer.azure.security; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class WhoamiController { + @RequestMapping(value = {"/", "/whoami"}) + @ResponseBody + public String whoami() { + final Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + String userName = auth.getName(); + String roles = String.valueOf(auth.getAuthorities()); + String details = String.valueOf(auth.getPrincipal()); + + return "user: " + userName + "<BR>" + + "roles: " + roles + "<BR>" + + "details: " + details; + } +} + diff --git a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/HeadersInfoAzureImpl.java b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/HeadersInfoAzureImpl.java index 86b3f3276..6a0416eff 100644 --- a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/HeadersInfoAzureImpl.java +++ b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/HeadersInfoAzureImpl.java @@ -42,8 +42,6 @@ public class HeadersInfoAzureImpl implements IHeadersInfo { FORBIDDEN_FROM_LOGGING.add(DpsHeaders.ON_BEHALF_OF); } - private static final HashSet<String> FORWARDED_HEADERS = new HashSet<>(); - @Override public DpsHeaders getHeaders() { if (headersMap == null) { @@ -78,7 +76,7 @@ public class HeadersInfoAzureImpl implements IHeadersInfo { Preconditions.checkNotNull(input, "input headers cannot be null"); DpsHeaders output = DpsHeaders.createFromMap(input); - output.addCorrelationIdIfMissing(); + //output.addCorrelationIdIfMissing(); return output; } diff --git a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/RequestInfoImpl.java b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/RequestInfoImpl.java index fc8d95928..08839dac4 100644 --- a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/RequestInfoImpl.java +++ b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/RequestInfoImpl.java @@ -3,14 +3,12 @@ package org.opendes.indexer.azure.util; import com.google.common.base.Strings; import org.apache.http.HttpStatus; import org.opendes.core.model.DeploymentEnvironment; -import org.opendes.core.util.AppException; -import org.opendes.core.util.Config; -import org.opendes.core.util.IHeadersInfo; -import org.opendes.core.util.IServiceAccountJwtClient; +import org.opendes.core.util.*; import org.opendes.indexer.util.IRequestInfo; import org.opengroup.osdu.client.api.DpsHeaders; import org.opengroup.osdu.client.multitenancy.TenantInfo; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.RequestScope; @@ -23,6 +21,9 @@ import static org.opengroup.osdu.client.api.DpsHeaders.AUTHORIZATION; @RequestScope public class RequestInfoImpl implements IRequestInfo { + @Autowired + private DpsHeaders dpsHeaders; + @Autowired private IHeadersInfo headersInfo; @Autowired @@ -31,8 +32,13 @@ public class RequestInfoImpl implements IRequestInfo { @Autowired private TenantInfo tenantInfo; + @Value("${DEPLOYMENT_ENVIRONMENT}") + private String DEPLOYMENT_ENVIRONMENT; + + @Override public DpsHeaders getHeaders() { + return this.headersInfo.getHeaders(); } @@ -53,19 +59,18 @@ public class RequestInfoImpl implements IRequestInfo { @Override public DpsHeaders getHeadersWithDwdAuthZ() { - DpsHeaders output = this.headersInfo.getHeaders(); - output.put(AUTHORIZATION, this.checkOrGetAuthorizationHeader()); - return output; + this.dpsHeaders.put(AUTHORIZATION, this.checkOrGetAuthorizationHeader()); + return this.headersInfo.getHeaders(); } @Override - public boolean isCronRequest() { return false; } + public boolean isCronRequest() { return false;} @Override - public boolean isTaskQueueRequest() { return false; } + public boolean isTaskQueueRequest() {return false;} - private String checkOrGetAuthorizationHeader() { - if (Config.getDeploymentEnvironment() == DeploymentEnvironment.LOCAL) { + public String checkOrGetAuthorizationHeader() { + if (DeploymentEnvironment.valueOf(DEPLOYMENT_ENVIRONMENT) == DeploymentEnvironment.LOCAL) { String authHeader = this.headersInfo.getHeaders().getAuthorization(); if (Strings.isNullOrEmpty(authHeader)) { throw new AppException(HttpStatus.SC_UNAUTHORIZED, "Invalid authorization header", "Authorization token cannot be empty"); @@ -75,5 +80,4 @@ public class RequestInfoImpl implements IRequestInfo { return "Bearer " + this.serviceAccountJwtClient.getIdToken(tenantInfo.getName()); } } - } diff --git a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/ServiceAccountJwtClientImpl.java b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/ServiceAccountJwtClientImpl.java index c3aad79a3..ae4e49f44 100644 --- a/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/ServiceAccountJwtClientImpl.java +++ b/indexer-service-azure/src/main/java/org/opendes/indexer/azure/util/ServiceAccountJwtClientImpl.java @@ -1,7 +1,10 @@ package org.opendes.indexer.azure.util; +import com.auth0.jwt.JWT; import com.auth0.jwt.exceptions.JWTDecodeException; -import com.microsoft.aad.msal4j.*; +import com.microsoft.aad.adal4j.AuthenticationContext; +import com.microsoft.aad.adal4j.AuthenticationResult; +import com.microsoft.aad.adal4j.ClientCredential; import org.apache.http.HttpStatus; import org.opendes.core.cache.JwtCache; import org.opendes.core.logging.JaxRsDpsLog; @@ -19,6 +22,7 @@ import org.springframework.web.context.annotation.RequestScope; import javax.naming.ServiceUnavailableException; +import java.net.MalformedURLException; import java.util.*; import java.util.concurrent.*; @@ -31,114 +35,60 @@ public class ServiceAccountJwtClientImpl implements IServiceAccountJwtClient { @Autowired private IHeadersInfo headersInfoAzure; @Autowired + private DpsHeaders dpsHeaders; + @Autowired private JwtCache cacheService; @Autowired private JaxRsDpsLog log; @Autowired private AADConfiguration configuration; -// -// public String getIdToken(String tenantName) { -// this.log.info("Tenant name received for auth token is: " + tenantName); -// TenantInfo tenant = this.tenantInfoServiceProvider.getTenantInfo(tenantName); -// if (tenant == null) { -// this.log.error("Invalid tenant name receiving from azure"); -// throw new AppException(HttpStatus.SC_BAD_REQUEST, "Invalid tenant Name", "Invalid tenant Name from azure"); -// } -// String ACCESS_TOKEN = ""; -// try { -// -// IdToken cachedToken = this.cacheService.get(tenant.getServiceAccount()); -// this.headersInfoAzure.getHeaders().put(DpsHeaders.USER_EMAIL, tenant.getServiceAccount()); -// -// if (!IdToken.refreshToken(cachedToken)) { -// ACCESS_TOKEN = cachedToken.getTokenValue(); -// } -// -// ExecutorService service = Executors.newFixedThreadPool(1); -// AuthenticationContext context = null; -// -// try { -// context = new AuthenticationContext(configuration.getAuthority(), false, service); -// ClientCredential credential = new ClientCredential(configuration.getClientId(), configuration.getSecretKey()); -// Future<AuthenticationResult> future = context.acquireToken(configuration.getOboApi(), credential, null); -// -// ACCESS_TOKEN = future.get().getAccessToken(); -// -// if (future == null) { -// log.error(String.format("Azure Authentication: %s", future.get().getAccessToken())); -// throw new AppException(HttpStatus.SC_FORBIDDEN, "Access denied", "The user is not authorized to perform this action"); -// } -// IdToken idToken = IdToken.builder().tokenValue(ACCESS_TOKEN).expirationTimeMillis(JWT.decode(ACCESS_TOKEN).getExpiresAt().getTime()).build(); -// -// this.cacheService.put(tenant.getServiceAccount(), idToken); -// -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } catch (ExecutionException e) { -// e.printStackTrace(); -// } catch (MalformedURLException e) { -// e.printStackTrace(); -// } finally { -// service.shutdown(); -// } -// } 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); -// } -// -// return ACCESS_TOKEN; -// } public String getIdToken(String tenantName) { - this.log.info("Tenant name received for auth token is: " + tenantName); TenantInfo tenant = this.tenantInfoServiceProvider.getTenantInfo(tenantName); if (tenant == null) { this.log.error("Invalid tenant name receiving from azure"); throw new AppException(HttpStatus.SC_BAD_REQUEST, "Invalid tenant Name", "Invalid tenant Name from azure"); } - - String authToken = ""; - IAuthenticationResult updatedResult; + String ACCESS_TOKEN = ""; try { IdToken cachedToken = this.cacheService.get(tenant.getServiceAccount()); this.headersInfoAzure.getHeaders().put(DpsHeaders.USER_EMAIL, tenant.getServiceAccount()); + this.dpsHeaders.put(DpsHeaders.USER_EMAIL, tenant.getServiceAccount()); if (!IdToken.refreshToken(cachedToken)) { - authToken = cachedToken.getTokenValue(); - return authToken; + ACCESS_TOKEN = cachedToken.getTokenValue(); } - ConfidentialClientApplication application = ConfidentialClientApplication.builder( - configuration.getClientId(), - ClientCredentialFactory.create(configuration.getSecretKey())) - .authority(configuration.getAuthority()) - .build(); + ExecutorService service = Executors.newFixedThreadPool(1); + AuthenticationContext context = null; + + try { + context = new AuthenticationContext(configuration.getAuthority(), false, service); + ClientCredential credential = new ClientCredential(configuration.getClientId(), configuration.getSecretKey()); + Future<AuthenticationResult> future = context.acquireToken(configuration.getOboApi(), credential, null); + + ACCESS_TOKEN = future.get().getAccessToken(); - SilentParameters silentParameters = - SilentParameters.builder(Collections.singleton(configuration.getOboApi())) - .build(); - CompletableFuture<IAuthenticationResult> auth = application.acquireTokenSilently(silentParameters); + if (future == null) { + log.error(String.format("Azure Authentication: %s", future.get().getAccessToken())); + throw new AppException(HttpStatus.SC_FORBIDDEN, "Access denied", "The user is not authorized to perform this action"); + } + IdToken idToken = IdToken.builder().tokenValue(ACCESS_TOKEN).expirationTimeMillis(JWT.decode(ACCESS_TOKEN).getExpiresAt().getTime()).build(); - updatedResult = auth.join(); + this.cacheService.put(tenant.getServiceAccount(), idToken); - if (updatedResult == null) { - throw new ServiceUnavailableException("authentication result was null"); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } finally { + service.shutdown(); } -// if (updatedResult == null){ -// OnBehalfOfParameters parameters = -// OnBehalfOfParameters.builder(Collections.singleton(configuration.getOboApi()), -// new UserAssertion(authToken)) -// .build(); -// -// updatedResult = application.acquireToken(parameters).join(); -// } - authToken = updatedResult.accessToken(); } catch (JWTDecodeException e) { throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Persistence error", "Invalid token, error decoding", e); } catch (AppException e) { @@ -147,6 +97,6 @@ public class ServiceAccountJwtClientImpl implements IServiceAccountJwtClient { throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Persistence error", "Error generating token", e); } - return authToken; + return ACCESS_TOKEN; } } -- GitLab