Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,4 @@ env.properties

/hercules/config.json
gradle.properties
center/appsettings.json
20 changes: 1 addition & 19 deletions azure-pipelines-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ stages:
script: |
cp android_client/app/build/outputs/apk/release/app-release.apk common/src/main/resources/record_release.apk -force
- task: Gradle@2
displayName: Build center (with MISE)
displayName: Build center
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'center:bootJar --stacktrace'
Expand All @@ -158,7 +158,6 @@ stages:
jdkVersionOption: '1.11'
sonarQubeRunAnalysis: false
spotBugsAnalysis: false
options: '-PIDDPUsername=IdentityDivision -PIDDPPassword=$(IDDPPassword) -PenableMISE=true'
condition: eq(variables.fullBuild, 'true')
- task: Gradle@2
displayName: Build agent
Expand Down Expand Up @@ -207,23 +206,6 @@ stages:
Contents: '*.jar'
TargetFolder: '$(Build.ArtifactStagingDirectory)/center_deploy'
condition: eq(variables.fullBuild, 'true')
- task: Gradle@2
displayName: Build center (without MISE)
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'center:bootJar --stacktrace'
publishJUnitResults: false
javaHomeOption: 'JDKVersion'
jdkVersionOption: '1.11'
sonarQubeRunAnalysis: false
spotBugsAnalysis: false
- task: CopyFiles@2
displayName: Copy center jar
inputs:
SourceFolder: 'center/build/libs/'
Contents: '*.jar'
TargetFolder: '$(Build.ArtifactStagingDirectory)/center_publish'
condition: eq(variables.fullBuild, 'true')
- task: Gradle@2
displayName: Package Mac installer
inputs:
Expand Down
7 changes: 7 additions & 0 deletions center/Dockerfile_MISE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# run this after: ./gradlew.bat :bootJar
FROM hydralabreistry.azurecr.io/mise-1p-container-image:1.36.0-mise-azurelinux3.0-distroless

ARG CONFIG_FILE=appsettings.json
COPY ${CONFIG_FILE} /app/appsettings.json

ENTRYPOINT ["dotnet", "Microsoft.Identity.ServiceEssentials.Container.dll"]
13 changes: 0 additions & 13 deletions center/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,6 @@ bootJar.dependsOn("checkstyleMain")

repositories {
mavenCentral()
if (project.hasProperty("enableMISE")) {
maven {
url 'https://identitydivision.pkgs.visualstudio.com/_packaging/IDDP/maven/v1'
name 'IDDP'
credentials(PasswordCredentials)
authentication {
basic(BasicAuthentication)
}
}
}
}

