mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 10:11:52 +03:00
serialize model credentials as List
This commit is contained in:
parent
b7ee31c70c
commit
0a65ad6a73
@ -3,8 +3,6 @@ package com.yubico.authenticator.oath
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
val jsonSerializer = Json {
|
||||
// allows us to use Credential as a key
|
||||
allowStructuredMapKeys = true
|
||||
// creates properties for default values
|
||||
encodeDefaults = true
|
||||
}
|
@ -93,36 +93,41 @@ class Model {
|
||||
|
||||
}
|
||||
|
||||
private var _credentials = mutableMapOf<Credential, Code?>(); private set
|
||||
|
||||
var session = Session()
|
||||
var credentials = mutableMapOf<Credential, Code?>(); private set
|
||||
val credentials: List<CredentialWithCode>
|
||||
get() = _credentials.map {
|
||||
CredentialWithCode(it.key, it.value)
|
||||
}
|
||||
|
||||
// resets the model to initial values
|
||||
// used when a usb key has been disconnected
|
||||
fun reset() {
|
||||
this.credentials.clear()
|
||||
this._credentials.clear()
|
||||
this.session = Session()
|
||||
}
|
||||
|
||||
fun update(deviceId: String, credentials: Map<Credential, Code?>) {
|
||||
if (this.session.deviceId != deviceId) {
|
||||
// device was changed, we use the new list
|
||||
this.credentials.clear()
|
||||
this.credentials.putAll(from = credentials)
|
||||
this._credentials.clear()
|
||||
this._credentials.putAll(from = credentials)
|
||||
this.session = Session(deviceId)
|
||||
} else {
|
||||
|
||||
// update codes for non interactive keys
|
||||
for ((credential, code) in credentials) {
|
||||
if (!credential.isInteractive()) {
|
||||
this.credentials[credential] = code
|
||||
this._credentials[credential] = code
|
||||
}
|
||||
}
|
||||
// remove obsolete credentials
|
||||
this.credentials.filter { entry ->
|
||||
this._credentials.filter { entry ->
|
||||
// get only keys which are not present in the input map
|
||||
!credentials.contains(entry.key)
|
||||
}.forEach(action = {
|
||||
this.credentials.remove(it.key)
|
||||
this._credentials.remove(it.key)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -132,7 +137,7 @@ class Model {
|
||||
return null
|
||||
}
|
||||
|
||||
credentials[credential] = code
|
||||
_credentials[credential] = code
|
||||
|
||||
return CredentialWithCode(credential, code)
|
||||
}
|
||||
@ -150,15 +155,15 @@ class Model {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!credentials.contains(oldCredential)) {
|
||||
if (!_credentials.contains(oldCredential)) {
|
||||
return null
|
||||
}
|
||||
|
||||
// preserve code
|
||||
val code = credentials[oldCredential]
|
||||
val code = _credentials[oldCredential]
|
||||
|
||||
credentials.remove(oldCredential)
|
||||
credentials[newCredential] = code
|
||||
_credentials.remove(oldCredential)
|
||||
_credentials[newCredential] = code
|
||||
|
||||
return newCredential
|
||||
}
|
||||
@ -168,11 +173,11 @@ class Model {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!credentials.contains(credential)) {
|
||||
if (!_credentials.contains(credential)) {
|
||||
return null
|
||||
}
|
||||
|
||||
credentials[credential] = code
|
||||
_credentials[credential] = code
|
||||
|
||||
return code
|
||||
}
|
||||
|
@ -105,9 +105,9 @@ class ModelTest {
|
||||
|
||||
assertEquals("device1", model.session.deviceId)
|
||||
assertEquals(3, model.credentials.size)
|
||||
assertTrue(model.credentials.containsKey(cred1))
|
||||
assertTrue(model.credentials.containsKey(cred2))
|
||||
assertTrue(model.credentials.containsKey(cred3))
|
||||
assertTrue(model.credentials.find { it.credential == cred1 } != null)
|
||||
assertTrue(model.credentials.find { it.credential == cred2 } != null)
|
||||
assertTrue(model.credentials.find { it.credential == cred3 } != null)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -117,13 +117,13 @@ class ModelTest {
|
||||
val m1 = mapOf(cred to code)
|
||||
model.update(cred.deviceId, m1)
|
||||
|
||||
assertTrue(model.credentials.containsValue(code))
|
||||
assertTrue(model.credentials.find { it.code == code } != null)
|
||||
|
||||
val updatedCode = code(value = "121212")
|
||||
val m2 = mapOf(cred to updatedCode)
|
||||
model.update(cred.deviceId, m2)
|
||||
|
||||
assertTrue(model.credentials.containsValue(updatedCode))
|
||||
assertTrue(model.credentials.find { it.code == updatedCode } != null)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -138,16 +138,16 @@ class ModelTest {
|
||||
val m1 = mapOf(hotp to hotpCode, totp to totpCode)
|
||||
model.update(d, m1)
|
||||
|
||||
assertTrue(model.credentials.containsValue(hotpCode))
|
||||
assertTrue(model.credentials.find { it.code == hotpCode } != null)
|
||||
|
||||
val updatedTotpCode = code(value = "121212")
|
||||
val updatedHotpCode = code(value = "098765")
|
||||
val m2 = mapOf(hotp to updatedHotpCode, totp to updatedTotpCode)
|
||||
model.update(d, m2)
|
||||
|
||||
assertTrue(model.credentials.containsValue(updatedTotpCode))
|
||||
assertTrue(model.credentials.containsValue(hotpCode))
|
||||
assertFalse(model.credentials.containsValue(updatedHotpCode))
|
||||
assertTrue(model.credentials.find { it.code == updatedTotpCode } != null)
|
||||
assertTrue(model.credentials.find { it.code == hotpCode } != null)
|
||||
assertFalse(model.credentials.find { it.code == updatedHotpCode } != null)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -166,7 +166,7 @@ class ModelTest {
|
||||
model.update(d, mapOf(totp to newCode))
|
||||
|
||||
assertEquals(1, model.credentials.size)
|
||||
assertEquals("00000", model.credentials[totp]?.value)
|
||||
assertEquals("00000", model.credentials.find { it.credential == totp }?.code?.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -203,7 +203,7 @@ class ModelTest {
|
||||
|
||||
// only t1 is part of credentials
|
||||
assertEquals(1, model.credentials.size)
|
||||
assertTrue(model.credentials.containsKey(t1))
|
||||
assertTrue(model.credentials.find { it.credential == t1 } != null)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -7,8 +7,7 @@ import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
|
||||
class SerializationTest {
|
||||
@ -16,7 +15,7 @@ class SerializationTest {
|
||||
@Test
|
||||
fun `serialization settings`() {
|
||||
assertTrue(jsonSerializer.configuration.encodeDefaults)
|
||||
assertTrue(jsonSerializer.configuration.allowStructuredMapKeys)
|
||||
assertFalse(jsonSerializer.configuration.allowStructuredMapKeys)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -94,32 +93,25 @@ class SerializationTest {
|
||||
|
||||
@Test
|
||||
fun `credentials json type`() {
|
||||
val m = mapOf(totp() to code(), hotp() to code())
|
||||
val l = listOf(
|
||||
Model.CredentialWithCode(totp(), code()), Model.CredentialWithCode(hotp(), code()),
|
||||
)
|
||||
|
||||
val jsonElement = jsonSerializer.encodeToJsonElement(m)
|
||||
val jsonElement = jsonSerializer.encodeToJsonElement(l)
|
||||
assertTrue(jsonElement is JsonArray)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `credentials json size`() {
|
||||
val m1 = mapOf<Model.Credential, Model.Code?>()
|
||||
val jsonElement1 = jsonSerializer.encodeToJsonElement(m1) as JsonArray
|
||||
val l1 = listOf<Model.CredentialWithCode>()
|
||||
val jsonElement1 = jsonSerializer.encodeToJsonElement(l1) as JsonArray
|
||||
assertEquals(0, jsonElement1.size)
|
||||
|
||||
val m2 = mapOf(totp() to code(), hotp() to code())
|
||||
val jsonElement2 = jsonSerializer.encodeToJsonElement(m2) as JsonArray
|
||||
assertEquals(4, jsonElement2.size)
|
||||
val l2 = listOf(
|
||||
Model.CredentialWithCode(totp(), code()), Model.CredentialWithCode(hotp(), code()),
|
||||
)
|
||||
val jsonElement2 = jsonSerializer.encodeToJsonElement(l2) as JsonArray
|
||||
assertEquals(2, jsonElement2.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `credentials json content`() {
|
||||
val m = mapOf(totp() to code())
|
||||
val jsonElement = jsonSerializer.encodeToJsonElement(m) as JsonArray
|
||||
|
||||
// the first element is Credential which has device_id property
|
||||
assertTrue((jsonElement[0] as JsonObject).containsKey("device_id"))
|
||||
|
||||
// the second element is Credential which has value property
|
||||
assertTrue((jsonElement[1] as JsonObject).containsKey("value"))
|
||||
}
|
||||
}
|
@ -29,23 +29,10 @@ class _CredentialsProvider extends StateNotifier<List<OathPair>?> {
|
||||
void setFromString(String input) {
|
||||
var result = jsonDecode(input);
|
||||
|
||||
/// structure of data in the json object is:
|
||||
/// [credential1, code1, credential2, code2, ...]
|
||||
|
||||
final List<OathPair> pairs = [];
|
||||
if (result is List<dynamic>) {
|
||||
for (var index = 0; index < result.length / 2; index++) {
|
||||
final credential = result[index * 2];
|
||||
final code = result[index * 2 + 1];
|
||||
pairs.add(
|
||||
OathPair(
|
||||
OathCredential.fromJson(credential),
|
||||
code == null ? null : OathCode.fromJson(code),
|
||||
),
|
||||
);
|
||||
if (result is List) {
|
||||
state = result.map((e) => OathPair.fromJson(e)).toList();
|
||||
} else {
|
||||
state = [];
|
||||
}
|
||||
}
|
||||
|
||||
state = pairs;
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,9 @@ class OathCode with _$OathCode {
|
||||
@freezed
|
||||
class OathPair with _$OathPair {
|
||||
factory OathPair(OathCredential credential, OathCode? code) = _OathPair;
|
||||
|
||||
factory OathPair.fromJson(Map<String, dynamic> json) =>
|
||||
_$OathPairFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -474,6 +474,10 @@ abstract class _OathCode implements OathCode {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
OathPair _$OathPairFromJson(Map<String, dynamic> json) {
|
||||
return _OathPair.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$OathPairTearOff {
|
||||
const _$OathPairTearOff();
|
||||
@ -484,6 +488,10 @@ class _$OathPairTearOff {
|
||||
code,
|
||||
);
|
||||
}
|
||||
|
||||
OathPair fromJson(Map<String, Object?> json) {
|
||||
return OathPair.fromJson(json);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -494,6 +502,7 @@ mixin _$OathPair {
|
||||
OathCredential get credential => throw _privateConstructorUsedError;
|
||||
OathCode? get code => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$OathPairCopyWith<OathPair> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
@ -594,10 +603,13 @@ class __$OathPairCopyWithImpl<$Res> extends _$OathPairCopyWithImpl<$Res>
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
@JsonSerializable()
|
||||
class _$_OathPair implements _OathPair {
|
||||
_$_OathPair(this.credential, this.code);
|
||||
|
||||
factory _$_OathPair.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_OathPairFromJson(json);
|
||||
|
||||
@override
|
||||
final OathCredential credential;
|
||||
@override
|
||||
@ -628,11 +640,18 @@ class _$_OathPair implements _OathPair {
|
||||
@override
|
||||
_$OathPairCopyWith<_OathPair> get copyWith =>
|
||||
__$OathPairCopyWithImpl<_OathPair>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_OathPairToJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _OathPair implements OathPair {
|
||||
factory _OathPair(OathCredential credential, OathCode? code) = _$_OathPair;
|
||||
|
||||
factory _OathPair.fromJson(Map<String, dynamic> json) = _$_OathPair.fromJson;
|
||||
|
||||
@override
|
||||
OathCredential get credential;
|
||||
@override
|
||||
|
@ -46,6 +46,19 @@ Map<String, dynamic> _$$_OathCodeToJson(_$_OathCode instance) =>
|
||||
'valid_to': instance.validTo,
|
||||
};
|
||||
|
||||
_$_OathPair _$$_OathPairFromJson(Map<String, dynamic> json) => _$_OathPair(
|
||||
OathCredential.fromJson(json['credential'] as Map<String, dynamic>),
|
||||
json['code'] == null
|
||||
? null
|
||||
: OathCode.fromJson(json['code'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_OathPairToJson(_$_OathPair instance) =>
|
||||
<String, dynamic>{
|
||||
'credential': instance.credential,
|
||||
'code': instance.code,
|
||||
};
|
||||
|
||||
_$_OathState _$$_OathStateFromJson(Map<String, dynamic> json) => _$_OathState(
|
||||
json['device_id'] as String,
|
||||
hasKey: json['has_key'] as bool,
|
||||
|
Loading…
Reference in New Issue
Block a user