Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[android] Add support for external SDK clients tokens management #14631

Merged
merged 1 commit into from
May 30, 2019
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 @@ -2,41 +2,94 @@

import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.text.format.DateUtils;

import com.mapbox.android.accounts.v1.MapboxAccounts;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.log.Logger;

import static com.mapbox.mapboxsdk.constants.MapboxConstants.KEY_PREFERENCE_SKU_TOKEN;

/**
* REMOVAL OR MODIFICATION OF THE FOLLOWING CODE VIOLATES THE MAPBOX TERMS
* OF SERVICE
*
* <p>
* The following code is used to access Mapbox's Mapping APIs.
*
* <p>
* Removal or modification of this code when used with Mapbox's Mapping APIs
* can result in termination of your agreement and/or your account with
* Mapbox.
*
* <p>
* Using this code to access Mapbox Mapping APIs from outside the Mapbox Maps
* SDK also violates the Mapbox Terms of Service. On Android, Mapping APIs
* should be accessed using the methods documented at
* https://www.mapbox.com/android.
*
* <p>
* You can access the Mapbox Terms of Service at https://www.mapbox.com/tos/
*/
class AccountsManager {
private static final String TAG = "Mbgl-AccountsManager";
private static final String PREFERENCE_USER_ID = "com.mapbox.mapboxsdk.accounts.userid";
private static final String PREFERENCE_TIMESTAMP = "com.mapbox.mapboxsdk.accounts.timestamp";
private static final String PREFERENCE_SKU_TOKEN = "com.mapbox.mapboxsdk.accounts.skutoken";

private long timestamp;
private SharedPreferences sharedPreferences;
private String skuToken;
private long timestamp;
private boolean isManaged;

AccountsManager() {
String userId = validateUserId();
validateRotation(userId);
isManaged = isSkuTokenManaged();
initialize();
}

@VisibleForTesting
AccountsManager(SharedPreferences sharedPreferences, boolean isManaged) {
LukasPaczos marked this conversation as resolved.
Show resolved Hide resolved
this.sharedPreferences = sharedPreferences;
this.isManaged = isManaged;
initialize();
}

private void initialize() {
retrieveSkuTokenAndTimestamp();
if (isManaged) {
String userId = validateUserId();
validateRotation(userId);
}
}

private boolean isSkuTokenManaged() {
boolean value = MapboxConstants.DEFAULT_MANAGE_SKU_TOKEN;
try {
// Try getting a custom value from the app Manifest
ApplicationInfo appInfo = retrieveApplicationInfo();
if (appInfo.metaData != null) {
value = appInfo.metaData.getBoolean(
MapboxConstants.KEY_META_DATA_MANAGE_SKU_TOKEN,
MapboxConstants.DEFAULT_MANAGE_SKU_TOKEN
);
}
} catch (Exception exception) {
Logger.e(TAG, "Failed to read the package metadata: ", exception);
}

return value;
}

private ApplicationInfo retrieveApplicationInfo() throws PackageManager.NameNotFoundException {
return Mapbox.getApplicationContext().getPackageManager().getApplicationInfo(
Mapbox.getApplicationContext().getPackageName(),
PackageManager.GET_META_DATA);
}

private void retrieveSkuTokenAndTimestamp() {
SharedPreferences sharedPreferences = getSharedPreferences();
skuToken = sharedPreferences.getString(KEY_PREFERENCE_SKU_TOKEN, "");
timestamp = sharedPreferences.getLong(PREFERENCE_TIMESTAMP, 0L);
}

private String validateUserId() {
Expand All @@ -53,23 +106,25 @@ private String validateUserId() {
}

private void validateRotation(String userId) {
SharedPreferences sharedPreferences = getSharedPreferences();
timestamp = sharedPreferences.getLong(PREFERENCE_TIMESTAMP, 0L);
skuToken = sharedPreferences.getString(PREFERENCE_SKU_TOKEN, "");
if (timestamp == 0L || TextUtils.isEmpty(skuToken)) {
if (TextUtils.isEmpty(skuToken) || timestamp == 0L) {
skuToken = generateSkuToken(userId);
timestamp = persistRotation(skuToken);
}
}

String getSkuToken() {
if (isExpired()) {
if (isManaged) {
if (isExpired()) {
SharedPreferences sharedPreferences = getSharedPreferences();
String userId = sharedPreferences.getString(PREFERENCE_USER_ID, "");
skuToken = generateSkuToken(userId);
timestamp = persistRotation(skuToken);
}
} else {
SharedPreferences sharedPreferences = getSharedPreferences();
String userId = sharedPreferences.getString(PREFERENCE_USER_ID, "");
skuToken = generateSkuToken(userId);
timestamp = persistRotation(skuToken);
String notManagedSkuToken = sharedPreferences.getString(KEY_PREFERENCE_SKU_TOKEN, "");
skuToken = notManagedSkuToken;
LukasPaczos marked this conversation as resolved.
Show resolved Hide resolved
}

return skuToken;
}

Expand All @@ -85,15 +140,18 @@ private long persistRotation(String skuToken) {
long now = getNow();
SharedPreferences.Editor editor = getSharedPreferences().edit();
editor.putLong(PREFERENCE_TIMESTAMP, now);
editor.putString(PREFERENCE_SKU_TOKEN, skuToken);
editor.putString(KEY_PREFERENCE_SKU_TOKEN, skuToken);
editor.apply();
return now;
}

@NonNull
private SharedPreferences getSharedPreferences() {
return Mapbox.getApplicationContext()
if (sharedPreferences == null) {
sharedPreferences = Mapbox.getApplicationContext()
.getSharedPreferences(MapboxConstants.MAPBOX_SHARED_PREFERENCES, Context.MODE_PRIVATE);
}
return sharedPreferences;
}

static long getNow() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ public class MapboxConstants {
*/
public static final boolean DEFAULT_MEASURE_TILE_DOWNLOAD_ON = false;

public static final String KEY_PREFERENCE_SKU_TOKEN = "com.mapbox.mapboxsdk.accounts.skutoken";

public static final String KEY_META_DATA_MANAGE_SKU_TOKEN = "com.mapbox.ManageSkuToken";

public static final boolean DEFAULT_MANAGE_SKU_TOKEN = true;

/**
* Unmeasured state
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package com.mapbox.mapboxsdk;

import android.content.SharedPreferences;
import android.text.format.DateUtils;

import org.junit.Assert;
import org.junit.Test;

import static com.mapbox.mapboxsdk.constants.MapboxConstants.KEY_PREFERENCE_SKU_TOKEN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class AccountsManagerTest {
@Test
public void testIsExpired() {
Expand All @@ -15,10 +23,37 @@ public void testIsExpired() {
long futureValue = now + 1;
long immediatePast = now - 1;

Assert.assertTrue(AccountsManager.isExpired(now, defaultValue));
Assert.assertTrue(AccountsManager.isExpired(now, tooOld));
assertTrue(AccountsManager.isExpired(now, defaultValue));
assertTrue(AccountsManager.isExpired(now, tooOld));

assertFalse(AccountsManager.isExpired(now, futureValue));
assertFalse(AccountsManager.isExpired(now, immediatePast));
}

@Test
public void checksSkuTokenExternalManagement() {
SharedPreferences mockedSharedPreferences = mock(SharedPreferences.class);
when(mockedSharedPreferences.getString(KEY_PREFERENCE_SKU_TOKEN, "")).thenReturn("external-sku-token");
boolean isNotManaged = false;
Copy link
Member

Choose a reason for hiding this comment

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

NIT: should be called isManaged.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

🤔 now the variable is called isNotManaged, and has value false, so that should mean that it is managed. Idk, just seems a little bit off to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👀 Working Effectively with Unit Tests

when writing tests you should prefer DAMP (Descriptive And Maintainable Procedures)

That includes a bunch of procedures Moving Towards Readability 😅

I highly recommend you reading that book (if you haven't already). It's 🔝 💯

Copy link
Member

Choose a reason for hiding this comment

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

Thanks! It had a reverse effect on me though haha 🤷‍♂️

AccountsManager theAccountsManager = new AccountsManager(mockedSharedPreferences, isNotManaged);

String skuToken = theAccountsManager.getSkuToken();

assertEquals("external-sku-token", skuToken);
}

@Test
public void checksSkuTokenInternalManagement() {
SharedPreferences mockedSharedPreferences = mock(SharedPreferences.class, RETURNS_DEEP_STUBS);
LukasPaczos marked this conversation as resolved.
Show resolved Hide resolved
when(mockedSharedPreferences.getString("com.mapbox.mapboxsdk.accounts.userid", ""))
.thenReturn("any-user-id");
boolean isManaged = true;
AccountsManager theAccountsManager = new AccountsManager(mockedSharedPreferences, isManaged);

String skuToken = theAccountsManager.getSkuToken();

Assert.assertFalse(AccountsManager.isExpired(now, futureValue));
Assert.assertFalse(AccountsManager.isExpired(now, immediatePast));
assertFalse(skuToken.isEmpty());
assertTrue(skuToken.startsWith("100"));
assertTrue(skuToken.endsWith("any-user-id"));
}
}
2 changes: 1 addition & 1 deletion platform/android/gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ext {
mapboxTelemetry : '4.4.1',
mapboxCore : '1.3.0',
mapboxGestures : '0.4.2',
mapboxAccounts : '0.1.0',
mapboxAccounts : '0.2.0',
supportLib : '27.1.1',
constraintLayout: '1.1.2',
uiAutomator : '2.1.3',
Expand Down