1/*
2* Copyright (C) 2008 Nicola Gigante <nicola.gigante@gmail.com>
3* Copyright (C) 2009 Dario Freddi <drf@kde.org>
4*
5* This program is free software; you can redistribute it and/or modify
6* it under the terms of the GNU Lesser General Public License as published by
7* the Free Software Foundation; either version 2.1 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU Lesser General Public License
16* along with this program; if not, write to the
17* Free Software Foundation, Inc.,
18* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .
19*/
20
21#ifndef ACTION_H
22#define ACTION_H
23
24#include <QtCore/QString>
25#include <QtCore/QVariant>
26#include <QtCore/QHash>
27
28#include <kdecore_export.h>
29
30#include "kauthactionreply.h"
31
32namespace KAuth
33{
34
35class ActionWatcher;
36
37/**
38 * @brief Class to access, authorize and execute actions.
39 *
40 * This is the main class of the kauth API. It provides the interface to
41 * manipulate actions. Every action is identified by its name. Every instance
42 * of the Action class with the same name refers to the same action.
43 *
44 * Once you have an action object you can tell the helper to execute it
45 * (asking the user to authenticate if needed) with one of the execute*() methods.
46 * The simplest thing to do is to execute a single action synchronously
47 * blocking for the reply, using the execute() method.
48 *
49 * For asynchronous calls, use the executeAsync() method. It sends the request
50 * to the helper and returns immediately. You can optionally provide an object
51 * and a slot. This will be connected to the actionPerformed() signal of the
52 * action's ActionWatcher object.
53 * By calling the watcher() method, you obtain an object that emits some useful
54 * signals that you can receive while the action is in progress. Those signals
55 * are emitted also with the synchronous calls.
56 * To execute a bunch of actions with a single call, you can use the executeActions()
57 * static method. This is not the same as calling executeAsync() for each action,
58 * because the actions are execute with a single request to the helper.
59 * To use any of the execute*() methods you have to set the default helper's ID using
60 * the setHelperID() static method. Alternatively, you can specify the helperID using
61 * the overloaded version of the methods that takes it as a parameter.
62 *
63 * Each action object contains a QVariantMap object that is passed directly to the
64 * helper when the action is executed. You can access this map using the arguments()
65 * method. You can insert into it any kind of custom data you need to pass to the helper.
66 *
67 * @since 4.4
68 */
69class KDECORE_EXPORT Action
70{
71 class Private;
72 Private * const d;
73
74public:
75 /**
76 * The three values returned by authorization methods
77 */
78 enum AuthStatus {
79 Denied, ///< The authorization has been denied by the authorization backend
80 Error, ///< An error occurred
81 Invalid, ///< An invalid action cannot be authorized
82 Authorized, ///< The authorization has been granted by the authorization backend
83 AuthRequired, ///< The user could obtain the authorization after authentication
84 UserCancelled ///< The user pressed Cancel the authentication dialog. Currently used only on the mac
85 };
86
87 /**
88 * @brief Default constructor
89 *
90 * This constructor sets the name to the empty string.
91 * Such an action is invalid and cannot be authorized nor executed, so
92 * you need to call setName() before you can use the object.
93 */
94 Action();
95
96 /** Copy constructor */
97 Action(const Action &action);
98
99 /**
100 * This creates a new action object with this name
101 * @param name The name of the new action
102 */
103 Action(const QString &name);
104
105 /**
106 * This creates a new action object with this name and details
107 * @param name The name of the new action
108 * @param details The details of the action
109 *
110 * @see setDetails
111 */
112 Action(const QString &name, const QString &details);
113
114 /// Virtual destructor
115 ~Action();
116
117 /// Assignment operator
118 Action &operator=(const Action &action);
119
120 /**
121 * @brief Comparison operator
122 *
123 * This comparison operator compares the <b>names</b> of two
124 * actions and returns whether they are the same. It does not
125 * care about the arguments stored in the actions. However,
126 * if two actions are invalid they'll match as equal, even
127 * if the invalid names are different.
128 *
129 * @returns true if the two actions are the same or both invalid
130 */
131 bool operator==(const Action &action) const;
132
133 /**
134 * @brief Negated comparison operator
135 *
136 * Returns the negation of operator==
137 *
138 * @returns true if the two actions are different and not both invalid
139 */
140 bool operator!=(const Action &action) const;
141
142 /**
143 * @brief Gets the action's name.
144 *
145 * This is the unique attribute that identifies
146 * an action object. Two action objects with the same
147 * name always refer to the same action.
148 *
149 * @return The action name
150 */
151 QString name() const;
152
153 /**
154 * @brief Sets the action's name.
155 *
156 * It's not common to change the action name
157 * after its creation. Usually you set the name
158 * with the constructor (and you have to, because
159 * there's no default constructor)
160 */
161 void setName(const QString &name);
162
163 /**
164 * @brief Sets the action's details
165 *
166 * You can use this function to provide the user more details
167 * (if the backend supports it) on the action being authorized in
168 * the authorization dialog
169 */
170 void setDetails(const QString &details);
171
172 /**
173 * @brief Gets the action's details
174 *
175 * The details that will be shown in the authorization dialog, if the
176 * backend supports it.
177 *
178 * @return The action's details
179 */
180 QString details() const;
181
182 /**
183 * @brief Returns if the object represents a valid action
184 *
185 * Action names have to respect a simple syntax.
186 * They have to be all in lowercase characters, separated
187 * by dots. Dots can't appear at the beginning and at the end of
188 * the name.
189 *
190 * In other words, the action name has to match this perl-like
191 * regular expression:
192 * @verbatim
193 * /^[a-z]+(\.[a-z]+)*$/
194 * @endverbatim
195 *
196 * This method returns false if the action name doesn't match the
197 * valid syntax.
198 *
199 * If the backend supports it, this method also checks if the action is
200 * valid and recognized by the backend itself.
201 *
202 * Invalid actions cannot be authorized nor executed.
203 * The empty string is not a valid action name, so the default
204 * constructor returns an invalid action.
205 */
206 bool isValid() const;
207
208 /**
209 * @brief Gets the default helper ID used for actions execution
210 *
211 * The helper ID is the string that uniquely identifies the helper in
212 * the system. It is the string passed to the KDE4_AUTH_HELPER() macro
213 * in the helper source. Because one could have different helpers,
214 * you need to specify an helper ID for each execution, or set a default
215 * ID by calling setHelperID(). This method returns the current default
216 * value.
217 *
218 * @return The default helper ID.
219 */
220 QString helperID() const;
221
222 /**
223 * @brief Sets the default helper ID used for actions execution
224 *
225 * This method sets the helper ID which contains the body of this action.
226 * If the string is non-empty, the corresponding helper will be fired and
227 * the action executed inside the helper. Otherwise, the action will be just
228 * authorized.
229 *
230 * @note To unset a previously set helper, just pass an empty string
231 *
232 * @param id The default helper ID.
233 *
234 * @see hasHelper
235 * @see helperID
236 */
237 void setHelperID(const QString &id);
238
239 /**
240 * @brief Checks if the action has an helper
241 *
242 * This function can be used to check if an helper will be called upon the
243 * execution of an action. Such an helper can be set through setHelperID. If
244 * this function returns false, upon execution the action will be just authorized.
245 *
246 * @since 4.5
247 *
248 * @return Whether the action has an helper or not
249 *
250 * @see setHelperID
251 */
252 bool hasHelper() const;
253
254 /**
255 * @brief Gets the ActionWatcher object for this action
256 *
257 * ActionWatcher objects are used to get notifications about the action
258 * execution status. Every action watcher is tied to an action and
259 * every action has a watcher. This means that if you call this method
260 * on two different Action objects with the same name, you'll get the
261 * same watcher object.
262 *
263 * @return The action watcher for this action
264 */
265 ActionWatcher *watcher();
266
267 /**
268 * @brief Sets the map object used to pass arguments to the helper.
269 *
270 * This method sets the variant map that the application
271 * can use to pass arbitrary data to the helper when executing the action.
272 *
273 * @param arguments The new arguments map
274 */
275 void setArguments(const QVariantMap &arguments);
276
277 /**
278 * @brief Returns map object used to pass arguments to the helper.
279 *
280 * This method returns the variant map that the application
281 * can use to pass arbitrary data to the helper when executing the action.
282 *
283 * @return The arguments map that will be passed to the helper.
284 */
285 QVariantMap arguments() const;
286
287 /**
288 * @brief Convenience method to add an argument.
289 *
290 * This method adds the pair @c key/value to the QVariantMap used to
291 * send custom data to the helper.
292 *
293 * Use this method if you don't want to create a new QVariantMap only to
294 * add a new entry.
295 *
296 * @param key The new entry's key
297 * @param value The value of the new entry
298 */
299 void addArgument(const QString &key, const QVariant &value);
300
301 /**
302 * @brief Acquires authorization for an action without excuting it.
303 *
304 * @note Please use this method if you really know what you are doing. If you are
305 * implementing a GUI, you probably should look into earlyAuthorize instead.
306 *
307 * @note Please remember that calling this method is not required for a successful action
308 * execution: it is safe and advised to call execute() only, without a previous call
309 * to authorize or earlyAuthorize.
310 *
311 * This method acquires the authorization rights for the action, asking
312 * the user to authenticate if needed. It tries very hard to resolve a possible
313 * challenge (AuthRequired); for this reason, it is meant only for advanced usages.
314 * If you are unsure, always use earlyAuthorize or execute the action directly.
315 *
316 * @return The result of the authorization process
317 *
318 * @see earlyAuthorize
319 */
320 AuthStatus authorize() const;
321
322 /**
323 * @brief Tries to resolve authorization status in the best possible way without executing the action
324 *
325 * This method checks for the status of the action, and tries to acquire authorization
326 * (if needed) if the backend being used supports client-side authorization.
327 *
328 * This means this method is not reliable - its purpose is to provide user interfaces with
329 * an efficient means to acquire authorization as early as possible, without interrupting
330 * the user's workflow. If the backend's authentication phase happens in the helper and the
331 * action requires authentication, \c Authorized will be returned.
332 *
333 * The main difference with authorize is that this method does not try to acquire authorization
334 * if the backend's authentication phase happens in the helper: using authorize in such a case
335 * might lead to ask the user its password twice, as the helper might time out, or in the case
336 * of a one shot authorization, the scope of the authorization would end with the authorization
337 * check itself. For this reason, you should @b always use this method instead of authorize, which
338 * is meant only for very advanced usages.
339 *
340 * This method is always safe to be called and used before an execution, even if not needed.
341 *
342 * @since 4.5
343 *
344 * @return The result of the early authorization process, with the caveats described above.
345 */
346 AuthStatus earlyAuthorize() const;
347
348 /**
349 * @brief Gets information about the authorization status of an action
350 *
351 * This methods query the authorization backend to know if the user can try
352 * to acquire the authorization for this action. If the result is Action::AuthRequired,
353 * the user can try to acquire the authorization by authenticating.
354 *
355 * It should not be needed to call this method directly, because the execution methods
356 * already take care of all the authorization stuff.
357 *
358 * @return @c Action::Denied if the user doesn't have the authorization to execute the action,
359 * @c Action::Authorized if the action can be executed,
360 * @c Action::AuthRequired if the user could acquire the authorization after authentication,
361 * @c Action::UserCancelled if the user cancels the authentication dialog. Not currently supported by the Polkit backend
362 */
363 AuthStatus status() const;
364
365 /**
366 * @brief Synchronously executes the action
367 *
368 * This is the simpler of all the action execution methods. It sends an execution request to the
369 * caller, and returns the reply directly to the caller. The ActionReply object will contain the
370 * custom data coming from the helper.
371 *
372 * The method blocks the execution, and will
373 * return only when the action has been completed (or failed). Take note, however, that with the D-Bus
374 * helper proxy (currently the only one implemented on all the supported platforms), the request is
375 * sent using the QDBus::BlockWithGui flag.
376 *
377 * This means the method will enter a local eventloop to wait
378 * for the reply. This allows the application GUI to stay responsive, but you have to be prepared to
379 * receive other events in the meantime.
380 *
381 * All the signals from the ActionWatcher class are emitted also with this method (although they're more
382 * useful with the asynchronous calls)
383 *
384 * The method checks for authorization before to execute the action. If the user is not authorized, the
385 * return value will be ActionReply::AuthorizationDeniedReply.
386 * If the user cancels the authentication, the return value should be ActionReply::UserCancelledReply.
387 * Due to policykit limitations, this currently only with the Mac OS X backend.
388 *
389 * If the helper is busy executing another action (or action group) the reply will be ActionReply::HelperBusyReply
390 *
391 * If the request cannot be sent for bus errors, the method returns ActionReply::DBusErrorReply.
392 *
393 * @return The reply from the helper, or an error reply if something's wrong.
394 */
395 ActionReply execute() const;
396
397 /**
398 * @brief Synchronously executes the action with a specific helperID
399 *
400 * This method does the exact same thing as execute(), but it takes a specific helperID, useful
401 * if you don't want to use the default one without changing it with setHelperID()
402 *
403 * @param helperID The helper ID to use for the execution of this action
404 * @return The reply from the helper, or an error if something's wrong.
405 */
406 ActionReply execute(const QString &helperID) const;
407
408 void setExecutesAsync(bool async);
409 bool executesAsync() const;
410
411 /**
412 * @brief Asynchronously executes a group of actions with a single request
413 *
414 * This method executes each action in the list. It checks for authorization of each action, and put the
415 * denied actions, if any, in the list pointed by the deniedActions parameter, if not NULL.
416 *
417 * Please note that with the D-Bus helper proxy (currently the only one implemented), the execution of a group
418 * of actions is very different from executing in sequence each action using, for example, executeAsync().
419 * Currently, the helper can execute only one request at the time. For this reason, if you have to call
420 * different actions in sequence, you can't call executeAsync() like this:
421 * @code
422 * action1.executeAsync();
423 * action2.executeAsync();
424 * @endcode
425 * because the second call will almost certainly return ActionReply::HelperBusy. You would have to execute the second
426 * action in the slot connected to the first action's actionPerformed() signal. This is not so good. This method
427 * allows the application to send a request with a list of actions. With this method, the code above becomes:
428 * @code
429 * QList<Action> list;
430 * list << action1 << action2;
431 * Action::executeActions(list);
432 * @endcode
433 * The return value will be false if communication errors occur. It will also be false if <b>all</b> the actions
434 * in the list are denied.
435 *
436 * @param actions The list of actions to execute
437 * @param deniedActions A pointer to a list to fill with the denied actions. Pass NULL if you don't need them.
438 * @param helperId The helper ID to execute the actions on.
439 */
440 static bool executeActions(const QList<Action> &actions, QList<Action> *deniedActions, const QString &helperId);
441
442 /**
443 * Convenience overload. This overload lets you specify, in addition, a QWidget which will be used as the
444 * authentication dialog's parent.
445 *
446 * @since 4.6
447 *
448 * @see executeActions
449 * @see setParentWidget
450 */
451 static bool executeActions(const QList<Action> &actions, QList<Action> *deniedActions, const QString &helperId,
452 QWidget *parent);
453
454 /**
455 * @brief Ask the helper to stop executing an action
456 *
457 * This method sends a request to the helper asking to stop the execution of an action. It is only
458 * useful for long-running actions, because short and fast actions won't obbey to this request most of the times.
459 * Calling this method will make the HelperSupport::isStopped() method to return true the next time it's called.
460 *
461 * It's the helper's responsibility to regularly call it and exit if requested
462 * The actionPerformed() signal is emitted normally because, actually, the helper exists regularly. The return data
463 * in this case is application-dependent.
464 */
465 void stop();
466
467 /**
468 * @brief Ask the helper to stop executing an action, using a specific helper ID
469 *
470 * This method works exactly as the stop() method, but it lets you specify an helper ID different from the
471 * default one.
472 *
473 * To stop an action you need to send the stop request to the helper that is executing that action. This of course means you have to
474 * use the same helperID used for the execution call (either passed as a parameter or set as default with setHelperID() )
475 */
476 void stop(const QString &helperID);
477
478 /**
479 * @brief Sets a parent widget for the authentication dialog
480 *
481 * This function is used for explicitly setting a parent window for an eventual authentication dialog required when
482 * authorization is triggered. Some backends, in fact, (like polkit-1) need to have a parent explicitly set for displaying
483 * the dialog correctly.
484 *
485 * @note If you are using KAuth through one of KDE's GUI components (KPushButton, KCModule...) you do not need and should not
486 * call this function, as it is already done by the component itself.
487 *
488 * @since 4.6
489 *
490 * @param parent A QWidget which will be used as the dialog's parent
491 */
492 void setParentWidget(QWidget *parent);
493
494 /**
495 * @brief Returns the parent widget for the authentication dialog for this action
496 *
497 * @since 4.6
498 *
499 * @returns A QWidget which will is being used as the dialog's parent
500 */
501 QWidget *parentWidget() const;
502};
503
504} // namespace Auth
505
506#endif
507