File indexing completed on 2024-03-24 17:13:37

0001 /*  This file is part of the KDE project
0002     SPDX-FileCopyrightText: 2007-2008 Gökçen Eraslan <gokcen@pardus.org.tr>
0003     SPDX-FileCopyrightText: 2008 Dirk Mueller <mueller@kde.org>
0004     SPDX-FileCopyrightText: 2008 Daniel Nicoletti <dantti85-pk@yahoo.com.br>
0005     SPDX-FileCopyrightText: 2008-2010 Dario Freddi <drf@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "AuthDialog.h"
0011 
0012 #include <QDebug>
0013 #include <QDesktopServices>
0014 #include <QPainter>
0015 #include <QPushButton>
0016 #include <QStandardItemModel>
0017 #include <QUrl>
0018 
0019 #include <KIconLoader>
0020 #include <KUser>
0021 #include <KWindowSystem>
0022 
0023 #include <PolkitQt1/Authority>
0024 #include <PolkitQt1/Details>
0025 
0026 AuthDialog::AuthDialog(const QString &actionId,
0027                        const QString &message,
0028                        const QString &iconName,
0029                        const PolkitQt1::Details &details,
0030                        const PolkitQt1::Identity::List &identities,
0031                        WId parent)
0032     : QDialog(nullptr)
0033 {
0034     // KAuth is able to circumvent polkit's limitations, and manages to send the wId to the auth agent.
0035     // If we received it, we use KWindowSystem to associate this dialog correctly.
0036     if (parent > 0) {
0037         qDebug() << "Associating the dialog with " << parent << " this dialog is " << winId();
0038 
0039         // Set the parent
0040         setAttribute(Qt::WA_NativeWindow, true);
0041         KWindowSystem::setMainWindow(windowHandle(), parent);
0042 
0043         // Set modal
0044         setWindowModality(Qt::ApplicationModal);
0045 
0046         // raise on top
0047         activateWindow();
0048         raise();
0049     }
0050 
0051     setupUi(this);
0052 
0053     connect(userCB, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AuthDialog::checkSelectedUser);
0054 
0055     connect(buttonBox, &QDialogButtonBox::accepted, this, &AuthDialog::okClicked);
0056     connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0057     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0058 
0059     QString detailsButtonText = i18n("Details");
0060     QPushButton *detailsButton = new QPushButton(detailsButtonText + " >>");
0061     detailsButton->setIcon(QIcon::fromTheme("help-about"));
0062     detailsButton->setCheckable(true);
0063     connect(detailsButton, &QAbstractButton::toggled, this, [=](bool toggled) {
0064         detailsWidgetContainer->setVisible(toggled);
0065         if (toggled) {
0066             detailsButton->setText(detailsButtonText + " <<");
0067         } else {
0068             detailsButton->setText(detailsButtonText + " >>");
0069         }
0070         adjustSize();
0071     });
0072     buttonBox->addButton(detailsButton, QDialogButtonBox::HelpRole);
0073     detailsWidgetContainer->hide();
0074 
0075     setWindowTitle(i18n("Authentication Required"));
0076 
0077     if (message.isEmpty()) {
0078         qWarning() << "Could not get action message for action.";
0079         lblHeader->hide();
0080     } else {
0081         qDebug() << "Message of action: " << message;
0082         lblHeader->setText("<h3>" + message + "</h3>");
0083         m_message = message;
0084     }
0085 
0086     // loads the standard key icon
0087     QPixmap icon = KIconLoader::global()->loadIcon("dialog-password", //
0088                                                    KIconLoader::NoGroup,
0089                                                    KIconLoader::SizeHuge,
0090                                                    KIconLoader::DefaultState);
0091     // create a painter to paint the action icon over the key icon
0092     QPainter painter(&icon);
0093     const int iconSize = icon.size().width();
0094     // the emblem icon to size 32
0095     int overlaySize = 32;
0096     // try to load the action icon
0097     const QPixmap pixmap = KIconLoader::global()->loadIcon(iconName, //
0098                                                            KIconLoader::NoGroup,
0099                                                            overlaySize,
0100                                                            KIconLoader::DefaultState,
0101                                                            QStringList(),
0102                                                            nullptr,
0103                                                            true);
0104     // if we're able to load the action icon paint it over the
0105     // key icon.
0106     if (!pixmap.isNull()) {
0107         QPoint startPoint;
0108         // bottom right corner
0109         startPoint = QPoint(iconSize - overlaySize - 2, iconSize - overlaySize - 2);
0110         painter.drawPixmap(startPoint, pixmap);
0111     }
0112 
0113     setWindowIcon(icon);
0114     lblPixmap->setPixmap(icon);
0115 
0116     // find action description for actionId
0117     const auto actions = PolkitQt1::Authority::instance()->enumerateActionsSync();
0118     for (const PolkitQt1::ActionDescription &desc : actions) {
0119         if (actionId == desc.actionId()) {
0120             m_actionDescription = desc;
0121             qDebug() << "Action description has been found";
0122             break;
0123         }
0124     }
0125 
0126     AuthDetails *detailsDialog = new AuthDetails(details, m_actionDescription, this);
0127     detailsWidgetContainer->layout()->addWidget(detailsDialog);
0128 
0129     userCB->hide();
0130     lePassword->setFocus();
0131 
0132     messageWidget->hide();
0133 
0134     // If there is more than 1 identity we will show the combobox for user selection
0135     if (identities.size() > 1) {
0136         connect(userCB, SIGNAL(currentIndexChanged(int)), this, SLOT(on_userCB_currentIndexChanged(int)));
0137 
0138         createUserCB(identities);
0139     } else {
0140         userCB->addItem("", identities[0].toString());
0141         userCB->setCurrentIndex(0);
0142     }
0143 }
0144 
0145 AuthDialog::~AuthDialog()
0146 {
0147 }
0148 
0149 void AuthDialog::accept()
0150 {
0151     // Do nothing, do not close the dialog. This is needed so that the dialog stays
0152     lePassword->setEnabled(false);
0153     return;
0154 }
0155 
0156 void AuthDialog::setRequest(const QString &request, bool requiresAdmin)
0157 {
0158     qDebug() << request;
0159     PolkitQt1::Identity identity = adminUserSelected();
0160     if (request.startsWith(QLatin1String("password:"), Qt::CaseInsensitive)) {
0161         if (requiresAdmin) {
0162             if (!identity.isValid()) {
0163                 lblPassword->setText(i18n("Password for root:"));
0164             } else {
0165                 lblPassword->setText(i18n("Password for %1:", identity.toString().remove("unix-user:")));
0166             }
0167         } else {
0168             lblPassword->setText(i18n("Password:"));
0169         }
0170     } else if (request.startsWith(QLatin1String("password or swipe finger:"), Qt::CaseInsensitive)) {
0171         if (requiresAdmin) {
0172             if (!identity.isValid()) {
0173                 lblPassword->setText(i18n("Password or swipe finger for root:"));
0174             } else {
0175                 lblPassword->setText(i18n("Password or swipe finger for %1:", identity.toString().remove("unix-user:")));
0176             }
0177         } else {
0178             lblPassword->setText(i18n("Password or swipe finger:"));
0179         }
0180     } else {
0181         lblPassword->setText(request);
0182     }
0183 }
0184 
0185 void AuthDialog::setOptions()
0186 {
0187     lblContent->setText(
0188         i18n("An application is attempting to perform an action that requires privileges."
0189              " Authentication is required to perform this action."));
0190 }
0191 
0192 void AuthDialog::createUserCB(const PolkitQt1::Identity::List &identities)
0193 {
0194     /* if we've already built the list of admin users once, then avoid
0195      * doing it again.. (this is mainly used when the user entered the
0196      * wrong password and the dialog is recycled)
0197      */
0198 
0199     if (identities.count() && (userCB->count() - 1) != identities.count()) {
0200         // Clears the combobox in the case some user be added
0201         userCB->clear();
0202 
0203         // Adds a Dummy user
0204         userCB->addItem(i18n("Select User"), QString());
0205         qobject_cast<QStandardItemModel *>(userCB->model())->item(userCB->count() - 1)->setEnabled(false);
0206 
0207         // For each user
0208         int index = 1; // Start at 1 because of the "Select User" entry
0209         int currentUserIndex = -1;
0210         const KUser currentUser;
0211         for (const PolkitQt1::Identity &identity : identities) {
0212             // First check to see if the user is valid
0213             qDebug() << "User: " << identity.toString();
0214             const KUser user(identity.toString().remove("unix-user:"));
0215             if (!user.isValid()) {
0216                 qWarning() << "User invalid: " << user.loginName();
0217                 continue;
0218             }
0219 
0220             // Display user Full Name IF available
0221             QString display;
0222             if (!user.property(KUser::FullName).toString().isEmpty()) {
0223                 display = i18nc("%1 is the full user name, %2 is the user login name", "%1 (%2)", user.property(KUser::FullName).toString(), user.loginName());
0224             } else {
0225                 display = user.loginName();
0226             }
0227 
0228             QIcon icon;
0229             // load user icon face
0230             if (!user.faceIconPath().isEmpty()) {
0231                 icon = QIcon(user.faceIconPath());
0232             } else {
0233                 icon = QIcon::fromTheme("user-identity");
0234             }
0235             // appends the user item
0236             userCB->addItem(icon, display, identity.toString());
0237 
0238             if (user == currentUser) {
0239                 currentUserIndex = index;
0240             }
0241             ++index;
0242         }
0243 
0244         // Show the widget and set focus
0245         if (currentUserIndex != -1) {
0246             userCB->setCurrentIndex(currentUserIndex);
0247         }
0248         userCB->show();
0249     }
0250 }
0251 
0252 PolkitQt1::Identity AuthDialog::adminUserSelected() const
0253 {
0254     if (userCB->currentIndex() == -1)
0255         return PolkitQt1::Identity();
0256 
0257     const QString id = userCB->currentData().toString();
0258     if (id.isEmpty())
0259         return PolkitQt1::Identity();
0260     return PolkitQt1::Identity::fromString(id);
0261 }
0262 
0263 void AuthDialog::checkSelectedUser()
0264 {
0265     PolkitQt1::Identity identity = adminUserSelected();
0266     // itemData is Null when "Select user" is selected
0267     if (!identity.isValid()) {
0268         lePassword->setEnabled(false);
0269         lblPassword->setEnabled(false);
0270         buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
0271     } else {
0272         lePassword->setEnabled(true);
0273         lblPassword->setEnabled(true);
0274         buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
0275         // We need this to restart the auth with the new user
0276         Q_EMIT adminUserSelected(identity);
0277         // git password label focus
0278         lePassword->setFocus();
0279     }
0280 }
0281 
0282 QString AuthDialog::password() const
0283 {
0284     return lePassword->password();
0285 }
0286 
0287 void AuthDialog::showError(const QString &message)
0288 {
0289     messageWidget->setMessageType(KMessageWidget::Error);
0290     messageWidget->setText(message);
0291     messageWidget->animatedShow();
0292 }
0293 
0294 void AuthDialog::showInfo(const QString &message)
0295 {
0296     messageWidget->setMessageType(KMessageWidget::Information);
0297     messageWidget->setText(message);
0298     messageWidget->animatedShow();
0299 }
0300 
0301 void AuthDialog::authenticationFailure()
0302 {
0303     showError(i18n("Authentication failure, please try again."));
0304 
0305     QFont bold = font();
0306     bold.setBold(true);
0307     lblPassword->setFont(bold);
0308     lePassword->setEnabled(true);
0309     lePassword->clear();
0310     lePassword->setFocus();
0311 }
0312 
0313 AuthDetails::AuthDetails(const PolkitQt1::Details &details, const PolkitQt1::ActionDescription &actionDescription, QWidget *parent)
0314     : QWidget(parent)
0315 {
0316     setupUi(this);
0317 
0318     const auto keys = details.keys();
0319     for (const QString &key : keys) {
0320         int row = gridLayout->rowCount() + 1;
0321 
0322         QLabel *keyLabel = new QLabel(this);
0323         keyLabel->setText(
0324             i18nc("%1 is the name of a detail about the current action "
0325                   "provided by polkit",
0326                   "%1:",
0327                   key));
0328         gridLayout->addWidget(keyLabel, row, 0);
0329 
0330         keyLabel->setAlignment(Qt::AlignRight);
0331         QFont lblFont(keyLabel->font());
0332         lblFont.setBold(true);
0333         keyLabel->setFont(lblFont);
0334 
0335         QLabel *valueLabel = new QLabel(this);
0336         valueLabel->setText(details.lookup(key));
0337         gridLayout->addWidget(valueLabel, row, 1);
0338     }
0339 
0340     if (actionDescription.description().isEmpty()) {
0341         QFont descrFont(action_label->font());
0342         descrFont.setItalic(true);
0343         action_label->setFont(descrFont);
0344         action_label->setText(i18n("'Description' not provided"));
0345     } else {
0346         action_label->setText(actionDescription.description());
0347     }
0348 
0349     action_id_label->setText(actionDescription.actionId());
0350 
0351     QString vendor = actionDescription.vendorName();
0352     QString vendorUrl = actionDescription.vendorUrl();
0353 
0354     if (!vendor.isEmpty()) {
0355         vendorUL->setText(vendor);
0356         vendorUL->setTipText(i18n("Click to open %1", vendorUrl));
0357         vendorUL->setUrl(vendorUrl);
0358     } else if (!vendorUrl.isEmpty()) {
0359         vendorUL->setText(vendorUrl);
0360         vendorUL->setTipText(i18n("Click to open %1", vendorUrl));
0361         vendorUL->setUrl(vendorUrl);
0362     } else {
0363         vendorL->hide();
0364         vendorUL->hide();
0365     }
0366 
0367     connect(vendorUL, SIGNAL(leftClickedUrl(QString)), SLOT(openUrl(QString)));
0368 }
0369 
0370 void AuthDetails::openUrl(const QString &url)
0371 {
0372     QDesktopServices::openUrl(QUrl(url));
0373 }