Skip to content

Commit 3503d1f

Browse files
fadidurahrichardtz12Copilot
authored
Update common UI infrastucture to point to ID4SLAB2, Fixes AB#3525444 (#2907)
Updating our ui infrastructure to point to ID4SLAB2 resources - Updated LAB api signatures - Updated UPN JSON fields - Rewire existing test classes to use the UPN JSON rather than LabQuery, which is no longer available on ID4SLAB2 [AB#3525444](https://identitydivision.visualstudio.com/fac9d424-53d2-45c0-91b5-ef6ba7a6bf26/_workitems/edit/3525444) --------- Co-authored-by: richardtz12 <richardtz12@gmail.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 111cc5a commit 3503d1f

25 files changed

Lines changed: 243 additions & 147 deletions

File tree

LabApiUtilities/src/main/com/microsoft/identity/labapi/utilities/client/ILabAccount.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ public interface ILabAccount {
5858
*/
5959
String getHomeTenantId();
6060

61+
/**
62+
* Get the guest tenant id of this account.
63+
*
64+
* @return a String representing the account's guest tenant id
65+
*/
66+
String getGuestTenantId();
67+
6168
/**
6269
* Get the object id in home tenant.
6370
*

LabApiUtilities/src/main/com/microsoft/identity/labapi/utilities/client/LabAccount.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class LabAccount implements ILabAccount {
5555
private final String mHomeObjectId;
5656

5757
private final String mAssociatedClientId;
58+
private final String mGuestTenantId;
5859

5960
private final String mCloudUrl;
6061

LabApiUtilities/src/main/com/microsoft/identity/labapi/utilities/client/LabClient.java

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,54 @@ public class LabClient implements ILabClient {
7373
public static final long TEMP_USER_WAIT_TIME = TimeUnit.SECONDS.toMillis(35);
7474

7575
private static final String ACCOUNT_UPN_JSON_STRING_SECRET_NAME = "Android-ID4SLAB2-User-Identifiers";
76+
private String mAlternativeUpnJsonStringSecretName;
7677
private Map<String, LabJsonStringAccountEntry> labUPNJsonMap = null;
7778

79+
/**
80+
* Holds the most recently fetched or created {@link ILabAccount} for this process.
81+
* <p>
82+
* Updated after each successful account fetch or temporary user creation via
83+
* {@link LabClient}. Intended as a convenience for single-threaded test scenarios
84+
* that need access to the last lab account without explicitly passing it between
85+
* methods.
86+
* </p>
87+
* <p>
88+
* The field is {@code volatile} to ensure writes performed by one thread are
89+
* immediately visible to all other threads. It may be {@code null} if no account
90+
* has been fetched or created yet. Do not rely on this field in multi-threaded
91+
* or production code.
92+
* </p>
93+
*/
94+
public static volatile ILabAccount latestLabAccount = null;
95+
96+
/**
97+
* Updates {@link #latestLabAccount} from a static context, avoiding the
98+
* {@code ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD} SpotBugs warning that would
99+
* be raised if instance methods assigned the field directly.
100+
* <p>
101+
* This method is thread-safe; the underlying field is {@code volatile}, so
102+
* the write is immediately visible to all threads. {@code account} may be
103+
* {@code null} to clear the stored account.
104+
* </p>
105+
*
106+
* @param account The {@link ILabAccount} to store as the latest; may be {@code null}.
107+
*/
108+
private static void setLatestLabAccount(final ILabAccount account) {
109+
latestLabAccount = account;
110+
}
111+
112+
/**
113+
* This method allows tests to specify an alternative secret name for the UPN JSON string in Key Vault,
114+
* as the default points specifically to Android Team's upn json. T
115+
*
116+
* IF YOU ARE THE ONEAUTH TEAM, PLEASE USE THIS METHOD TO POINT TO YOUR OWN JSON SECRET NAME
117+
*
118+
* @param secretName The name of the alternative Key Vault secret to use for fetching the UPN JSON string.
119+
*/
120+
public void setAccountUpnJsonStringSecretName(final String secretName) {
121+
mAlternativeUpnJsonStringSecretName = secretName;
122+
}
123+
78124
@Override
79125
public ILabAccount getLabAccount(@NonNull final LabQuery labQuery) throws LabApiException {
80126
// Adding a second attempt here, api sometimes fails to fetch the user.
@@ -142,7 +188,7 @@ private ILabAccount getLabAccountObject(@NonNull final ConfigInfo configInfo) th
142188

143189
final String password = getPassword(configInfo);
144190

145-
return new LabAccount.LabAccountBuilder()
191+
final LabAccount account = new LabAccount.LabAccountBuilder()
146192
.username(username)
147193
.password(password)
148194
.userType(UserType.fromName(configInfo.getUserInfo().getUserType()))
@@ -152,6 +198,10 @@ private ILabAccount getLabAccountObject(@NonNull final ConfigInfo configInfo) th
152198
.cloudUrl(configInfo.getLabInfo().getAuthority())
153199
.azureEnvironment(configInfo.getLabInfo().getAzureEnvironment())
154200
.build();
201+
202+
setLatestLabAccount(account);
203+
204+
return account;
155205
}
156206

157207
private List<ConfigInfo> fetchConfigsFromLab(@NonNull final String upn) throws LabApiException {
@@ -252,14 +302,17 @@ private ILabAccount createTempAccountInternal(@NonNull final TempUserType tempUs
252302

253303
final String password = getPassword(tempUser);
254304

255-
return new LabAccount.LabAccountBuilder()
305+
final LabAccount account = new LabAccount.LabAccountBuilder()
256306
.username(tempUser.getUpn())
257307
.password(password)
258308
// all temp users created by Lab Api are currently cloud users
259309
.userType(UserType.CLOUD)
260310
.homeTenantId(tempUser.getTenantId())
261311
.homeObjectId(tempUser.getObjectId())
262312
.build();
313+
314+
setLatestLabAccount(account);
315+
return account;
263316
}
264317

265318
@Override
@@ -335,8 +388,15 @@ public Map<String, LabJsonStringAccountEntry> getAccountMapJsonFromMobileBuildKe
335388
);
336389
final KeyVaultSecretsApi keyVaultSecretsApi = new KeyVaultSecretsApi(KeyVaultSecretsApi.MOBILE_BUILD_VAULT_URL);
337390

391+
final String keyvaultSecret;
392+
if (mAlternativeUpnJsonStringSecretName != null && !mAlternativeUpnJsonStringSecretName.isEmpty()) {
393+
keyvaultSecret = mAlternativeUpnJsonStringSecretName;
394+
} else {
395+
keyvaultSecret = ACCOUNT_UPN_JSON_STRING_SECRET_NAME;
396+
}
397+
338398
try {
339-
final SecretBundle secretBundle = keyVaultSecretsApi.getKeyVaultSecret(ACCOUNT_UPN_JSON_STRING_SECRET_NAME);
399+
final SecretBundle secretBundle = keyVaultSecretsApi.getKeyVaultSecret(keyvaultSecret);
340400

341401
labUPNJsonMap = LabJsonStringAccountEntry.parseJsonToMap(secretBundle.getValue());
342402
return labUPNJsonMap;
@@ -356,16 +416,21 @@ public ILabAccount getAccountFromLabJsonStringInMobileBuildVault(UserType userTy
356416
}
357417
final String accountPassword = getPassword(accountEntry.getKeyVaultEntry());
358418

359-
return new LabAccount.LabAccountBuilder()
419+
final LabAccount account = new LabAccount.LabAccountBuilder()
360420
.username(accountEntry.getUpn())
361421
.password(accountPassword)
362422
.userType(userType)
363423
.homeTenantId(accountEntry.getHomeTenantId())
364424
.homeObjectId(accountEntry.getHomeObjectId())
425+
.guestTenantId(accountEntry.getGuestTenantId())
426+
.associatedClientId(accountEntry.getAssociatedClientId())
365427
.azureEnvironment(accountEntry.getAzureEnvironment())
366428
.cloudUrl(accountEntry.getCloudUrl())
367429
.build();
368430

431+
setLatestLabAccount(account);
432+
433+
return account;
369434
}
370435

371436
@Override

LabApiUtilities/src/main/com/microsoft/identity/labapi/utilities/client/LabJsonStringAccountEntry.java

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,20 @@
2424
package com.microsoft.identity.labapi.utilities.client;
2525

2626
import com.google.gson.Gson;
27+
import com.google.gson.GsonBuilder;
28+
import com.google.gson.JsonDeserializationContext;
29+
import com.google.gson.JsonDeserializer;
30+
import com.google.gson.JsonElement;
31+
import com.google.gson.JsonObject;
32+
import com.google.gson.JsonParseException;
2733
import com.google.gson.annotations.SerializedName;
2834
import com.google.gson.reflect.TypeToken;
2935

3036
import java.io.Serializable;
37+
import java.lang.reflect.Field;
3138
import java.lang.reflect.Type;
39+
import java.util.HashMap;
40+
import java.util.Locale;
3241
import java.util.Map;
3342

3443
import lombok.Getter;
@@ -48,6 +57,12 @@ public class LabJsonStringAccountEntry implements Serializable {
4857
@SerializedName("HomeTenantId")
4958
private String homeTenantId;
5059

60+
@SerializedName("GuestTenantId")
61+
private String guestTenantId;
62+
63+
@SerializedName("AssociatedClientId")
64+
private String associatedClientId;
65+
5166
@SerializedName("KeyVaultEntry")
5267
private String keyVaultEntry;
5368

@@ -64,8 +79,48 @@ public class LabJsonStringAccountEntry implements Serializable {
6479
* @return a map of key to LabJsonStringAccountEntry
6580
*/
6681
public static Map<String, LabJsonStringAccountEntry> parseJsonToMap(String json) {
67-
Gson gson = new Gson();
82+
final Gson gson = new GsonBuilder()
83+
.registerTypeAdapter(LabJsonStringAccountEntry.class, new CaseInsensitiveDeserializer())
84+
.create();
6885
Type type = new TypeToken<Map<String, LabJsonStringAccountEntry>>(){}.getType();
6986
return gson.fromJson(json, type);
7087
}
88+
89+
/**
90+
* Gson deserializer that matches JSON property names to {@link SerializedName} values
91+
* in a case-insensitive manner, so {@code upn}, {@code Upn} and {@code UPN} are all
92+
* treated as the same field.
93+
*/
94+
private static class CaseInsensitiveDeserializer implements JsonDeserializer<LabJsonStringAccountEntry> {
95+
@Override
96+
public LabJsonStringAccountEntry deserialize(JsonElement json, Type typeOfT,
97+
JsonDeserializationContext context) throws JsonParseException {
98+
final JsonObject src = json.getAsJsonObject();
99+
100+
// Build a lowercase-keyed view of the incoming JSON object.
101+
final Map<String, JsonElement> lowerCased = new HashMap<>();
102+
for (Map.Entry<String, JsonElement> e : src.entrySet()) {
103+
lowerCased.put(e.getKey().toLowerCase(Locale.ROOT), e.getValue());
104+
}
105+
106+
final LabJsonStringAccountEntry result = new LabJsonStringAccountEntry();
107+
for (Field field : LabJsonStringAccountEntry.class.getDeclaredFields()) {
108+
final SerializedName annotation = field.getAnnotation(SerializedName.class);
109+
if (annotation == null) {
110+
continue;
111+
}
112+
final JsonElement value = lowerCased.get(annotation.value().toLowerCase(Locale.ROOT));
113+
if (value == null || value.isJsonNull()) {
114+
continue;
115+
}
116+
try {
117+
field.setAccessible(true);
118+
field.set(result, context.deserialize(value, field.getGenericType()));
119+
} catch (IllegalAccessException ex) {
120+
throw new JsonParseException("Failed to set field " + field.getName(), ex);
121+
}
122+
}
123+
return result;
124+
}
125+
}
71126
}

LabApiUtilities/src/main/com/microsoft/identity/labapi/utilities/constants/LabConstants.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,29 @@ public class LabConstants {
2626
private static final String NONE = "none";
2727

2828
public static final String DEFAULT_LAB_CLIENT_ID = "f62c5ae3-bf3a-4af5-afa8-a68b800396e9";
29+
public static final String DEFAULT_ID4SLAB2_CLIENT_ID = "c6bb302a-1e38-408e-9754-87c18fe81c80";
2930
public static final String DEFAULT_LAB_SCOPE = "https://request.msidlab.com/.default";
3031
public static final String KEYVAULT_SCOPE = "https://vault.azure.net/.default";
3132
public static final String DEFAULT_LAB_CERT_ALIAS = "LabAuth.MSIDLab.com";
32-
public static final String MSID_LAB3 = "https://login.microsoftonline.com/msidlab3.com";
33-
public static final String MSID_LAB4 = "https://login.microsoftonline.com/msidlab4.com";
33+
public static final String ID4SLAB2 = "https://login.microsoftonline.com/id4slab2.onmicrosoft.com";
34+
public static final String ID4SLAB1 = "https://login.microsoftonline.com/id4slab1.onmicrosoft.com";
3435

3536
// TODO, REMOVE LEGACY UserTypes WHEN WE REMOVE LAB QUERY USAGE
3637
static final class UserType {
3738
public static final String BASIC = "basic";
3839
public static final String MSA = "msa";
3940
public static final String MDM_CA = "mdm_ca";
4041
public static final String MAM_CA = "mam_ca";
42+
public static final String MAM_ON_SPO = "mam_on_spo";
43+
public static final String TP_CA = "tpca";
4144
public static final String TRUE_MAM_CA = "true_mam_ca";
4245
public static final String WP = "wp";
4346
public static final String FEDERATED = "federated";
4447
public static final String DEVICE_ADMIN = "device_admin";
4548
public static final String USGOV = "usgov";
4649
public static final String USGOV_GUEST = "usgov_guest";
4750
public static final String CHINA = "china";
51+
public static final String CHINA_GUEST = "china_guest";
4852
public static final String QR_PIN = "qr_pin";
4953
public static final String TOKEN_BINDING = "token_binding";
5054
public static final String CBA = "cba";
@@ -87,6 +91,9 @@ static final class ProtectionPolicy {
8791
public static final String TRUE_MAM_CA = "truemamca";
8892
public static final String MAM_SPO = "mamspo";
8993
public static final String BLOCKED = "blocked";
94+
public static final String GLOBAL_MFA = "globalmfa";
95+
public static final String AUTHAPP_LBAC = "authapplbac";
96+
public static final String AUTHAPP_RICH_CONTEXT = "authapprichcontext";
9097
}
9198

9299
static final class HomeDomain {
@@ -176,6 +183,8 @@ static final class TempUserType {
176183
public static final String MFAONEXO = "MFAONEXO";
177184
public static final String MAMCA = "MAMCA";
178185
public static final String MDMCA = "MDMCA";
186+
public static final String AUTHAPP_LBAC = "AuthappLBAC";
187+
public static final String AUTHAPP_RICH_CONTEXT = "AuthappRichContext";
179188
}
180189

181190
static final class TempUserPolicy {
@@ -184,6 +193,8 @@ static final class TempUserPolicy {
184193
public static final String MFAONEXO = TempUserType.MFAONEXO;
185194
public static final String MAMCA = TempUserType.MAMCA;
186195
public static final String MDMCA = TempUserType.MDMCA;
196+
public static final String AUTHAPP_LBAC = TempUserType.AUTHAPP_LBAC;
197+
public static final String AUTHAPP_RICH_CONTEXT = TempUserType.AUTHAPP_RICH_CONTEXT;
187198
}
188199

189200
static final class ResetOperation {

LabApiUtilities/src/main/com/microsoft/identity/labapi/utilities/constants/ProtectionPolicy.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ public enum ProtectionPolicy {
2727
CA(LabConstants.ProtectionPolicy.CA),
2828
CADJ(LabConstants.ProtectionPolicy.CADJ),
2929
MAM(LabConstants.ProtectionPolicy.MAM),
30+
GLOBAL_MFA(LabConstants.ProtectionPolicy.GLOBAL_MFA),
3031
MDM(LabConstants.ProtectionPolicy.MDM),
3132
MAM_CA(LabConstants.ProtectionPolicy.MAM_CA),
3233
MDM_CA(LabConstants.ProtectionPolicy.MDM_CA),
3334
TRUE_MAM_CA(LabConstants.ProtectionPolicy.TRUE_MAM_CA),
3435
MAM_SPO(LabConstants.ProtectionPolicy.MAM_SPO),
35-
BLOCKED(LabConstants.ProtectionPolicy.BLOCKED);
36+
BLOCKED(LabConstants.ProtectionPolicy.BLOCKED),
37+
AUTHAPP_LBAC(LabConstants.ProtectionPolicy.AUTHAPP_LBAC),
38+
AUTHAPP_RICH_CONTEXT(LabConstants.ProtectionPolicy.AUTHAPP_RICH_CONTEXT);
3639

3740
final String value;
3841

LabApiUtilities/src/main/com/microsoft/identity/labapi/utilities/constants/TempUserType.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ public enum TempUserType {
2828
MFA_ON_SPO(LabConstants.TempUserType.MFAONSPO),
2929
MFA_ON_EXO(LabConstants.TempUserType.MFAONEXO),
3030
MAM_CA(LabConstants.TempUserType.MAMCA),
31-
MDM_CA(LabConstants.TempUserType.MDMCA);
31+
MDM_CA(LabConstants.TempUserType.MDMCA),
32+
AUTHAPP_LBAC(LabConstants.TempUserType.AUTHAPP_LBAC),
33+
AUTHAPP_RICH_CONTEXT(LabConstants.TempUserType.AUTHAPP_RICH_CONTEXT);
3234

3335
final String value;
3436

LabApiUtilities/src/main/com/microsoft/identity/labapi/utilities/constants/UserType.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ public enum UserType {
2929
MSA(LabConstants.UserType.MSA),
3030
MDM_CA(LabConstants.UserType.MDM_CA),
3131
MAM_CA(LabConstants.UserType.MAM_CA),
32+
MAM_ON_SPO(LabConstants.UserType.MAM_ON_SPO),
3233
TRUE_MAM_CA(LabConstants.UserType.TRUE_MAM_CA),
3334
WP(LabConstants.UserType.WP),
3435
FEDERATED(LabConstants.UserType.FEDERATED),
3536
DEVICE_ADMIN(LabConstants.UserType.DEVICE_ADMIN),
3637
USGOV(LabConstants.UserType.USGOV),
3738
USGOV_GUEST(LabConstants.UserType.USGOV_GUEST),
3839
CHINA(LabConstants.UserType.CHINA),
40+
CHINA_GUEST(LabConstants.UserType.CHINA_GUEST),
3941
QR_PIN(LabConstants.UserType.QR_PIN),
4042
TOKEN_BINDING(LabConstants.UserType.TOKEN_BINDING),
4143
CBA(LabConstants.UserType.CBA),
@@ -51,9 +53,9 @@ public enum UserType {
5153
DUNA_MFA_2(LabConstants.UserType.DUNA_MFA_2),
5254
CLOUD(LabConstants.UserType.CLOUD),
5355
B2C(LabConstants.UserType.B2C),
56+
CIAM(LabConstants.UserType.CIAM),
5457
GUEST(LabConstants.UserType.GUEST),
55-
ONPREM(LabConstants.UserType.ONPREM),
56-
CIAM(LabConstants.UserType.CIAM);
58+
ONPREM(LabConstants.UserType.ONPREM);
5759

5860
final String value;
5961

LabApiUtilities/src/test/com/microsoft/identity/labapi/utilities/authentication/PublicAuthClientRopcTest.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import com.microsoft.identity.labapi.utilities.authentication.msal4j.Msal4jAuthClient;
2929
import com.microsoft.identity.labapi.utilities.client.ILabAccount;
3030
import com.microsoft.identity.labapi.utilities.client.LabClient;
31-
import com.microsoft.identity.labapi.utilities.client.LabQuery;
3231
import com.microsoft.identity.labapi.utilities.constants.UserType;
3332
import com.microsoft.identity.labapi.utilities.exception.LabApiException;
3433
import com.microsoft.identity.labapi.utilities.jwt.IJWTParser;
@@ -80,11 +79,7 @@ public void canGetTokenUsingRopcWithAdal4j() throws LabApiException {
8079
}
8180

8281
private IAuthenticationResult performTestWithAuthClient(@NonNull final IPublicAuthClient publicAuthClient) throws LabApiException {
83-
final LabQuery query = LabQuery.builder()
84-
.userType(UserType.CLOUD)
85-
.build();
86-
87-
final ILabAccount labAccount = labClient.getLabAccount(query);
82+
final ILabAccount labAccount = labClient.getAccountFromLabJsonStringInMobileBuildVault(UserType.BASIC);
8883

8984
final TokenParameters.TokenParametersBuilder tokenParametersBuilder = TokenParameters.builder()
9085
.clientId(labAccount.getAssociatedClientId());

0 commit comments

Comments
 (0)