Skip to content

Commit c3d5543

Browse files
committed
Merge pull request #415 from Iterable/MOB-3850-Patch-for-allowedProtocols
Mob 3850 patch for allowed protocols
2 parents 6109022 + 2bfb786 commit c3d5543

10 files changed

+46
-80
lines changed

iterableapi/src/main/java/com/iterable/iterableapi/IterableActionRunner.java

+10-32
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,33 @@
1414
import java.util.List;
1515

1616
class IterableActionRunner {
17+
1718
@VisibleForTesting
1819
static IterableActionRunnerImpl instance = new IterableActionRunnerImpl();
1920

2021
static boolean executeAction(@NonNull Context context, @Nullable IterableAction action, @NonNull IterableActionSource source) {
21-
return instance.executeAction(context, action, source, new String[0]);
22-
}
23-
24-
static boolean executeAction(@NonNull Context context, @Nullable IterableAction action, @NonNull IterableActionSource source, @NonNull String[] allowedProtocols) {
25-
return instance.executeAction(context, action, source, allowedProtocols);
22+
return instance.executeAction(context, action, source);
2623
}
2724

2825
static class IterableActionRunnerImpl {
2926
private static final String TAG = "IterableActionRunner";
3027

31-
boolean executeAction(@NonNull Context context, @Nullable IterableAction action, @NonNull IterableActionSource source) {
32-
return executeAction(context, action, source, new String[0]);
33-
}
34-
3528
/**
3629
* Execute an {@link IterableAction} as a response to push action
3730
*
3831
* @param context Context
3932
* @param action The original action object
40-
* @param allowedProtocols protocols that the SDK is allowed to open in addition to `https`
4133
* @return `true` if the action was handled, `false` if it was not
4234
*/
43-
boolean executeAction(@NonNull Context context, @Nullable IterableAction action, @NonNull IterableActionSource source, @NonNull String[] allowedProtocols) {
35+
boolean executeAction(@NonNull Context context, @Nullable IterableAction action, @NonNull IterableActionSource source) {
4436
if (action == null) {
4537
return false;
4638
}
4739

4840
IterableActionContext actionContext = new IterableActionContext(action, source);
4941

5042
if (action.isOfType(IterableAction.ACTION_TYPE_OPEN_URL)) {
51-
return openUri(context, Uri.parse(action.getData()), actionContext, allowedProtocols);
43+
return openUri(context, Uri.parse(action.getData()), actionContext);
5244
} else {
5345
return callCustomActionIfSpecified(action, actionContext);
5446
}
@@ -65,19 +57,19 @@ boolean executeAction(@NonNull Context context, @Nullable IterableAction action,
6557
* @return `true` if the action was handled, or an activity was found for this URL
6658
* `false` if the handler did not handle this URL and no activity was found to open it with
6759
*/
68-
private boolean openUri(@NonNull Context context, @NonNull Uri uri, @NonNull IterableActionContext actionContext, String[] allowedProtocols) {
60+
private boolean openUri(@NonNull Context context, @NonNull Uri uri, @NonNull IterableActionContext actionContext) {
61+
// Handle URL: check for deep links within the app
62+
if (!IterableUtil.isUrlOpenAllowed(uri.toString())) {
63+
return false;
64+
}
65+
6966
if (IterableApi.sharedInstance.config.urlHandler != null) {
7067
if (IterableApi.sharedInstance.config.urlHandler.handleIterableURL(uri, actionContext)) {
7168
return true;
7269
}
7370
}
7471

7572
// Handle URL: check for deep links within the app
76-
if (!isUrlOpenAllowed(uri.toString(), allowedProtocols)) {
77-
IterableLogger.e(TAG, "URL was not in the allowed protocols list");
78-
return false;
79-
}
80-
8173
Intent intent = new Intent(Intent.ACTION_VIEW);
8274
intent.setData(uri);
8375

@@ -119,19 +111,5 @@ private boolean callCustomActionIfSpecified(@NonNull IterableAction action, @Non
119111
}
120112
return false;
121113
}
122-
123-
private static boolean isUrlOpenAllowed(@NonNull String url, @NonNull String[] allowedProtocols) {
124-
if (url.startsWith("https")) {
125-
return true;
126-
}
127-
128-
for (String protocol : allowedProtocols) {
129-
if (url.startsWith(protocol)) {
130-
return true;
131-
}
132-
}
133-
134-
return false;
135-
}
136114
}
137115
}

iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ public static void initialize(@NonNull Context context, @NonNull String apiKey,
299299

300300
if (sharedInstance.inAppManager == null) {
301301
sharedInstance.inAppManager = new IterableInAppManager(sharedInstance, sharedInstance.config.inAppHandler,
302-
sharedInstance.config.inAppDisplayInterval, sharedInstance.config.allowedProtocols);
302+
sharedInstance.config.inAppDisplayInterval);
303303
}
304304

305305
loadLastSavedConfiguration(context);
@@ -402,7 +402,7 @@ public void setUserId(@Nullable String userId) {
402402
* or the original url if it is not an Iterable link.
403403
*/
404404
public void getAndTrackDeepLink(@NonNull String uri, @NonNull IterableHelper.IterableActionHandler onCallback) {
405-
IterableDeeplinkManager.getAndTrackDeeplink(uri, onCallback, config.allowedProtocols);
405+
IterableDeeplinkManager.getAndTrackDeeplink(uri, onCallback);
406406
}
407407

408408
/**
@@ -427,7 +427,7 @@ public void execute(String originalUrl) {
427427
IterableAction action = IterableAction.actionOpenUrl(originalUrl);
428428
IterableActionRunner.executeAction(getInstance().getMainActivityContext(), action, IterableActionSource.APP_LINK);
429429
}
430-
}, config.allowedProtocols);
430+
});
431431
return true;
432432
} else {
433433
IterableAction action = IterableAction.actionOpenUrl(uri);

iterableapi/src/main/java/com/iterable/iterableapi/IterableDeeplinkManager.java

+2-28
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import java.util.regex.Pattern;
1414

1515
class IterableDeeplinkManager {
16-
private static final String TAG = "IterableDeeplinkManager";
16+
1717
private static Pattern deeplinkPattern = Pattern.compile(IterableConstants.ITBL_DEEPLINK_IDENTIFIER);
1818

1919
/**
@@ -22,22 +22,10 @@ class IterableDeeplinkManager {
2222
* @param callback The callback to execute the original URL is retrieved
2323
*/
2424
static void getAndTrackDeeplink(@Nullable String url, @NonNull IterableHelper.IterableActionHandler callback) {
25-
IterableDeeplinkManager.getAndTrackDeeplink(url, callback, new String[0]);
26-
}
27-
28-
/**
29-
* Tracks a link click and passes the redirected URL to the callback
30-
* @param url The URL that was clicked
31-
* @param callback The callback to execute the original URL is retrieved
32-
* @param allowedProtocols a list of protocols (on top of `https`) to allow opening
33-
*/
34-
static void getAndTrackDeeplink(@Nullable String url, @NonNull IterableHelper.IterableActionHandler callback, @NonNull String[] allowedProtocols) {
3525
if (url != null) {
36-
if (!isUrlOpenAllowed(url, allowedProtocols)) {
37-
IterableLogger.e(TAG, "URL was not in the allowed protocols list");
26+
if (!IterableUtil.isUrlOpenAllowed(url)) {
3827
return;
3928
}
40-
4129
if (isIterableDeeplink(url)) {
4230
new RedirectTask(callback).execute(url);
4331
} else {
@@ -63,20 +51,6 @@ static boolean isIterableDeeplink(@Nullable String url) {
6351
return false;
6452
}
6553

66-
private static boolean isUrlOpenAllowed(@NonNull String url, @NonNull String[] allowedProtocols) {
67-
if (url.startsWith("https")) {
68-
return true;
69-
}
70-
71-
for (String protocol : allowedProtocols) {
72-
if (url.startsWith(protocol)) {
73-
return true;
74-
}
75-
}
76-
77-
return false;
78-
}
79-
8054
private static class RedirectTask extends AsyncTask<String, Void, String> {
8155
static final String TAG = "RedirectTask";
8256
static final int DEFAULT_TIMEOUT_MS = 3000; //3 seconds

iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import androidx.fragment.app.DialogFragment;
3636

3737
public class IterableInAppFragmentHTMLNotification extends DialogFragment implements IterableWebViewClient.HTMLNotificationCallbacks {
38+
3839
private static final String BACK_BUTTON = "itbl://backButton";
3940
private static final String JAVASCRIPT_INTERFACE = "ITBL";
4041
private static final String TAG = "IterableInAppFragmentHTMLNotification";

iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java

+6-10
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,20 @@ public interface Listener {
4343
private final IterableInAppStorage storage;
4444
private final IterableInAppHandler handler;
4545
private final IterableInAppDisplayer displayer;
46-
private final String[] allowedProtocols;
4746
private final IterableActivityMonitor activityMonitor;
4847
private final double inAppDisplayInterval;
4948
private final List<Listener> listeners = new ArrayList<>();
5049
private long lastSyncTime = 0;
5150
private long lastInAppShown = 0;
5251
private boolean autoDisplayPaused = false;
5352

54-
IterableInAppManager(IterableApi iterableApi, IterableInAppHandler handler, double inAppDisplayInterval, String[] allowedProtocols) {
53+
IterableInAppManager(IterableApi iterableApi, IterableInAppHandler handler, double inAppDisplayInterval) {
5554
this(iterableApi,
5655
handler,
5756
inAppDisplayInterval,
5857
new IterableInAppFileStorage(iterableApi.getMainActivityContext()),
5958
IterableActivityMonitor.getInstance(),
60-
new IterableInAppDisplayer(IterableActivityMonitor.getInstance()),
61-
allowedProtocols);
59+
new IterableInAppDisplayer(IterableActivityMonitor.getInstance()));
6260
}
6361

6462
@VisibleForTesting
@@ -67,8 +65,7 @@ public interface Listener {
6765
double inAppDisplayInterval,
6866
IterableInAppStorage storage,
6967
IterableActivityMonitor activityMonitor,
70-
IterableInAppDisplayer displayer,
71-
String[] allowedProtocols) {
68+
IterableInAppDisplayer displayer) {
7269
this.api = iterableApi;
7370
this.context = iterableApi.getMainActivityContext();
7471
this.handler = handler;
@@ -77,7 +74,6 @@ public interface Listener {
7774
this.displayer = displayer;
7875
this.activityMonitor = activityMonitor;
7976
this.activityMonitor.addCallback(this);
80-
this.allowedProtocols = allowedProtocols;
8177

8278
syncInApp();
8379
}
@@ -275,17 +271,17 @@ public void handleInAppClick(@NonNull IterableInAppMessage message, @Nullable Ur
275271
if (urlString.startsWith(IterableConstants.URL_SCHEME_ACTION)) {
276272
// This is an action:// URL, pass that to the custom action handler
277273
String actionName = urlString.replace(IterableConstants.URL_SCHEME_ACTION, "");
278-
IterableActionRunner.executeAction(context, IterableAction.actionCustomAction(actionName), IterableActionSource.IN_APP, allowedProtocols);
274+
IterableActionRunner.executeAction(context, IterableAction.actionCustomAction(actionName), IterableActionSource.IN_APP);
279275
} else if (urlString.startsWith(IterableConstants.URL_SCHEME_ITBL)) {
280276
// Handle itbl:// URLs, pass that to the custom action handler for compatibility
281277
String actionName = urlString.replace(IterableConstants.URL_SCHEME_ITBL, "");
282-
IterableActionRunner.executeAction(context, IterableAction.actionCustomAction(actionName), IterableActionSource.IN_APP, allowedProtocols);
278+
IterableActionRunner.executeAction(context, IterableAction.actionCustomAction(actionName), IterableActionSource.IN_APP);
283279
} else if (urlString.startsWith(IterableConstants.URL_SCHEME_ITERABLE)) {
284280
// Handle iterable:// URLs - reserved for actions defined by the SDK only
285281
String actionName = urlString.replace(IterableConstants.URL_SCHEME_ITERABLE, "");
286282
handleIterableCustomAction(actionName, message);
287283
} else {
288-
IterableActionRunner.executeAction(context, IterableAction.actionOpenUrl(urlString), IterableActionSource.IN_APP, allowedProtocols);
284+
IterableActionRunner.executeAction(context, IterableAction.actionOpenUrl(urlString), IterableActionSource.IN_APP);
289285
}
290286
}
291287
}

iterableapi/src/main/java/com/iterable/iterableapi/IterableUtil.java

+17
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import android.content.pm.PackageInfo;
66
import android.content.pm.PackageManager;
77

8+
import androidx.annotation.NonNull;
89
import androidx.annotation.Nullable;
910
import androidx.annotation.VisibleForTesting;
1011

@@ -277,4 +278,20 @@ boolean writeFile(File file, String content) {
277278
return false;
278279
}
279280
}
281+
282+
static boolean isUrlOpenAllowed(@NonNull String url) {
283+
String urlProtocol = url.split("://")[0];
284+
if (urlProtocol.equals("https")) {
285+
return true;
286+
}
287+
288+
for (String allowedProtocol : IterableApi.getInstance().config.allowedProtocols) {
289+
if (urlProtocol.equals(allowedProtocol)) {
290+
return true;
291+
}
292+
}
293+
294+
IterableLogger.d(TAG, urlProtocol + " is not in the allowed protocols");
295+
return false;
296+
}
280297
}

iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerSyncTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class IterableInAppManagerSyncTest extends BaseTest {
3838
@Before
3939
public void setUp() throws Exception {
4040
MockitoAnnotations.initMocks(this);
41-
inAppManager = spy(new IterableInAppManager(iterableApiMock, handlerMock, 30.0, storageMock, activityMonitorMock, inAppDisplayerMock, new String[0]));
41+
inAppManager = spy(new IterableInAppManager(iterableApiMock, handlerMock, 30.0, storageMock, activityMonitorMock, inAppDisplayerMock));
4242
doAnswer(new Answer() {
4343
@Override
4444
public Object answer(InvocationOnMock invocation) throws Throwable {

iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ public void testHandleActionLink() throws Exception {
264264
IterableActivityMonitor.instance = new IterableActivityMonitor();
265265

266266
IterableInAppDisplayer inAppDisplayerMock = mock(IterableInAppDisplayer.class);
267-
IterableInAppManager inAppManager = spy(new IterableInAppManager(IterableApi.sharedInstance, new IterableDefaultInAppHandler(), 30.0, new IterableInAppMemoryStorage(), IterableActivityMonitor.getInstance(), inAppDisplayerMock, new String[0]));
267+
IterableInAppManager inAppManager = spy(new IterableInAppManager(IterableApi.sharedInstance, new IterableDefaultInAppHandler(), 30.0, new IterableInAppMemoryStorage(), IterableActivityMonitor.getInstance(), inAppDisplayerMock));
268268
IterableApi.sharedInstance = new IterableApi(inAppManager);
269269
IterableTestUtils.createIterableApiNew(new IterableTestUtils.ConfigBuilderExtender() {
270270
@Override
@@ -328,7 +328,7 @@ public void testHandleCustomActionDelete() throws Exception {
328328
IterableActivityMonitor.instance = new IterableActivityMonitor();
329329

330330
IterableInAppDisplayer inAppDisplayerMock = mock(IterableInAppDisplayer.class);
331-
IterableInAppManager inAppManager = spy(new IterableInAppManager(IterableApi.sharedInstance, new IterableSkipInAppHandler(), 30.0, new IterableInAppMemoryStorage(), IterableActivityMonitor.getInstance(), inAppDisplayerMock, new String[0]));
331+
IterableInAppManager inAppManager = spy(new IterableInAppManager(IterableApi.sharedInstance, new IterableSkipInAppHandler(), 30.0, new IterableInAppMemoryStorage(), IterableActivityMonitor.getInstance(), inAppDisplayerMock));
332332
IterableApi.sharedInstance = new IterableApi(inAppManager);
333333
IterableTestUtils.createIterableApiNew(new IterableTestUtils.ConfigBuilderExtender() {
334334
@Override

iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public void testShowInboxMessageImmediate() throws Exception {
119119

120120
IterableInAppDisplayer inAppDisplayerMock = mock(IterableInAppDisplayer.class);
121121
when(inAppDisplayerMock.showMessage(any(IterableInAppMessage.class), eq(IterableInAppLocation.IN_APP), any(IterableHelper.IterableUrlCallback.class))).thenReturn(true);
122-
IterableInAppManager inAppManager = spy(new IterableInAppManager(IterableApi.sharedInstance, new IterableDefaultInAppHandler(), 30.0, new IterableInAppMemoryStorage(), IterableActivityMonitor.getInstance(), inAppDisplayerMock, new String[0]));
122+
IterableInAppManager inAppManager = spy(new IterableInAppManager(IterableApi.sharedInstance, new IterableDefaultInAppHandler(), 30.0, new IterableInAppMemoryStorage(), IterableActivityMonitor.getInstance(), inAppDisplayerMock));
123123
IterableApi.sharedInstance = new IterableApi(inAppManager);
124124
IterableTestUtils.createIterableApiNew(new IterableTestUtils.ConfigBuilderExtender() {
125125
@Override

iterableapi/src/test/java/com/iterable/iterableapi/IterablePushActionReceiverTest.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public void testTrackPushOpenWithCustomAction() throws Exception {
8080

8181
// Verify that IterableActionRunner was called with the proper action
8282
ArgumentCaptor<IterableAction> capturedAction = ArgumentCaptor.forClass(IterableAction.class);
83-
verify(actionRunnerMock).executeAction(any(Context.class), capturedAction.capture(), eq(IterableActionSource.PUSH), eq(new String[0]));
83+
verify(actionRunnerMock).executeAction(any(Context.class), capturedAction.capture(), eq(IterableActionSource.PUSH));
8484
assertEquals("customAction", capturedAction.getValue().getType());
8585

8686
// Verify that the main app activity was launched
@@ -133,7 +133,7 @@ public void testPushActionWithTextInput() throws Exception {
133133

134134
// Verify that IterableActionRunner was called with the proper action
135135
ArgumentCaptor<IterableAction> actionCaptor = ArgumentCaptor.forClass(IterableAction.class);
136-
verify(actionRunnerMock).executeAction(any(Context.class), actionCaptor.capture(), eq(IterableActionSource.PUSH), eq(new String[0]));
136+
verify(actionRunnerMock).executeAction(any(Context.class), actionCaptor.capture(), eq(IterableActionSource.PUSH));
137137
IterableAction capturedAction = actionCaptor.getValue();
138138
assertEquals("handleTextInput", capturedAction.getType());
139139
assertEquals("input text", capturedAction.userInput);
@@ -151,7 +151,7 @@ public void testLegacyDeepLinkPayload() throws Exception {
151151

152152
// Verify that IterableActionRunner was called with openUrl action
153153
ArgumentCaptor<IterableAction> capturedAction = ArgumentCaptor.forClass(IterableAction.class);
154-
verify(actionRunnerMock).executeAction(any(Context.class), capturedAction.capture(), eq(IterableActionSource.PUSH), eq(new String[0]));
154+
verify(actionRunnerMock).executeAction(any(Context.class), capturedAction.capture(), eq(IterableActionSource.PUSH));
155155
assertEquals("openUrl", capturedAction.getValue().getType());
156156
assertEquals("https://example.com", capturedAction.getValue().getData());
157157
}

0 commit comments

Comments
 (0)