Unverified Commit d2349239 authored by Matthew Finkel's avatar Matthew Finkel
Browse files

Add Security Level UI

Bug 40026: Implement Security Level settings

Bug 40026: Integrate Security Level settings
parent 045a0328
Pipeline #2297 failed with stages
......@@ -118,6 +118,7 @@ class Core(
context,
R.color.foundation_normal_theme
),
torSecurityLevel = context.settings().torSecurityLevel().intRepresentation,
spoofEnglish = context.settings().spoofEnglish
)
......
......@@ -435,6 +435,8 @@ class DefaultSessionControlController(
}
override fun handleOpenSecurityLevelSettingsClicked() {
val directions = HomeFragmentDirections.actionGlobalTorSecurityLevelFragment()
navController.nav(R.id.homeFragment, directions)
}
override fun handleWhatsNewGetAnswersClicked() {
......
......@@ -8,8 +8,11 @@ import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.tor_onboarding_security_level.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.home.sessioncontrol.OnboardingInteractor
import org.mozilla.fenix.onboarding.OnboardingRadioButton
import org.mozilla.fenix.tor.SecurityLevel
import org.mozilla.fenix.tor.SecurityLevelUtil
import org.mozilla.fenix.utils.view.addToRadioGroup
class TorOnboardingSecurityLevelViewHolder(
......@@ -33,34 +36,56 @@ class TorOnboardingSecurityLevelViewHolder(
)
view.open_settings_button.setOnClickListener {
interactor.onOpenSettingsClicked()
interactor.onOpenSecurityLevelSettingsClicked()
}
setupRadioGroup()
setupRadioGroup(view)
}
private fun setupRadioGroup() {
private fun setupRadioGroup(view: View) {
addToRadioGroup(standardSecurityLevel, saferSecurityLevel, safestSecurityLevel)
standardSecurityLevel.isChecked = true
safestSecurityLevel.isChecked = false
saferSecurityLevel.isChecked = false
val securityLevel = try {
SecurityLevelUtil.getSecurityLevelFromInt(
view.context.components.core.engine.settings.torSecurityLevel
)
} catch (e: IllegalStateException) {
SecurityLevel.STANDARD
}
standardSecurityLevel.isChecked = securityLevel == SecurityLevel.STANDARD
safestSecurityLevel.isChecked = securityLevel == SecurityLevel.SAFEST
saferSecurityLevel.isChecked = securityLevel == SecurityLevel.SAFER
standardSecurityLevel.onClickListener {
updateSecurityLevel()
updateSecurityLevel(SecurityLevel.STANDARD)
}
saferSecurityLevel.onClickListener {
updateSecurityLevel()
updateSecurityLevel(SecurityLevel.SAFER)
}
safestSecurityLevel.onClickListener {
updateSecurityLevel()
updateSecurityLevel(SecurityLevel.SAFEST)
}
updateSecurityLevel(securityLevel)
}
private fun updateSecurityLevel() {
private fun updateSecurityLevel(newLevel: SecurityLevel) {
val resources = itemView.context.resources
val securityLevel = when (newLevel) {
SecurityLevel.STANDARD -> resources.getString(R.string.tor_security_level_standard_option)
SecurityLevel.SAFER -> resources.getString(R.string.tor_security_level_safer_option)
SecurityLevel.SAFEST -> resources.getString(R.string.tor_security_level_safest_option)
}
itemView.current_level.text = resources.getString(
R.string.tor_onboarding_chosen_security_level_label, securityLevel
)
itemView.context.components.let {
it.core.engine.settings.torSecurityLevel = newLevel.intRepresentation
}
}
companion object {
......
......@@ -224,6 +224,9 @@ class SettingsFragment : PreferenceFragmentCompat() {
resources.getString(R.string.pref_key_tor_network_settings) -> {
SettingsFragmentDirections.actionSettingsFragmentToTorNetworkSettingsFragment()
}
resources.getString(R.string.pref_key_tor_security_level_settings) -> {
SettingsFragmentDirections.actionSettingsFragmentToTorSecurityLevelFragment()
}
resources.getString(R.string.pref_key_tracking_protection_settings) -> {
requireContext().metrics.track(Event.TrackingProtectionSettings)
SettingsFragmentDirections.actionSettingsFragmentToTrackingProtectionFragment()
......
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.settings
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.tor.SecurityLevel
import org.mozilla.fenix.tor.SecurityLevelUtil
import org.mozilla.fenix.utils.view.GroupableRadioButton
import org.mozilla.fenix.utils.view.addToRadioGroup
import org.mozilla.fenix.utils.view.uncheckAll
/**
* Lets the user choose their security level
*/
@Suppress("SpreadOperator")
class TorSecurityLevelFragment : PreferenceFragmentCompat() {
private val securityLevelRadioGroups = mutableListOf<GroupableRadioButton>()
private var previousSecurityLevel: SecurityLevel? = null
override fun onResume() {
super.onResume()
showToolbar(getString(R.string.preferences_tor_security_level_options))
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.tor_security_level_preferences, rootKey)
val currentLevel: SecurityLevel? = context?.components?.let {
try {
SecurityLevelUtil.getSecurityLevelFromInt(
it.core.engine.settings.torSecurityLevel
)
} catch (e: IllegalStateException) {
// The default state is 0. If we get an invalid state then
// default to Standard (4).
SecurityLevel.STANDARD
}
}
if (currentLevel == null) {
throw IllegalStateException("context or Components is null.")
}
val radioSafer = bindSecurityLevelRadio(SecurityLevel.SAFER)
val radioSafest = bindSecurityLevelRadio(SecurityLevel.SAFEST)
val radioStandard = bindSecurityLevelRadio(SecurityLevel.STANDARD)
securityLevelRadioGroups.addAll(mutableListOf(radioSafer, radioSafest, radioStandard))
// `*` is Kotlin's "spread" operator, for expanding an Array as a vararg.
addToRadioGroup(*securityLevelRadioGroups.toTypedArray())
securityLevelRadioGroups.uncheckAll()
val securityLevelRadioButton = requirePreference<RadioButtonPreference>(currentLevel.preferenceKey)
// Cache this for later comparison in the OnPreferenceChangeListener
previousSecurityLevel = currentLevel
securityLevelRadioButton.setCheckedWithoutClickListener(true)
}
private fun bindSecurityLevelRadio(
level: SecurityLevel
): RadioButtonPreference {
val radio = requirePreference<RadioButtonPreference>(level.preferenceKey)
radio.summary = getString(level.contentDescriptionRes)
radio.apply {
setOnPreferenceChangeListener<Boolean> { preference, isChecked ->
if (isChecked && (previousSecurityLevel!! != level)) {
preference.context.components.core.engine.settings.torSecurityLevel =
level.intRepresentation
previousSecurityLevel = level
}
true
}
}
return radio
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.tor
import android.os.Parcelable
import androidx.annotation.StringRes
import kotlinx.android.parcel.Parcelize
import org.mozilla.fenix.R
@Parcelize
enum class SecurityLevel(
@StringRes val preferenceKey: Int,
@StringRes val contentDescriptionRes: Int,
val intRepresentation: Int
) : Parcelable {
STANDARD(
preferenceKey = R.string.pref_key_tor_security_level_standard_option,
contentDescriptionRes = R.string.tor_security_level_standard_description,
intRepresentation = SecurityLevel.SECURITY_LEVEL_STANDARD
),
SAFER(
preferenceKey = R.string.pref_key_tor_security_level_safer_option,
contentDescriptionRes = R.string.tor_security_level_safer_description,
intRepresentation = SecurityLevel.SECURITY_LEVEL_SAFER
),
SAFEST(
preferenceKey = R.string.pref_key_tor_security_level_safest_option,
contentDescriptionRes = R.string.tor_security_level_safest_description,
intRepresentation = SecurityLevel.SECURITY_LEVEL_SAFEST
);
companion object {
const val SECURITY_LEVEL_STANDARD = 4
const val SECURITY_LEVEL_SAFER = 2
const val SECURITY_LEVEL_SAFEST = 1
}
}
object SecurityLevelUtil {
fun getSecurityLevelFromInt(level: Int): SecurityLevel {
return when (level) {
SecurityLevel.SECURITY_LEVEL_STANDARD -> SecurityLevel.STANDARD
SecurityLevel.SECURITY_LEVEL_SAFER -> SecurityLevel.SAFER
SecurityLevel.SECURITY_LEVEL_SAFEST -> SecurityLevel.SAFEST
else -> throw IllegalStateException("Security Level $level is not valid")
}
}
}
......@@ -40,6 +40,7 @@ import org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataOnQuitTyp
import org.mozilla.fenix.settings.logins.SavedLoginsSortingStrategyMenu
import org.mozilla.fenix.settings.logins.SortingStrategy
import org.mozilla.fenix.settings.registerOnSharedPreferenceChangeListener
import org.mozilla.fenix.tor.SecurityLevel
import java.security.InvalidParameterException
private const val AUTOPLAY_USER_SETTING = "AUTOPLAY_USER_SETTING"
......@@ -174,6 +175,33 @@ class Settings(private val appContext: Context) : PreferencesHolder {
false
)
var standardSecurityLevel by booleanPreference(
appContext.getPreferenceKey(SecurityLevel.STANDARD.preferenceKey),
default = true
)
var saferSecurityLevel by booleanPreference(
appContext.getPreferenceKey(SecurityLevel.SAFER.preferenceKey),
default = false
)
var safestSecurityLevel by booleanPreference(
appContext.getPreferenceKey(SecurityLevel.SAFEST.preferenceKey),
default = false
)
// torSecurityLevel is defined as the first |true| preference,
// beginning at the safest level.
// If multiple preferences are true, then that is a bug and the
// highest |true| security level is chosen.
// Standard is the default level.
fun torSecurityLevel(): SecurityLevel = when {
safestSecurityLevel -> SecurityLevel.SAFEST
saferSecurityLevel -> SecurityLevel.SAFER
standardSecurityLevel -> SecurityLevel.STANDARD
else -> SecurityLevel.STANDARD
}
var spoofEnglish by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_spoof_english),
default = false
......
......@@ -32,9 +32,18 @@
android:textAppearance="@style/Body14TextStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/header_text"
app:layout_constraintTop_toBottomOf="@id/current_level"
tools:text="@string/tor_onboarding_security_level_description" />
<TextView
android:id="@+id/current_level"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/header_text"
tools:text="@string/tor_onboarding_chosen_security_level_label" />
<org.mozilla.fenix.onboarding.OnboardingRadioButton
android:id="@+id/security_level_standard_option"
......
......@@ -60,6 +60,9 @@
<action
android:id="@+id/action_global_syncedTabsFragment"
app:destination="@id/syncedTabsFragment" />
<action
android:id="@+id/action_global_torSecurityLevelFragment"
app:destination="@id/torSecurityLevelFragment" />
<action
android:id="@+id/action_global_privateBrowsingFragment"
app:destination="@id/privateBrowsingFragment" />
......@@ -491,6 +494,13 @@
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<action
android:id="@+id/action_settingsFragment_to_torSecurityLevelFragment"
app:destination="@id/torSecurityLevelFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<action
android:id="@+id/action_settingsFragment_to_privateBrowsingFragment"
app:destination="@id/privateBrowsingFragment"
......@@ -654,6 +664,10 @@
android:id="@+id/customizationFragment"
android:name="org.mozilla.fenix.settings.CustomizationFragment"
android:label="@string/preferences_customize" />
<fragment
android:id="@+id/torSecurityLevelFragment"
android:name="org.mozilla.fenix.settings.TorSecurityLevelFragment"
android:label="@string/preferences_tor_security_level_settings" />
<fragment
android:id="@+id/privateBrowsingFragment"
android:name="org.mozilla.fenix.settings.PrivateBrowsingFragment"
......
......@@ -23,6 +23,7 @@
<string name="tor_onboarding_security_level">Set your Security Level</string>
<string name="tor_onboarding_security_level_description">Disable certain web features that can be used to attack you, and harm your security, anonymity, and privacy.</string>
<string name="tor_onboarding_chosen_security_level_label">Current Security Level: %s</string>
<string name="tor_onboarding_security_settings_button">Open Security Settings</string>
<string name="tor_onboarding_donate_header">Donate and keep Tor safe</string>
<string name="tor_onboarding_donate_description">Tor is free to use because of donations from people like you.</string>
......@@ -57,6 +58,10 @@
<string name="preferences_tor_network_settings_restarting">Restarting</string>
<string name="preferences_tor_network_settings_bridges_enabled">Bridges are enabled: %s</string>
<!-- Preference title for security level settings -->
<string name="preferences_tor_security_level_settings">Security Settings</string>
<string name="preferences_tor_security_level_options">Security Level</string>
<!-- Description of security levels -->
<string name="tor_security_level_standard_option">Standard</string>
<string name="tor_security_level_standard_description">All Tor Browser and website features are enabled.</string>
......
......@@ -89,6 +89,11 @@
app:iconSpaceReserved="false"
android:layout="@layout/preference_cat_style">
<androidx.preference.Preference
android:icon="@drawable/ic_tracking_protection_enabled"
android:key="@string/pref_key_tor_security_level_settings"
android:title="@string/preferences_tor_security_level_settings"/>
<androidx.preference.Preference
android:icon="@drawable/ic_private_browsing"
android:key="@string/pref_key_private_browsing"
......
<?xml version="1.0" encoding="utf-8"?><!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<org.mozilla.fenix.settings.RadioButtonPreference
android:defaultValue="true"
android:key="@string/pref_key_tor_security_level_standard_option"
android:summary="@string/tor_security_level_standard_description"
android:title="@string/tor_security_level_standard_option" />
<org.mozilla.fenix.settings.RadioButtonPreference
android:defaultValue="false"
android:key="@string/pref_key_tor_security_level_safer_option"
android:summary="@string/tor_security_level_safer_description"
android:title="@string/tor_security_level_safer_option" />
<org.mozilla.fenix.settings.RadioButtonPreference
android:defaultValue="false"
android:key="@string/pref_key_tor_security_level_safest_option"
android:summary="@string/tor_security_level_safest_description"
android:title="@string/tor_security_level_safest_option" />
</androidx.preference.PreferenceScreen>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment