forked from Silverfish/proton-bridge
feat(BRIDGE-14): HV3 implementation - GUI & CLI; ownership verification & CAPTCHA are supported
This commit is contained in:
@ -29,13 +29,11 @@ using namespace bridgepp;
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
QString const defaultKeychain = "defaultKeychain"; ///< The default keychain.
|
||||
|
||||
QString const HV_ERROR_TEMPLATE = "failed to create new API client: 422 POST https://mail-api.proton.me/auth/v4: CAPTCHA validation failed (Code=12087, Status=422)";
|
||||
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
//
|
||||
//****************************************************************************************************************************************************
|
||||
@ -349,6 +347,7 @@ Status GRPCService::ForceLauncher(ServerContext *, StringValue const *request, E
|
||||
/// \return The status for the call.
|
||||
//****************************************************************************************************************************************************
|
||||
Status GRPCService::SetMainExecutable(ServerContext *, StringValue const *request, Empty *) {
|
||||
resetHv();
|
||||
app().log().debug(__FUNCTION__);
|
||||
app().log().info(QString("SetMainExecutable: %1").arg(QString::fromStdString(request->value())));
|
||||
return Status::OK;
|
||||
@ -418,7 +417,19 @@ Status GRPCService::Login(ServerContext *, LoginRequest const *request, Empty *)
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
|
||||
if (usersTab.nextUserHvRequired() && !hvWasRequested_ && previousHvUsername_ != QString::fromStdString(request->username())) {
|
||||
hvWasRequested_ = true;
|
||||
previousHvUsername_ = QString::fromStdString(request->username());
|
||||
qtProxy_.sendDelayedEvent(newLoginHvRequestedEvent());
|
||||
return Status::OK;
|
||||
} else {
|
||||
hvWasRequested_ = false;
|
||||
previousHvUsername_ = "";
|
||||
}
|
||||
if (usersTab.nextUserHvError()) {
|
||||
qtProxy_.sendDelayedEvent(newLoginError(LoginErrorType::HV_ERROR, HV_ERROR_TEMPLATE));
|
||||
return Status::OK;
|
||||
}
|
||||
if (usersTab.nextUserUsernamePasswordError()) {
|
||||
qtProxy_.sendDelayedEvent(newLoginError(LoginErrorType::USERNAME_PASSWORD_ERROR, usersTab.usernamePasswordErrorMessage()));
|
||||
return Status::OK;
|
||||
@ -495,6 +506,7 @@ Status GRPCService::Login2Passwords(ServerContext *, LoginRequest const *request
|
||||
//****************************************************************************************************************************************************
|
||||
Status GRPCService::LoginAbort(ServerContext *, LoginAbortRequest const *request, Empty *) {
|
||||
app().log().debug(__FUNCTION__);
|
||||
this->resetHv();
|
||||
loginUsername_ = QString();
|
||||
return Status::OK;
|
||||
}
|
||||
@ -953,3 +965,11 @@ void GRPCService::finishLogin() {
|
||||
|
||||
qtProxy_.sendDelayedEvent(newLoginFinishedEvent(user->id(), alreadyExist));
|
||||
}
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
//
|
||||
//****************************************************************************************************************************************************
|
||||
void GRPCService::resetHv() {
|
||||
hvWasRequested_ = false;
|
||||
previousHvUsername_ = "";
|
||||
}
|
||||
|
||||
@ -106,6 +106,7 @@ public: // member functions.
|
||||
|
||||
private: // member functions
|
||||
void finishLogin(); ///< finish the login procedure once the credentials have been validated.
|
||||
void resetHv(); ///< Resets the human verification state.
|
||||
|
||||
private: // data member
|
||||
mutable QMutex eventStreamMutex_; ///< Mutex used to access eventQueue_, isStreaming_ and shouldStopStreaming_;
|
||||
@ -113,6 +114,8 @@ private: // data member
|
||||
bool isStreaming_; ///< Is the gRPC stream running. Access protected by eventStreamMutex_;
|
||||
bool eventStreamShouldStop_; ///< Should the stream be stopped? Access protected by eventStreamMutex
|
||||
QString loginUsername_; ///< The username used for the current login procedure.
|
||||
QString previousHvUsername_; ///< The previous username used for HV.
|
||||
bool hvWasRequested_ {false}; ///< Was human verification requested.
|
||||
GRPCQtProxy qtProxy_; ///< Qt Proxy used to send signals, as this class is not a QObject.
|
||||
};
|
||||
|
||||
|
||||
@ -277,6 +277,22 @@ bridgepp::SPUser UsersTab::userWithUsernameOrEmail(QString const &username) {
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return true if the next login attempt should trigger a human verification request
|
||||
//****************************************************************************************************************************************************
|
||||
bool UsersTab::nextUserHvRequired() const {
|
||||
return ui_.checkHV3Required->isChecked();
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return true if the next login attempt should trigger a human verification error
|
||||
//****************************************************************************************************************************************************
|
||||
bool UsersTab::nextUserHvError() const {
|
||||
return ui_.checkHV3Error->isChecked();
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return true iff the next login attempt should trigger a username/password error.
|
||||
//****************************************************************************************************************************************************
|
||||
|
||||
@ -39,6 +39,8 @@ public: // member functions.
|
||||
UserTable &userTable(); ///< Returns a reference to the user table.
|
||||
bridgepp::SPUser userWithID(QString const &userID); ///< Get the user with the given ID.
|
||||
bridgepp::SPUser userWithUsernameOrEmail(QString const &username); ///< Get the user with the given username.
|
||||
bool nextUserHvRequired() const; ///< Check if next user login should trigger HV
|
||||
bool nextUserHvError() const; ///< Check if next user login should trigger HV error
|
||||
bool nextUserUsernamePasswordError() const; ///< Check if next user login should trigger a username/password error.
|
||||
bool nextUserFreeUserError() const; ///< Check if next user login should trigger a Free user error.
|
||||
bool nextUserTFARequired() const; ///< Check if next user login should requires 2FA.
|
||||
|
||||
@ -290,6 +290,20 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkHV3Required">
|
||||
<property name="text">
|
||||
<string>HV3 required</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkHV3Error">
|
||||
<property name="text">
|
||||
<string>HV3 error</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkFreeUserError">
|
||||
<property name="text">
|
||||
|
||||
@ -810,6 +810,18 @@ void QMLBackend::login(QString const &username, QString const &password) const {
|
||||
)
|
||||
}
|
||||
|
||||
void QMLBackend::loginHv(QString const &username, QString const &password) const {
|
||||
HANDLE_EXCEPTION(
|
||||
if (username.compare("coco@bandicoot", Qt::CaseInsensitive) == 0) {
|
||||
throw Exception("User requested bridge-gui to crash by trying to log as coco@bandicoot",
|
||||
"This error exists for test purposes and should be ignored.", __func__, tailOfLatestBridgeLog(app().sessionID()));
|
||||
}
|
||||
app().grpc().loginHv(username, password);
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] username The username.
|
||||
@ -1334,6 +1346,8 @@ void QMLBackend::connectGrpcEvents() {
|
||||
connect(client, &GRPCClient::login2PasswordErrorAbort, this, &QMLBackend::login2PasswordErrorAbort);
|
||||
connect(client, &GRPCClient::loginFinished, this, &QMLBackend::onLoginFinished);
|
||||
connect(client, &GRPCClient::loginAlreadyLoggedIn, this, &QMLBackend::onLoginAlreadyLoggedIn);
|
||||
connect(client, &GRPCClient::loginHvRequested, this, &QMLBackend::loginHvRequested);
|
||||
connect(client, &GRPCClient::loginHvError, this, &QMLBackend::loginHvError);
|
||||
|
||||
// update events
|
||||
connect(client, &GRPCClient::updateManualError, this, &QMLBackend::updateManualError);
|
||||
|
||||
@ -183,6 +183,7 @@ public slots: // slot for signals received from QML -> To be forwarded to Bridge
|
||||
void changeColorScheme(QString const &scheme); ///< Slot for the change of the theme.
|
||||
void setDiskCachePath(QUrl const &path) const; ///< Slot for the change of the disk cache path.
|
||||
void login(QString const &username, QString const &password) const; ///< Slot for the login button (initial login).
|
||||
void loginHv(QString const &username, QString const &password) const; ///< Slot for the login button (after HV challenge completed).
|
||||
void login2FA(QString const &username, QString const &code) const; ///< Slot for the login button (2FA login).
|
||||
void login2Password(QString const &username, QString const &password) const; ///< Slot for the login button (mailbox password login).
|
||||
void loginAbort(QString const &username) const; ///< Slot for the login abort procedure.
|
||||
@ -238,6 +239,8 @@ signals: // Signals received from the Go backend, to be forwarded to QML
|
||||
void login2PasswordErrorAbort(QString const &errorMsg); ///< Signal for the 'login2PasswordErrorAbort' gRPC stream event.
|
||||
void loginFinished(int index, bool wasSignedOut); ///< Signal for the 'loginFinished' gRPC stream event.
|
||||
void loginAlreadyLoggedIn(int index); ///< Signal for the 'loginAlreadyLoggedIn' gRPC stream event.
|
||||
void loginHvRequested(QString const &hvUrl); ///< Signal for the 'loginHvRequested' gRPC stream event.
|
||||
void loginHvError(QString const &errorMsg); ///< Signal for the 'loginHvError' gRPC stream event.
|
||||
void updateManualReady(QString const &version); ///< Signal for the 'updateManualReady' gRPC stream event.
|
||||
void updateManualRestartNeeded(); ///< Signal for the 'updateManualRestartNeeded' gRPC stream event.
|
||||
void updateManualError(); ///< Signal for the 'updateManualError' gRPC stream event.
|
||||
|
||||
@ -60,7 +60,7 @@ QtObject {
|
||||
target: Backend
|
||||
}
|
||||
}
|
||||
property var all: [root.noInternet, root.imapPortStartupError, root.smtpPortStartupError, root.imapPortChangeError, root.smtpPortChangeError, root.imapConnectionModeChangeError, root.smtpConnectionModeChangeError, root.updateManualReady, root.updateManualRestartNeeded, root.updateManualError, root.updateForce, root.updateForceError, root.updateSilentRestartNeeded, root.updateSilentError, root.updateIsLatestVersion, root.loginConnectionError, root.onlyPaidUsers, root.alreadyLoggedIn, root.enableBeta, root.bugReportSendSuccess, root.bugReportSendError, root.bugReportSendFallback, root.cacheCantMove, root.cacheLocationChangeSuccess, root.enableSplitMode, root.resetBridge, root.changeAllMailVisibility, root.deleteAccount, root.noKeychain, root.rebuildKeychain, root.addressChanged, root.apiCertIssue, root.userBadEvent, root.imapLoginWhileSignedOut, root.genericError, root.genericQuestion]
|
||||
property var all: [root.noInternet, root.imapPortStartupError, root.smtpPortStartupError, root.imapPortChangeError, root.smtpPortChangeError, root.imapConnectionModeChangeError, root.smtpConnectionModeChangeError, root.updateManualReady, root.updateManualRestartNeeded, root.updateManualError, root.updateForce, root.updateForceError, root.updateSilentRestartNeeded, root.updateSilentError, root.updateIsLatestVersion, root.loginConnectionError, root.onlyPaidUsers, root.alreadyLoggedIn, root.enableBeta, root.bugReportSendSuccess, root.bugReportSendError, root.bugReportSendFallback, root.cacheCantMove, root.cacheLocationChangeSuccess, root.enableSplitMode, root.resetBridge, root.changeAllMailVisibility, root.deleteAccount, root.noKeychain, root.rebuildKeychain, root.addressChanged, root.apiCertIssue, root.userBadEvent, root.imapLoginWhileSignedOut, root.genericError, root.genericQuestion, root.hvErrorEvent]
|
||||
property Notification alreadyLoggedIn: Notification {
|
||||
brief: qsTr("Already signed in")
|
||||
description: qsTr("This account is already signed in.")
|
||||
@ -1130,6 +1130,27 @@ QtObject {
|
||||
target: Backend
|
||||
}
|
||||
}
|
||||
property Notification hvErrorEvent: Notification {
|
||||
group: Notifications.Group.Configuration
|
||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||
type: Notification.NotificationType.Danger
|
||||
|
||||
action: Action {
|
||||
text: qsTr("OK")
|
||||
onTriggered: {
|
||||
root.hvErrorEvent.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onLoginHvError(errorMsg) {
|
||||
root.hvErrorEvent.active = true;
|
||||
root.hvErrorEvent.description = errorMsg;
|
||||
}
|
||||
target: Backend
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
signal askChangeAllMailVisibility(var isVisibleNow)
|
||||
signal askDeleteAccount(var user)
|
||||
|
||||
@ -20,12 +20,14 @@ FocusScope {
|
||||
enum RootStack {
|
||||
Login,
|
||||
TOTP,
|
||||
MailboxPassword
|
||||
MailboxPassword,
|
||||
HV
|
||||
}
|
||||
|
||||
property alias currentIndex: stackLayout.currentIndex
|
||||
property alias username: usernameTextField.text
|
||||
property var wizard
|
||||
property string hvLinkUrl: ""
|
||||
|
||||
signal loginAbort(string username, bool wasSignedOut)
|
||||
|
||||
@ -47,6 +49,14 @@ FocusScope {
|
||||
passwordTextField.hidePassword();
|
||||
secondPasswordTextField.hidePassword();
|
||||
}
|
||||
function resetViaHv() {
|
||||
usernameTextField.enabled = false;
|
||||
passwordTextField.enabled = false;
|
||||
signInButton.loading = true;
|
||||
secondPasswordButton.loading = false;
|
||||
secondPasswordTextField.enabled = true;
|
||||
totpLayout.reset();
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
@ -124,6 +134,18 @@ FocusScope {
|
||||
else
|
||||
errorLabel.text = qsTr("Incorrect login credentials");
|
||||
}
|
||||
function onLoginHvRequested(hvUrl) {
|
||||
console.assert(stackLayout.currentIndex === Login.RootStack.Login || stackLayout.currentIndex === Login.RootStack.MailboxPassword, "Unexpected loginHvRequested");
|
||||
stackLayout.currentIndex = Login.RootStack.HV;
|
||||
hvUsernameLabel.text = usernameTextField.text;
|
||||
hvLinkUrl = hvUrl;
|
||||
}
|
||||
function onLoginHvError(_) {
|
||||
console.assert(stackLayout.currentIndex === Login.RootStack.Login || stackLayout.currentIndex === Login.RootStack.MailboxPassword, "Unexpected onLoginHvInvalidTokenError");
|
||||
stackLayout.currentIndex = Login.RootStack.Login;
|
||||
root.resetViaHv();
|
||||
root.reset()
|
||||
}
|
||||
|
||||
target: Backend
|
||||
}
|
||||
@ -475,5 +497,112 @@ FocusScope {
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
id: hvLayout
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: ProtonStyle.wizard_spacing_extra_large
|
||||
|
||||
ColumnLayout {
|
||||
spacing: ProtonStyle.wizard_spacing_medium
|
||||
|
||||
ColumnLayout {
|
||||
spacing: ProtonStyle.wizard_spacing_small
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
colorScheme: wizard.colorScheme
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("Human verification")
|
||||
type: Label.LabelType.Title
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Label {
|
||||
id: hvUsernameLabel
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
color: wizard.colorScheme.text_weak
|
||||
colorScheme: wizard.colorScheme
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
type: Label.LabelType.Body
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
colorScheme: wizard.colorScheme
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: qsTr("Please open the following link in your favourite web browser to verify you are human.")
|
||||
type: Label.LabelType.Body
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Label {
|
||||
id: hvRequestedUrlText
|
||||
type: Label.LabelType.Lead
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
colorScheme: wizard.colorScheme
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: "<a href='" + hvLinkUrl + "'>" + hvLinkUrl.replace("&", "&")+ "</a>"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Qt.openUrlExternally(hvLinkUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: ProtonStyle.wizard_spacing_medium
|
||||
|
||||
Button {
|
||||
id: hVContinueButton
|
||||
Layout.fillWidth: true
|
||||
colorScheme: wizard.colorScheme
|
||||
text: qsTr("Continue")
|
||||
|
||||
function checkAndSignInHv() {
|
||||
console.assert(stackLayout.currentIndex === Login.RootStack.HV || stackLayout.currentIndex === Login.RootStack.MailboxPassword, "Unexpected checkInAndSignInHv")
|
||||
stackLayout.currentIndex = Login.RootStack.Login
|
||||
usernameTextField.validate();
|
||||
passwordTextField.validate();
|
||||
if (usernameTextField.error || passwordTextField.error) {
|
||||
return;
|
||||
}
|
||||
root.resetViaHv();
|
||||
Backend.loginHv(usernameTextField.text, Qt.btoa(passwordTextField.text));
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
checkAndSignInHv()
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
colorScheme: wizard.colorScheme
|
||||
secondary: true
|
||||
secondaryIsOpaque: true
|
||||
text: qsTr("Cancel")
|
||||
onClicked: {
|
||||
root.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,6 +302,18 @@ SPStreamEvent newLoginTfaRequestedEvent(QString const &username) {
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return The event.
|
||||
//****************************************************************************************************************************************************
|
||||
SPStreamEvent newLoginHvRequestedEvent() {
|
||||
auto event = new ::grpc::LoginHvRequestedEvent;
|
||||
event->set_hvurl("https://verify.proton.me/?methods=captcha&token=SOME_RANDOM_TOKEN");
|
||||
auto loginEvent = new grpc::LoginEvent;
|
||||
loginEvent->set_allocated_hvrequested(event);
|
||||
return wrapLoginEvent(loginEvent);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] username The username.
|
||||
/// \return The event.
|
||||
|
||||
@ -48,6 +48,7 @@ SPStreamEvent newLoginTfaRequestedEvent(QString const &username); ///< Create a
|
||||
SPStreamEvent newLoginTwoPasswordsRequestedEvent(QString const &username); ///< Create a new LoginTwoPasswordsRequestedEvent event.
|
||||
SPStreamEvent newLoginFinishedEvent(QString const &userID, bool wasSignedOut); ///< Create a new LoginFinishedEvent event.
|
||||
SPStreamEvent newLoginAlreadyLoggedInEvent(QString const &userID); ///< Create a new LoginAlreadyLoggedInEvent event.
|
||||
SPStreamEvent newLoginHvRequestedEvent(); ///< Create a new LoginHvRequestedEvent
|
||||
|
||||
// Update related events
|
||||
SPStreamEvent newUpdateErrorEvent(grpc::UpdateErrorType errorType); ///< Create a new UpdateErrorEvent event.
|
||||
|
||||
@ -23,7 +23,6 @@
|
||||
#include "../ProcessMonitor.h"
|
||||
#include "../Log/LogUtils.h"
|
||||
|
||||
|
||||
using namespace google::protobuf;
|
||||
using namespace grpc;
|
||||
|
||||
@ -607,6 +606,20 @@ grpc::Status GRPCClient::login(QString const &username, QString const &password)
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] username The username.
|
||||
/// \param[in] password The password.
|
||||
/// \return the status for the gRPC call.
|
||||
//****************************************************************************************************************************************************
|
||||
grpc::Status GRPCClient::loginHv(QString const &username, QString const &password) {
|
||||
LoginRequest request;
|
||||
request.set_username(username.toStdString());
|
||||
request.set_password(password.toStdString());
|
||||
request.set_usehvdetails(true);
|
||||
return this->logGRPCCallStatus(stub_->Login(this->clientContext().get(), request, &empty), __FUNCTION__);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] username The username.
|
||||
/// \param[in] code The The 2FA code.
|
||||
@ -1221,6 +1234,9 @@ void GRPCClient::processLoginEvent(LoginEvent const &event) {
|
||||
case TWO_PASSWORDS_ABORT:
|
||||
emit login2PasswordErrorAbort(QString::fromStdString(error.message()));
|
||||
break;
|
||||
case HV_ERROR:
|
||||
emit loginHvError(QString::fromStdString(error.message()));
|
||||
break;
|
||||
default:
|
||||
this->logError("Unknown login error event received.");
|
||||
break;
|
||||
@ -1245,6 +1261,10 @@ void GRPCClient::processLoginEvent(LoginEvent const &event) {
|
||||
this->logTrace("Login event received: AlreadyLoggedIn.");
|
||||
emit loginAlreadyLoggedIn(QString::fromStdString(event.finished().userid()));
|
||||
break;
|
||||
case LoginEvent::kHvRequested:
|
||||
this->logTrace("Login event Received: HvRequested");
|
||||
emit loginHvRequested(QString::fromStdString(event.hvrequested().hvurl()));
|
||||
break;
|
||||
default:
|
||||
this->logError("Unknown Login event received.");
|
||||
break;
|
||||
|
||||
@ -155,6 +155,7 @@ public: // login related calls
|
||||
grpc::Status login2FA(QString const &username, QString const &code); ///< Performs the 'login2FA' call.
|
||||
grpc::Status login2Passwords(QString const &username, QString const &password); ///< Performs the 'login2Passwords' call.
|
||||
grpc::Status loginAbort(QString const &username); ///< Performs the 'loginAbort' call.
|
||||
grpc::Status loginHv(QString const &username, QString const &password); ///< Performs the 'login' call with additional useHv flag
|
||||
|
||||
signals:
|
||||
void loginUsernamePasswordError(QString const &errMsg);
|
||||
@ -168,6 +169,8 @@ signals:
|
||||
void login2PasswordErrorAbort(QString const &errMsg);
|
||||
void loginFinished(QString const &userID, bool wasSignedOut);
|
||||
void loginAlreadyLoggedIn(QString const &userID);
|
||||
void loginHvRequested(QString const &hvUrl);
|
||||
void loginHvError(QString const &errMsg);
|
||||
|
||||
public: // Update related calls
|
||||
grpc::Status checkUpdate();
|
||||
|
||||
Reference in New Issue
Block a user