2017-03-07 13:58:08 +03:00
|
|
|
import QtQuick 2.5
|
2017-01-27 15:55:38 +03:00
|
|
|
import QtQuick.Controls 1.4
|
|
|
|
import QtQuick.Layouts 1.1
|
2017-02-02 16:16:52 +03:00
|
|
|
import QtQuick.Controls.Styles 1.4
|
2017-01-27 15:55:38 +03:00
|
|
|
import QtQuick.Dialogs 1.2
|
2017-02-20 15:47:39 +03:00
|
|
|
import Qt.labs.settings 1.0
|
2017-01-27 15:55:38 +03:00
|
|
|
|
|
|
|
ApplicationWindow {
|
2017-02-06 17:00:11 +03:00
|
|
|
id: appWindow
|
2017-02-02 16:16:52 +03:00
|
|
|
width: 300
|
|
|
|
height: 400
|
2017-02-04 00:28:57 +03:00
|
|
|
minimumHeight: 400
|
|
|
|
minimumWidth: 300
|
2017-01-27 15:55:38 +03:00
|
|
|
visible: true
|
2017-03-16 17:10:15 +03:00
|
|
|
title: getTitle()
|
2017-02-07 12:12:22 +03:00
|
|
|
property var device: yk
|
|
|
|
property var credentials: device.credentials
|
2017-02-07 16:17:54 +03:00
|
|
|
property bool hasDevice: device.hasDevice
|
2017-03-16 17:29:50 +03:00
|
|
|
property bool canShowCredentials: device.hasDevice && modeAndKeyMatch
|
|
|
|
property bool modeAndKeyMatch: slotModeMatch || ccidModeMatch
|
|
|
|
property bool slotModeMatch: (settings.slotMode && device.hasOTP)
|
|
|
|
property bool ccidModeMatch: (!settings.slotMode && device.hasCCID)
|
2017-02-17 16:09:55 +03:00
|
|
|
property var hotpCoolDowns: []
|
|
|
|
|
2017-03-01 15:17:47 +03:00
|
|
|
// Don't refresh credentials when window is minimized or hidden
|
|
|
|
// See http://doc.qt.io/qt-5/qwindow.html#Visibility-enum
|
|
|
|
property bool shouldRefresh: visibility != 3 && visibility != 0
|
|
|
|
|
2017-03-17 11:07:41 +03:00
|
|
|
onHasDeviceChanged: handleNewDevice()
|
|
|
|
|
2017-03-16 17:41:26 +03:00
|
|
|
menuBar: MainMenuBar {
|
|
|
|
slotMode: settings.slotMode
|
|
|
|
hasDevice: device.hasDevice
|
|
|
|
onOpenAddCredential: openClearAddCredential()
|
|
|
|
onOpenSetPassword: setPassword.open()
|
|
|
|
onOpenReset: reset.open()
|
|
|
|
onOpenSettings: settingsDialog.open()
|
|
|
|
onOpenAbout: aboutPage.open()
|
|
|
|
}
|
|
|
|
|
2017-02-28 14:08:42 +03:00
|
|
|
SystemPalette {
|
|
|
|
id: palette
|
|
|
|
}
|
2017-02-20 15:47:39 +03:00
|
|
|
|
2017-03-16 17:41:26 +03:00
|
|
|
// This information is stored in the system registry on Windows,
|
|
|
|
// and in XML preferences files on macOS. On other Unix systems,
|
|
|
|
// in the absence of a standard, INI text files are used.
|
|
|
|
// See http://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html#details
|
2017-02-20 15:47:39 +03:00
|
|
|
Settings {
|
|
|
|
id: settings
|
|
|
|
property bool slotMode
|
|
|
|
property bool slot1
|
|
|
|
property bool slot2
|
|
|
|
property var slot1digits
|
|
|
|
property var slot2digits
|
2017-03-01 13:57:40 +03:00
|
|
|
property string savedPasswords
|
2017-02-20 15:47:39 +03:00
|
|
|
}
|
|
|
|
|
2017-01-27 15:55:38 +03:00
|
|
|
AboutPage {
|
|
|
|
id: aboutPage
|
|
|
|
}
|
|
|
|
|
2017-02-03 11:58:17 +03:00
|
|
|
AddCredential {
|
|
|
|
id: addCredential
|
2017-03-07 17:16:10 +03:00
|
|
|
device: yk
|
|
|
|
}
|
|
|
|
|
|
|
|
AddCredentialSlot {
|
|
|
|
id: addCredentialSlot
|
2017-03-01 16:38:02 +03:00
|
|
|
settings: settings
|
|
|
|
device: yk
|
2017-02-03 11:58:17 +03:00
|
|
|
}
|
|
|
|
|
2017-02-20 15:47:39 +03:00
|
|
|
SettingsDialog {
|
|
|
|
id: settingsDialog
|
|
|
|
settings: settings
|
|
|
|
onAccepted: {
|
2017-03-16 17:40:51 +03:00
|
|
|
saveSettings()
|
2017-02-23 14:18:20 +03:00
|
|
|
refreshDependingOnMode(true)
|
2017-02-20 15:47:39 +03:00
|
|
|
}
|
2017-02-20 14:27:10 +03:00
|
|
|
}
|
|
|
|
|
2017-02-10 14:44:10 +03:00
|
|
|
SetPassword {
|
|
|
|
id: setPassword
|
2017-02-16 17:22:20 +03:00
|
|
|
onAccepted: {
|
2017-03-16 18:34:00 +03:00
|
|
|
trySetPassword()
|
|
|
|
passwordUpdated.open()
|
2017-02-16 17:22:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 10:48:38 +03:00
|
|
|
PasswordSetConfirmation {
|
2017-02-10 16:01:49 +03:00
|
|
|
id: passwordUpdated
|
|
|
|
}
|
|
|
|
|
2017-03-17 10:57:30 +03:00
|
|
|
Reset {
|
2017-02-17 10:52:34 +03:00
|
|
|
id: reset
|
|
|
|
onAccepted: {
|
|
|
|
device.reset()
|
2017-03-08 10:56:40 +03:00
|
|
|
device.refreshCCIDCredentials(true)
|
2017-02-17 10:52:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-17 12:50:17 +03:00
|
|
|
PasswordPrompt {
|
|
|
|
id: passwordPrompt
|
2017-03-01 13:57:40 +03:00
|
|
|
onAccepted: {
|
|
|
|
if (passwordPrompt.remember) {
|
|
|
|
device.validate(passwordPrompt.password, rememberPassword)
|
|
|
|
} else {
|
|
|
|
device.validate(passwordPrompt.password)
|
|
|
|
}
|
2017-03-13 17:19:55 +03:00
|
|
|
passwordPrompt.clear()
|
2017-03-01 13:57:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-17 12:50:17 +03:00
|
|
|
onCredentialsChanged: {
|
2017-02-17 16:09:55 +03:00
|
|
|
hotpTouchTimer.stop()
|
2017-02-17 12:50:17 +03:00
|
|
|
touchYourYubikey.close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// @disable-check M301
|
|
|
|
YubiKey {
|
|
|
|
id: yk
|
|
|
|
onError: {
|
|
|
|
console.log(error)
|
|
|
|
}
|
|
|
|
onWrongPassword: {
|
|
|
|
passwordPrompt.open()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Text {
|
|
|
|
visible: !device.hasDevice
|
|
|
|
id: noLoadedDeviceMessage
|
|
|
|
text: if (device.nDevices == 0) {
|
2017-03-13 15:16:59 +03:00
|
|
|
qsTr("No YubiKey detected.")
|
2017-02-17 12:50:17 +03:00
|
|
|
} else if (device.nDevices == 1) {
|
|
|
|
qsTr("Connecting to YubiKey...")
|
|
|
|
} else {
|
|
|
|
qsTr("Multiple YubiKeys detected!")
|
|
|
|
}
|
2017-02-28 12:04:46 +03:00
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
wrapMode: Text.WordWrap
|
|
|
|
width: parent.width
|
2017-02-17 12:50:17 +03:00
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
}
|
|
|
|
|
2017-02-28 11:32:48 +03:00
|
|
|
Text {
|
|
|
|
visible: device.hasDevice
|
2017-02-28 14:08:42 +03:00
|
|
|
text: if (credentials !== null && filteredCredentials(
|
|
|
|
credentials).length === 0) {
|
|
|
|
qsTr("No credentials found.")
|
2017-02-28 12:04:46 +03:00
|
|
|
} else if (settings.slotMode && !device.hasOTP) {
|
|
|
|
qsTr("Authenticator mode is set to YubiKey slots, but the OTP connection mode is not enabled.")
|
|
|
|
} else if (!settings.slotMode && !device.hasCCID) {
|
|
|
|
qsTr("Authenticator mode is set to CCID, but the CCID connection mode is not enabled.")
|
2017-03-01 13:57:40 +03:00
|
|
|
} else if (credentials == null) {
|
2017-02-28 14:30:02 +03:00
|
|
|
qsTr("Reading credentials...")
|
2017-02-28 11:32:48 +03:00
|
|
|
} else {
|
2017-03-01 13:57:40 +03:00
|
|
|
""
|
2017-02-28 11:32:48 +03:00
|
|
|
}
|
2017-02-28 12:04:46 +03:00
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
wrapMode: Text.WordWrap
|
|
|
|
width: parent.width
|
2017-02-28 11:32:48 +03:00
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
}
|
|
|
|
|
2017-02-17 12:50:17 +03:00
|
|
|
TextEdit {
|
|
|
|
id: clipboard
|
|
|
|
visible: false
|
|
|
|
function setClipboard(value) {
|
|
|
|
text = value
|
|
|
|
selectAll()
|
|
|
|
copy()
|
2017-02-03 11:58:17 +03:00
|
|
|
}
|
2017-02-03 18:23:28 +03:00
|
|
|
}
|
2017-02-03 11:58:17 +03:00
|
|
|
|
2017-02-03 15:58:22 +03:00
|
|
|
Menu {
|
|
|
|
id: credentialMenu
|
2017-02-03 18:23:28 +03:00
|
|
|
MenuItem {
|
|
|
|
text: qsTr('Copy')
|
2017-02-16 11:55:47 +03:00
|
|
|
shortcut: StandardKey.Copy
|
2017-03-13 17:01:09 +03:00
|
|
|
enabled: (repeater.selected != null)
|
|
|
|
&& (repeater.selected.code != null)
|
2017-02-16 11:55:47 +03:00
|
|
|
onTriggered: {
|
2017-02-16 13:07:41 +03:00
|
|
|
if (repeater.selected != null) {
|
|
|
|
clipboard.setClipboard(repeater.selected.code)
|
2017-02-16 11:55:47 +03:00
|
|
|
}
|
|
|
|
}
|
2017-02-03 18:23:28 +03:00
|
|
|
}
|
|
|
|
MenuItem {
|
2017-02-17 16:09:55 +03:00
|
|
|
visible: allowManualGenerate(repeater.selected)
|
2017-03-08 16:41:56 +03:00
|
|
|
enabled: allowManualGenerate(repeater.selected)
|
|
|
|
&& enableManualGenerate(repeater.selected)
|
2017-03-16 16:04:50 +03:00
|
|
|
text: qsTr("Generate code")
|
2017-02-16 11:55:47 +03:00
|
|
|
shortcut: "Space"
|
2017-02-17 12:14:37 +03:00
|
|
|
onTriggered: {
|
|
|
|
if (!isInCoolDown(repeater.selected.name)) {
|
|
|
|
calculateCredential(repeater.selected)
|
|
|
|
if (repeater.selected.oath_type === "hotp") {
|
2017-02-17 16:09:55 +03:00
|
|
|
hotpCoolDowns.push(repeater.selected.name)
|
|
|
|
hotpCoolDownTimer.restart()
|
2017-02-17 12:14:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-03 18:23:28 +03:00
|
|
|
}
|
|
|
|
MenuItem {
|
2017-03-16 16:04:50 +03:00
|
|
|
text: qsTr("Delete")
|
2017-02-16 11:55:47 +03:00
|
|
|
shortcut: StandardKey.Delete
|
2017-02-03 18:23:28 +03:00
|
|
|
onTriggered: confirmDeleteCredential.open()
|
2017-02-03 15:58:22 +03:00
|
|
|
}
|
2017-02-03 18:23:28 +03:00
|
|
|
}
|
2017-02-03 11:58:17 +03:00
|
|
|
|
2017-02-17 12:50:17 +03:00
|
|
|
MessageDialog {
|
|
|
|
id: confirmDeleteCredential
|
|
|
|
icon: StandardIcon.Warning
|
|
|
|
title: qsTr("Delete credential?")
|
2017-03-16 16:47:26 +03:00
|
|
|
text: qsTr("Are you sure you want to delete this credential?")
|
2017-02-17 12:50:17 +03:00
|
|
|
standardButtons: StandardButton.Ok | StandardButton.Cancel
|
|
|
|
onAccepted: {
|
2017-02-28 14:08:42 +03:00
|
|
|
if (settings.slotMode) {
|
2017-02-23 18:00:17 +03:00
|
|
|
device.deleteSlotCredential(getSlot(repeater.selected['name']))
|
|
|
|
} else {
|
|
|
|
device.deleteCredential(repeater.selected)
|
|
|
|
}
|
|
|
|
refreshDependingOnMode(true)
|
2017-02-17 12:50:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-16 16:13:54 +03:00
|
|
|
TouchYubiKey {
|
2017-02-17 12:50:17 +03:00
|
|
|
id: touchYourYubikey
|
2017-02-17 12:14:37 +03:00
|
|
|
}
|
|
|
|
|
2017-02-16 13:07:41 +03:00
|
|
|
Item {
|
|
|
|
id: arrowKeys
|
|
|
|
focus: true
|
|
|
|
Keys.onUpPressed: {
|
2017-02-16 13:25:15 +03:00
|
|
|
if (repeater.selectedIndex == null) {
|
|
|
|
repeater.selected = repeater.model[repeater.model.length - 1]
|
|
|
|
repeater.selectedIndex = repeater.model.length - 1
|
|
|
|
} else if (repeater.selectedIndex > 0) {
|
2017-02-16 13:07:41 +03:00
|
|
|
repeater.selected = repeater.model[repeater.selectedIndex - 1]
|
|
|
|
repeater.selectedIndex = repeater.selectedIndex - 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Keys.onDownPressed: {
|
2017-02-16 13:25:15 +03:00
|
|
|
if (repeater.selectedIndex == null) {
|
|
|
|
repeater.selected = repeater.model[0]
|
|
|
|
repeater.selectedIndex = 0
|
|
|
|
} else if (repeater.selectedIndex < repeater.model.length - 1) {
|
2017-02-16 13:07:41 +03:00
|
|
|
repeater.selected = repeater.model[repeater.selectedIndex + 1]
|
|
|
|
repeater.selectedIndex = repeater.selectedIndex + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-06 17:00:11 +03:00
|
|
|
ColumnLayout {
|
2017-02-06 15:42:17 +03:00
|
|
|
anchors.fill: parent
|
2017-02-06 17:00:11 +03:00
|
|
|
spacing: 0
|
|
|
|
|
|
|
|
ProgressBar {
|
|
|
|
id: progressBar
|
2017-02-28 14:29:23 +03:00
|
|
|
visible: canShowCredentials
|
2017-02-06 17:00:11 +03:00
|
|
|
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
|
|
|
Layout.maximumHeight: 10
|
|
|
|
Layout.minimumHeight: 10
|
|
|
|
Layout.minimumWidth: 300
|
|
|
|
Layout.fillWidth: true
|
|
|
|
maximumValue: 30
|
|
|
|
minimumValue: 0
|
|
|
|
|
|
|
|
style: ProgressBarStyle {
|
|
|
|
progress: Rectangle {
|
|
|
|
color: "#9aca3c"
|
|
|
|
}
|
2017-02-02 16:16:52 +03:00
|
|
|
|
2017-02-06 17:00:11 +03:00
|
|
|
background: Rectangle {
|
2017-03-07 15:56:40 +03:00
|
|
|
color: palette.alternateBase
|
2017-02-02 16:16:52 +03:00
|
|
|
}
|
2017-02-06 15:42:17 +03:00
|
|
|
}
|
2017-02-06 17:00:11 +03:00
|
|
|
}
|
2017-02-02 16:16:52 +03:00
|
|
|
|
2017-02-06 17:00:11 +03:00
|
|
|
ScrollView {
|
|
|
|
id: scrollView
|
|
|
|
Layout.fillHeight: true
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
width: scrollView.viewport.width
|
|
|
|
id: credentialsColumn
|
|
|
|
spacing: 0
|
2017-02-07 12:12:22 +03:00
|
|
|
visible: device.hasDevice
|
2017-02-06 17:07:43 +03:00
|
|
|
anchors.right: appWindow.right
|
|
|
|
anchors.left: appWindow.left
|
|
|
|
anchors.top: appWindow.top
|
2017-02-06 17:00:11 +03:00
|
|
|
|
|
|
|
Repeater {
|
2017-02-06 17:57:32 +03:00
|
|
|
id: repeater
|
2017-02-28 11:32:48 +03:00
|
|
|
model: filteredCredentials(credentials)
|
2017-02-16 13:07:41 +03:00
|
|
|
property var selected
|
|
|
|
property var selectedIndex
|
2017-02-06 17:00:11 +03:00
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
id: credentialRectangle
|
2017-02-16 13:07:41 +03:00
|
|
|
focus: true
|
2017-02-16 11:18:52 +03:00
|
|
|
color: {
|
2017-02-16 13:07:41 +03:00
|
|
|
if (repeater.selected != null) {
|
|
|
|
if (repeater.selected.name == modelData.name) {
|
2017-02-16 11:18:52 +03:00
|
|
|
return palette.dark
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (index % 2 == 0) {
|
|
|
|
return "#00000000"
|
|
|
|
}
|
|
|
|
return palette.alternateBase
|
|
|
|
}
|
2017-02-06 17:00:11 +03:00
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.minimumHeight: 70
|
|
|
|
Layout.alignment: Qt.AlignTop
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
anchors.fill: parent
|
|
|
|
onClicked: {
|
2017-02-16 13:07:41 +03:00
|
|
|
arrowKeys.forceActiveFocus()
|
2017-02-16 11:18:52 +03:00
|
|
|
if (mouse.button & Qt.LeftButton) {
|
2017-02-16 13:07:41 +03:00
|
|
|
if (repeater.selected != null
|
|
|
|
&& repeater.selected.name == modelData.name) {
|
|
|
|
repeater.selected = null
|
|
|
|
repeater.selectedIndex = null
|
2017-02-16 11:18:52 +03:00
|
|
|
} else {
|
2017-02-16 13:07:41 +03:00
|
|
|
repeater.selected = modelData
|
|
|
|
repeater.selectedIndex = index
|
2017-02-16 11:18:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mouse.button & Qt.RightButton) {
|
2017-02-16 13:07:41 +03:00
|
|
|
repeater.selected = modelData
|
|
|
|
repeater.selectedIndex = index
|
2017-02-16 11:18:52 +03:00
|
|
|
credentialMenu.popup()
|
|
|
|
}
|
2017-02-06 17:00:11 +03:00
|
|
|
}
|
2017-02-16 11:18:52 +03:00
|
|
|
acceptedButtons: Qt.RightButton | Qt.LeftButton
|
2017-02-06 15:42:17 +03:00
|
|
|
}
|
|
|
|
|
2017-02-06 17:00:11 +03:00
|
|
|
ColumnLayout {
|
|
|
|
anchors.leftMargin: 10
|
2017-03-07 13:58:08 +03:00
|
|
|
anchors.topMargin: 5
|
|
|
|
anchors.bottomMargin: 5
|
|
|
|
spacing: 0
|
2017-02-06 17:00:11 +03:00
|
|
|
anchors.fill: parent
|
|
|
|
Text {
|
|
|
|
visible: hasIssuer(modelData.name)
|
|
|
|
text: qsTr('') + parseIssuer(modelData.name)
|
2017-03-09 12:26:11 +03:00
|
|
|
font.pixelSize: 12
|
2017-02-06 17:00:11 +03:00
|
|
|
}
|
2017-02-15 17:14:34 +03:00
|
|
|
Text {
|
2017-03-08 15:58:42 +03:00
|
|
|
opacity: isExpired(modelData) ? 0.6 : 1
|
2017-02-06 17:00:11 +03:00
|
|
|
visible: modelData.code != null
|
|
|
|
text: qsTr('') + modelData.code
|
|
|
|
font.family: "Verdana"
|
2017-03-09 12:26:11 +03:00
|
|
|
font.pixelSize: 20
|
2017-02-06 17:00:11 +03:00
|
|
|
}
|
|
|
|
Text {
|
|
|
|
text: hasIssuer(
|
|
|
|
modelData.name) ? qsTr(
|
|
|
|
'') + parseName(
|
|
|
|
modelData.name) : modelData.name
|
2017-03-09 12:26:11 +03:00
|
|
|
font.pixelSize: 12
|
2017-02-06 17:00:11 +03:00
|
|
|
}
|
2017-02-06 15:42:17 +03:00
|
|
|
}
|
2017-02-02 16:16:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-06 17:00:11 +03:00
|
|
|
|
2017-02-06 17:07:43 +03:00
|
|
|
TextField {
|
|
|
|
id: search
|
2017-03-13 16:51:14 +03:00
|
|
|
visible: canShowCredentials && device.hasAnyCredentials()
|
2017-03-16 16:04:50 +03:00
|
|
|
placeholderText: qsTr("Search...")
|
2017-02-06 17:07:43 +03:00
|
|
|
Layout.fillWidth: true
|
2017-03-15 16:57:01 +03:00
|
|
|
KeyNavigation.tab: scrollView
|
2017-02-06 17:00:11 +03:00
|
|
|
}
|
2017-02-02 16:16:52 +03:00
|
|
|
}
|
2017-02-06 17:57:32 +03:00
|
|
|
|
2017-01-27 15:55:38 +03:00
|
|
|
Timer {
|
2017-02-02 16:16:52 +03:00
|
|
|
id: ykTimer
|
2017-01-27 15:55:38 +03:00
|
|
|
triggeredOnStart: true
|
|
|
|
interval: 500
|
|
|
|
repeat: true
|
|
|
|
running: true
|
2017-02-23 12:40:24 +03:00
|
|
|
onTriggered: device.refresh(refreshDependingOnMode)
|
2017-01-27 15:55:38 +03:00
|
|
|
}
|
|
|
|
|
2017-02-02 16:16:52 +03:00
|
|
|
Timer {
|
|
|
|
id: progressBarTimer
|
|
|
|
interval: 100
|
|
|
|
repeat: true
|
|
|
|
running: true
|
|
|
|
triggeredOnStart: true
|
|
|
|
onTriggered: {
|
2017-02-28 15:20:44 +03:00
|
|
|
var timeLeft = device.expiration - (Date.now() / 1000)
|
2017-02-02 16:16:52 +03:00
|
|
|
if (timeLeft <= 0 && progressBar.value > 0) {
|
2017-03-08 15:58:42 +03:00
|
|
|
refreshDependingOnMode(true)
|
2017-02-02 16:16:52 +03:00
|
|
|
}
|
|
|
|
progressBar.value = timeLeft
|
|
|
|
}
|
2017-01-27 15:55:38 +03:00
|
|
|
}
|
|
|
|
|
2017-02-17 12:50:17 +03:00
|
|
|
Timer {
|
2017-02-17 16:09:55 +03:00
|
|
|
id: hotpCoolDownTimer
|
2017-02-17 12:50:17 +03:00
|
|
|
interval: 5000
|
2017-02-17 16:09:55 +03:00
|
|
|
onTriggered: hotpCoolDowns = []
|
|
|
|
}
|
|
|
|
|
|
|
|
Timer {
|
|
|
|
id: hotpTouchTimer
|
|
|
|
interval: 500
|
|
|
|
onTriggered: touchYourYubikey.open()
|
2017-01-27 15:55:38 +03:00
|
|
|
}
|
|
|
|
|
2017-03-09 12:49:42 +03:00
|
|
|
function allowManualGenerate(cred) {
|
|
|
|
return cred != null && (cred.oath_type === "hotp"
|
|
|
|
|| repeater.selected.touch)
|
|
|
|
}
|
|
|
|
|
|
|
|
function enableManualGenerate(cred) {
|
|
|
|
if (cred.oath_type !== 'hotp') {
|
|
|
|
return cred.code == null || isExpired(repeater.selected)
|
|
|
|
} else {
|
|
|
|
return !isInCoolDown(cred.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-08 15:58:42 +03:00
|
|
|
function isExpired(cred) {
|
2017-03-08 16:41:56 +03:00
|
|
|
return cred != null && (cred.oath_type !== 'hotp')
|
2017-03-08 15:58:42 +03:00
|
|
|
&& (cred.expiration - (Date.now() / 1000) <= 0)
|
2017-03-09 12:49:42 +03:00
|
|
|
}
|
2017-03-09 12:26:11 +03:00
|
|
|
|
2017-03-09 12:49:42 +03:00
|
|
|
function rememberPassword() {
|
|
|
|
var deviceId = device.oathId
|
|
|
|
settings.savedPasswords += deviceId + ':' + device.passwordKey + ';'
|
2017-03-08 15:58:42 +03:00
|
|
|
}
|
|
|
|
|
2017-02-23 14:18:20 +03:00
|
|
|
function refreshDependingOnMode(force) {
|
2017-03-01 15:17:47 +03:00
|
|
|
if (hasDevice && shouldRefresh) {
|
2017-02-28 12:57:10 +03:00
|
|
|
if (settings.slotMode && device.hasOTP) {
|
2017-02-28 14:08:42 +03:00
|
|
|
device.refreshSlotCredentials([settings.slot1, settings.slot2],
|
|
|
|
getSlotDigitsSettings(), force)
|
2017-02-28 12:57:10 +03:00
|
|
|
} else if (!settings.slotMode && device.hasCCID) {
|
2017-02-23 14:18:20 +03:00
|
|
|
device.refreshCCIDCredentials(force)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getSlotDigitsSettings() {
|
|
|
|
var slot1digits = settings.slot1digits === 1 ? 8 : 6
|
|
|
|
var slot2digits = settings.slot2digits === 1 ? 8 : 6
|
|
|
|
return [slot1digits, slot2digits]
|
|
|
|
}
|
|
|
|
|
2017-02-28 11:32:48 +03:00
|
|
|
function filteredCredentials(creds) {
|
2017-02-06 17:57:32 +03:00
|
|
|
var result = []
|
|
|
|
if (creds != null) {
|
|
|
|
for (var i = 0; i < creds.length; i++) {
|
|
|
|
var cred = creds[i]
|
2017-02-28 11:32:48 +03:00
|
|
|
if (cred.name.toLowerCase().indexOf(search.text.toLowerCase(
|
2017-02-06 17:57:32 +03:00
|
|
|
)) !== -1) {
|
|
|
|
result.push(creds[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2017-02-17 12:14:37 +03:00
|
|
|
function isInCoolDown(name) {
|
2017-03-08 16:50:50 +03:00
|
|
|
return hotpCoolDowns.indexOf(name) !== -1
|
2017-02-17 12:14:37 +03:00
|
|
|
}
|
2017-03-09 12:49:42 +03:00
|
|
|
|
2017-02-02 17:44:49 +03:00
|
|
|
function hasIssuer(name) {
|
|
|
|
return name.indexOf(':') !== -1
|
|
|
|
}
|
2017-03-09 12:49:42 +03:00
|
|
|
|
2017-02-02 17:44:49 +03:00
|
|
|
function parseName(name) {
|
|
|
|
return name.split(":").slice(1).join(":")
|
|
|
|
}
|
2017-03-09 12:49:42 +03:00
|
|
|
|
2017-02-02 17:44:49 +03:00
|
|
|
function parseIssuer(name) {
|
|
|
|
return name.split(":", 1)
|
|
|
|
}
|
|
|
|
|
2017-02-02 16:16:52 +03:00
|
|
|
function calculateCredential(credential) {
|
2017-02-23 15:51:44 +03:00
|
|
|
if (settings.slotMode) {
|
2017-02-23 18:00:17 +03:00
|
|
|
var slot = getSlot(credential.name)
|
2017-02-23 15:51:44 +03:00
|
|
|
var digits = getDigits(slot)
|
|
|
|
device.calculateSlotMode(slot, digits)
|
|
|
|
} else {
|
|
|
|
device.calculate(credential)
|
|
|
|
}
|
2017-02-17 16:09:55 +03:00
|
|
|
if (credential.oath_type === 'hotp') {
|
|
|
|
hotpTouchTimer.restart()
|
|
|
|
}
|
2017-02-02 16:16:52 +03:00
|
|
|
if (credential.touch) {
|
|
|
|
touchYourYubikey.open()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-23 18:00:17 +03:00
|
|
|
function getSlot(name) {
|
|
|
|
if (name.indexOf('1') !== -1) {
|
2017-02-23 15:51:44 +03:00
|
|
|
return 1
|
|
|
|
}
|
2017-02-23 18:00:17 +03:00
|
|
|
if (name.indexOf('2') !== -1) {
|
2017-02-23 15:51:44 +03:00
|
|
|
return 2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getDigits(slot) {
|
2017-02-28 14:08:42 +03:00
|
|
|
return getSlotDigitsSettings()[slot - 1]
|
2017-02-23 15:51:44 +03:00
|
|
|
}
|
2017-03-16 16:59:30 +03:00
|
|
|
|
|
|
|
function openClearAddCredential() {
|
|
|
|
if (settings.slotMode) {
|
|
|
|
addCredentialSlot.clear()
|
|
|
|
device.getSlotStatus(addCredentialSlot.open)
|
|
|
|
} else {
|
|
|
|
addCredential.clear()
|
|
|
|
addCredential.open()
|
|
|
|
}
|
|
|
|
}
|
2017-03-16 17:10:15 +03:00
|
|
|
|
|
|
|
function getTitle() {
|
|
|
|
return qsTr("Yubico Authenticator") + (settings.slotMode ? qsTr(" [Slot mode]") : '')
|
|
|
|
}
|
2017-03-16 18:34:00 +03:00
|
|
|
|
|
|
|
function saveSettings() {
|
|
|
|
settings.slotMode = settingsDialog.slotMode
|
|
|
|
settings.slot1 = settingsDialog.slot1
|
|
|
|
settings.slot2 = settingsDialog.slot2
|
|
|
|
settings.slot1digits = settingsDialog.slot1digits
|
|
|
|
settings.slot2digits = settingsDialog.slot2digits
|
|
|
|
}
|
|
|
|
|
|
|
|
function trySetPassword() {
|
|
|
|
if (setPassword.newPassword.length > 0) {
|
|
|
|
device.setPassword(setPassword.newPassword)
|
|
|
|
} else {
|
|
|
|
device.setPassword(null)
|
|
|
|
}
|
|
|
|
}
|
2017-03-17 11:07:41 +03:00
|
|
|
|
|
|
|
function handleNewDevice() {
|
|
|
|
if (device.hasDevice && ccidModeMatch) {
|
|
|
|
device.promptOrSkip(passwordPrompt, settings.savedPasswords)
|
|
|
|
} else {
|
|
|
|
passwordPrompt.close()
|
|
|
|
setPassword.close()
|
|
|
|
addCredential.close()
|
|
|
|
addCredentialSlot.close()
|
|
|
|
}
|
|
|
|
}
|
2017-01-27 15:55:38 +03:00
|
|
|
}
|