File indexing completed on 2024-12-01 10:40:59

0001 /*
0002    SPDX-FileCopyrightText: 2020-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "ruqolaloginwidget.h"
0008 #include "colorsandmessageviewstyle.h"
0009 #include "common/authenticationoauthwidget.h"
0010 #include "misc/passwordlineeditwidget.h"
0011 #include "misc/twoauthenticationpasswordwidget.h"
0012 #include "rocketchataccount.h"
0013 #include "ruqola_password_widgets_debug.h"
0014 #include <KBusyIndicatorWidget>
0015 #include <KColorScheme>
0016 #include <KLocalizedString>
0017 #include <KPasswordLineEdit>
0018 #include <QCheckBox>
0019 #include <QFormLayout>
0020 #include <QLabel>
0021 #include <QLineEdit>
0022 #include <QPushButton>
0023 #include <QVBoxLayout>
0024 
0025 RuqolaLoginWidget::RuqolaLoginWidget(QWidget *parent)
0026     : QWidget(parent)
0027     , mAccountName(new QLineEdit(this))
0028     , mServerUrl(new QLineEdit(this))
0029     , mUserName(new QLineEdit(this))
0030     , mPasswordLineEditWidget(new PasswordLineEditWidget(this))
0031     , mLoginButton(new QPushButton(i18n("Login"), this))
0032     , mBusyIndicatorWidget(new KBusyIndicatorWidget(this))
0033     , mFailedError(new QLabel(this))
0034     , mTwoFactorAuthenticationPasswordLineEdit(new TwoAuthenticationPasswordWidget(this))
0035     , mTwoFactorAuthenticationWidget(new QWidget(this))
0036     , mAuthenticationAccountWidget(new QWidget(this))
0037     , mAuthenticationWidget(new AuthenticationOauthWidget(this))
0038 {
0039     auto mainLayout = new QFormLayout(this);
0040     mainLayout->setObjectName(QStringLiteral("mainLayout"));
0041 
0042     mServerUrl->setObjectName(QStringLiteral("mServerUrl"));
0043     mainLayout->addRow(i18n("Server URL:"), mServerUrl);
0044 
0045     mAccountName->setObjectName(QStringLiteral("mAccountName"));
0046     mainLayout->addRow(i18n("Account Name:"), mAccountName);
0047     mAccountName->setPlaceholderText(i18n("Account name"));
0048     connect(mAccountName, &QLineEdit::textChanged, this, &RuqolaLoginWidget::slotUpdateLoginButton);
0049 
0050     mUserName->setObjectName(QStringLiteral("mUserName"));
0051     mainLayout->addRow(i18n("Username or Email:"), mUserName);
0052     mUserName->setPlaceholderText(i18n("Username or email"));
0053 
0054     // Type of account
0055     mAuthenticationAccountWidget->setObjectName(QStringLiteral("mAuthenticationAccountWidget"));
0056 
0057     auto authenticationAccountLayout = new QVBoxLayout(mAuthenticationAccountWidget);
0058     authenticationAccountLayout->setObjectName(QStringLiteral("authenticationAccountLayout"));
0059 
0060     auto authenticationAccountLabel = new QLabel(i18n("Authentication Method"), this);
0061     authenticationAccountLabel->setTextFormat(Qt::PlainText);
0062     authenticationAccountLabel->setObjectName(QStringLiteral("authenticationAccountLabel"));
0063     authenticationAccountLayout->addWidget(authenticationAccountLabel);
0064 
0065     mAuthenticationWidget->setObjectName(QStringLiteral("mAuthenticationWidget"));
0066     authenticationAccountLayout->addWidget(mAuthenticationWidget);
0067     mainLayout->addWidget(mAuthenticationAccountWidget);
0068     mAuthenticationAccountWidget->setVisible(false);
0069 
0070     // Password
0071     mPasswordLineEditWidget->setObjectName(QStringLiteral("mPasswordLineEditWidget"));
0072     connect(mPasswordLineEditWidget->passwordLineEdit()->lineEdit(), &QLineEdit::returnPressed, this, &RuqolaLoginWidget::slotLogin);
0073     mainLayout->addRow(i18n("Password:"), mPasswordLineEditWidget);
0074     connect(mPasswordLineEditWidget, &PasswordLineEditWidget::resetPasswordRequested, this, &RuqolaLoginWidget::slotResetPasswordRequested);
0075 
0076     // Two Factor authentication
0077     mTwoFactorAuthenticationWidget->setObjectName(QStringLiteral("mTwoFactorAuthenticationWidget"));
0078     mTwoFactorAuthenticationWidget->setVisible(false);
0079 
0080     auto twoFactorAuthenticationLayout = new QVBoxLayout(mTwoFactorAuthenticationWidget);
0081     twoFactorAuthenticationLayout->setObjectName(QStringLiteral("twoFactorAuthenticationLayout"));
0082     twoFactorAuthenticationLayout->setContentsMargins(QMargins{});
0083 
0084     auto twoFactorAuthenticationLabel =
0085         new QLabel(i18n("You have enabled second factor authentication.\nPlease enter the generated code or a backup code."), this);
0086     twoFactorAuthenticationLabel->setObjectName(QStringLiteral("twoFactorAuthenticationLabel"));
0087     twoFactorAuthenticationLayout->addWidget(twoFactorAuthenticationLabel);
0088 
0089     mTwoFactorAuthenticationPasswordLineEdit->setObjectName(QStringLiteral("mTwoFactorAuthenticationPasswordLineEdit"));
0090     twoFactorAuthenticationLayout->addWidget(mTwoFactorAuthenticationPasswordLineEdit);
0091 
0092     mainLayout->addWidget(mTwoFactorAuthenticationWidget);
0093 
0094     mLoginButton->setObjectName(QStringLiteral("mLoginButton"));
0095     mainLayout->addWidget(mLoginButton);
0096     connect(mLoginButton, &QPushButton::clicked, this, &RuqolaLoginWidget::slotLogin);
0097 
0098     mBusyIndicatorWidget->setObjectName(QStringLiteral("mBusyIndicatorWidget"));
0099     mainLayout->addWidget(mBusyIndicatorWidget);
0100     // Hide by default
0101     mBusyIndicatorWidget->hide();
0102 
0103     mFailedError->setObjectName(QStringLiteral("mFailedError"));
0104     QPalette pal = mFailedError->palette();
0105     pal.setColor(foregroundRole(), ColorsAndMessageViewStyle::self().schemeView().foreground(KColorScheme::NegativeText).color());
0106     mFailedError->setPalette(pal);
0107     QFont font = mFailedError->font();
0108     font.setBold(true);
0109     mFailedError->setFont(font);
0110 
0111     mainLayout->addWidget(mFailedError);
0112     // Hide by default
0113     mFailedError->hide();
0114 }
0115 
0116 RuqolaLoginWidget::~RuqolaLoginWidget() = default;
0117 
0118 void RuqolaLoginWidget::slotUpdateLoginButton()
0119 {
0120     mLoginButton->setEnabled(!mAccountName->text().isEmpty());
0121 }
0122 
0123 void RuqolaLoginWidget::setRocketChatAccount(RocketChatAccount *rocketChatAccount)
0124 {
0125     mRocketChatAccount = rocketChatAccount;
0126     mAccountName->setText(mRocketChatAccount->displayName());
0127     mAccountName->setReadOnly(!mRocketChatAccount->displayName().isEmpty());
0128     mServerUrl->setText(mRocketChatAccount->serverUrl());
0129     mServerUrl->setReadOnly(true);
0130 
0131     mUserName->setText(mRocketChatAccount->userName());
0132     disconnect(mUpdatePasswordConnection);
0133     mPasswordLineEditWidget->passwordLineEdit()->setPassword(mRocketChatAccount->password());
0134     qCDebug(RUQOLA_PASSWORD_WIDGETS_LOG) << " RuqolaLoginWidget::setRocketChatAccount: password is empty ? " << mRocketChatAccount->password().isEmpty();
0135     mUpdatePasswordConnection = connect(mRocketChatAccount, &RocketChatAccount::passwordChanged, mPasswordLineEditWidget, [this]() {
0136         qCDebug(RUQOLA_PASSWORD_WIDGETS_LOG) << " RuqolaLoginWidget::setRocketChatAccount: password has changed => password is empty ? "
0137                                              << mRocketChatAccount->password().isEmpty();
0138         mPasswordLineEditWidget->passwordLineEdit()->setPassword(mRocketChatAccount->password());
0139     });
0140 
0141     mTwoFactorAuthenticationPasswordLineEdit->setRocketChatAccount(mRocketChatAccount);
0142     mPasswordLineEditWidget->setAllowPasswordReset(mRocketChatAccount->allowPasswordReset() && mRocketChatAccount->allowPasswordChange());
0143 }
0144 
0145 void RuqolaLoginWidget::slotLogin()
0146 {
0147     mRocketChatAccount->setAccountName(mAccountName->isEnabled() ? mAccountName->text() : mRocketChatAccount->accountName());
0148     mRocketChatAccount->setServerUrl(mServerUrl->text());
0149     mRocketChatAccount->setUserName(mUserName->text());
0150     mRocketChatAccount->setPassword(mPasswordLineEditWidget->passwordLineEdit()->password());
0151     if (mTwoFactorAuthenticationPasswordLineEdit->isHidden()) {
0152         mTwoFactorAuthenticationPasswordLineEdit->clear();
0153     } else {
0154         mRocketChatAccount->setTwoFactorAuthenticationCode(mTwoFactorAuthenticationPasswordLineEdit->code());
0155     }
0156     mRocketChatAccount->tryLogin();
0157 }
0158 
0159 void RuqolaLoginWidget::changeWidgetStatus(bool enabled)
0160 {
0161     mServerUrl->setEnabled(enabled);
0162     mUserName->setEnabled(enabled);
0163     mPasswordLineEditWidget->setEnabled(enabled);
0164     mLoginButton->setEnabled(enabled && !mAccountName->text().isEmpty());
0165 }
0166 
0167 void RuqolaLoginWidget::setLoginStatus(DDPAuthenticationManager::LoginStatus status)
0168 {
0169     mFailedError->setHidden(true);
0170     switch (status) {
0171     case DDPAuthenticationManager::LoginStatus::Connecting:
0172     case DDPAuthenticationManager::LoginStatus::LoginOngoing:
0173         mBusyIndicatorWidget->show();
0174         changeWidgetStatus(false);
0175         mTwoFactorAuthenticationWidget->setVisible(false);
0176         break;
0177     case DDPAuthenticationManager::LoginStatus::LoggedOut:
0178     case DDPAuthenticationManager::LoginStatus::LoggedIn:
0179         mBusyIndicatorWidget->hide();
0180         changeWidgetStatus(true);
0181         mTwoFactorAuthenticationWidget->setVisible(false);
0182         break;
0183     case DDPAuthenticationManager::LoginStatus::LoginFailedInvalidUserOrPassword:
0184         mBusyIndicatorWidget->hide();
0185         changeWidgetStatus(true);
0186         showError(i18n("Login Failed: invalid username or password"));
0187         mTwoFactorAuthenticationWidget->setVisible(false);
0188         break;
0189     case DDPAuthenticationManager::LoginFailedInvalidOtp:
0190         showError(i18n("Login failed: Invalid OTP code."));
0191         Q_FALLTHROUGH();
0192     case DDPAuthenticationManager::LoginStatus::LoginOtpRequired:
0193         mBusyIndicatorWidget->hide();
0194         changeWidgetStatus(true);
0195         mTwoFactorAuthenticationWidget->setVisible(true);
0196         break;
0197     case DDPAuthenticationManager::LoginStatus::FailedToLoginPluginProblem:
0198         mBusyIndicatorWidget->hide();
0199         changeWidgetStatus(true);
0200         showError(i18n("Installation Problem found. No plugins found here."));
0201         mTwoFactorAuthenticationWidget->setVisible(false);
0202         break;
0203     case DDPAuthenticationManager::LoginStatus::GenericError:
0204         mBusyIndicatorWidget->hide();
0205         changeWidgetStatus(true);
0206         showError(i18n("Login Failed: generic error"));
0207         mTwoFactorAuthenticationWidget->setVisible(false);
0208         break;
0209     case DDPAuthenticationManager::LoginFailedLoginBlockForIp:
0210         mBusyIndicatorWidget->hide();
0211         changeWidgetStatus(true);
0212         showError(i18n("Login Failed: Login has been temporarily blocked For IP."));
0213         mTwoFactorAuthenticationWidget->setVisible(false);
0214         break;
0215     case DDPAuthenticationManager::LoginFailedLoginBlockedForUser:
0216         mBusyIndicatorWidget->hide();
0217         changeWidgetStatus(true);
0218         showError(i18n("Login Failed: Login has been temporarily blocked For User."));
0219         mTwoFactorAuthenticationWidget->setVisible(false);
0220         break;
0221     case DDPAuthenticationManager::LoginStatus::LoginFailedUserNotActivated:
0222         mBusyIndicatorWidget->hide();
0223         changeWidgetStatus(true);
0224         showError(i18n("Login Failed: User is not activated."));
0225         mTwoFactorAuthenticationWidget->setVisible(false);
0226         break;
0227     case DDPAuthenticationManager::LoginStatus::LoginFailedLoginAppNotAllowedToLogin:
0228         mBusyIndicatorWidget->hide();
0229         changeWidgetStatus(true);
0230         showError(i18n("Login Failed: App user is not allowed to login."));
0231         mTwoFactorAuthenticationWidget->setVisible(false);
0232         break;
0233     case DDPAuthenticationManager::LoginOtpAuthOngoing:
0234     case DDPAuthenticationManager::LogoutOngoing:
0235     case DDPAuthenticationManager::LogoutCleanUpOngoing:
0236     case DDPAuthenticationManager::LoggedOutAndCleanedUp:
0237         // TODO
0238         mTwoFactorAuthenticationWidget->setVisible(false);
0239         mBusyIndicatorWidget->hide();
0240         changeWidgetStatus(true);
0241         break;
0242     }
0243 }
0244 
0245 void RuqolaLoginWidget::showError(const QString &text)
0246 {
0247     mFailedError->setVisible(true);
0248     mFailedError->setText(text);
0249 }
0250 
0251 void RuqolaLoginWidget::slotResetPasswordRequested(const QString &email)
0252 {
0253     mRocketChatAccount->requestNewPassword(email);
0254 }
0255 
0256 #include "moc_ruqolaloginwidget.cpp"