Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIT-1845] Add FLAG_SECURE #279

Merged
merged 9 commits into from
Oct 20, 2023
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
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

By default, **Omise Android SDK** protects the screen from being screenshot and screen recording, if you want to disable this feature you can pass this `OmiseActivity.EXTRA_IS_SECURE` extra data to `false` when starting these activities `CreditCardActivity`, `PaymentCreatorActivity`, and `AuthorizingPaymentActivity`.
nuxzero marked this conversation as resolved.
Show resolved Hide resolved

## 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)
}
}
}
Loading