bootJar {
Expand Down Expand Up @@ -77,9 +67,6 @@ dependencies {

compileOnly 'org.projectlombok:lombok:1.18.20'
annotationProcessor 'org.projectlombok:lombok:1.18.20'
if (project.hasProperty("enableMISE")) {
compile(group: 'com.microsoft.identity.service.essentials', name: 'java-adapter', version: '1.32.0')
}
}

import org.apache.tools.ant.taskdefs.condition.Os
Expand Down
184 changes: 106 additions & 78 deletions center/src/main/java/com/microsoft/hydralab/center/util/AuthUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,16 @@ public class AuthUtil {
String scope;
@Value("${spring.security.oauth2.client.provider.azure-ad.mise-enabled:false}")
boolean miseEnabled;
@Value("${spring.security.oauth2.client.provider.azure-ad.mise-endpoint:}")
String miseEndpoint;

Map<String, Boolean> urlMapping = null;
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(AuthUtil.class);

public boolean isValidToken(String token) {
LOGGER.info("Starting token validation...");
if (miseEnabled) {
return validateTokenWithMISE(token);
if (miseEnabled && miseEndpoint != null && !miseEndpoint.isEmpty()) {
return validateTokenWithMISEEndpoint(token);
}
return validateTokenWithPublicKey(token) && validateAudienceAndExpiredTime(token);
}
Expand Down Expand Up @@ -118,91 +120,117 @@ private boolean validateTokenWithPublicKey(String token) {
}
}

private boolean validateTokenWithMISE(String token) {
private boolean validateTokenWithMISEEndpoint(String token) {
LOGGER.info("Starting MISE token validation...");

// call MISE endpoint to validate the token
try {
// Mise mise = Mise.createClient();
Class<?> miseClass = Class.forName("com.microsoft.identity.service.essentials.Mise");
Object mise = miseClass.getMethod("createClient").invoke(null);

LOGGER.info("Initializing MISE...");
// mise.assignLogMessageCallback(new Mise.ILogCallback() {...}, null);
Class<?> iLogCallbackClass = Class.forName("com.microsoft.identity.service.essentials.Mise$ILogCallback");

Object logCallback = java.lang.reflect.Proxy.newProxyInstance(
iLogCallbackClass.getClassLoader(),
new Class<?>[]{iLogCallbackClass},
(proxy, method, args) -> {
String methodName = method.getName();
if ("callback".equals(methodName)) {
Object level = args[0];
String message = (String) args[1];
// Print all log levels for simplicity
LOGGER.info(message);
}
return null;
}
);

miseClass.getMethod("assignLogMessageCallback", iLogCallbackClass, Object.class)
.invoke(mise, logCallback, null);
// Configure MISE
JSONObject config = new JSONObject();
JSONObject azureAd = new JSONObject();
azureAd.put("Instance", instanceUri);
azureAd.put("ClientId", clientId);
azureAd.put("TenantId", tenantId);
String[] audiences = audience.split(",");
azureAd.put("Audiences", audiences);
JSONObject logging = new JSONObject();
logging.put("logLevel", "Debug");
azureAd.put("Logging", logging);
config.put("AzureAd", azureAd);

miseClass.getMethod("configure", String.class, String.class)
.invoke(mise, config.toString(), null);

// MiseValidationInput miseValidationInput = new MiseValidationInput();
Class<?> miseValidationInputClass = Class.forName("com.microsoft.identity.service.essentials.MiseValidationInput");
Object miseValidationInput = miseValidationInputClass.getDeclaredConstructor().newInstance();

miseValidationInputClass.getField("authorizationHeader").set(miseValidationInput, "Bearer " + token);
miseValidationInputClass.getField("originalMethodHeader").set(miseValidationInput, "GET");
miseValidationInputClass.getField("originalUriHeader").set(miseValidationInput, "https://myapi.com/api/values");

// try (MiseValidationResult validationResult = mise.validate(miseValidationInput)) { ... }
Object validationResult = miseClass.getMethod("validate", miseValidationInputClass)
.invoke(mise, miseValidationInput);

// mise.unassignLogMessageCallback();
miseClass.getMethod("unassignLogMessageCallback")
.invoke(mise);

Class<?> miseValidationResultClass = Class.forName("com.microsoft.identity.service.essentials.MiseValidationResult");
int statusCode = (int) miseValidationResultClass.getMethod("getHttpResponseStatusCode").invoke(validationResult);
LOGGER.info("Status code " + statusCode);

String errorDescription = (String) miseValidationResultClass.getMethod("getErrorDescription").invoke(validationResult);
if (errorDescription != null) {
LOGGER.error("Error message " + errorDescription);
}
// Close validationResult if AutoCloseable
if (validationResult instanceof AutoCloseable) {
((AutoCloseable) validationResult).close();
}
if (statusCode != 200) {
LOGGER.error("MISE token validation failed with status code: " + statusCode);
RestTemplate restTemplateHttps = new RestTemplate(RestTemplateConfig.generateHttpRequestFactory());
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
headers.add("Authorization", "Bearer " + token);
headers.add("Original-Uri", "https://myapi.com/api/values");
headers.add("Original-Method", "GET");
headers.add("X-Forwarded-For", "1.2.3.4");
HttpEntity<HttpHeaders> entity = new HttpEntity<>(headers);
ResponseEntity<JSONObject> response = restTemplateHttps.exchange(miseEndpoint, HttpMethod.POST, entity, JSONObject.class);
if (response.getStatusCode().is2xxSuccessful()) {
LOGGER.info("MISE token validation passed");
return true;
} else {
LOGGER.error("MISE token validation failed with status code: " + response.getStatusCodeValue());
return false;
}
LOGGER.info("MISE token validation passed");
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("MISE token validation failed: " + e.getMessage());
return false;
}
return true;
}

// private boolean validateTokenWithMISE(String token) {
// LOGGER.info("Starting MISE token validation...");
//
// try {
// // Mise mise = Mise.createClient();
// Class<?> miseClass = Class.forName("com.microsoft.identity.service.essentials.Mise");
// Object mise = miseClass.getMethod("createClient").invoke(null);
//
// LOGGER.info("Initializing MISE...");
// // mise.assignLogMessageCallback(new Mise.ILogCallback() {...}, null);
// Class<?> iLogCallbackClass = Class.forName("com.microsoft.identity.service.essentials.Mise$ILogCallback");
//
// Object logCallback = java.lang.reflect.Proxy.newProxyInstance(
// iLogCallbackClass.getClassLoader(),
// new Class<?>[]{iLogCallbackClass},
// (proxy, method, args) -> {
// String methodName = method.getName();
// if ("callback".equals(methodName)) {
// Object level = args[0];
// String message = (String) args[1];
// // Print all log levels for simplicity
// LOGGER.info(message);
// }
// return null;
// }
// );
//
// miseClass.getMethod("assignLogMessageCallback", iLogCallbackClass, Object.class)
// .invoke(mise, logCallback, null);
// // Configure MISE
// JSONObject config = new JSONObject();
// JSONObject azureAd = new JSONObject();
// azureAd.put("Instance", instanceUri);
// azureAd.put("ClientId", clientId);
// azureAd.put("TenantId", tenantId);
// String[] audiences = audience.split(",");
// azureAd.put("Audiences", audiences);
// JSONObject logging = new JSONObject();
// logging.put("logLevel", "Debug");
// azureAd.put("Logging", logging);
// config.put("AzureAd", azureAd);
//
// miseClass.getMethod("configure", String.class, String.class)
// .invoke(mise, config.toString(), null);
//
// // MiseValidationInput miseValidationInput = new MiseValidationInput();
// Class<?> miseValidationInputClass = Class.forName("com.microsoft.identity.service.essentials.MiseValidationInput");
// Object miseValidationInput = miseValidationInputClass.getDeclaredConstructor().newInstance();
//
// miseValidationInputClass.getField("authorizationHeader").set(miseValidationInput, "Bearer " + token);
// miseValidationInputClass.getField("originalMethodHeader").set(miseValidationInput, "GET");
// miseValidationInputClass.getField("originalUriHeader").set(miseValidationInput, "https://myapi.com/api/values");
//
// // try (MiseValidationResult validationResult = mise.validate(miseValidationInput)) { ... }
// Object validationResult = miseClass.getMethod("validate", miseValidationInputClass)
// .invoke(mise, miseValidationInput);
//
// // mise.unassignLogMessageCallback();
// miseClass.getMethod("unassignLogMessageCallback")
// .invoke(mise);
//
// Class<?> miseValidationResultClass = Class.forName("com.microsoft.identity.service.essentials.MiseValidationResult");
// int statusCode = (int) miseValidationResultClass.getMethod("getHttpResponseStatusCode").invoke(validationResult);
// LOGGER.info("Status code " + statusCode);
//
// String errorDescription = (String) miseValidationResultClass.getMethod("getErrorDescription").invoke(validationResult);
// if (errorDescription != null) {
// LOGGER.error("Error message " + errorDescription);
// }
// // Close validationResult if AutoCloseable
// if (validationResult instanceof AutoCloseable) {
// ((AutoCloseable) validationResult).close();
// }
// if (statusCode != 200) {
// LOGGER.error("MISE token validation failed with status code: " + statusCode);
// return false;
// }
// LOGGER.info("MISE token validation passed");
// } catch (Exception e) {
// e.printStackTrace();
// return false;
// }
// return true;
// }

private PublicKey getPublicKey(JWSObject jwsObject, JWKSet jwkSet) throws JOSEException {
JWSAlgorithm algorithm = jwsObject.getHeader().getAlgorithm();
if (!algorithm.equals(JWSAlgorithm.RS256)) {
Expand Down
1 change: 1 addition & 0 deletions center/src/main/resources/application-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ spring:
audience: ${MICROSOFT_PROVIDER_AUDIENCE}
instance-uri: ${MICROSOFT_PROVIDER_INSTANCE_URI}
mise-enabled: ${MICROSOFT_MISE_ENABLED:false}
mise-endpoint: ${MICROSOFT_MISE_ENDPOINT}
registration:
azure-client:
provider: azure-ad
Expand Down
Loading