yubioath-flutter/qml/YubiKey.qml

281 lines
8.7 KiB
QML
Raw Normal View History

2017-03-07 11:10:22 +03:00
import QtQuick 2.5
import io.thp.pyotherside 1.4
2017-03-17 16:06:34 +03:00
// @disable-check M300
Python {
id: py
property int nDevices
property bool hasDevice
property string name
property string version
2017-03-01 13:57:40 +03:00
property string oathId
property var connections: []
property var entries: []
property int nextRefresh: 0
property var enabled: []
property bool ready: false
property var queue: []
property bool hasOTP: enabled.indexOf('OTP') !== -1
property bool hasCCID: enabled.indexOf('CCID') !== -1
2017-02-07 16:17:54 +03:00
property bool validated
2017-03-08 18:07:12 +03:00
property bool slot1inUse
property bool slot2inUse
2017-02-28 15:20:44 +03:00
property int expiration: 0
2017-02-08 18:47:28 +03:00
signal wrongPassword
signal credentialsRefreshed
Component.onCompleted: {
2017-01-30 16:59:58 +03:00
importModule('site', function () {
call('site.addsitedir', [appDir + '/pymodules'], function () {
addImportPath(urlPrefix + '/py')
importModule('yubikey', function () {
ready = true
for (var i in queue) {
do_call(queue[i][0], queue[i][1], queue[i][2])
}
queue = []
})
})
})
}
2017-02-09 14:45:04 +03:00
onHasDeviceChanged: {
2017-02-16 16:14:14 +03:00
device.validated = false
2017-02-09 14:45:04 +03:00
}
function do_call(func, args, cb) {
if (!ready) {
queue.push([func, args, cb])
} else {
call(func, args.map(JSON.stringify), function (json) {
if (cb) {
try {
cb(json ? JSON.parse(json) : undefined)
} catch(err) {
console.log(err, json)
}
}
})
}
}
function refresh(slotMode, refreshCredentialsOnMode) {
do_call('yubikey.controller.count_devices', [], function (n) {
nDevices = n
if (nDevices == 1) {
do_call('yubikey.controller.refresh', [slotMode], function (dev) {
name = dev ? dev.name : ''
version = dev ? dev.version : ''
enabled = dev ? dev.enabled : []
connections = dev ? dev.connections : []
2017-02-28 12:57:10 +03:00
hasDevice = dev !== undefined && dev !== null
})
} else if (hasDevice) {
// No longer has device
hasDevice = false
entries = null
nextRefresh = 0
}
2017-02-23 12:40:24 +03:00
refreshCredentialsOnMode()
})
2017-01-30 16:59:58 +03:00
}
2017-02-23 14:18:20 +03:00
function refreshCCIDCredentials(force) {
2017-02-23 12:40:24 +03:00
var now = Math.floor(Date.now() / 1000)
if (force || (validated && nextRefresh <= now)) {
2017-03-01 13:57:40 +03:00
do_call('yubikey.controller.refresh_credentials',
[now], updateAllCredentials)
2017-02-23 12:40:24 +03:00
}
}
2017-02-23 14:18:20 +03:00
function refreshSlotCredentials(slots, digits, force) {
2017-02-23 12:40:24 +03:00
var now = Math.floor(Date.now() / 1000)
if (force || (nextRefresh <= now)) {
2017-03-01 13:57:40 +03:00
do_call('yubikey.controller.refresh_slot_credentials',
2017-03-30 11:00:07 +03:00
[slots, digits, now], updateAllCredentials)
2017-02-23 12:40:24 +03:00
}
}
function validate(password, remember) {
do_call('yubikey.controller.provide_password', [password, remember],
function (res) {
if (res) {
validated = true
} else {
wrongPassword()
}
2017-03-01 13:57:40 +03:00
})
}
function promptOrSkip(prompt) {
do_call('yubikey.controller.needs_validation', [], function (res) {
if (res === true) {
prompt.open()
} else {
validated = true
2017-03-01 13:57:40 +03:00
}
2017-02-28 17:07:43 +03:00
})
2017-02-01 18:27:45 +03:00
}
function setPassword(password, remember) {
do_call('yubikey.controller.set_password', [password, remember],
2017-02-16 16:14:14 +03:00
function () {
validated = true
2017-02-16 16:14:14 +03:00
})
2017-02-10 16:01:49 +03:00
}
function updateAllCredentials(newEntries) {
2017-01-31 11:57:44 +03:00
var result = []
var minExpiration = (Date.now() / 1000) + 60
for (var i = 0; i < newEntries.length; i++) {
var entry = newEntries[i]
// Update min expiration
if (entry.code && entry.code.valid_to < minExpiration
&& entry.credential.period === 30) {
minExpiration = entry.code.valid_to
}
// Touch credentials should only be replaced by user
if (credentialExists(entry.credential.key) && entry.credential.touch) {
result.push(getEntry(entry.credential.key))
continue
}
// HOTP credentials should only be replaced by user
if (credentialExists(entry.credential.key) && entry.credential.oath_type === 'HOTP') {
result.push(getEntry(entry.credential.key))
continue
}
// The selected credential should still be selected,
// with an updated code.
if (selected != null) {
if (selected.credential.key === entry.credential.key) {
selected = entry
}
}
// TOTP credentials should be updated
result.push(entry)
2017-01-31 11:57:44 +03:00
}
nextRefresh = minExpiration
// Credentials is cleared so that
2017-04-25 11:16:13 +03:00
// the view will refresh even if objects are the same
entries = result
2017-02-28 15:20:44 +03:00
updateExpiration()
credentialsRefreshed()
2017-01-31 11:57:44 +03:00
}
function getEntry(key) {
for (var i = 0; i < entries.length; i++) {
if (entries[i].credential.key === key) {
return entries[i]
}
}
}
function credentialExists(key) {
if (entries != null) {
for (var i = 0; i < entries.length; i++) {
if (entries[i].credential.key === key) {
return true
}
}
}
return false
}
function hasAnyCredentials() {
return entries != null && entries.length > 0
}
2017-02-28 15:20:44 +03:00
function updateExpiration() {
2017-03-01 13:57:40 +03:00
var maxExpiration = 0
if (entries !== null) {
for (var i = 0; i < entries.length; i++) {
if (entries[i].credential.period === 30) {
var exp = entries[i].code && entries[i].code.valid_to
if (exp !== null && exp > maxExpiration) {
maxExpiration = exp
}
2017-02-28 15:20:44 +03:00
}
}
2017-03-01 13:57:40 +03:00
expiration = maxExpiration
2017-02-28 15:20:44 +03:00
}
2017-03-01 13:57:40 +03:00
}
2017-02-28 15:20:44 +03:00
function calculate(entry, copyAfterUpdate) {
2017-02-07 16:17:54 +03:00
var now = Math.floor(Date.now() / 1000)
do_call('yubikey.controller.calculate', [entry.credential, now],
function (code) {
updateSingleCredential(entry.credential, code, copyAfterUpdate)
})
2017-02-07 16:17:54 +03:00
}
function calculateSlotMode(slot, digits, copyAfterUpdate) {
var now = Math.floor(Date.now() / 1000)
do_call('yubikey.controller.calculate_slot_mode', [slot, digits, now],
function (entry) {
updateSingleCredential(entry.credential, entry.code, copyAfterUpdate)
})
}
/**
Put a credential coming from the YubiKey in the
right position in the credential list.
*/
function updateSingleCredential(cred, code, copyAfterUpdate) {
var entry = null;
for (var i = 0; i < entries.length; i++) {
if (entries[i].credential.key === cred.key) {
entry = entries[i]
entry.code = code
2017-02-07 16:17:54 +03:00
}
}
2017-03-08 10:56:40 +03:00
updateExpiration()
credentialsRefreshed()
// Update the selected credential
// after update, since the code now
// might be available.
selected = entry
if (copyAfterUpdate) {
copy()
}
2017-02-07 16:17:54 +03:00
}
function addCredential(name, key, issuer, oathType, algo, digits, period, touch, cb) {
2017-02-03 14:24:41 +03:00
do_call('yubikey.controller.add_credential',
[name, key, issuer, oathType, algo, digits, period, touch],
2017-02-16 16:14:14 +03:00
cb)
2017-02-03 14:24:41 +03:00
}
2017-02-27 16:13:54 +03:00
function addSlotCredential(slot, key, touch, cb) {
2017-03-01 13:57:40 +03:00
do_call('yubikey.controller.add_slot_credential',
[slot, key, touch], cb)
2017-02-27 16:13:54 +03:00
}
2017-02-03 18:23:28 +03:00
function deleteCredential(credential) {
2017-02-16 16:14:14 +03:00
do_call('yubikey.controller.delete_credential',
[credential])
}
2017-02-14 15:12:24 +03:00
2017-02-23 18:00:17 +03:00
function deleteSlotCredential(slot) {
2017-03-01 13:57:40 +03:00
do_call('yubikey.controller.delete_slot_credential', [slot])
2017-02-23 18:00:17 +03:00
}
2017-02-16 16:14:14 +03:00
function parseQr(screenShots, cb) {
2017-02-15 17:00:30 +03:00
do_call('yubikey.controller.parse_qr', [screenShots], cb)
2017-02-14 15:12:24 +03:00
}
2017-02-17 10:52:34 +03:00
function reset() {
do_call('yubikey.controller.reset', [])
}
2017-03-08 18:07:12 +03:00
function getSlotStatus(cb) {
do_call('yubikey.controller.slot_status', [], function (res) {
slot1inUse = res[0]
slot2inUse = res[1]
cb()
})
}
}