Add support for SSO authentication
This commit is contained in:
@@ -7,8 +7,12 @@ import "../../Base"
|
||||
HSwipeView {
|
||||
id: swipeView
|
||||
clip: true
|
||||
interactive: currentIndex !== 0 || signIn.serverUrl
|
||||
onCurrentItemChanged: if (currentIndex === 0) serverBrowser.takeFocus()
|
||||
interactive: serverBrowser.acceptedUrl
|
||||
onCurrentItemChanged:
|
||||
currentIndex === 0 ?
|
||||
serverBrowser.takeFocus() :
|
||||
signInLoader.takeFocus()
|
||||
|
||||
Component.onCompleted: serverBrowser.takeFocus()
|
||||
|
||||
HPage {
|
||||
@@ -26,6 +30,8 @@ HSwipeView {
|
||||
HPage {
|
||||
id: tabPage
|
||||
|
||||
enabled: swipeView.currentItem === this
|
||||
|
||||
HTabbedBox {
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(implicitWidth, tabPage.availableWidth)
|
||||
@@ -37,11 +43,32 @@ HSwipeView {
|
||||
HTabButton { text: qsTr("Reset") }
|
||||
}
|
||||
|
||||
SignIn {
|
||||
id: signIn
|
||||
serverUrl: serverBrowser.acceptedUrl
|
||||
displayUrl: serverBrowser.acceptedUserUrl
|
||||
onExitRequested: swipeView.currentIndex = 0
|
||||
HLoader {
|
||||
id: signInLoader
|
||||
|
||||
readonly property Component signInPassword: SignInPassword {
|
||||
serverUrl: serverBrowser.acceptedUrl
|
||||
displayUrl: serverBrowser.acceptedUserUrl
|
||||
onExitRequested: swipeView.currentIndex = 0
|
||||
}
|
||||
|
||||
readonly property Component signInSso: SignInSso {
|
||||
serverUrl: serverBrowser.acceptedUrl
|
||||
displayUrl: serverBrowser.acceptedUserUrl
|
||||
onExitRequested: swipeView.currentIndex = 0
|
||||
}
|
||||
|
||||
function takeFocus() { if (item) item.takeFocus() }
|
||||
|
||||
sourceComponent:
|
||||
serverBrowser.loginFlows.includes("m.login.password") ?
|
||||
signInPassword :
|
||||
|
||||
serverBrowser.loginFlows.includes("m.login.sso") &&
|
||||
serverBrowser.loginFlows.includes("m.login.token") ?
|
||||
signInSso :
|
||||
|
||||
null
|
||||
}
|
||||
|
||||
Register {}
|
||||
|
@@ -11,7 +11,7 @@ HBox {
|
||||
|
||||
property string acceptedUserUrl: ""
|
||||
property string acceptedUrl: ""
|
||||
property var loginFlows: ["m.login.password"]
|
||||
property var loginFlows: []
|
||||
|
||||
property string saveName: "serverBrowser"
|
||||
property var saveProperties: ["acceptedUserUrl"]
|
||||
@@ -30,8 +30,20 @@ HBox {
|
||||
connectFuture = py.callCoro("server_info", args, ([url, flows]) => {
|
||||
connectTimeout.stop()
|
||||
errorMessage.text = ""
|
||||
connectFuture = null
|
||||
|
||||
if (! (
|
||||
flows.includes("m.login.password") ||
|
||||
(
|
||||
flows.includes("m.login.sso") &&
|
||||
flows.includes("m.login.token")
|
||||
)
|
||||
)) {
|
||||
errorMessage.text =
|
||||
qsTr("No supported sign-in method for this homeserver.")
|
||||
return
|
||||
}
|
||||
|
||||
connectFuture = null
|
||||
acceptedUrl = url
|
||||
acceptedUserUrl = args[0]
|
||||
loginFlows = flows
|
||||
|
@@ -12,66 +12,42 @@ HFlickableColumnPage {
|
||||
|
||||
property string serverUrl
|
||||
property string displayUrl: serverUrl
|
||||
property var loginFuture: null
|
||||
|
||||
signal exitRequested()
|
||||
property var loginFuture: null
|
||||
|
||||
readonly property int security:
|
||||
serverUrl.startsWith("https://") ?
|
||||
SignIn.Security.Secure :
|
||||
SignInBase.Security.Secure :
|
||||
|
||||
["//localhost", "//127.0.0.1", "//:1"].includes(
|
||||
serverUrl.split(":")[1],
|
||||
) ?
|
||||
SignIn.Security.LocalHttp :
|
||||
SignInBase.Security.LocalHttp :
|
||||
|
||||
SignIn.Security.Insecure
|
||||
SignInBase.Security.Insecure
|
||||
|
||||
function takeFocus() { idField.item.forceActiveFocus() }
|
||||
default property alias innerData: inner.data
|
||||
readonly property alias rememberAccount: rememberAccount
|
||||
readonly property alias errorMessage: errorMessage
|
||||
readonly property alias applyButton: applyButton
|
||||
|
||||
function signIn() {
|
||||
if (page.loginFuture) page.loginFuture.cancel()
|
||||
signal exitRequested()
|
||||
|
||||
function finishSignIn(receivedUserId) {
|
||||
errorMessage.text = ""
|
||||
page.loginFuture = null
|
||||
|
||||
const args = [
|
||||
idField.item.text.trim(), passwordField.item.text,
|
||||
undefined, page.serverUrl,
|
||||
]
|
||||
py.callCoro(
|
||||
rememberAccount.checked ?
|
||||
"saved_accounts.add":
|
||||
"saved_accounts.delete",
|
||||
|
||||
page.loginFuture = py.callCoro("login_client", args, userId => {
|
||||
errorMessage.text = ""
|
||||
page.loginFuture = null
|
||||
[receivedUserId]
|
||||
)
|
||||
|
||||
print(rememberAccount.checked)
|
||||
py.callCoro(
|
||||
rememberAccount.checked ?
|
||||
"saved_accounts.add": "saved_accounts.delete",
|
||||
|
||||
[userId]
|
||||
)
|
||||
|
||||
pageLoader.showPage(
|
||||
"AccountSettings/AccountSettings", {userId}
|
||||
)
|
||||
|
||||
}, (type, args, error, traceback, uuid) => {
|
||||
page.loginFuture = null
|
||||
|
||||
let txt = qsTr(
|
||||
"Invalid request, login type or unknown error: %1",
|
||||
).arg(type)
|
||||
|
||||
type === "MatrixForbidden" ?
|
||||
txt = qsTr("Invalid username or password") :
|
||||
|
||||
type === "MatrixUserDeactivated" ?
|
||||
txt = qsTr("This account was deactivated") :
|
||||
|
||||
utils.showError(type, traceback, uuid)
|
||||
|
||||
errorMessage.text = txt
|
||||
})
|
||||
pageLoader.showPage(
|
||||
"AccountSettings/AccountSettings", {userId: receivedUserId}
|
||||
)
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
@@ -91,12 +67,11 @@ HFlickableColumnPage {
|
||||
footer: AutoDirectionLayout {
|
||||
ApplyButton {
|
||||
id: applyButton
|
||||
enabled: idField.item.text.trim() && passwordField.item.text
|
||||
|
||||
text: qsTr("Sign in")
|
||||
icon.name: "sign-in"
|
||||
loading: page.loginFuture !== null
|
||||
disableWhileLoading: false
|
||||
onClicked: page.signIn()
|
||||
}
|
||||
|
||||
CancelButton {
|
||||
@@ -104,27 +79,28 @@ HFlickableColumnPage {
|
||||
}
|
||||
}
|
||||
|
||||
onKeyboardAccept: if (applyButton.enabled) page.signIn()
|
||||
onKeyboardAccept: if (applyButton.enabled) applyButton.clicked()
|
||||
onKeyboardCancel: page.cancel()
|
||||
Component.onDestruction: if (loginFuture) loginFuture.cancel()
|
||||
|
||||
HButton {
|
||||
icon.name: "sign-in-" + (
|
||||
page.security === SignIn.Security.Insecure ? "insecure" :
|
||||
page.security === SignIn.Security.LocalHttp ? "local-http" :
|
||||
page.security === SignInBase.Security.Insecure ? "insecure" :
|
||||
page.security === SignInBase.Security.LocalHttp ? "local-http" :
|
||||
"secure"
|
||||
)
|
||||
|
||||
icon.color:
|
||||
page.security === SignIn.Security.Insecure ?
|
||||
page.security === SignInBase.Security.Insecure ?
|
||||
theme.colors.negativeBackground :
|
||||
|
||||
page.security === SignIn.Security.LocalHttp ?
|
||||
page.security === SignInBase.Security.LocalHttp ?
|
||||
theme.colors.middleBackground :
|
||||
|
||||
theme.colors.positiveBackground
|
||||
|
||||
text:
|
||||
page.security === SignIn.Security.Insecure ?
|
||||
page.security === SignInBase.Security.Insecure ?
|
||||
page.serverUrl :
|
||||
page.displayUrl.replace(/^(https?:\/\/)?(www\.)?/, "")
|
||||
|
||||
@@ -134,27 +110,9 @@ HFlickableColumnPage {
|
||||
Layout.maximumWidth: parent.width
|
||||
}
|
||||
|
||||
HLabeledItem {
|
||||
id: idField
|
||||
label.text: qsTr("Username:")
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
HTextField {
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
HLabeledItem {
|
||||
id: passwordField
|
||||
label.text: qsTr("Password:")
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
HTextField {
|
||||
width: parent.width
|
||||
echoMode: HTextField.Password
|
||||
}
|
||||
HColumnLayout {
|
||||
id: inner
|
||||
spacing: page.column.spacing
|
||||
}
|
||||
|
||||
HCheckBox {
|
69
src/gui/Pages/AddAccount/SignInPassword.qml
Normal file
69
src/gui/Pages/AddAccount/SignInPassword.qml
Normal file
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import "../../Base"
|
||||
import "../../Base/Buttons"
|
||||
|
||||
SignInBase {
|
||||
id: page
|
||||
|
||||
function takeFocus() { idField.item.forceActiveFocus() }
|
||||
|
||||
function signIn() {
|
||||
if (page.loginFuture) page.loginFuture.cancel()
|
||||
|
||||
errorMessage.text = ""
|
||||
|
||||
page.loginFuture = py.callCoro(
|
||||
"password_auth",
|
||||
[idField.item.text.trim(), passField.item.text, page.serverUrl],
|
||||
page.finishSignIn,
|
||||
|
||||
(type, args, error, traceback, uuid) => {
|
||||
page.loginFuture = null
|
||||
|
||||
let txt = qsTr(
|
||||
"Invalid request, login type or unknown error: %1",
|
||||
).arg(type)
|
||||
|
||||
type === "MatrixForbidden" ?
|
||||
txt = qsTr("Invalid username or password") :
|
||||
|
||||
type === "MatrixUserDeactivated" ?
|
||||
txt = qsTr("This account was deactivated") :
|
||||
|
||||
utils.showError(type, traceback, uuid)
|
||||
|
||||
page.errorMessage.text = txt
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
applyButton.enabled: idField.item.text.trim() && passField.item.text
|
||||
applyButton.onClicked: page.signIn()
|
||||
|
||||
HLabeledItem {
|
||||
id: idField
|
||||
label.text: qsTr("Username:")
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
HTextField {
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
HLabeledItem {
|
||||
id: passField
|
||||
label.text: qsTr("Password:")
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
HTextField {
|
||||
width: parent.width
|
||||
echoMode: HTextField.Password
|
||||
}
|
||||
}
|
||||
}
|
55
src/gui/Pages/AddAccount/SignInSso.qml
Normal file
55
src/gui/Pages/AddAccount/SignInSso.qml
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import "../../Base"
|
||||
import "../../Base/Buttons"
|
||||
|
||||
SignInBase {
|
||||
id: page
|
||||
|
||||
function takeFocus() { urlField.forceActiveFocus() }
|
||||
|
||||
function startSignIn() {
|
||||
errorMessage.text = ""
|
||||
|
||||
page.loginFuture = py.callCoro("start_sso_auth", [serverUrl], url => {
|
||||
urlField.text = url
|
||||
urlField.cursorPosition = 0
|
||||
|
||||
Qt.openUrlExternally(url)
|
||||
|
||||
page.loginFuture = py.callCoro("continue_sso_auth", [], userId => {
|
||||
page.loginFuture = null
|
||||
page.finishSignIn(userId)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
applyButton.text: qsTr("Waiting")
|
||||
applyButton.loading: true
|
||||
Component.onCompleted: page.startSignIn()
|
||||
|
||||
HLabel {
|
||||
wrapMode: HLabel.Wrap
|
||||
text: qsTr(
|
||||
"Complete the single sign-on process in your web browser to " +
|
||||
"continue.\n\n" +
|
||||
"If no page appeared, you can also manually open this address:"
|
||||
)
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
HTextArea {
|
||||
id: urlField
|
||||
width: parent.width
|
||||
readOnly: true
|
||||
radius: 0
|
||||
wrapMode: HTextArea.WrapAnywhere
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
@@ -86,6 +86,7 @@ HMxcImage {
|
||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||
|
||||
onTapped: {
|
||||
print(loader.mediaUrl, loader.singleMediaInfo.media_http_url)
|
||||
if (eventList.selectedCount) {
|
||||
eventDelegate.toggleChecked()
|
||||
return
|
||||
|
@@ -10,6 +10,7 @@ import "MainPane"
|
||||
|
||||
Item {
|
||||
id: mainUI
|
||||
enabled: ! window.anyPopup
|
||||
|
||||
property bool accountsPresent:
|
||||
ModelStore.get("accounts").count > 0 || py.startupAnyAccountsSaved
|
||||
|
Reference in New Issue
Block a user