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
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public class UserAccount {
public static final String TOKEN_FORMAT = "tokenFormat";
public static final String BEACON_CHILD_CONSUMER_KEY = "beacon_child_consumer_key";
public static final String BEACON_CHILD_CONSUMER_SECRET = "beacon_child_consumer_secret";
public static final String SCOPE = "scope";

private static final String TAG = "UserAccount";
private static final String FORWARD_SLASH = "/";
Expand Down Expand Up @@ -142,6 +143,7 @@ public class UserAccount {
private Map<String, String> additionalOauthValues;
private String beaconChildConsumerKey;
private String beaconChildConsumerSecret;
private String scope;

/**
* Parameterized constructor.
Expand Down Expand Up @@ -182,6 +184,7 @@ public class UserAccount {
* @param beaconChildConsumerKey beacon child consumer key
* @param beaconChildConsumerSecret beacon child consumer secret
* @param apiInstanceServer API instance server
* @param scope Scope
*/
UserAccount(String authToken, String refreshToken,
String loginServer, String idUrl, String instanceServer,
Expand All @@ -193,7 +196,7 @@ public class UserAccount {
String contentDomain, String contentSid, String csrfToken, Boolean nativeLogin,
String language, String locale, String cookieClientSrc, String cookieSidClient,
String sidCookieName, String clientId, String parentSid, String tokenFormat,
String beaconChildConsumerKey, String beaconChildConsumerSecret, String apiInstanceServer) {
String beaconChildConsumerKey, String beaconChildConsumerSecret, String apiInstanceServer, String scope) {
this.authToken = authToken;
this.refreshToken = refreshToken;
this.loginServer = loginServer;
Expand Down Expand Up @@ -231,6 +234,7 @@ public class UserAccount {
this.tokenFormat = tokenFormat;
this.beaconChildConsumerKey = beaconChildConsumerKey;
this.beaconChildConsumerSecret = beaconChildConsumerSecret;
this.scope = scope;
SalesforceSDKManager.getInstance().registerUsedAppFeature(Features.FEATURE_USER_AUTH);
}

Expand Down Expand Up @@ -281,6 +285,7 @@ public class UserAccount {
tokenFormat = object.optString(TOKEN_FORMAT, null);
beaconChildConsumerKey = object.optString(BEACON_CHILD_CONSUMER_KEY, null);
beaconChildConsumerSecret = object.optString(BEACON_CHILD_CONSUMER_SECRET, null);
scope = object.optString(SCOPE, null);
additionalOauthValues = MapUtil.addJSONObjectToMap(object, additionalOauthKeys, additionalOauthValues);
}
}
Expand Down Expand Up @@ -337,6 +342,7 @@ public UserAccount(JSONObject object) {
tokenFormat = bundle.getString(TOKEN_FORMAT);
beaconChildConsumerKey = bundle.getString(BEACON_CHILD_CONSUMER_KEY);
beaconChildConsumerSecret = bundle.getString(BEACON_CHILD_CONSUMER_SECRET);
scope = bundle.getString(SCOPE);
additionalOauthValues = MapUtil.addBundleToMap(bundle, additionalOauthKeys, additionalOauthValues);
}
}
Expand Down Expand Up @@ -667,7 +673,49 @@ public String getTokenFormat() {
}

/**
* Returns the beacon child consumer key .
* Returns the OAuth scopes returned by the token endpoint.
*
* @return scope string.
*/
public String getScope() {
return scope;
}

/**
* Parses the space-delimited scope string into its individual components.
*
* @return Array of scope strings (empty if scope is null/empty).
*/
public String[] parseScopes() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find Lists to be a lot easier to deal with than Arrays, especially in Kotlin. Seems like the List contains function would be useful for scopes, but you could call contains() on the String scope itself as well so really not a big deal.

Edit: Apparently I should have just kept reading, hasScope resolves the above.

if (TextUtils.isEmpty(scope)) {
return new String[0];
}
final String trimmed = scope.trim();
if (trimmed.isEmpty()) {
return new String[0];
}
return trimmed.split("\\s+");
}

/**
* Checks whether the provided scope exists in this account's scope list.
*
* @param scopeToCheck Scope name to check.
* @return True if present, false otherwise.
*/
public boolean hasScope(String scopeToCheck) {
if (TextUtils.isEmpty(scopeToCheck)) {
return false;
}
for (final String s : parseScopes()) {
if (scopeToCheck.equals(s)) {
return true;
}
}
return false;
}
/**
* Returns the beacon child consumer key.
*
* @return beacon child consumer key.
*/
Expand Down Expand Up @@ -956,6 +1004,7 @@ JSONObject toJson(List<String> additionalOauthKeys) {
object.put(TOKEN_FORMAT, tokenFormat);
object.put(BEACON_CHILD_CONSUMER_KEY, beaconChildConsumerKey);
object.put(BEACON_CHILD_CONSUMER_SECRET, beaconChildConsumerSecret);
object.put(SCOPE, scope);
object = MapUtil.addMapToJSONObject(additionalOauthValues, additionalOauthKeys, object);
} catch (JSONException e) {
SalesforceSDKLogger.e(TAG, "Unable to convert to JSON", e);
Expand Down Expand Up @@ -1016,6 +1065,7 @@ Bundle toBundle(List<String> additionalOauthKeys) {
object.putString(TOKEN_FORMAT, tokenFormat);
object.putString(BEACON_CHILD_CONSUMER_KEY, beaconChildConsumerKey);
object.putString(BEACON_CHILD_CONSUMER_SECRET, beaconChildConsumerSecret);
object.putString(SCOPE, scope);
object = MapUtil.addMapToBundle(additionalOauthValues, additionalOauthKeys, object);
return object;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class UserAccountBuilder private constructor() {
private var allowUnset = true
private var beaconChildConsumerKey: String? = null
private var beaconChildConsumerSecret: String? = null
private var scope: String? = null

/**
* Set fields from token end point response
Expand Down Expand Up @@ -106,6 +107,7 @@ class UserAccountBuilder private constructor() {
.tokenFormat(tr.tokenFormat)
.beaconChildConsumerKey(tr.beaconChildConsumerKey)
.beaconChildConsumerSecret(tr.beaconChildConsumerSecret)
.scope(tr.scope)
}

/**
Expand Down Expand Up @@ -173,6 +175,7 @@ class UserAccountBuilder private constructor() {
.tokenFormat(userAccount.tokenFormat)
.beaconChildConsumerKey(userAccount.beaconChildConsumerKey)
.beaconChildConsumerSecret(userAccount.beaconChildConsumerSecret)
.scope(userAccount.scope)
}

/**
Expand Down Expand Up @@ -572,6 +575,16 @@ class UserAccountBuilder private constructor() {
return if (!allowUnset && beaconChildConsumerSecret == null) this else apply { this.beaconChildConsumerSecret = beaconChildConsumerSecret }
}

/**
* Sets scope returned by token endpoint.
*
* @param scope OAuth scopes string.
* @return Instance of this class.
*/
fun scope(scope: String?): UserAccountBuilder {
return if (!allowUnset && scope == null) this else apply { this.scope = scope }
}

/**
* Builds and returns a UserAccount object.
*
Expand Down Expand Up @@ -616,6 +629,7 @@ class UserAccountBuilder private constructor() {
beaconChildConsumerKey,
beaconChildConsumerSecret,
apiInstanceServer,
scope,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ public Bundle updateAccount(Account account, UserAccount userAccount) {
final String tokenFormat = decryptUserData(account, AuthenticatorService.KEY_TOKEN_FORMAT, encryptionKey);
final String beaconChildConsumerKey = decryptUserData(account, AuthenticatorService.KEY_BEACON_CHILD_CONSUMER_KEY, encryptionKey);
final String beaconChildConsumerSecret = decryptUserData(account, AuthenticatorService.KEY_BEACON_CHILD_CONSUMER_SECRET, encryptionKey);
final String scope = decryptUserData(account, AuthenticatorService.KEY_SCOPE, encryptionKey);

Map<String, String> additionalOauthValues = null;
List<String> additionalOauthKeys = SalesforceSDKManager.getInstance().getAdditionalOauthKeys();
Expand Down Expand Up @@ -593,6 +594,7 @@ public Bundle updateAccount(Account account, UserAccount userAccount) {
.tokenFormat(tokenFormat)
.beaconChildConsumerKey(beaconChildConsumerKey)
.beaconChildConsumerSecret(beaconChildConsumerSecret)
.scope(scope)
.additionalOauthValues(additionalOauthValues)
.build();
}
Expand Down Expand Up @@ -742,6 +744,7 @@ private Bundle buildAuthBundle(UserAccount userAccount) {
extras.putString(AuthenticatorService.KEY_TOKEN_FORMAT, SalesforceSDKManager.encrypt(userAccount.getTokenFormat(), encryptionKey));
extras.putString(AuthenticatorService.KEY_BEACON_CHILD_CONSUMER_KEY, SalesforceSDKManager.encrypt(userAccount.getBeaconChildConsumerKey(), encryptionKey));
extras.putString(AuthenticatorService.KEY_BEACON_CHILD_CONSUMER_SECRET, SalesforceSDKManager.encrypt(userAccount.getBeaconChildConsumerSecret(), encryptionKey));
extras.putString(AuthenticatorService.KEY_SCOPE, SalesforceSDKManager.encrypt(userAccount.getScope(), encryptionKey));

final List<String> additionalOauthKeys = SalesforceSDKManager.getInstance().getAdditionalOauthKeys();
if (additionalOauthKeys != null && !additionalOauthKeys.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public class AuthenticatorService extends Service {
public static final String KEY_TOKEN_FORMAT = "tokenFormat";
public static final String KEY_BEACON_CHILD_CONSUMER_KEY = "beacon_child_consumer_key";
public static final String KEY_BEACON_CHILD_CONSUMER_SECRET = "beacon_child_consumer_secret";
public static final String KEY_SCOPE = "scope";

private static final String TAG = "AuthenticatorService";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,7 @@ public static class TokenEndpointResponse {
public String tokenFormat;
public String beaconChildConsumerKey;
public String beaconChildConsumerSecret;
public String scope;

/**
* Parameterized constructor built from params during user agent login flow.
Expand Down Expand Up @@ -882,6 +883,7 @@ public TokenEndpointResponse(Map<String, String> callbackUrlParams, List<String>
sidCookieName = callbackUrlParams.get(SID_COOKIE_NAME);
parentSid = callbackUrlParams.get(PARENT_SID);
tokenFormat = callbackUrlParams.get(TOKEN_FORMAT);
scope = callbackUrlParams.get(SCOPE);

// NB: beacon apps not supported with user agent flow so no beacon child fields expected

Expand Down Expand Up @@ -959,6 +961,7 @@ public TokenEndpointResponse(Response response, List<String> additionalOauthKeys
if (parsedResponse.has(BEACON_CHILD_CONSUMER_SECRET)) {
beaconChildConsumerSecret = parsedResponse.getString(BEACON_CHILD_CONSUMER_SECRET);
}
scope = parsedResponse.optString(SCOPE);

} catch (Exception e) {
SalesforceSDKLogger.w(TAG, "Could not parse token endpoint response", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,14 @@ public class UserAccountTest {
public static final String TEST_TOKEN_FORMAT = "test-token-format";
public static final String TEST_BEACON_CHILD_CONSUMER_KEY = "test-beacon-child-consumer-key";
public static final String TEST_BEACON_CHILD_CONSUMER_SECRET = "test-beacon-child-consumer-secret";
public static final String TEST_SCOPE = "api web openid refresh_token";

// other user
public static final String TEST_ORG_ID_2 = "test_org_id_2";
public static final String TEST_USER_ID_2 = "test_user_id_2";
public static final String TEST_ACCOUNT_NAME_2 = "test_username_2 (https://cs1.salesforce.com) (SalesforceSDKTest)";
public static final String TEST_USERNAME_2 = "test_username_2";
public static final String TEST_SCOPE_2 = "api web refresh_token sfap_api";


private EventsListenerQueue eq;
Expand All @@ -125,6 +127,43 @@ public void testConvertAccountToBundle() {
BundleTestHelper.checkSameBundle("UserAccount bundles do not match", expected, actual);
}

@Test
public void testParseScopes() {
UserAccount account = createTestAccount();
String[] scopes = account.parseScopes();
// Our TEST_SCOPE is "api web openid refresh_token"
Assert.assertArrayEquals(new String[]{"api", "web", "openid", "refresh_token"}, scopes);

// Empty / null handling
UserAccount emptyScope = UserAccountBuilder.getInstance()
.populateFromUserAccount(account)
.scope(null)
.build();
Assert.assertArrayEquals(new String[]{}, emptyScope.parseScopes());

UserAccount blankScope = UserAccountBuilder.getInstance()
.populateFromUserAccount(account)
.scope(" ")
.build();
Assert.assertArrayEquals(new String[]{}, blankScope.parseScopes());
}

@Test
public void testHasScope() {
UserAccount account = createTestAccount();
Assert.assertTrue(account.hasScope("api"));
Assert.assertTrue(account.hasScope("web"));
Assert.assertTrue(account.hasScope("openid"));
Assert.assertTrue(account.hasScope("refresh_token"));
Assert.assertFalse(account.hasScope("unknown"));

UserAccount emptyScope = UserAccountBuilder.getInstance()
.populateFromUserAccount(account)
.scope("")
.build();
Assert.assertFalse(emptyScope.hasScope("api"));
}

/**
* Tests user account to json conversion.
*/
Expand Down Expand Up @@ -203,6 +242,7 @@ public void testPopulateFromUserAccount() {
.orgId(TEST_ORG_ID_2)
.username(TEST_USERNAME_2)
.accountName(TEST_ACCOUNT_NAME_2)
.scope(TEST_SCOPE_2)
.build();
checkOtherTestAccount(otherUserAccount);
}
Expand Down Expand Up @@ -403,6 +443,7 @@ private JSONObject createTestAccountJSON() throws JSONException{
object.put(UserAccount.SID_COOKIE_NAME, TEST_SID_COOKIE_NAME);
object.put(UserAccount.PARENT_SID, TEST_PARENT_SID);
object.put(UserAccount.TOKEN_FORMAT, TEST_TOKEN_FORMAT);
object.put(UserAccount.SCOPE, TEST_SCOPE);
object.put(UserAccount.BEACON_CHILD_CONSUMER_KEY, TEST_BEACON_CHILD_CONSUMER_KEY);
object.put(UserAccount.BEACON_CHILD_CONSUMER_SECRET, TEST_BEACON_CHILD_CONSUMER_SECRET);
object = MapUtil.addMapToJSONObject(createAdditionalOauthValues(), createAdditionalOauthKeys(), object);
Expand Down Expand Up @@ -452,6 +493,7 @@ private Bundle createTestAccountBundle() {
object.putString(UserAccount.TOKEN_FORMAT, TEST_TOKEN_FORMAT);
object.putString(UserAccount.BEACON_CHILD_CONSUMER_KEY, TEST_BEACON_CHILD_CONSUMER_KEY);
object.putString(UserAccount.BEACON_CHILD_CONSUMER_SECRET, TEST_BEACON_CHILD_CONSUMER_SECRET);
object.putString(UserAccount.SCOPE, TEST_SCOPE);
object = MapUtil.addMapToBundle(createAdditionalOauthValues(), createAdditionalOauthKeys(), object);
return object;
}
Expand Down Expand Up @@ -497,6 +539,7 @@ public static UserAccount createTestAccount() {
.tokenFormat(TEST_TOKEN_FORMAT)
.beaconChildConsumerKey(TEST_BEACON_CHILD_CONSUMER_KEY)
.beaconChildConsumerSecret(TEST_BEACON_CHILD_CONSUMER_SECRET)
.scope(TEST_SCOPE)
.additionalOauthValues(createAdditionalOauthValues())
.build();
}
Expand All @@ -511,6 +554,7 @@ public static UserAccount createOtherTestAccount() {
.orgId(TEST_ORG_ID_2)
.username(TEST_USERNAME_2)
.accountName(TEST_ACCOUNT_NAME_2)
.scope(TEST_SCOPE_2)
.build();
}

Expand Down Expand Up @@ -567,6 +611,7 @@ void checkTestAccount(UserAccount account, boolean expectBeaconChildFields) {
Assert.assertNull("Beacon child consumer key should be null", account.getBeaconChildConsumerKey());
Assert.assertNull("Beacon child consumer secret should be null", account.getBeaconChildConsumerSecret());
}
Assert.assertEquals("Scope should match", TEST_SCOPE, account.getScope());
Assert.assertEquals("Additional OAuth values should match", createAdditionalOauthValues(), account.getAdditionalOauthValues());
}

Expand Down Expand Up @@ -609,6 +654,7 @@ void checkOtherTestAccount(UserAccount account) {
Assert.assertEquals("Token format should match", TEST_TOKEN_FORMAT, account.getTokenFormat());
Assert.assertEquals("Beacon child consumer key should match", TEST_BEACON_CHILD_CONSUMER_KEY, account.getBeaconChildConsumerKey());
Assert.assertEquals("Beacon child consumer secret should match", TEST_BEACON_CHILD_CONSUMER_SECRET, account.getBeaconChildConsumerSecret());
Assert.assertEquals("Scope should match", TEST_SCOPE_2, account.getScope());
Assert.assertEquals("Additional OAuth values should match", createAdditionalOauthValues(), account.getAdditionalOauthValues());
}

Expand Down Expand Up @@ -669,6 +715,7 @@ private Map<String, String> createTokenEndpointParams() {
params.put("sidCookieName", TEST_SID_COOKIE_NAME);
params.put("parent_sid", TEST_PARENT_SID);
params.put("token_format", TEST_TOKEN_FORMAT);
params.put("scope", TEST_SCOPE);

return params;
}
Expand Down
Loading