Changeset View
Changeset View
Standalone View
Standalone View
ksmserver/policykit-agent/AuthDialog.cpp
- This file was added.
1 | /* This file is part of the KDE project | ||||
---|---|---|---|---|---|
2 | Copyright (C) 2007-2008 Gökçen Eraslan <gokcen@pardus.org.tr> | ||||
3 | Copyright (C) 2008 Dirk Mueller <mueller@kde.org> | ||||
4 | Copyright (C) 2008 Daniel Nicoletti <dantti85-pk@yahoo.com.br> | ||||
5 | Copyright (C) 2008-2010 Dario Freddi <drf@kde.org> | ||||
6 | | ||||
7 | This program is free software; you can redistribute it and/or | ||||
8 | modify it under the terms of the GNU General Public | ||||
9 | License as published by the Free Software Foundation; either | ||||
10 | version 2 of the License, or (at your option) any later version. | ||||
11 | | ||||
12 | This library is distributed in the hope that it will be useful, | ||||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
15 | Library General Public License for more details. | ||||
16 | | ||||
17 | You should have received a copy of the GNU Library General Public License | ||||
18 | along with this library; see the file COPYING.LIB. If not, write to | ||||
19 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
20 | Boston, MA 02110-1301, USA. | ||||
21 | | ||||
22 | */ | ||||
23 | | ||||
24 | #include "AuthDialog.h" | ||||
25 | | ||||
26 | #include <QProcess> | ||||
27 | #include <QPainter> | ||||
28 | #include <QStandardItemModel> | ||||
29 | #include <QDebug> | ||||
30 | #include <QDesktopServices> | ||||
31 | #include <QDialogButtonBox> | ||||
32 | #include <QPushButton> | ||||
33 | #include <QVBoxLayout> | ||||
34 | #include <QUrl> | ||||
35 | | ||||
36 | #include <KWindowSystem> | ||||
37 | #include <KIconLoader> | ||||
38 | #include <KUser> | ||||
39 | | ||||
40 | #include <PolkitQt1/Authority> | ||||
41 | #include <PolkitQt1/Details> | ||||
42 | | ||||
43 | AuthDialog::AuthDialog(const QString &actionId, | ||||
44 | const QString &message, | ||||
45 | const QString &iconName, | ||||
46 | const PolkitQt1::Details &details, | ||||
47 | const PolkitQt1::Identity::List &identities, | ||||
48 | WId parent) | ||||
49 | : QDialog(0) | ||||
50 | { | ||||
51 | // KAuth is able to circumvent polkit's limitations, and manages to send the wId to the auth agent. | ||||
52 | // If we received it, we use KWindowSystem to associate this dialog correctly. | ||||
53 | if (parent > 0) { | ||||
54 | qDebug() << "Associating the dialog with " << parent << " this dialog is " << winId(); | ||||
55 | | ||||
56 | // Set the parent | ||||
57 | KWindowSystem::setMainWindow(this, parent); | ||||
58 | | ||||
59 | // Set modal | ||||
60 | setWindowModality(Qt::ApplicationModal); | ||||
61 | | ||||
62 | // raise on top | ||||
63 | activateWindow(); | ||||
64 | raise(); | ||||
65 | } | ||||
66 | | ||||
67 | setupUi(this); | ||||
68 | | ||||
69 | connect(buttonBox, &QDialogButtonBox::accepted, this, &AuthDialog::okClicked); | ||||
70 | connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); | ||||
71 | connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); | ||||
72 | | ||||
73 | QString detailsButtonText = i18n("Details"); | ||||
74 | QPushButton* detailsButton = new QPushButton(detailsButtonText + " >>"); | ||||
75 | detailsButton->setIcon(QIcon::fromTheme("help-about")); | ||||
76 | detailsButton->setCheckable(true); | ||||
77 | connect(detailsButton, &QAbstractButton::toggled, this, [=](bool toggled) { | ||||
78 | detailsWidgetContainer->setVisible(toggled); | ||||
79 | if (toggled) { | ||||
80 | detailsButton->setText(detailsButtonText + " <<"); | ||||
81 | } else { | ||||
82 | detailsButton->setText(detailsButtonText + " >>"); | ||||
83 | } | ||||
84 | adjustSize(); | ||||
85 | }); | ||||
86 | buttonBox->addButton(detailsButton, QDialogButtonBox::HelpRole); | ||||
87 | detailsWidgetContainer->hide(); | ||||
88 | | ||||
89 | setWindowTitle(i18n("Authentication Required")); | ||||
90 | | ||||
91 | if (message.isEmpty()) { | ||||
92 | qWarning() << "Could not get action message for action."; | ||||
93 | lblHeader->hide(); | ||||
94 | } else { | ||||
95 | qDebug() << "Message of action: " << message; | ||||
96 | lblHeader->setText("<h3>" + message + "</h3>"); | ||||
97 | m_message = message; | ||||
98 | } | ||||
99 | | ||||
100 | // loads the standard key icon | ||||
101 | QPixmap icon = KIconLoader::global()->loadIcon("dialog-password", | ||||
102 | KIconLoader::NoGroup, | ||||
103 | KIconLoader::SizeHuge, | ||||
104 | KIconLoader::DefaultState); | ||||
105 | // create a painter to paint the action icon over the key icon | ||||
106 | QPainter painter(&icon); | ||||
107 | const int iconSize = icon.size().width(); | ||||
108 | // the emblem icon to size 32 | ||||
109 | int overlaySize = 32; | ||||
110 | // try to load the action icon | ||||
111 | const QPixmap pixmap = KIconLoader::global()->loadIcon(iconName, | ||||
112 | KIconLoader::NoGroup, | ||||
113 | overlaySize, | ||||
114 | KIconLoader::DefaultState, | ||||
115 | QStringList(), | ||||
116 | 0, | ||||
117 | true); | ||||
118 | // if we're able to load the action icon paint it over the | ||||
119 | // key icon. | ||||
120 | if (!pixmap.isNull()) { | ||||
121 | QPoint startPoint; | ||||
122 | // bottom right corner | ||||
123 | startPoint = QPoint(iconSize - overlaySize - 2, | ||||
124 | iconSize - overlaySize - 2); | ||||
125 | painter.drawPixmap(startPoint, pixmap); | ||||
126 | } | ||||
127 | | ||||
128 | setWindowIcon(icon); | ||||
129 | lblPixmap->setPixmap(icon); | ||||
130 | | ||||
131 | // find action description for actionId | ||||
132 | foreach(const PolkitQt1::ActionDescription &desc, PolkitQt1::Authority::instance()->enumerateActionsSync()) { | ||||
133 | if (actionId == desc.actionId()) { | ||||
134 | m_actionDescription = desc; | ||||
135 | qDebug() << "Action description has been found" ; | ||||
136 | break; | ||||
137 | } | ||||
138 | } | ||||
139 | | ||||
140 | AuthDetails *detailsDialog = new AuthDetails(details, m_actionDescription, m_appname, this); | ||||
141 | detailsWidgetContainer->layout()->addWidget(detailsDialog); | ||||
142 | | ||||
143 | userCB->hide(); | ||||
144 | lePassword->setFocus(); | ||||
145 | | ||||
146 | errorMessageWidget->setMessageType(KMessageWidget::Error); | ||||
147 | errorMessageWidget->hide(); | ||||
148 | | ||||
149 | // If there is more than 1 identity we will show the combobox for user selection | ||||
150 | if (identities.size() > 1) { | ||||
151 | connect(userCB, SIGNAL(currentIndexChanged(int)), | ||||
152 | this, SLOT(on_userCB_currentIndexChanged(int))); | ||||
153 | | ||||
154 | createUserCB(identities); | ||||
155 | } else { | ||||
156 | userCB->addItem("", identities[0].toString()); | ||||
157 | userCB->setCurrentIndex(0); | ||||
158 | } | ||||
159 | } | ||||
160 | | ||||
161 | AuthDialog::~AuthDialog() | ||||
162 | { | ||||
163 | } | ||||
164 | | ||||
165 | void AuthDialog::accept() | ||||
166 | { | ||||
167 | // Do nothing, do not close the dialog. This is needed so that the dialog stays | ||||
168 | lePassword->setEnabled(false); | ||||
169 | return; | ||||
170 | } | ||||
171 | | ||||
172 | void AuthDialog::setRequest(const QString &request, bool requiresAdmin) | ||||
173 | { | ||||
174 | qDebug() << request; | ||||
175 | PolkitQt1::Identity identity = adminUserSelected(); | ||||
176 | if (request.startsWith(QLatin1String("password:"), Qt::CaseInsensitive)) { | ||||
177 | if (requiresAdmin) { | ||||
178 | if (!identity.isValid()) { | ||||
179 | lblPassword->setText(i18n("Password for root:")); | ||||
180 | } else { | ||||
181 | lblPassword->setText(i18n("Password for %1:", | ||||
182 | identity.toString().remove("unix-user:"))); | ||||
183 | } | ||||
184 | } else { | ||||
185 | lblPassword->setText(i18n("Password:")); | ||||
186 | } | ||||
187 | } else if (request.startsWith(QLatin1String("password or swipe finger:"), | ||||
188 | Qt::CaseInsensitive)) { | ||||
189 | if (requiresAdmin) { | ||||
190 | if (!identity.isValid()) { | ||||
191 | lblPassword->setText(i18n("Password or swipe finger for root:")); | ||||
192 | } else { | ||||
193 | lblPassword->setText(i18n("Password or swipe finger for %1:", | ||||
194 | identity.toString().remove("unix-user:"))); | ||||
195 | } | ||||
196 | } else { | ||||
197 | lblPassword->setText(i18n("Password or swipe finger:")); | ||||
198 | } | ||||
199 | } else { | ||||
200 | lblPassword->setText(request); | ||||
201 | } | ||||
202 | | ||||
203 | } | ||||
204 | | ||||
205 | void AuthDialog::setOptions() | ||||
206 | { | ||||
207 | lblContent->setText(i18n("An application is attempting to perform an action that requires privileges." | ||||
208 | " Authentication is required to perform this action.")); | ||||
209 | } | ||||
210 | | ||||
211 | void AuthDialog::createUserCB(const PolkitQt1::Identity::List &identities) | ||||
212 | { | ||||
213 | /* if we've already built the list of admin users once, then avoid | ||||
214 | * doing it again.. (this is mainly used when the user entered the | ||||
215 | * wrong password and the dialog is recycled) | ||||
216 | */ | ||||
217 | if (identities.count() && (userCB->count() - 1) != identities.count()) { | ||||
218 | // Clears the combobox in the case some user be added | ||||
219 | userCB->clear(); | ||||
220 | | ||||
221 | // Adds a Dummy user | ||||
222 | userCB->addItem(i18n("Select User"), QString()); | ||||
223 | qobject_cast<QStandardItemModel *>(userCB->model())->item(userCB->count()-1)->setEnabled(false); | ||||
224 | | ||||
225 | // For each user | ||||
226 | int index = 1; // Start at 1 because of the "Select User" entry | ||||
227 | int currentUserIndex = -1; | ||||
228 | const KUser currentUser; | ||||
229 | foreach(const PolkitQt1::Identity &identity, identities) { | ||||
230 | // First check to see if the user is valid | ||||
231 | qDebug() << "User: " << identity.toString(); | ||||
232 | const KUser user(identity.toString().remove("unix-user:")); | ||||
233 | if (!user.isValid()) { | ||||
234 | qWarning() << "User invalid: " << user.loginName(); | ||||
235 | continue; | ||||
236 | } | ||||
237 | | ||||
238 | // Display user Full Name IF available | ||||
239 | QString display; | ||||
240 | if (!user.property(KUser::FullName).toString().isEmpty()) { | ||||
241 | display = i18nc("%1 is the full user name, %2 is the user login name", "%1 (%2)", user.property(KUser::FullName).toString(), user.loginName()); | ||||
242 | } else { | ||||
243 | display = user.loginName(); | ||||
244 | } | ||||
245 | | ||||
246 | QIcon icon; | ||||
247 | // load user icon face | ||||
248 | if (!user.faceIconPath().isEmpty()) { | ||||
249 | icon = QIcon(user.faceIconPath()); | ||||
250 | } else { | ||||
251 | icon = QIcon::fromTheme("user-identity"); | ||||
252 | } | ||||
253 | // appends the user item | ||||
254 | userCB->addItem(icon, display, identity.toString()); | ||||
255 | | ||||
256 | if (user == currentUser) { | ||||
257 | currentUserIndex = index; | ||||
258 | } | ||||
259 | ++index; | ||||
260 | } | ||||
261 | | ||||
262 | // Show the widget and set focus | ||||
263 | if (currentUserIndex != -1) { | ||||
264 | userCB->setCurrentIndex(currentUserIndex); | ||||
265 | } | ||||
266 | userCB->show(); | ||||
267 | } | ||||
268 | } | ||||
269 | | ||||
270 | PolkitQt1::Identity AuthDialog::adminUserSelected() const | ||||
271 | { | ||||
272 | if (userCB->currentIndex() == -1) | ||||
273 | return PolkitQt1::Identity(); | ||||
274 | | ||||
275 | const QString id = userCB->currentData().toString(); | ||||
276 | if (id.isEmpty()) | ||||
277 | return PolkitQt1::Identity(); | ||||
278 | return PolkitQt1::Identity::fromString(id); | ||||
279 | } | ||||
280 | | ||||
281 | void AuthDialog::on_userCB_currentIndexChanged(int /*index*/) | ||||
282 | { | ||||
283 | PolkitQt1::Identity identity = adminUserSelected(); | ||||
284 | // itemData is Null when "Select user" is selected | ||||
285 | if (!identity.isValid()) { | ||||
286 | lePassword->setEnabled(false); | ||||
287 | lblPassword->setEnabled(false); | ||||
288 | buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); | ||||
289 | } else { | ||||
290 | lePassword->setEnabled(true); | ||||
291 | lblPassword->setEnabled(true); | ||||
292 | buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); | ||||
293 | // We need this to restart the auth with the new user | ||||
294 | emit adminUserSelected(identity); | ||||
295 | // git password label focus | ||||
296 | lePassword->setFocus(); | ||||
297 | } | ||||
298 | } | ||||
299 | | ||||
300 | QString AuthDialog::password() const | ||||
301 | { | ||||
302 | return lePassword->text(); | ||||
303 | } | ||||
304 | | ||||
305 | void AuthDialog::authenticationFailure() | ||||
306 | { | ||||
307 | errorMessageWidget->setText(i18n("Authentication failure, please try again.")); | ||||
308 | errorMessageWidget->animatedShow(); | ||||
309 | | ||||
310 | QFont bold = font(); | ||||
311 | bold.setBold(true); | ||||
312 | lblPassword->setFont(bold); | ||||
313 | lePassword->setEnabled(true); | ||||
314 | lePassword->clear(); | ||||
315 | lePassword->setFocus(); | ||||
316 | } | ||||
317 | | ||||
318 | AuthDetails::AuthDetails(const PolkitQt1::Details &details, | ||||
319 | const PolkitQt1::ActionDescription &actionDescription, | ||||
320 | const QString &appname, | ||||
321 | QWidget *parent) | ||||
322 | : QWidget(parent) | ||||
323 | { | ||||
324 | setupUi(this); | ||||
325 | | ||||
326 | app_label->setText(appname); | ||||
327 | | ||||
328 | foreach(const QString &key, details.keys()) { //krazy:exclude=foreach (Details is not a map/hash, but rather a method) | ||||
329 | int row = gridLayout->rowCount() + 1; | ||||
330 | | ||||
331 | QLabel *keyLabel = new QLabel(this); | ||||
332 | keyLabel->setText(i18nc("%1 is the name of a detail about the current action " | ||||
333 | "provided by polkit", "%1:", key)); | ||||
334 | gridLayout->addWidget(keyLabel, row, 0); | ||||
335 | | ||||
336 | QLabel *valueLabel = new QLabel(this); | ||||
337 | valueLabel->setText(details.lookup(key)); | ||||
338 | gridLayout->addWidget(valueLabel, row, 1); | ||||
339 | } | ||||
340 | | ||||
341 | action_label->setText(actionDescription.description()); | ||||
342 | | ||||
343 | action_label->setTipText(i18n("Click to edit %1", actionDescription.actionId())); | ||||
344 | action_label->setUrl(actionDescription.actionId()); | ||||
345 | | ||||
346 | QString vendor = actionDescription.vendorName(); | ||||
347 | QString vendorUrl = actionDescription.vendorUrl(); | ||||
348 | | ||||
349 | if (!vendor.isEmpty()) { | ||||
350 | vendorUL->setText(vendor); | ||||
351 | vendorUL->setTipText(i18n("Click to open %1", vendorUrl)); | ||||
352 | vendorUL->setUrl(vendorUrl); | ||||
353 | } else if (!vendorUrl.isEmpty()) { | ||||
354 | vendorUL->setText(vendorUrl); | ||||
355 | vendorUL->setTipText(i18n("Click to open %1", vendorUrl)); | ||||
356 | vendorUL->setUrl(vendorUrl); | ||||
357 | } else { | ||||
358 | vendorL->hide(); | ||||
359 | vendorUL->hide(); | ||||
360 | } | ||||
361 | | ||||
362 | connect(vendorUL, SIGNAL(leftClickedUrl(QString)), SLOT(openUrl(QString))); | ||||
363 | connect(action_label, SIGNAL(leftClickedUrl(QString)), SLOT(openAction(QString))); | ||||
364 | } | ||||
365 | | ||||
366 | void AuthDetails::openUrl(const QString& url) | ||||
367 | { | ||||
368 | QDesktopServices::openUrl(QUrl(url)); | ||||
369 | } | ||||
370 | | ||||
371 | void AuthDetails::openAction(const QString &url) | ||||
372 | { | ||||
373 | // FIXME what's this? :) | ||||
374 | QProcess::startDetached("polkit-kde-authorization", QStringList() << url); | ||||
375 | } |