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 }