Skip to content

Commit

Permalink
[MIT-1845] Add FLAG_SECURE (#279)
Browse files Browse the repository at this point in the history
* feat: add FLAG_SECURE to Activity classes

* test: add UI tests

* test: fix failed test

* feat: add EXTRA_IS_SECURE

* test: add unit tests

* docs: update README.md

* chore: enable FLAG_SECURE by default

* docs: update README.md

* Update README.md

Co-authored-by: Aashish Gurung <[email protected]>

---------

Co-authored-by: Aashish Gurung <[email protected]>
  • Loading branch information
nuxzero and aashishgurung authored Oct 20, 2023
1 parent a515a4c commit e8d08c0
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 13 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,10 @@ If you enable ProGuard, then add this rules in your ProGuard file.
-keep class com.nimbusds.jose.** { *; }
```
## Protecting screenshot and screen recording
**Omise Android SDK** comes with built-in protection against screenshoot and screen recording. If you wish to disable this feature, you can pass `OmiseActivity.EXTRA_IS_SECURE` with a value of `false` when starting the following activities: `CreditCardActivity`, `PaymentCreatorActivity`, and `AuthorizingPaymentActivity`.
## Contributing
Pull requests and bug fixes are welcome. For larger scope of work, please pop on to our [forum](https://forum.omise.co) to discuss first.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Activity
import android.app.Instrumentation
import android.content.Intent
import android.net.Uri
import android.view.WindowManager
import android.widget.ProgressBar
import androidx.arch.core.executor.testing.CountingTaskExecutorRule
import androidx.lifecycle.MutableLiveData
Expand Down Expand Up @@ -55,6 +56,7 @@ import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.instanceOf
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
Expand Down Expand Up @@ -331,4 +333,21 @@ class AuthorizingPaymentActivityTest {
)
)
}

@Test
fun flagSecure_whenParameterIsFalseThenAttributesMustNotContainFlagSecure() {
intent.putExtra(OmiseActivity.EXTRA_IS_SECURE, false)
val scenario = ActivityScenario.launchActivityForResult<AuthorizingPaymentActivity>(intent)
scenario.onActivity {
assertNotEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE)
}
}

@Test
fun flagSecure_whenParameterNotSetThenAttributesMustContainFlagSecure() {
val scenario = ActivityScenario.launchActivityForResult<AuthorizingPaymentActivity>(intent)
scenario.onActivity {
assertEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import android.webkit.CookieManager
import android.webkit.JsPromptResult
import android.webkit.JsResult
Expand Down Expand Up @@ -54,6 +55,11 @@ class AuthorizingPaymentActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

if (intent.getBooleanExtra(OmiseActivity.EXTRA_IS_SECURE, true)) {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
}

setContentView(R.layout.activity_authorizing_payment)

supportActionBar?.title = threeDSConfig.uiCustomization?.toolbarCustomization?.headerText
Expand Down
5 changes: 5 additions & 0 deletions sdk/src/main/java/co/omise/android/ui/CreditCardActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.view.WindowManager
import android.widget.Button
import android.widget.EditText
import android.widget.ImageButton
Expand Down Expand Up @@ -125,6 +126,10 @@ class CreditCardActivity : OmiseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

if (intent.getBooleanExtra(EXTRA_IS_SECURE, true)) {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
}

setContentView(R.layout.activity_credit_card)

require(intent.hasExtra(EXTRA_PKEY)) { "Could not find ${::EXTRA_PKEY.name}." }
Expand Down
6 changes: 6 additions & 0 deletions sdk/src/main/java/co/omise/android/ui/OmiseActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ abstract class OmiseActivity : AppCompatActivity() {
const val EXTRA_TOKEN = "OmiseActivity.token"
const val EXTRA_TOKEN_OBJECT = "OmiseActivity.tokenObject"
const val EXTRA_CARD_OBJECT = "OmiseActivity.cardObject"

/**
* Applies [android.view.WindowManager.LayoutParams.FLAG_SECURE] to the activity.
* This will prevent the activity from being captured by screenshots and video recordings.
*/
const val EXTRA_IS_SECURE = "OmiseActivity.isSecure"
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package co.omise.android.ui
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import co.omise.android.R
Expand All @@ -18,6 +18,7 @@ import co.omise.android.ui.OmiseActivity.Companion.EXTRA_CURRENCY
import co.omise.android.ui.OmiseActivity.Companion.EXTRA_GOOGLEPAY_MERCHANT_ID
import co.omise.android.ui.OmiseActivity.Companion.EXTRA_GOOGLEPAY_REQUEST_BILLING_ADDRESS
import co.omise.android.ui.OmiseActivity.Companion.EXTRA_GOOGLEPAY_REQUEST_PHONE_NUMBER
import co.omise.android.ui.OmiseActivity.Companion.EXTRA_IS_SECURE
import co.omise.android.ui.OmiseActivity.Companion.EXTRA_PKEY
import co.omise.android.ui.OmiseActivity.Companion.EXTRA_SOURCE_OBJECT
import com.google.android.material.snackbar.Snackbar
Expand Down Expand Up @@ -64,6 +65,11 @@ class PaymentCreatorActivity : OmiseActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

if (intent.getBooleanExtra(EXTRA_IS_SECURE, true)) {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
}

setContentView(R.layout.activity_payment_creator)

initialize()
Expand Down Expand Up @@ -201,6 +207,7 @@ private class PaymentCreatorNavigationImpl(
override fun navigateToCreditCardForm() {
val intent = Intent(activity, CreditCardActivity::class.java).apply {
putExtra(EXTRA_PKEY, pkey)
putExtra(EXTRA_IS_SECURE, activity.intent.getBooleanExtra(EXTRA_IS_SECURE, true))
}
activity.startActivityForResult(intent, requestCode)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.app.Application
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ActivityScenario.launchActivityForResult
Expand Down Expand Up @@ -42,8 +43,8 @@ import org.hamcrest.CoreMatchers.not
import org.hamcrest.Matcher
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
Expand All @@ -54,7 +55,6 @@ import org.mockito.kotlin.reset
import org.mockito.kotlin.whenever

@RunWith(AndroidJUnit4::class)
@Ignore
class CreditCardActivityTest {

private lateinit var scenario: ActivityScenario<CreditCardActivity>
Expand Down Expand Up @@ -295,10 +295,7 @@ class CreditCardActivityTest {

@Test
fun submitForm_disableFormWhenPressSubmit() {
whenever(mockClient.send<Token>(any(), any())).doAnswer { invocation ->
val callback = invocation.getArgument<RequestListener<Token>>(1)
callback.onRequestSucceed(Token())
}
whenever(mockClient.send<Token>(any(), any())).doAnswer {}
onView(withId(R.id.edit_card_number)).perform(typeText("4242424242424242"))
onView(withId(R.id.edit_card_name)).perform(typeText("John Doe"))
onView(withId(R.id.edit_expiry_date)).perform(typeText("1234"))
Expand Down Expand Up @@ -386,6 +383,23 @@ class CreditCardActivityTest {
val result = scenario.result
assertEquals(RESULT_CANCELED, result.resultCode)
}

@Test
fun flagSecure_whenParameterIsFalseThenAttributesMustNotContainFlagSecure() {
intent.putExtra(OmiseActivity.EXTRA_IS_SECURE, false)
val scenario = ActivityScenario.launchActivityForResult<CreditCardActivity>(intent)
scenario.onActivity {
assertNotEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE)
}
}

@Test
fun flagSecure_whenParameterNotSetThenAttributesMustContainFlagSecure() {
val scenario = ActivityScenario.launchActivityForResult<CreditCardActivity>(intent)
scenario.onActivity {
assertEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE)
}
}
}

private fun typeNumberText(numberText: String): ViewAction =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@ package co.omise.android.ui

import android.app.Activity.RESULT_OK
import android.content.Intent
import android.view.WindowManager
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.matcher.ComponentNameMatchers.hasClassName
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.test.espresso.intent.rule.IntentsRule
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import co.omise.android.R
import co.omise.android.models.Capability
import co.omise.android.models.Token
import co.omise.android.ui.OmiseActivity.Companion.EXTRA_TOKEN
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -26,9 +29,8 @@ import org.junit.runner.RunWith
class PaymentCreatorActivityTest {

@get:Rule
val intentRule = IntentsTestRule<TestFragmentActivity>(TestFragmentActivity::class.java)
val intentRule = IntentsRule()

private lateinit var scenario: ActivityScenario<PaymentCreatorActivity>
private val capability = Capability()
private val intent = Intent(
ApplicationProvider.getApplicationContext(),
Expand All @@ -42,15 +44,15 @@ class PaymentCreatorActivityTest {

@Test
fun initialActivity_collectExtrasIntent() {
scenario = ActivityScenario.launch(intent)
ActivityScenario.launchActivityForResult<PaymentCreatorActivity>(intent)

onView(withId(R.id.payment_creator_container)).check(matches(isDisplayed()))
}

@Test
fun navigateToCreditCardForm_startCreditCartActivity() {
var activity: PaymentCreatorActivity? = null
scenario = ActivityScenario.launch<PaymentCreatorActivity>(intent).onActivity {
ActivityScenario.launchActivityForResult<PaymentCreatorActivity>(intent).onActivity {
activity = it
}

Expand All @@ -64,10 +66,27 @@ class PaymentCreatorActivityTest {
val creditCardIntent = Intent().apply {
putExtra(EXTRA_TOKEN, Token())
}
scenario = ActivityScenario.launchActivityForResult<PaymentCreatorActivity>(intent).onActivity {
val scenario = ActivityScenario.launchActivityForResult<PaymentCreatorActivity>(intent).onActivity {
it.performActivityResult(100, RESULT_OK, creditCardIntent)
}

assertEquals(RESULT_OK, scenario.result.resultCode)
}

@Test
fun flagSecure_whenParameterIsFalseThenAttributesMustNotContainFlagSecure() {
intent.putExtra(OmiseActivity.EXTRA_IS_SECURE, false)
val scenario = ActivityScenario.launchActivityForResult<PaymentCreatorActivity>(intent)
scenario.onActivity {
assertNotEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE)
}
}

@Test
fun flagSecure_whenParameterNotSetThenAttributesMustContainFlagSecure() {
val scenario = ActivityScenario.launchActivityForResult<PaymentCreatorActivity>(intent)
scenario.onActivity {
assertEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE)
}
}
}

0 comments on commit e8d08c0

Please sign in to comment.