Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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 changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
vNext
----------
- [MINOR] Add API for resource account provisioning (API only) (#2640)

Version 21.1.0
----------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1626,7 +1626,8 @@ public enum API {
BROKER_RESTORE_MSA_ACCOUNTS_WITH_TRANSFER_TOKENS(BROKER_RESTORE_MSA_ACCOUNTS_WITH_TRANSFER_TOKENS_PATH, BROKER_VERSION_5, null),

WEBAPPS_GET_SUPPORTED_WEB_APPS_CONTRACTS(WEBAPPS_GET_SUPPORTED_WEB_APPS_CONTRACTS_PATH, null, null),
WEBAPPS_EXECUTE_WEB_APPS_REQUEST(WEBAPPS_EXECUTE_WEB_APPS_REQUEST_PATH, null, null);
WEBAPPS_EXECUTE_WEB_APPS_REQUEST(WEBAPPS_EXECUTE_WEB_APPS_REQUEST_PATH, null, null),
PROVISION_RESOURCE_ACCOUNT(PROVISION_RESOURCE_ACCOUNT_PATH, null, null); // TODO: add broker version

/**
* The content provider path that the API exists behind.
Expand Down Expand Up @@ -1805,6 +1806,11 @@ public String getMsalVersion(){
*/
public static final String WEBAPPS_EXECUTE_WEB_APPS_REQUEST_PATH = "/webapp/executeWebAppsRequest";


public static final String PROVISION_RESOURCE_ACCOUNT_PATH = "/provisionResourceAccount";

public static final String GET_AAD_DEVICE_ID_PATH = "/getAadDeviceId";

/**
* BrokerContentProvider URI code constant for MSAL-to-Broker hello request.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ private static final class SerializedNames {
* Scopes for the request. This is expected to be of the format
* "scope 1 scope2 scope3" with space as a delimiter
*/
@NonNull
@SerializedName(SerializedNames.SCOPE)
private String mScope;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ public enum Operation {
BROKER_INDIVIDUAL_LOGS_UPLOAD(API.BROKER_INDIVIDUAL_LOGS_UPLOAD, null),
BROKER_API_RESTORE_MSA_ACCOUNTS_WITH_TRANSFER_TOKENS(API.BROKER_RESTORE_MSA_ACCOUNTS_WITH_TRANSFER_TOKENS, null),
BROKER_WEBAPPS_API_GET_SUPPORTED_WEB_APPS_CONTRACTS(API.WEBAPPS_GET_SUPPORTED_WEB_APPS_CONTRACTS, null),
BROKER_WEBAPPS_API_EXECUTE_WEB_APPS_REQUEST(API.WEBAPPS_EXECUTE_WEB_APPS_REQUEST, null);
BROKER_WEBAPPS_API_EXECUTE_WEB_APPS_REQUEST(API.WEBAPPS_EXECUTE_WEB_APPS_REQUEST, null),
PROVISION_RESOURCE_ACCOUNT(API.PROVISION_RESOURCE_ACCOUNT, null);

final API mContentApi;
final String mAccountManagerOperation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import static com.microsoft.identity.common.internal.broker.ipc.BrokerOperationBundle.Operation.MSAL_REMOVE_ACCOUNT;
import static com.microsoft.identity.common.internal.broker.ipc.BrokerOperationBundle.Operation.MSAL_SIGN_OUT_FROM_SHARED_DEVICE;
import static com.microsoft.identity.common.internal.broker.ipc.BrokerOperationBundle.Operation.MSAL_SSO_TOKEN;
import static com.microsoft.identity.common.internal.broker.ipc.BrokerOperationBundle.Operation.PROVISION_RESOURCE_ACCOUNT;
import static com.microsoft.identity.common.internal.controllers.BrokerOperationExecutor.BrokerOperation;
import static com.microsoft.identity.common.java.AuthenticationConstants.LocalBroadcasterAliases.RETURN_BROKER_INTERACTIVE_ACQUIRE_TOKEN_RESULT;
import static com.microsoft.identity.common.java.AuthenticationConstants.LocalBroadcasterFields.REQUEST_CODE;
Expand Down Expand Up @@ -86,6 +87,7 @@
import com.microsoft.identity.common.java.commands.parameters.GenerateShrCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.InteractiveTokenCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.RemoveAccountCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.ResourceAccountCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.RopcTokenCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.SilentTokenCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.TokenCommandParameters;
Expand Down Expand Up @@ -1181,7 +1183,62 @@ public void putValueInSuccessEvent(@NonNull final ApiEndEvent event,
// TODO Needed?
}
});
}

