kotlin cleanup
This commit is contained in:
parent
5c04780de1
commit
4abc53e7b8
|
@ -6,8 +6,8 @@ import info.nightscout.androidaps.R
|
|||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.OneTimePassword
|
||||
import info.nightscout.androidaps.utils.OneTimePasswordValidationResult
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePasswordValidationResult
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -21,7 +21,7 @@ class AuthRequest internal constructor(
|
|||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
|
||||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var otp :OneTimePassword
|
||||
@Inject lateinit var otp : OneTimePassword
|
||||
|
||||
private val date = DateUtil.now()
|
||||
private var processed = false
|
||||
|
@ -32,10 +32,10 @@ class AuthRequest internal constructor(
|
|||
}
|
||||
|
||||
private fun codeIsValid(toValidate: String) : Boolean {
|
||||
if (otp.isEnabled()) {
|
||||
return otp.checkOTP(toValidate) == OneTimePasswordValidationResult.OK
|
||||
return if (otp.isEnabled()) {
|
||||
otp.checkOTP(toValidate) == OneTimePasswordValidationResult.OK
|
||||
} else {
|
||||
return confirmCode.equals(toValidate)
|
||||
confirmCode == toValidate
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.notifications.Notification
|
||||
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.IobCobCalculatorPlugin
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
|
||||
|
|
|
@ -8,21 +8,21 @@ import android.text.TextWatcher
|
|||
import android.view.View
|
||||
import com.google.common.primitives.Ints.min
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
|
||||
import dagger.android.DaggerActivity
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
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.OKDialog
|
||||
import info.nightscout.androidaps.utils.OneTimePassword
|
||||
import info.nightscout.androidaps.utils.OneTimePasswordValidationResult
|
||||
import info.nightscout.androidaps.utils.ToastUtils
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import kotlinx.android.synthetic.main.activity_smscommunicator_otp.*
|
||||
import net.glxn.qrgen.android.QRCode
|
||||
import javax.inject.Inject
|
||||
|
||||
class SmsCommunicatorOtpActivity : DaggerActivity() {
|
||||
class SmsCommunicatorOtpActivity : NoSplashAppCompatActivity() {
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Inject lateinit var rxBus: RxBusWrapper
|
||||
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
|
||||
|
@ -35,27 +35,21 @@ class SmsCommunicatorOtpActivity : DaggerActivity() {
|
|||
|
||||
smscommunicator_otp_verify_edit.addTextChangedListener(object : TextWatcher {
|
||||
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) {
|
||||
OneTimePasswordValidationResult.OK -> "OK"
|
||||
OneTimePasswordValidationResult.ERROR_WRONG_LENGTH -> "INVALID SIZE!"
|
||||
OneTimePasswordValidationResult.ERROR_WRONG_PIN -> "WRONG PIN"
|
||||
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.text = when (checkResult) {
|
||||
OneTimePasswordValidationResult.OK -> "OK"
|
||||
OneTimePasswordValidationResult.ERROR_WRONG_LENGTH -> "INVALID SIZE!"
|
||||
OneTimePasswordValidationResult.ERROR_WRONG_PIN -> "WRONG PIN"
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
|
@ -82,13 +76,13 @@ class SmsCommunicatorOtpActivity : DaggerActivity() {
|
|||
}
|
||||
|
||||
fun updateGui() {
|
||||
val displayMetrics = Resources.getSystem().getDisplayMetrics()
|
||||
val displayMetrics = Resources.getSystem().displayMetrics
|
||||
val width = displayMetrics.widthPixels
|
||||
val height = displayMetrics.heightPixels
|
||||
|
||||
// ensure QRCode is big enough to fit on screen
|
||||
val dim = (min(width, height) * 0.85).toInt()
|
||||
val provURI = otp.provisioningURI();
|
||||
val provURI = otp.provisioningURI()
|
||||
|
||||
if (provURI != null) {
|
||||
val myBitmap = QRCode.from(provURI).withErrorCorrection(ErrorCorrectionLevel.H).withSize(dim, dim).bitmap()
|
||||
|
|
|
@ -1,135 +1,124 @@
|
|||
package info.nightscout.androidaps.utils
|
||||
|
||||
import android.util.Base64
|
||||
import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator
|
||||
import com.google.common.io.BaseEncoding
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import org.joda.time.DateTimeUtils
|
||||
import java.net.URLEncoder
|
||||
import javax.crypto.KeyGenerator
|
||||
import javax.crypto.SecretKey
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class OneTimePassword @Inject constructor(
|
||||
private val sp: SP,
|
||||
private val resourceHelper: ResourceHelper
|
||||
) {
|
||||
|
||||
private var key: SecretKey? = null
|
||||
private var pin: String = ""
|
||||
private val totp = TimeBasedOneTimePasswordGenerator()
|
||||
|
||||
init {
|
||||
instance = this
|
||||
configure()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private lateinit var instance: OneTimePassword
|
||||
@JvmStatic
|
||||
fun getInstance(): OneTimePassword = instance
|
||||
}
|
||||
|
||||
/**
|
||||
* If OTP Authenticator support is enabled by user
|
||||
*/
|
||||
fun isEnabled(): Boolean {
|
||||
return sp.getBoolean(R.string.key_smscommunicator_otp_enabled, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of master device (target of OTP)
|
||||
*/
|
||||
fun name(): String {
|
||||
val defaultUserName = resourceHelper.gs(R.string.smscommunicator_default_user_display_name)
|
||||
var userName = sp.getString(R.string.key_smscommunicator_otp_name, defaultUserName).replace(":", "").trim()
|
||||
if (userName.length == 0)
|
||||
userName = defaultUserName
|
||||
return userName
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure if private key for TOTP is generated, creating it when necessary or requested
|
||||
*/
|
||||
fun ensureKey(forceNewKey: Boolean = false) {
|
||||
var keyBytes: ByteArray = byteArrayOf()
|
||||
val strSecret = sp.getString(R.string.key_smscommunicator_otp_secret, "").trim()
|
||||
if (strSecret.length == 0 || forceNewKey) {
|
||||
val keyGenerator = KeyGenerator.getInstance(totp.getAlgorithm());
|
||||
keyGenerator.init(Constants.OTP_GENERATED_KEY_LENGTH_BITS);
|
||||
val generatedKey = keyGenerator.generateKey();
|
||||
keyBytes = generatedKey.encoded
|
||||
sp.putString(R.string.key_smscommunicator_otp_secret, Base64.encodeToString(keyBytes, Base64.NO_WRAP + Base64.NO_PADDING))
|
||||
} else {
|
||||
keyBytes = Base64.decode(strSecret, Base64.DEFAULT);
|
||||
}
|
||||
key = SecretKeySpec(keyBytes, 0, keyBytes.size, "SHA1")
|
||||
}
|
||||
|
||||
private fun configure() {
|
||||
ensureKey()
|
||||
pin = sp.getString(R.string.key_smscommunicator_otp_password, "").trim()
|
||||
}
|
||||
|
||||
private fun generateOneTimePassword(counter: Long): String {
|
||||
if (key != null) {
|
||||
return String.format("%06d", totp.generateOneTimePassword(key, counter))
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given OTP+PIN is valid
|
||||
*/
|
||||
fun checkOTP(otp: String): OneTimePasswordValidationResult {
|
||||
configure()
|
||||
val normalisedOtp = otp.replace(" ", "").replace("-", "").trim()
|
||||
|
||||
if (pin.length < 3) {
|
||||
return OneTimePasswordValidationResult.ERROR_WRONG_PIN
|
||||
}
|
||||
|
||||
if (normalisedOtp.length != (6 + pin.length)) {
|
||||
return OneTimePasswordValidationResult.ERROR_WRONG_LENGTH
|
||||
}
|
||||
|
||||
if (!normalisedOtp.substring(6).equals(pin)) {
|
||||
return OneTimePasswordValidationResult.ERROR_WRONG_PIN
|
||||
}
|
||||
|
||||
val milis: Long = DateTimeUtils.currentTimeMillis()
|
||||
val counter: Long = (milis / 30000L)
|
||||
|
||||
var acceptableTokens: MutableList<String> = mutableListOf(generateOneTimePassword(counter))
|
||||
for (i in 0 until Constants.OTP_ACCEPT_OLD_TOKENS_COUNT) {
|
||||
acceptableTokens.add(generateOneTimePassword(counter - i - 1))
|
||||
}
|
||||
val candidateOtp = normalisedOtp.substring(0, 6)
|
||||
|
||||
if (acceptableTokens.any { candidate -> candidateOtp.equals(candidate) }) {
|
||||
return OneTimePasswordValidationResult.OK
|
||||
}
|
||||
|
||||
return OneTimePasswordValidationResult.ERROR_WRONG_OTP
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
package info.nightscout.androidaps.plugins.general.smsCommunicator.otp
|
||||
|
||||
import android.util.Base64
|
||||
import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator
|
||||
import com.google.common.io.BaseEncoding
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import java.net.URLEncoder
|
||||
import javax.crypto.KeyGenerator
|
||||
import javax.crypto.SecretKey
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class OneTimePassword @Inject constructor(
|
||||
private val sp: SP,
|
||||
private val resourceHelper: ResourceHelper
|
||||
) {
|
||||
|
||||
private var key: SecretKey? = null
|
||||
private var pin: String = ""
|
||||
private val totp = TimeBasedOneTimePasswordGenerator()
|
||||
|
||||
init {
|
||||
instance = this
|
||||
configure()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private lateinit var instance: OneTimePassword
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(): OneTimePassword = instance
|
||||
}
|
||||
|
||||
/**
|
||||
* If OTP Authenticator support is enabled by user
|
||||
*/
|
||||
fun isEnabled(): Boolean {
|
||||
return sp.getBoolean(R.string.key_smscommunicator_otp_enabled, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of master device (target of OTP)
|
||||
*/
|
||||
fun name(): String {
|
||||
val defaultUserName = resourceHelper.gs(R.string.smscommunicator_default_user_display_name)
|
||||
var userName = sp.getString(R.string.key_smscommunicator_otp_name, defaultUserName).replace(":", "").trim()
|
||||
if (userName.isEmpty())
|
||||
userName = defaultUserName
|
||||
return userName
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure if private key for TOTP is generated, creating it when necessary or requested
|
||||
*/
|
||||
fun ensureKey(forceNewKey: Boolean = false) {
|
||||
val keyBytes: ByteArray
|
||||
val strSecret = sp.getString(R.string.key_smscommunicator_otp_secret, "").trim()
|
||||
if (strSecret.isEmpty() || forceNewKey) {
|
||||
val keyGenerator = KeyGenerator.getInstance(totp.algorithm)
|
||||
keyGenerator.init(Constants.OTP_GENERATED_KEY_LENGTH_BITS)
|
||||
val generatedKey = keyGenerator.generateKey()
|
||||
keyBytes = generatedKey.encoded
|
||||
sp.putString(R.string.key_smscommunicator_otp_secret, Base64.encodeToString(keyBytes, Base64.NO_WRAP + Base64.NO_PADDING))
|
||||
} else {
|
||||
keyBytes = Base64.decode(strSecret, Base64.DEFAULT)
|
||||
}
|
||||
key = SecretKeySpec(keyBytes, 0, keyBytes.size, "SHA1")
|
||||
}
|
||||
|
||||
private fun configure() {
|
||||
ensureKey()
|
||||
pin = sp.getString(R.string.key_smscommunicator_otp_password, "").trim()
|
||||
}
|
||||
|
||||
private fun generateOneTimePassword(counter: Long): String =
|
||||
key?.let { String.format("%06d", totp.generateOneTimePassword(key, counter)) } ?: ""
|
||||
|
||||
/**
|
||||
* Check if given OTP+PIN is valid
|
||||
*/
|
||||
fun checkOTP(otp: String): OneTimePasswordValidationResult {
|
||||
configure()
|
||||
val normalisedOtp = otp.replace(" ", "").replace("-", "").trim()
|
||||
|
||||
if (pin.length < 3) {
|
||||
return OneTimePasswordValidationResult.ERROR_WRONG_PIN
|
||||
}
|
||||
|
||||
if (normalisedOtp.length != (6 + pin.length)) {
|
||||
return OneTimePasswordValidationResult.ERROR_WRONG_LENGTH
|
||||
}
|
||||
|
||||
if (normalisedOtp.substring(6) != pin) {
|
||||
return OneTimePasswordValidationResult.ERROR_WRONG_PIN
|
||||
}
|
||||
|
||||
val counter: Long = DateUtil.now() / 30000L
|
||||
|
||||
val acceptableTokens: MutableList<String> = mutableListOf(generateOneTimePassword(counter))
|
||||
for (i in 0 until Constants.OTP_ACCEPT_OLD_TOKENS_COUNT) {
|
||||
acceptableTokens.add(generateOneTimePassword(counter - i - 1))
|
||||
}
|
||||
val candidateOtp = normalisedOtp.substring(0, 6)
|
||||
|
||||
if (acceptableTokens.any { candidate -> candidateOtp == candidate }) {
|
||||
return OneTimePasswordValidationResult.OK
|
||||
}
|
||||
|
||||
return OneTimePasswordValidationResult.ERROR_WRONG_OTP
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URI used to provision Authenticator apps
|
||||
*/
|
||||
fun provisioningURI(): String? =
|
||||
key?.let { "otpauth://totp/AndroidAPS:" + URLEncoder.encode(name(), "utf-8") + "?secret=" + BaseEncoding.base32().encode(it.encoded).replace("=", "") + "&issuer=AndroidAPS" }
|
||||
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package info.nightscout.androidaps.utils
|
||||
|
||||
enum class OneTimePasswordValidationResult {
|
||||
OK,
|
||||
ERROR_WRONG_LENGTH,
|
||||
ERROR_WRONG_PIN,
|
||||
ERROR_WRONG_OTP
|
||||
package info.nightscout.androidaps.plugins.general.smsCommunicator.otp
|
||||
|
||||
enum class OneTimePasswordValidationResult {
|
||||
OK,
|
||||
ERROR_WRONG_LENGTH,
|
||||
ERROR_WRONG_PIN,
|
||||
ERROR_WRONG_OTP
|
||||
}
|
|
@ -2,10 +2,11 @@ package info.nightscout.androidaps.plugins.general.smsCommunicator
|
|||
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.TestBase
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.TestBase
|
||||
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.T
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
|
@ -22,12 +23,13 @@ import org.powermock.core.classloader.annotations.PrepareForTest
|
|||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
|
||||
@RunWith(PowerMockRunner::class)
|
||||
@PrepareForTest(SmsCommunicatorPlugin::class, DateUtil::class)
|
||||
@PrepareForTest(SmsCommunicatorPlugin::class, DateUtil::class, OneTimePassword::class)
|
||||
class AuthRequestTest : TestBase() {
|
||||
|
||||
@Mock lateinit var aapsLogger: AAPSLogger
|
||||
@Mock lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
|
||||
@Mock lateinit var resourceHelper: ResourceHelper
|
||||
@Mock lateinit var otp: OneTimePassword
|
||||
|
||||
var injector: HasAndroidInjector = HasAndroidInjector {
|
||||
AndroidInjector {
|
||||
|
@ -35,6 +37,7 @@ class AuthRequestTest : TestBase() {
|
|||
it.aapsLogger = aapsLogger
|
||||
it.resourceHelper = resourceHelper
|
||||
it.smsCommunicatorPlugin = smsCommunicatorPlugin
|
||||
it.otp = otp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.telephony.SmsManager
|
|||
import dagger.android.AndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.MainApp
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.TestBaseWithProfile
|
||||
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.PluginType
|
||||
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.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.GlucoseStatus
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
|
||||
|
|
Loading…
Reference in a new issue