1 /*
2    SPDX-FileCopyrightText: 2020-2021 Laurent Montel <montel@kde.org>
3 
4    SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "ruqolaloginwidget.h"
8 #include "common/authenticationcombobox.h"
9 #include "misc/passwordlineeditwidget.h"
10 #include "misc/twoauthenticationpasswordwidget.h"
11 #include "rocketchataccount.h"
12 #include "ruqola.h"
13 #include <KBusyIndicatorWidget>
14 #include <KColorScheme>
15 #include <KLocalizedString>
16 #include <KPasswordLineEdit>
17 #include <QCheckBox>
18 #include <QFormLayout>
19 #include <QLabel>
20 #include <QLineEdit>
21 #include <QPushButton>
22 #include <QVBoxLayout>
23 
RuqolaLoginWidget(QWidget * parent)24 RuqolaLoginWidget::RuqolaLoginWidget(QWidget *parent)
25     : QWidget(parent)
26     , mAccountName(new QLineEdit(this))
27     , mServerName(new QLineEdit(this))
28     , mUserName(new QLineEdit(this))
29     , mPasswordLineEditWidget(new PasswordLineEditWidget(this))
30     , mLdapCheckBox(new QCheckBox(i18n("Use LDAP"), this))
31     , mLoginButton(new QPushButton(i18n("Login"), this))
32     , mBusyIndicatorWidget(new KBusyIndicatorWidget(this))
33     , mFailedError(new QLabel(this))
34     , mTwoFactorAuthenticationPasswordLineEdit(new TwoAuthenticationPasswordWidget(this))
35     , mAuthenticationWidget(new QWidget(this))
36     , mAuthenticationAccountWidget(new QWidget(this))
37     , mAuthenticationCombobox(new AuthenticationComboBox(this))
38 {
39     auto mainLayout = new QFormLayout(this);
40     mainLayout->setObjectName(QStringLiteral("mainLayout"));
41 
42     mAccountName->setObjectName(QStringLiteral("mAccountName"));
43     mainLayout->addRow(i18n("Account Name:"), mAccountName);
44     mAccountName->setPlaceholderText(i18n("Account Name"));
45     connect(mAccountName, &QLineEdit::textChanged, this, &RuqolaLoginWidget::slotUpdateLoginButton);
46 
47     mServerName->setObjectName(QStringLiteral("mServerName"));
48     mainLayout->addRow(i18n("Server Name:"), mServerName);
49 
50     mUserName->setObjectName(QStringLiteral("mUserName"));
51     mainLayout->addRow(i18n("User Name:"), mUserName);
52 
53     // Type of account
54     mAuthenticationAccountWidget->setObjectName(QStringLiteral("mAuthenticationAccountWidget"));
55     mAuthenticationAccountWidget->setVisible(false);
56 
57     auto authenticationAccountLayout = new QVBoxLayout(mAuthenticationAccountWidget);
58     authenticationAccountLayout->setObjectName(QStringLiteral("authenticationAccountLayout"));
59 
60     auto authenticationAccountLabel = new QLabel(i18n("Authentication Method"), this);
61     authenticationAccountLabel->setTextFormat(Qt::PlainText);
62     authenticationAccountLabel->setObjectName(QStringLiteral("authenticationAccountLabel"));
63     authenticationAccountLayout->addWidget(authenticationAccountLabel);
64 
65     mAuthenticationCombobox->setObjectName(QStringLiteral("mAuthenticationCombobox"));
66     authenticationAccountLayout->addWidget(mAuthenticationCombobox);
67     mainLayout->addWidget(mAuthenticationAccountWidget);
68     mAuthenticationAccountWidget->setVisible(false);
69 
70     // Password
71     mPasswordLineEditWidget->setObjectName(QStringLiteral("mPasswordLineEditWidget"));
72     connect(mPasswordLineEditWidget->passwordLineEdit()->lineEdit(), &QLineEdit::returnPressed, this, &RuqolaLoginWidget::slotLogin);
73     mainLayout->addRow(i18n("Password:"), mPasswordLineEditWidget);
74     connect(mPasswordLineEditWidget, &PasswordLineEditWidget::resetPasswordRequested, this, &RuqolaLoginWidget::slotResetPasswordRequested);
75 
76     mLdapCheckBox->setObjectName(QStringLiteral("mLdapCheckBox"));
77     mainLayout->addWidget(mLdapCheckBox);
78 
79     // Two Factor authentication
80     mAuthenticationWidget->setObjectName(QStringLiteral("authenticationWidget"));
81     mAuthenticationWidget->setVisible(false);
82 
83     auto twoFactorAuthenticationLayout = new QVBoxLayout(mAuthenticationWidget);
84     twoFactorAuthenticationLayout->setObjectName(QStringLiteral("twoFactorAuthenticationLayout"));
85 
86     auto *twoFactorAuthenticationLabel =
87         new QLabel(i18n("You have enabled second factor authentication.\nPlease enter the generated code or a backup code."), this);
88     twoFactorAuthenticationLabel->setObjectName(QStringLiteral("twoFactorAuthenticationLabel"));
89     twoFactorAuthenticationLayout->addWidget(twoFactorAuthenticationLabel);
90 
91     mTwoFactorAuthenticationPasswordLineEdit->setObjectName(QStringLiteral("mTwoFactorAuthenticationPasswordLineEdit"));
92     twoFactorAuthenticationLayout->addWidget(mTwoFactorAuthenticationPasswordLineEdit);
93 
94     mainLayout->addWidget(mAuthenticationWidget);
95 
96     mLoginButton->setObjectName(QStringLiteral("mLoginButton"));
97     mainLayout->addWidget(mLoginButton);
98     connect(mLoginButton, &QPushButton::clicked, this, &RuqolaLoginWidget::slotLogin);
99 
100     mBusyIndicatorWidget->setObjectName(QStringLiteral("mBusyIndicatorWidget"));
101     mainLayout->addWidget(mBusyIndicatorWidget);
102     // Hide by default
103     mBusyIndicatorWidget->hide();
104 
105     mFailedError->setObjectName(QStringLiteral("mFailedError"));
106     QPalette pal = mFailedError->palette();
107     const KColorScheme colorScheme{QPalette::Active};
108     pal.setColor(foregroundRole(), colorScheme.foreground(KColorScheme::NegativeText).color());
109     mFailedError->setPalette(pal);
110     QFont font = mFailedError->font();
111     font.setBold(true);
112     mFailedError->setFont(font);
113 
114     mainLayout->addWidget(mFailedError);
115     // Hide by default
116     mFailedError->hide();
117 }
118 
119 RuqolaLoginWidget::~RuqolaLoginWidget() = default;
120 
slotUpdateLoginButton()121 void RuqolaLoginWidget::slotUpdateLoginButton()
122 {
123     mLoginButton->setEnabled(!mAccountName->text().isEmpty());
124 }
125 
setRocketChatAccount(RocketChatAccount * rocketChatAccount)126 void RuqolaLoginWidget::setRocketChatAccount(RocketChatAccount *rocketChatAccount)
127 {
128     mRocketChatAccount = rocketChatAccount;
129     mAccountName->setText(mRocketChatAccount->displayName());
130     mAccountName->setReadOnly(!mRocketChatAccount->displayName().isEmpty());
131     mServerName->setText(mRocketChatAccount->serverUrl());
132     mUserName->setText(mRocketChatAccount->userName());
133     mLdapCheckBox->setChecked(mRocketChatAccount->useLdap());
134     mPasswordLineEditWidget->passwordLineEdit()->setPassword(mRocketChatAccount->password());
135     mAuthenticationCombobox->switchRocketChatAccount(mRocketChatAccount); // Authentication is rocketchatAccount specific
136     mAuthenticationCombobox->setVisible(mAuthenticationCombobox->count() > 1);
137     mLdapCheckBox->setVisible(rocketChatAccount->ldapEnabled());
138     mTwoFactorAuthenticationPasswordLineEdit->setRocketChatAccount(mRocketChatAccount);
139 }
140 
slotLogin()141 void RuqolaLoginWidget::slotLogin()
142 {
143     mRocketChatAccount->setAccountName(mAccountName->isEnabled() ? mAccountName->text() : mRocketChatAccount->accountName());
144     mRocketChatAccount->setServerUrl(mServerName->text());
145     mRocketChatAccount->setUserName(mUserName->text());
146     mRocketChatAccount->setPassword(mPasswordLineEditWidget->passwordLineEdit()->password());
147     mRocketChatAccount->setUseLdap(mLdapCheckBox->isChecked());
148     if (!mAuthenticationWidget->isHidden()) {
149         mRocketChatAccount->setTwoFactorAuthenticationCode(mTwoFactorAuthenticationPasswordLineEdit->code());
150     } else {
151         mTwoFactorAuthenticationPasswordLineEdit->clear();
152     }
153     mRocketChatAccount->tryLogin();
154 }
155 
changeWidgetStatus(bool enabled)156 void RuqolaLoginWidget::changeWidgetStatus(bool enabled)
157 {
158     mServerName->setEnabled(enabled);
159     mUserName->setEnabled(enabled);
160     mPasswordLineEditWidget->setEnabled(enabled);
161     mLoginButton->setEnabled(enabled && !mAccountName->text().isEmpty());
162 }
163 
setLoginStatus(DDPAuthenticationManager::LoginStatus status)164 void RuqolaLoginWidget::setLoginStatus(DDPAuthenticationManager::LoginStatus status)
165 {
166     mFailedError->setHidden(true);
167     switch (status) {
168     case DDPAuthenticationManager::LoginStatus::Connecting:
169     case DDPAuthenticationManager::LoginStatus::LoginOngoing:
170         mBusyIndicatorWidget->show();
171         changeWidgetStatus(false);
172         mAuthenticationWidget->setVisible(false);
173         break;
174     case DDPAuthenticationManager::LoginStatus::LoggedOut:
175     case DDPAuthenticationManager::LoginStatus::LoggedIn:
176         mBusyIndicatorWidget->hide();
177         changeWidgetStatus(true);
178         mAuthenticationWidget->setVisible(false);
179         break;
180     case DDPAuthenticationManager::LoginStatus::LoginFailedInvalidUserOrPassword:
181         mBusyIndicatorWidget->hide();
182         changeWidgetStatus(true);
183         showError(i18n("Login Failed: invalid username or password"));
184         mAuthenticationWidget->setVisible(false);
185         break;
186     case DDPAuthenticationManager::LoginFailedInvalidOtp:
187         showError(i18n("Login failed: Invalid OTP code."));
188         Q_FALLTHROUGH();
189     case DDPAuthenticationManager::LoginStatus::LoginOtpRequired:
190         mBusyIndicatorWidget->hide();
191         changeWidgetStatus(true);
192         mAuthenticationWidget->setVisible(true);
193         break;
194     case DDPAuthenticationManager::LoginStatus::FailedToLoginPluginProblem:
195         mBusyIndicatorWidget->hide();
196         changeWidgetStatus(true);
197         showError(i18n("Installation Problem found. No plugins found here."));
198         mAuthenticationWidget->setVisible(false);
199         break;
200     case DDPAuthenticationManager::LoginStatus::GenericError:
201         mBusyIndicatorWidget->hide();
202         changeWidgetStatus(true);
203         showError(i18n("Login Failed: generic error"));
204         mAuthenticationWidget->setVisible(false);
205         break;
206     case DDPAuthenticationManager::LoginOtpAuthOngoing:
207     case DDPAuthenticationManager::LogoutOngoing:
208     case DDPAuthenticationManager::LogoutCleanUpOngoing:
209     case DDPAuthenticationManager::LoggedOutAndCleanedUp:
210         // TODO
211         mAuthenticationWidget->setVisible(false);
212         mBusyIndicatorWidget->hide();
213         changeWidgetStatus(true);
214         break;
215     }
216 }
217 
showError(const QString & text)218 void RuqolaLoginWidget::showError(const QString &text)
219 {
220     mFailedError->setVisible(true);
221     mFailedError->setText(text);
222 }
223 
slotResetPasswordRequested(const QString & email)224 void RuqolaLoginWidget::slotResetPasswordRequested(const QString &email)
225 {
226     mRocketChatAccount->requestNewPassword(email);
227 }
228