/**
* Sign in a resource account in broker based on given parameters. Should called by OneAuth/MSAL
* to provision resource account in broker.
* @param parameters a {@link ResourceAccountCommandParameters}
*/
public ICacheRecord provisionResourceAccount(@NonNull final ResourceAccountCommandParameters parameters) throws BaseException {
return getBrokerOperationExecutor().execute(parameters,
new BrokerOperation<ICacheRecord>() {
private String negotiatedBrokerProtocolVersion;

@Override
public void performPrerequisites(final @NonNull IIpcStrategy strategy) throws BaseException {
negotiatedBrokerProtocolVersion = hello(strategy, parameters.getRequiredBrokerProtocolVersion());
}

@Override
@NonNull
public BrokerOperationBundle getBundle() {
return new BrokerOperationBundle(
PROVISION_RESOURCE_ACCOUNT,
mActiveBrokerPackageName,
mRequestAdapter.getRequestBundleForProvisionResourceAccount(
parameters,
negotiatedBrokerProtocolVersion
));
}

@Override
@NonNull
public ICacheRecord extractResultBundle(final @Nullable Bundle resultBundle) throws BaseException {
if (resultBundle == null) {
throw mResultAdapter.getExceptionForEmptyResultBundle();
}
verifyBrokerVersionIsSupported(resultBundle, parameters.getRequiredBrokerProtocolVersion());
return mResultAdapter.resourceAccountRecordFromBundle(resultBundle);
}

@Override
public @NonNull
String getMethodName() {
return ":provisionResourceAccount";
}

@Nullable
@Override
public String getTelemetryApiId() {
return null;
}

@Override
public void putValueInSuccessEvent(@lombok.NonNull ApiEndEvent event, @lombok.NonNull ICacheRecord result) {

}
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import com.microsoft.identity.common.java.commands.parameters.DeviceCodeFlowCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.GenerateShrCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.RemoveAccountCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.ResourceAccountCommandParameters;
import com.microsoft.identity.common.java.opentelemetry.SerializableSpanContext;
import com.microsoft.identity.common.java.opentelemetry.SpanExtension;
import com.microsoft.identity.common.java.providers.microsoft.microsoftsts.MicrosoftStsAuthorizationResult;
Expand Down Expand Up @@ -264,6 +265,49 @@ public Bundle getRequestBundleForAcquireTokenInteractive(@NonNull final Interact
);
}

/**
* Method to construct a request bundle for broker provisionResourceAccountRequest request.
* @param parameters input parameters of type {@link ResourceAccountCommandParameters}
* @param negotiatedBrokerProtocolVersion protocol version established by broker hello.
* @return request Bundle
*/
public Bundle getRequestBundleForProvisionResourceAccount(
@NonNull final ResourceAccountCommandParameters parameters,
@Nullable final String negotiatedBrokerProtocolVersion
) {
final String methodTag = TAG + ":getRequestBundleForProvisionResourceAccount";

Logger.info(methodTag, "Constructing result bundle from ProvisionResourceAccount.");
final String extraOptions = parameters.getExtraOptions() != null ?
QueryParamsAdapter._toJson(parameters.getExtraOptions()) : null;

final BrokerRequest brokerRequest = BrokerRequest.builder()
.authority(parameters.getAuthority().getAuthorityURL().toString())
.extraOptions(extraOptions)
.homeAccountId(parameters.getHomeAccountId())
.userName(parameters.getLoginHint())
.correlationId(parameters.getCorrelationId())
.clientId(parameters.getClientId())
.applicationName(parameters.getApplicationName())
.applicationVersion(parameters.getApplicationVersion())
.msalVersion(parameters.getSdkVersion())
.sdkType(parameters.getSdkType())
.environment(AzureActiveDirectory.getEnvironment().name())
.powerOptCheckEnabled(parameters.isPowerOptCheckEnabled())
.spanContext(SerializableSpanContext.builder()
.traceId(SpanExtension.current().getSpanContext().getTraceId())
.spanId(SpanExtension.current().getSpanContext().getSpanId())
.traceFlags(SpanExtension.current().getSpanContext().getTraceFlags().asByte())
.build()
)
.build();
return getRequestBundleFromBrokerRequest(
brokerRequest,
negotiatedBrokerProtocolVersion,
parameters.getRequiredBrokerProtocolVersion()
);
}

/**
* Method to construct a request bundle for broker deviceCodeFlowAuthRequest request.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,26 @@ public List<ICacheRecord> getAccountsFromResultBundle(@NonNull final Bundle bund
return JsonExtensions.getICacheRecordListFromJsonString(accountJson);
}

/**
* Get resource account record from the result bundle. If successful, new account
* record part of ICachedRecord is returned.
* @param bundle The result bundle from the broker.
* @throws BaseException
*/
public ICacheRecord resourceAccountRecordFromBundle(@NonNull final Bundle bundle) throws BaseException {
final String methodTag = TAG + ":resourceAccountRecordFromBundle";
final List<ICacheRecord> cacheRecords = getAccountsFromResultBundle(bundle);
if (cacheRecords.isEmpty()) {
Logger.error(methodTag, "No accounts found in the result bundle", null);
throw new ClientException(INVALID_BROKER_BUNDLE, "No accounts found in the result bundle");
}
if (cacheRecords.size() > 1) {
Logger.error(methodTag, "Multiple accounts found in the result bundle", null);
throw new ClientException(INVALID_BROKER_BUNDLE, "Multiple accounts found in the result bundle");
}
return cacheRecords.get(0);
}

public void verifyRemoveAccountResultFromBundle(@Nullable final Bundle bundle) throws BaseException {
final String methodTag = TAG + ":verifyRemoveAccountResultFromBundle";
if (bundle == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,27 @@
// THE SOFTWARE.
package com.microsoft.identity.common.internal.controllers;

import android.content.Context;
import android.os.Build;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.platform.app.InstrumentationRegistry;

import com.google.gson.Gson;
import com.microsoft.identity.common.adal.internal.AuthenticationConstants;
import com.microsoft.identity.common.components.MockPlatformComponentsFactory;
import com.microsoft.identity.common.exception.BrokerCommunicationException;
import com.microsoft.identity.common.internal.broker.ipc.BrokerOperationBundle;
import com.microsoft.identity.common.internal.broker.ipc.IIpcStrategy;
import com.microsoft.identity.common.internal.result.MsalBrokerResultAdapter;
import com.microsoft.identity.common.java.authorities.Authority;
import com.microsoft.identity.common.java.cache.CacheRecord;
import com.microsoft.identity.common.java.cache.ICacheRecord;
import com.microsoft.identity.common.java.commands.AcquirePrtSsoTokenResult;
import com.microsoft.identity.common.java.commands.parameters.AcquirePrtSsoTokenCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.ResourceAccountCommandParameters;
import com.microsoft.identity.common.java.dto.AccountRecord;
import com.microsoft.identity.common.java.interfaces.IPlatformComponents;
import com.microsoft.identity.common.java.request.SdkType;

import org.junit.Assert;
import org.junit.Test;
Expand All @@ -47,7 +51,8 @@
import org.robolectric.annotation.Config;

import java.util.Collections;
import java.util.List;

import lombok.SneakyThrows;

@RunWith(RobolectricTestRunner.class)
@Config(sdk = {Build.VERSION_CODES.N}, shadows = {})
Expand Down Expand Up @@ -125,4 +130,72 @@ public Type getType() {
Assert.assertEquals("x-ms-RefreshTokenCredential", ssoTokenResult.getCookieName());
}

/**
* This test simulates a result calling the ProvisionResourceAccount Api.
*/
@SneakyThrows
@Test
public void testProvisionResourceAccount() {
final String mockHomeAccountId = "mockHomeAccountId";
final String mockCorrelationId = "mockCorrelationId";
final String mockAuthorityStr = "https://login.microsoft.com/mockAuthority";
final Authority mockAuthority = Authority.getAuthorityFromAuthorityUrl(mockAuthorityStr);
final String mockNegotiatedBrokerVersion = "18.0";
final String mockAccountName = "mockAccountName";
final AccountRecord mockAccountRecord = new AccountRecord();
mockAccountRecord.setHomeAccountId(mockHomeAccountId);
mockAccountRecord.setUsername(mockAccountName);
final CacheRecord mockCacheRecord = CacheRecord.builder()
.account(mockAccountRecord)
.build();
final MsalBrokerResultAdapter resultAdapter = new MsalBrokerResultAdapter();
final IIpcStrategy strategy = new IIpcStrategy() {
@Override
public Bundle communicateToBroker(@NonNull BrokerOperationBundle bundle) {
Bundle retBundle = new Bundle();
if (bundle.getOperation().equals(BrokerOperationBundle.Operation.MSAL_HELLO)) {
retBundle.putString(AuthenticationConstants.Broker.NEGOTIATED_BP_VERSION_KEY, mockNegotiatedBrokerVersion);
} else if (bundle.getOperation().equals(BrokerOperationBundle.Operation.PROVISION_RESOURCE_ACCOUNT)) {
retBundle = resultAdapter.bundleFromAccounts(Collections.singletonList(mockCacheRecord), mockNegotiatedBrokerVersion);
}
return retBundle;
}

@Override
public boolean isSupportedByTargetedBroker(@NonNull final String targetedBrokerPackageName) {
return true;
}

@Override
@NonNull
public Type getType() {
return Type.CONTENT_PROVIDER;
}
};

final IPlatformComponents components = MockPlatformComponentsFactory.getNonFunctionalBuilder().build();
final ResourceAccountCommandParameters parameters = ResourceAccountCommandParameters.builder()
.platformComponents(components)
.homeAccountId(mockHomeAccountId)
.authority(mockAuthority)
.correlationId(mockCorrelationId)
.applicationName("mockApplicationName")
.applicationVersion("mockApplicationVersion")
.sdkVersion("mockSdkVersion")
.sdkType(SdkType.MSAL)
.requiredBrokerProtocolVersion(mockNegotiatedBrokerVersion)
.build();

final BrokerMsalController controller = new BrokerMsalController(
InstrumentationRegistry.getInstrumentation().getContext(),
components,
"aBrokerPackage",
Collections.singletonList(strategy));

final ICacheRecord cacheRecord = controller.provisionResourceAccount(parameters);

// verify the cache record
Assert.assertEquals(mockHomeAccountId, cacheRecord.getAccount().getHomeAccountId());
Assert.assertEquals(mockAccountName, cacheRecord.getAccount().getUsername());
}
}
Loading
Loading