kotlin cleanup

This commit is contained in:
Milos Kozak 2020-03-20 18:13:07 +01:00
parent 5c04780de1
commit 4abc53e7b8
7 changed files with 162 additions and 176 deletions

View file

@ -6,8 +6,8 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.OneTimePassword import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword
import info.nightscout.androidaps.utils.OneTimePasswordValidationResult import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePasswordValidationResult
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject import javax.inject.Inject
@ -21,7 +21,7 @@ class AuthRequest internal constructor(
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var otp :OneTimePassword @Inject lateinit var otp : OneTimePassword
private val date = DateUtil.now() private val date = DateUtil.now()
private var processed = false private var processed = false
@ -32,10 +32,10 @@ class AuthRequest internal constructor(
} }
private fun codeIsValid(toValidate: String) : Boolean { private fun codeIsValid(toValidate: String) : Boolean {
if (otp.isEnabled()) { return if (otp.isEnabled()) {
return otp.checkOTP(toValidate) == OneTimePasswordValidationResult.OK otp.checkOTP(toValidate) == OneTimePasswordValidationResult.OK
} else { } else {
return confirmCode.equals(toValidate) confirmCode == toValidate
} }
} }

View file

@ -28,6 +28,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientR
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin

View file

@ -8,21 +8,21 @@ import android.text.TextWatcher
import android.view.View import android.view.View
import com.google.common.primitives.Ints.min import com.google.common.primitives.Ints.min
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import dagger.android.DaggerActivity
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePasswordValidationResult
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.OKDialog import info.nightscout.androidaps.utils.OKDialog
import info.nightscout.androidaps.utils.OneTimePassword
import info.nightscout.androidaps.utils.OneTimePasswordValidationResult
import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import kotlinx.android.synthetic.main.activity_smscommunicator_otp.* import kotlinx.android.synthetic.main.activity_smscommunicator_otp.*
import net.glxn.qrgen.android.QRCode import net.glxn.qrgen.android.QRCode
import javax.inject.Inject import javax.inject.Inject
class SmsCommunicatorOtpActivity : DaggerActivity() { class SmsCommunicatorOtpActivity : NoSplashAppCompatActivity() {
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@ -35,27 +35,21 @@ class SmsCommunicatorOtpActivity : DaggerActivity() {
smscommunicator_otp_verify_edit.addTextChangedListener(object : TextWatcher { smscommunicator_otp_verify_edit.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {
if (s != null) { val checkResult = otp.checkOTP(s.toString())
val checkResult = otp.checkOTP(s.toString())
smscommunicator_otp_verify_label.text = when (checkResult) { smscommunicator_otp_verify_label.text = when (checkResult) {
OneTimePasswordValidationResult.OK -> "OK" OneTimePasswordValidationResult.OK -> "OK"
OneTimePasswordValidationResult.ERROR_WRONG_LENGTH -> "INVALID SIZE!" OneTimePasswordValidationResult.ERROR_WRONG_LENGTH -> "INVALID SIZE!"
OneTimePasswordValidationResult.ERROR_WRONG_PIN -> "WRONG PIN" OneTimePasswordValidationResult.ERROR_WRONG_PIN -> "WRONG PIN"
OneTimePasswordValidationResult.ERROR_WRONG_OTP -> "WRONG OTP" OneTimePasswordValidationResult.ERROR_WRONG_OTP -> "WRONG OTP"
}
smscommunicator_otp_verify_label.setTextColor(when (checkResult) {
OneTimePasswordValidationResult.OK -> Color.GREEN
OneTimePasswordValidationResult.ERROR_WRONG_LENGTH -> Color.YELLOW
OneTimePasswordValidationResult.ERROR_WRONG_PIN -> Color.RED
OneTimePasswordValidationResult.ERROR_WRONG_OTP -> Color.RED
})
} else {
smscommunicator_otp_verify_label.text = "EMPTY";
smscommunicator_otp_verify_label.setTextColor(Color.YELLOW)
} }
smscommunicator_otp_verify_label.setTextColor(when (checkResult) {
OneTimePasswordValidationResult.OK -> Color.GREEN
OneTimePasswordValidationResult.ERROR_WRONG_LENGTH -> Color.YELLOW
OneTimePasswordValidationResult.ERROR_WRONG_PIN -> Color.RED
OneTimePasswordValidationResult.ERROR_WRONG_OTP -> Color.RED
})
} }
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
@ -82,13 +76,13 @@ class SmsCommunicatorOtpActivity : DaggerActivity() {
} }
fun updateGui() { fun updateGui() {
val displayMetrics = Resources.getSystem().getDisplayMetrics() val displayMetrics = Resources.getSystem().displayMetrics
val width = displayMetrics.widthPixels val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels val height = displayMetrics.heightPixels
// ensure QRCode is big enough to fit on screen // ensure QRCode is big enough to fit on screen
val dim = (min(width, height) * 0.85).toInt() val dim = (min(width, height) * 0.85).toInt()
val provURI = otp.provisioningURI(); val provURI = otp.provisioningURI()
if (provURI != null) { if (provURI != null) {
val myBitmap = QRCode.from(provURI).withErrorCorrection(ErrorCorrectionLevel.H).withSize(dim, dim).bitmap() val myBitmap = QRCode.from(provURI).withErrorCorrection(ErrorCorrectionLevel.H).withSize(dim, dim).bitmap()

View file

@ -1,135 +1,124 @@
package info.nightscout.androidaps.utils package info.nightscout.androidaps.plugins.general.smsCommunicator.otp
import android.util.Base64 import android.util.Base64
import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator
import com.google.common.io.BaseEncoding import com.google.common.io.BaseEncoding
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.joda.time.DateTimeUtils import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.net.URLEncoder import java.net.URLEncoder
import javax.crypto.KeyGenerator import javax.crypto.KeyGenerator
import javax.crypto.SecretKey import javax.crypto.SecretKey
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class OneTimePassword @Inject constructor( class OneTimePassword @Inject constructor(
private val sp: SP, private val sp: SP,
private val resourceHelper: ResourceHelper private val resourceHelper: ResourceHelper
) { ) {
private var key: SecretKey? = null private var key: SecretKey? = null
private var pin: String = "" private var pin: String = ""
private val totp = TimeBasedOneTimePasswordGenerator() private val totp = TimeBasedOneTimePasswordGenerator()
init { init {
instance = this instance = this
configure() configure()
} }
companion object { companion object {
private lateinit var instance: OneTimePassword private lateinit var instance: OneTimePassword
@JvmStatic
fun getInstance(): OneTimePassword = instance @JvmStatic
} fun getInstance(): OneTimePassword = instance
}
/**
* If OTP Authenticator support is enabled by user /**
*/ * If OTP Authenticator support is enabled by user
fun isEnabled(): Boolean { */
return sp.getBoolean(R.string.key_smscommunicator_otp_enabled, true); fun isEnabled(): Boolean {
} return sp.getBoolean(R.string.key_smscommunicator_otp_enabled, true)
}
/**
* Name of master device (target of OTP) /**
*/ * Name of master device (target of OTP)
fun name(): String { */
val defaultUserName = resourceHelper.gs(R.string.smscommunicator_default_user_display_name) fun name(): String {
var userName = sp.getString(R.string.key_smscommunicator_otp_name, defaultUserName).replace(":", "").trim() val defaultUserName = resourceHelper.gs(R.string.smscommunicator_default_user_display_name)
if (userName.length == 0) var userName = sp.getString(R.string.key_smscommunicator_otp_name, defaultUserName).replace(":", "").trim()
userName = defaultUserName if (userName.isEmpty())
return userName userName = defaultUserName
} return userName
}
/**
* Make sure if private key for TOTP is generated, creating it when necessary or requested /**
*/ * Make sure if private key for TOTP is generated, creating it when necessary or requested
fun ensureKey(forceNewKey: Boolean = false) { */
var keyBytes: ByteArray = byteArrayOf() fun ensureKey(forceNewKey: Boolean = false) {
val strSecret = sp.getString(R.string.key_smscommunicator_otp_secret, "").trim() val keyBytes: ByteArray
if (strSecret.length == 0 || forceNewKey) { val strSecret = sp.getString(R.string.key_smscommunicator_otp_secret, "").trim()
val keyGenerator = KeyGenerator.getInstance(totp.getAlgorithm()); if (strSecret.isEmpty() || forceNewKey) {
keyGenerator.init(Constants.OTP_GENERATED_KEY_LENGTH_BITS); val keyGenerator = KeyGenerator.getInstance(totp.algorithm)
val generatedKey = keyGenerator.generateKey(); keyGenerator.init(Constants.OTP_GENERATED_KEY_LENGTH_BITS)
keyBytes = generatedKey.encoded val generatedKey = keyGenerator.generateKey()
sp.putString(R.string.key_smscommunicator_otp_secret, Base64.encodeToString(keyBytes, Base64.NO_WRAP + Base64.NO_PADDING)) keyBytes = generatedKey.encoded
} else { sp.putString(R.string.key_smscommunicator_otp_secret, Base64.encodeToString(keyBytes, Base64.NO_WRAP + Base64.NO_PADDING))
keyBytes = Base64.decode(strSecret, Base64.DEFAULT); } else {
} keyBytes = Base64.decode(strSecret, Base64.DEFAULT)
key = SecretKeySpec(keyBytes, 0, keyBytes.size, "SHA1") }
} key = SecretKeySpec(keyBytes, 0, keyBytes.size, "SHA1")
}
private fun configure() {
ensureKey() private fun configure() {
pin = sp.getString(R.string.key_smscommunicator_otp_password, "").trim() ensureKey()
} pin = sp.getString(R.string.key_smscommunicator_otp_password, "").trim()
}
private fun generateOneTimePassword(counter: Long): String {
if (key != null) { private fun generateOneTimePassword(counter: Long): String =
return String.format("%06d", totp.generateOneTimePassword(key, counter)) key?.let { String.format("%06d", totp.generateOneTimePassword(key, counter)) } ?: ""
} else {
return "" /**
} * Check if given OTP+PIN is valid
} */
fun checkOTP(otp: String): OneTimePasswordValidationResult {
/** configure()
* Check if given OTP+PIN is valid val normalisedOtp = otp.replace(" ", "").replace("-", "").trim()
*/
fun checkOTP(otp: String): OneTimePasswordValidationResult { if (pin.length < 3) {
configure() return OneTimePasswordValidationResult.ERROR_WRONG_PIN
val normalisedOtp = otp.replace(" ", "").replace("-", "").trim() }
if (pin.length < 3) { if (normalisedOtp.length != (6 + pin.length)) {
return OneTimePasswordValidationResult.ERROR_WRONG_PIN return OneTimePasswordValidationResult.ERROR_WRONG_LENGTH
} }
if (normalisedOtp.length != (6 + pin.length)) { if (normalisedOtp.substring(6) != pin) {
return OneTimePasswordValidationResult.ERROR_WRONG_LENGTH return OneTimePasswordValidationResult.ERROR_WRONG_PIN
} }
if (!normalisedOtp.substring(6).equals(pin)) { val counter: Long = DateUtil.now() / 30000L
return OneTimePasswordValidationResult.ERROR_WRONG_PIN
} val acceptableTokens: MutableList<String> = mutableListOf(generateOneTimePassword(counter))
for (i in 0 until Constants.OTP_ACCEPT_OLD_TOKENS_COUNT) {
val milis: Long = DateTimeUtils.currentTimeMillis() acceptableTokens.add(generateOneTimePassword(counter - i - 1))
val counter: Long = (milis / 30000L) }
val candidateOtp = normalisedOtp.substring(0, 6)
var acceptableTokens: MutableList<String> = mutableListOf(generateOneTimePassword(counter))
for (i in 0 until Constants.OTP_ACCEPT_OLD_TOKENS_COUNT) { if (acceptableTokens.any { candidate -> candidateOtp == candidate }) {
acceptableTokens.add(generateOneTimePassword(counter - i - 1)) return OneTimePasswordValidationResult.OK
} }
val candidateOtp = normalisedOtp.substring(0, 6)
return OneTimePasswordValidationResult.ERROR_WRONG_OTP
if (acceptableTokens.any { candidate -> candidateOtp.equals(candidate) }) { }
return OneTimePasswordValidationResult.OK
} /**
* Return URI used to provision Authenticator apps
return OneTimePasswordValidationResult.ERROR_WRONG_OTP */
} fun provisioningURI(): String? =
key?.let { "otpauth://totp/AndroidAPS:" + URLEncoder.encode(name(), "utf-8") + "?secret=" + BaseEncoding.base32().encode(it.encoded).replace("=", "") + "&issuer=AndroidAPS" }
/**
* Return URI used to provision Authenticator apps
*/
fun provisioningURI(): String? {
val keyImm = key
if (keyImm != null) {
return "otpauth://totp/AndroidAPS:" + URLEncoder.encode(name(), "utf-8") + "?secret=" + BaseEncoding.base32().encode(keyImm.encoded).replace("=", "") + "&issuer=AndroidAPS"
} else {
return null
}
}
} }

View file

@ -1,8 +1,8 @@
package info.nightscout.androidaps.utils package info.nightscout.androidaps.plugins.general.smsCommunicator.otp
enum class OneTimePasswordValidationResult { enum class OneTimePasswordValidationResult {
OK, OK,
ERROR_WRONG_LENGTH, ERROR_WRONG_LENGTH,
ERROR_WRONG_PIN, ERROR_WRONG_PIN,
ERROR_WRONG_OTP ERROR_WRONG_OTP
} }

View file

@ -2,10 +2,11 @@ package info.nightscout.androidaps.plugins.general.smsCommunicator
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBase
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.TestBase
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -22,12 +23,13 @@ import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner import org.powermock.modules.junit4.PowerMockRunner
@RunWith(PowerMockRunner::class) @RunWith(PowerMockRunner::class)
@PrepareForTest(SmsCommunicatorPlugin::class, DateUtil::class) @PrepareForTest(SmsCommunicatorPlugin::class, DateUtil::class, OneTimePassword::class)
class AuthRequestTest : TestBase() { class AuthRequestTest : TestBase() {
@Mock lateinit var aapsLogger: AAPSLogger @Mock lateinit var aapsLogger: AAPSLogger
@Mock lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin @Mock lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Mock lateinit var resourceHelper: ResourceHelper @Mock lateinit var resourceHelper: ResourceHelper
@Mock lateinit var otp: OneTimePassword
var injector: HasAndroidInjector = HasAndroidInjector { var injector: HasAndroidInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {
@ -35,6 +37,7 @@ class AuthRequestTest : TestBase() {
it.aapsLogger = aapsLogger it.aapsLogger = aapsLogger
it.resourceHelper = resourceHelper it.resourceHelper = resourceHelper
it.smsCommunicatorPlugin = smsCommunicatorPlugin it.smsCommunicatorPlugin = smsCommunicatorPlugin
it.otp = otp
} }
} }
} }

View file

@ -4,7 +4,6 @@ import android.telephony.SmsManager
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.TestBaseWithProfile import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.data.IobTotal
@ -15,9 +14,9 @@ import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin