Skip to content
Snippets Groups Projects
Commit e314fd3d authored by helayoty's avatar helayoty
Browse files

Add Azure implementation for retrieve access token task:

- implement getToken using MSAL lib
parent 682211d8
No related branches found
No related tags found
1 merge request!6Trusted ibm
......@@ -34,6 +34,17 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
......@@ -69,13 +80,11 @@
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-active-directory-spring-boot-starter</artifactId>
<version>${azure.version}</version>
<artifactId>msal4j</artifactId>
<version>0.5.0-preview</version>
</dependency>
<!-- Key vault dependency-->
<dependency>
<groupId>com.microsoft.azure</groupId>
......@@ -85,6 +94,7 @@
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-keyvault</artifactId>
<version>1.2.2</version>
<exclusions>
<exclusion>
<groupId>com.microsoft.azure</groupId>
......@@ -95,6 +105,7 @@
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-client-authentication</artifactId>
<version>1.6.12</version>
</dependency>
<!-- end KeyVault dependencies-->
</dependencies>
......
......@@ -7,7 +7,8 @@ import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfi
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class })
//@SpringBootApplication(exclude = { SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class })
@SpringBootApplication
@Configuration
@ComponentScan({"org.opendes.core", "org.opendes.indexer"})
public class IndexerAzureApplication {
......
package org.opendes.indexer.azure.security.msal4j;
import javax.servlet.http.HttpServletRequest;
import org.opendes.indexer.azure.util.ServiceAccountJwtClientImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
@Controller
public class ApiController {
@Autowired
ServiceAccountJwtClientImpl service;
@RequestMapping("/obo_api")
public ResponseEntity<String> callOboApi(HttpServletRequest httpRequest) throws Throwable {
String token = service.getIdToken("tenant1");
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;
}
}
package org.opendes.indexer.azure.security.msal4j;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Getter
@Setter
@Component
@ConfigurationProperties("aad")
public class BasicConfiguration {
String clientId;
String authority;
String redirectUri;
String secretKey;
String oboApi;
public String getAuthority(){
if (!authority.endsWith("/")) {
authority += "/";
}
return authority;
}
}
\ No newline at end of file
......@@ -2,23 +2,11 @@ package org.opendes.indexer.azure.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
import com.nimbusds.oauth2.sdk.GrantType;
import org.apache.http.HttpHeaders;
import com.microsoft.aad.msal4j.*;
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.opendes.client.api.DpsHeaders;
import org.opendes.client.multitenancy.ITenantFactory;
import org.opendes.client.multitenancy.TenantInfo;
......@@ -29,35 +17,18 @@ import org.opendes.core.util.AppException;
import org.opendes.core.util.IHeadersInfo;
import org.opendes.core.util.IServiceAccountJwtClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.opendes.indexer.azure.security.msal4j.BasicConfiguration;
import javax.naming.ServiceUnavailableException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.*;
import java.util.concurrent.*;
@Component
public class ServiceAccountJwtClientImpl implements IServiceAccountJwtClient {
@Value("${Auth_URL}")
private String Auth_URL;
@Value("${azure.activedirectory.client-id}")
private String client_id;
@Value("${azure.activedirectory.client-secret}")
private String client_secret;
@Value("${azure.activedirectory.AppIdUri}")
private String resource;
@Autowired
private ITenantFactory tenantInfoServiceProvider;
@Autowired
......@@ -66,51 +37,110 @@ public class ServiceAccountJwtClientImpl implements IServiceAccountJwtClient {
private JwtCache cacheService;
@Autowired
private JaxRsDpsLog log;
@Autowired
private BasicConfiguration 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 ACCESS_TOKEN = "";
String authToken = "";
IAuthenticationResult updatedResult;
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();
authToken = cachedToken.getTokenValue();
return authToken;
}
ExecutorService service = Executors.newFixedThreadPool(1);
AuthenticationContext context = null;
try {
context = new AuthenticationContext(Auth_URL, false, service);
ClientCredential credential = new ClientCredential(client_id, client_secret);
Future<AuthenticationResult> future = context.acquireToken(resource, credential, null);
ConfidentialClientApplication application = ConfidentialClientApplication.builder(
configuration.getClientId(),
ClientCredentialFactory.create(configuration.getSecretKey()))
.authority(configuration.getAuthority())
.build();
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);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} finally {
service.shutdown();
if (updatedResult == null) {
throw new ServiceUnavailableException("authentication result was null");
}
// 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) {
......@@ -119,60 +149,6 @@ public class ServiceAccountJwtClientImpl implements IServiceAccountJwtClient {
throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Persistence error", "Error generating token", e);
}
return ACCESS_TOKEN;
}
/*
private String getAccessTokenByRequest(){
//prepare auth request
Map<String, Object> requestParam = this.getParameterHeaders();
TokenRequest tokenRequest = new TokenRequest(GrantType.CLIENT_CREDENTIALS);
tokenRequest.setPayload(JSON_FACTORY.toString(requestParam));
String serviceAccountName = String.format(SERVICE_ACCOUNT_NAME_FORMAT, tenant.getProjectId(), tenant.getServiceAccount());
Iam.Projects.ServiceAccounts.SignJwt signJwt = this.getIam().projects().serviceAccounts().signJwt(serviceAccountName, signJwtRequest);
SignJwtResponse signJwtResponse = signJwt.execute();
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();
IdToken idToken = IdToken.builder().tokenValue(token).expirationTimeMillis(JWT.decode(token).getExpiresAt().getTime()).build();
this.cacheService.put(tenant.getServiceAccount(), idToken);
return token;
}
}
private Map<String, Object> getParameterHeaders() {
Map<String, Object> param = new HashMap<>();
param.put("grant_type", GRANT_Type);
param.put("client_id", client_id);
param.put("client_secret", client_secret);
param.put("resource", resource );
return param;
return authToken;
}
*/
}
......@@ -15,11 +15,7 @@
limitations under the License.
-->
<configuration scan="true">
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="org.springframework.security" level="DEBUG"/>
<configuration>
<appender name="Console"
class="ch.qos.logback.core.ConsoleAppender">
......
......@@ -14,36 +14,36 @@
package org.opendes.indexer.middleware;
import com.google.api.client.http.HttpMethods;
import com.google.common.base.Strings;
import org.apache.http.HttpStatus;
import org.opendes.client.api.DpsHeaders;
import org.opendes.core.auth.AuthorizationService;
import org.opendes.core.logging.JaxRsDpsLog;
import org.opendes.core.model.AuthorizationResponse;
import org.opendes.core.util.AppException;
import org.opendes.indexer.util.IRequestInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.servlet.*;
import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
//
//import com.google.api.client.http.HttpMethods;
//import com.google.common.base.Strings;
//
//import org.apache.http.HttpStatus;
//import org.opendes.client.api.DpsHeaders;
//import org.opendes.core.auth.AuthorizationService;
//import org.opendes.core.logging.JaxRsDpsLog;
//import org.opendes.core.model.AuthorizationResponse;
//import org.opendes.core.util.AppException;
//import org.opendes.indexer.util.IRequestInfo;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Lazy;
//import org.springframework.stereotype.Component;
//
//import javax.annotation.security.PermitAll;
//import javax.annotation.security.RolesAllowed;
//import javax.servlet.*;
//import javax.servlet.Filter;
//import javax.servlet.http.HttpServletRequest;
//import javax.ws.rs.container.ResourceInfo;
//import javax.ws.rs.core.Context;
//
//import java.io.IOException;
//import java.lang.reflect.Method;
//import java.util.Arrays;
//import java.util.List;
//@Component
public class IndexerFilter {
//public class IndexerFilter {
// private static final String DISABLE_AUTH_PROPERTY = "com.slb.indexer.disableAuth";
// private static final String PATH_SWAGGER = "/swagger.json";
......@@ -141,4 +141,4 @@ public class IndexerFilter {
// }
}
\ No newline at end of file
//}
\ 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