Skip to content

Commit 68bba51

Browse files
committed
Required feature update
1 parent 440e42d commit 68bba51

File tree

3 files changed

+72
-17
lines changed

3 files changed

+72
-17
lines changed

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AzureArcManagedIdentitySource.java

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@
1919

2020
class AzureArcManagedIdentitySource extends AbstractManagedIdentitySource{
2121

22-
private final static Logger LOG = LoggerFactory.getLogger(AzureArcManagedIdentitySource.class);
22+
private static final Logger LOG = LoggerFactory.getLogger(AzureArcManagedIdentitySource.class);
2323
private static final String ARC_API_VERSION = "2019-11-01";
2424
private static final String AZURE_ARC = "Azure Arc";
25+
private static final String WINDOWS_PATH = System.getenv("ProgramData") + "/AzureConnectedMachineAgent/Tokens/";
26+
private static final String LINUX_PATH = "/var/opt/azcmagent/tokens/";
27+
private static final String FILE_EXTENSION = ".key";
28+
private static final int MAX_FILE_SIZE_BYTES = 4096;
2529

2630
private final URI MSI_ENDPOINT;
2731

@@ -91,13 +95,13 @@ public ManagedIdentityResponse handleResponse(
9195
LOG.info("[Managed Identity] Response received. Status code: {response.StatusCode}");
9296

9397
if (response.statusCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
94-
if(!response.headers().containsKey("Www-Authenticate")) {
98+
if(!response.headers().containsKey("WWW-Authenticate")) {
9599
LOG.error("[Managed Identity] WWW-Authenticate header is expected but not found.");
96100
throw new MsalServiceException(MsalErrorMessage.MANAGED_IDENTITY_NO_CHALLENGE_ERROR, MsalError.MANAGED_IDENTITY_REQUEST_FAILED,
97101
ManagedIdentitySourceType.AZURE_ARC);
98102
}
99103

100-
String challenge = response.headers().get("Www-Authenticate").get(0);
104+
String challenge = response.headers().get("WWW-Authenticate").get(0);
101105
String[] splitChallenge = challenge.split("=");
102106

103107
if (splitChallenge.length != 2) {
@@ -106,7 +110,15 @@ public ManagedIdentityResponse handleResponse(
106110
ManagedIdentitySourceType.AZURE_ARC);
107111
}
108112

109-
Path path = Paths.get(splitChallenge[1]);
113+
Path path = Paths.get(splitChallenge[1]).normalize();
114+
115+
validateFile(path);
116+
117+
if (!path.toFile().exists()) {
118+
LOG.error("[Managed Identity] The WWW-Authenticate header specifies a file that does not exist");
119+
throw new MsalServiceException(MsalErrorMessage.MANAGED_IDENTITY_INVALID_FILEPATH, MsalError.MANAGED_IDENTITY_FILE_READ_ERROR,
120+
ManagedIdentitySourceType.AZURE_ARC);
121+
}
110122

111123
String authHeaderValue = null;
112124
try {
@@ -137,4 +149,35 @@ public ManagedIdentityResponse handleResponse(
137149

138150
return super.handleResponse(parameters, response);
139151
}
152+
153+
private void validateFile(Path path) {
154+
String osName = System.getProperty("os.name").toLowerCase();
155+
if (!(osName.contains("windows") || osName.contains("linux"))) {
156+
LOG.error(String.format("[Managed Identity] Unsupported platform: %s", osName));
157+
throw new MsalServiceException(MsalErrorMessage.MANAGED_IDENTITY_PLATFORM_NOT_SUPPORTED, MsalError.MANAGED_IDENTITY_FILE_READ_ERROR,
158+
ManagedIdentitySourceType.AZURE_ARC);
159+
}
160+
161+
if (isValidWindowsPath(path) || isValidLinuxPath(path)) {
162+
if (path.toFile().length() > MAX_FILE_SIZE_BYTES) {
163+
LOG.error(String.format("[Managed Identity] File is larger than %s bytes.", MAX_FILE_SIZE_BYTES));
164+
throw new MsalServiceException(MsalErrorMessage.MANAGED_IDENTITY_INVALID_FILEPATH, MsalError.MANAGED_IDENTITY_FILE_READ_ERROR,
165+
ManagedIdentitySourceType.AZURE_ARC);
166+
}
167+
} else {
168+
LOG.error("[Managed Identity] Invalid filepath.");
169+
throw new MsalServiceException(MsalErrorMessage.MANAGED_IDENTITY_INVALID_FILEPATH, MsalError.MANAGED_IDENTITY_FILE_READ_ERROR,
170+
ManagedIdentitySourceType.AZURE_ARC);
171+
}
172+
173+
LOG.error("[Managed Identity] Path passed validation.");
174+
}
175+
176+
private boolean isValidWindowsPath(Path path) {
177+
return path.startsWith(WINDOWS_PATH) && path.toString().toLowerCase().endsWith(FILE_EXTENSION);
178+
}
179+
180+
private boolean isValidLinuxPath(Path path) {
181+
return path.startsWith(LINUX_PATH) && path.toString().toLowerCase().endsWith(FILE_EXTENSION);
182+
}
140183
}

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/MsalErrorMessage.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ class MsalErrorMessage {
1111

1212
public static final String MANAGED_IDENTITY_INVALID_CHALLENGE = "[Managed Identity] The WWW-Authenticate header in the response from Azure Arc Managed Identity Endpoint did not match the expected format.";
1313

14+
public static final String MANAGED_IDENTITY_PLATFORM_NOT_SUPPORTED = "[Managed Identity] This managed identity source is not available on this platform.";
15+
16+
public static final String MANAGED_IDENTITY_INVALID_FILEPATH = "[Managed Identity] The file on the file path in the WWW-Authenticate header is not secure or could not be found.";
17+
1418
public static final String MANAGED_IDENTITY_USER_ASSIGNED_NOT_CONFIGURABLE_AT_RUNTIME = "[Managed Identity] Service Fabric user assigned managed identity ClientId or ResourceId is not configurable at runtime.";
1519

1620
public static final String MANAGED_IDENTITY_USER_ASSIGNED_NOT_SUPPORTED = "[Managed Identity] User assigned identity is not supported by the %s Managed Identity. To authenticate with the system assigned identity use ManagedIdentityApplication.builder(ManagedIdentityId.systemAssigned()).build().";

msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/ManagedIdentityTests.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import java.util.HashMap;
2525
import java.util.List;
2626
import java.util.Map;
27+
import java.util.concurrent.CompletableFuture;
28+
import java.util.concurrent.ExecutionException;
2729

2830
import static org.junit.jupiter.api.Assertions.*;
2931
import static org.mockito.Mockito.*;
@@ -602,25 +604,23 @@ void azureArcManagedIdentity_InvalidAuthHeader() throws Exception {
602604
}
603605

604606
@Test
605-
void azureArcManagedIdentityAuthheaderTest() throws Exception {
606-
Path path = Paths.get(this.getClass().getResource("/msi-azure-arc-secret.txt").toURI());
607+
void azureArcManagedIdentityAuthheaderValidationTest() throws Exception {
607608
IEnvironmentVariables environmentVariables = new EnvironmentVariablesHelper(ManagedIdentitySourceType.AZURE_ARC, azureArcEndpoint);
608609
ManagedIdentityApplication.setEnvironmentVariables(environmentVariables);
609610
ManagedIdentityClient.resetManagedIdentitySourceType();
610611
DefaultHttpClient httpClientMock = mock(DefaultHttpClient.class);
611612

612-
// Mock 401 response that returns www-authenticate header
613+
//Both a missing file and an invalid path structure should throw an exception
614+
Path validPathWithMissingFile = Paths.get(System.getenv("ProgramData")+ "/AzureConnectedMachineAgent/Tokens/secret.key");
615+
Path invalidPathWithRealFile = Paths.get(this.getClass().getResource("/msi-azure-arc-secret.txt").toURI());
616+
617+
// Mock 401 response that returns WWW-Authenticate header
613618
HttpResponse response = new HttpResponse();
614619
response.statusCode(HttpStatus.SC_UNAUTHORIZED);
615-
response.headers().put("Www-Authenticate", Collections.singletonList("Basic realm=" + path));
620+
response.headers().put("WWW-Authenticate", Collections.singletonList("Basic realm=" + validPathWithMissingFile));
616621

617622
when(httpClientMock.send(expectedRequest(ManagedIdentitySourceType.AZURE_ARC, resource))).thenReturn(response);
618623

619-
// Mock the response when Authorization header is sent in request
620-
HttpRequest expectedRequest = expectedRequest(ManagedIdentitySourceType.AZURE_ARC, resource);
621-
expectedRequest.headers().put("Authorization", "Basic secret");
622-
when(httpClientMock.send(expectedRequest)).thenReturn(expectedResponse(200, getSuccessfulResponse(resource)));
623-
624624
miApp = ManagedIdentityApplication
625625
.builder(ManagedIdentityId.systemAssigned())
626626
.httpClient(httpClientMock)
@@ -629,10 +629,18 @@ void azureArcManagedIdentityAuthheaderTest() throws Exception {
629629
// Clear caching to avoid cross test pollution.
630630
miApp.tokenCache().accessTokens.clear();
631631

632-
IAuthenticationResult result = miApp.acquireTokenForManagedIdentity(
633-
ManagedIdentityParameters.builder(resource)
634-
.build()).get();
632+
CompletableFuture<IAuthenticationResult> future = miApp.acquireTokenForManagedIdentity(ManagedIdentityParameters.builder(resource).build());
635633

636-
assertNotNull(result.accessToken());
634+
ExecutionException ex = assertThrows(ExecutionException.class, future::get);
635+
assertTrue(ex.getCause() instanceof MsalServiceException);
636+
assertTrue(ex.getMessage().contains(MsalErrorMessage.MANAGED_IDENTITY_INVALID_FILEPATH));
637+
638+
response.headers().put("WWW-Authenticate", Collections.singletonList("Basic realm=" + invalidPathWithRealFile));
639+
640+
future = miApp.acquireTokenForManagedIdentity(ManagedIdentityParameters.builder(resource).build());
641+
642+
ex = assertThrows(ExecutionException.class, future::get);
643+
assertTrue(ex.getCause() instanceof MsalServiceException);
644+
assertTrue(ex.getMessage().contains(MsalErrorMessage.MANAGED_IDENTITY_INVALID_FILEPATH));
637645
}
638646
}

0 commit comments

Comments
 (0)