diff --git a/src/kauthaction.h b/src/kauthaction.h index 129db22..4be2490 100644 --- a/src/kauthaction.h +++ b/src/kauthaction.h @@ -1,409 +1,384 @@ /* * Copyright (C) 2009-2012 Dario Freddi * Copyright (C) 2008 Nicola Gigante * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . */ #ifndef ACTION_H #define ACTION_H #include #include #include #include #include namespace KAuth { class ExecuteJob; class ActionData; /** * @brief Class to access, authorize and execute actions. * * This is the main class of the kauth API. It provides the interface to * manipulate actions. Every action is identified by its name. Every instance * of the Action class with the same name refers to the same action. * * Once you have an action object you can tell the helper to execute it * (asking the user to authenticate if needed) with the execute() method. * The simplest thing to do is to execute a single action synchronously * blocking for the reply by callin exec() on the job object returned by * execute(). * * For asynchronous calls, use KAuth::ExecuteJob::start() instead. * It sends the request * to the helper and returns immediately. Before doing so you should however * connect to at least the KJob::result(KJob *) signal to receive a slot call * once the action is done executing. * * To use the execute() method you have to set the default helper's ID using * the setHelperID() static method. Alternatively, you can specify the helperID using * the overloaded version of the methods that takes it as a parameter. * * Each action object contains a QVariantMap object that is passed directly to the * helper when the action is executed. You can access this map using the arguments() * method. You can insert into it any kind of custom data you need to pass to the helper. * * @code * void MyApp::runAction() * { * action = KAuth::Action("org.kde.myapp.action"); * KAuth::ExecuteJob *job = action.execute(); * connect(job, &KAuth::ExecuteJob::result, this, &MyApp::actionResult); * job->start(); * } * * void MyApp::actionResult(KJob *kjob) * { * auto job = qobject_cast(kjob); * qDebug() << job.error() << job.data(); * } * @endcode * * @since 4.4 */ class KAUTH_EXPORT Action { Q_GADGET public: /** - * The three values returned by authorization methods + * The three values set by authorization methods */ enum AuthStatus { DeniedStatus, ///< The authorization has been denied by the authorization backend ErrorStatus, ///< An error occurred InvalidStatus, ///< An invalid action cannot be authorized AuthorizedStatus, ///< The authorization has been granted by the authorization backend AuthRequiredStatus, ///< The user could obtain the authorization after authentication UserCancelledStatus ///< The user pressed Cancel the authentication dialog. Currently used only on the mac }; Q_ENUM(AuthStatus) enum ExecutionMode { ExecuteMode, AuthorizeOnlyMode }; Q_ENUM(ExecutionMode) /** * @brief Default constructor * * This constructor sets the name to the empty string. * Such an action is invalid and cannot be authorized nor executed, so * you need to call setName() before you can use the object. */ Action(); /** Copy constructor */ Action(const Action &action); /** * This creates a new action object with this name * @param name The name of the new action */ Action(const QString &name); /** * This creates a new action object with this name and details * @param name The name of the new action * @param details The details of the action * * @see setDetails */ Action(const QString &name, const QString &details); /// Virtual destructor ~Action(); /// Assignment operator Action &operator=(const Action &action); /** * @brief Comparison operator * * This comparison operator compares the names of two * actions and returns whether they are the same. It does not * care about the arguments stored in the actions. However, * if two actions are invalid they'll match as equal, even * if the invalid names are different. * * @returns true if the two actions are the same or both invalid */ bool operator==(const Action &action) const; /** * @brief Negated comparison operator * * Returns the negation of operator== * * @returns true if the two actions are different and not both invalid */ bool operator!=(const Action &action) const; /** * @brief Gets the action's name. * * This is the unique attribute that identifies * an action object. Two action objects with the same * name always refer to the same action. * * @return The action name */ QString name() const; /** * @brief Sets the action's name. * * It's not common to change the action name * after its creation. Usually you set the name * with the constructor (and you have to, because * there's no default constructor) */ void setName(const QString &name); /** * @brief Gets the action's timeout. * * The timeout of the action in milliseconds * -1 means the default DBus timeout (usually 25 seconds) * * @since 5.29 * * @return The action timeouts */ int timeout() const; /** * @brief Sets the action's timeout. * * The timeout of the action in milliseconds * -1 means the default DBus timeout (usually 25 seconds) * * @since 5.29 * */ void setTimeout(int timeout); /** * @brief Sets the action's details * * You can use this function to provide the user more details * (if the backend supports it) on the action being authorized in * the authorization dialog */ void setDetails(const QString &details); /** * @brief Gets the action's details * * The details that will be shown in the authorization dialog, if the * backend supports it. * * @return The action's details */ QString details() const; /** * @brief Returns if the object represents a valid action * * Action names have to respect a simple syntax. * They have to be all in lowercase characters, separated * by dots. Dots can't appear at the beginning and at the end of * the name. * * In other words, the action name has to match this perl-like * regular expression: * @verbatim * /^[a-z]+(\.[a-z]+)*$/ * @endverbatim * * This method returns false if the action name doesn't match the * valid syntax. * * If the backend supports it, this method also checks if the action is * valid and recognized by the backend itself. * * Invalid actions cannot be authorized nor executed. * The empty string is not a valid action name, so the default * constructor returns an invalid action. */ bool isValid() const; /** * @brief Gets the default helper ID used for actions execution * * The helper ID is the string that uniquely identifies the helper in * the system. It is the string passed to the KAUTH_HELPER() macro * in the helper source. Because one could have different helpers, * you need to specify an helper ID for each execution, or set a default * ID by calling setHelperID(). This method returns the current default * value. * * @return The default helper ID. */ QString helperId() const; /** * @brief Sets the default helper ID used for actions execution * * This method sets the helper ID which contains the body of this action. * If the string is non-empty, the corresponding helper will be fired and * the action executed inside the helper. Otherwise, the action will be just * authorized. * * @note To unset a previously set helper, just pass an empty string * * @param id The default helper ID. * * @see hasHelper * @see helperId */ void setHelperId(const QString &id); /** * @brief Checks if the action has an helper * * This function can be used to check if an helper will be called upon the * execution of an action. Such an helper can be set through setHelperID. If * this function returns false, upon execution the action will be just authorized. * * @since 4.5 * * @return Whether the action has an helper or not * * @see setHelperID */ bool hasHelper() const; /** * @brief Sets the map object used to pass arguments to the helper. * * This method sets the variant map that the application * can use to pass arbitrary data to the helper when executing the action. * * @param arguments The new arguments map */ void setArguments(const QVariantMap &arguments); /** * @brief Returns map object used to pass arguments to the helper. * * This method returns the variant map that the application * can use to pass arbitrary data to the helper when executing the action. * * @return The arguments map that will be passed to the helper. */ QVariantMap arguments() const; /** * @brief Convenience method to add an argument. * * This method adds the pair @c key/value to the QVariantMap used to * send custom data to the helper. * * Use this method if you don't want to create a new QVariantMap only to * add a new entry. * * @param key The new entry's key * @param value The value of the new entry */ void addArgument(const QString &key, const QVariant &value); /** * @brief Gets information about the authorization status of an action * * This methods query the authorization backend to know if the user can try * to acquire the authorization for this action. If the result is Action::AuthRequired, * the user can try to acquire the authorization by authenticating. * * It should not be needed to call this method directly, because the execution methods * already take care of all the authorization stuff. * * @return @c Action::Denied if the user doesn't have the authorization to execute the action, * @c Action::Authorized if the action can be executed, * @c Action::AuthRequired if the user could acquire the authorization after authentication, * @c Action::UserCancelled if the user cancels the authentication dialog. Not currently supported by the Polkit backend */ AuthStatus status() const; /** - * @brief Synchronously executes the action + * @brief Get the job object used to execute the action * - * This is the simpler of all the action execution methods. It sends an execution request to the - * caller, and returns the reply directly to the caller. The ActionReply object will contain the - * custom data coming from the helper. - * - * The method blocks the execution, and will - * return only when the action has been completed (or failed). Take note, however, that with the D-Bus - * helper proxy (currently the only one implemented on all the supported platforms), the request is - * sent using the QDBus::BlockWithGui flag. - * - * This means the method will enter a local eventloop to wait - * for the reply. This allows the application GUI to stay responsive, but you have to be prepared to - * receive other events in the meantime. - * - * All the signals from the ActionWatcher class are emitted also with this method (although they're more - * useful with the asynchronous calls) - * - * The method checks for authorization before to execute the action. If the user is not authorized, the - * return value will be ActionReply::AuthorizationDeniedReply. - * If the user cancels the authentication, the return value should be ActionReply::UserCancelledReply. - * Due to policykit limitations, this currently only with the Mac OS X backend. - * - * If the helper is busy executing another action (or action group) the reply will be ActionReply::HelperBusyReply - * - * If the request cannot be sent for bus errors, the method returns ActionReply::DBusErrorReply. - * - * @return The reply from the helper, or an error reply if something's wrong. + * @return The KJob::ExecuteJob object to be used to run the action. */ ExecuteJob *execute(ExecutionMode mode = ExecuteMode); /** * @brief Sets a parent widget for the authentication dialog * * This function is used for explicitly setting a parent window for an eventual authentication dialog required when * authorization is triggered. Some backends, in fact, (like polkit-1) need to have a parent explicitly set for displaying * the dialog correctly. * * @note If you are using KAuth through one of KDE's GUI components (KPushButton, KCModule...) you do not need and should not * call this function, as it is already done by the component itself. * * @since 4.6 * * @param parent A QWidget which will be used as the dialog's parent */ void setParentWidget(QWidget *parent); /** * @brief Returns the parent widget for the authentication dialog for this action * * @since 4.6 * * @returns A QWidget which will is being used as the dialog's parent */ QWidget *parentWidget() const; private: QSharedDataPointer d; }; } // namespace Auth #endif diff --git a/src/kauthactionreply.h b/src/kauthactionreply.h index 2421221..3ed40ce 100644 --- a/src/kauthactionreply.h +++ b/src/kauthactionreply.h @@ -1,632 +1,602 @@ /* * Copyright (C) 2008 Nicola Gigante * Copyright (C) 2009-2012 Dario Freddi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . */ #ifndef ACTION_REPLY_H #define ACTION_REPLY_H #include #include #include #include #include #include /** @namespace KAuth @section kauth_intro Introduction The KDE Authorization API allows developers to write desktop applications that run high-privileged tasks in an easy, secure and cross-platform way. Previously, if an application had to do administrative tasks, it had to be run as root, using mechanisms such as sudo or graphical equivalents, or by setting the executable's setuid bit. This approach has some drawbacks. For example, the whole application code, including GUI handling and network communication, had to be done as root. More code that runs as root means more possible security holes. The solution is the caller/helper pattern. With this pattern, the privileged code is isolated in a small helper tool that runs as root. This tool includes only the few lines of code that actually need to be run with privileges, not the whole application logic. All the other parts of the application are run as a normal user, and the helper tool is called when needed, using a secure mechanism that ensures that the user is authorized to do so. This pattern is not very easy to implement, because the developer has to deal with a lot of details about how to authorize the user, how to call the helper with the right privileges, how to exchange data with the helper, etc.. This is where the new KDE Authorization API becomes useful. Thanks to this new library, every developer can implement the caller/helper pattern to write application that require high privileges, with a few lines of code in an easy, secure and cross-platform way. Not only: the library can also be used to lock down some actions in your application without using a helper but just checking for authorization and verifying if the user is allowed to perform it. The KDE Authorization library uses different backends depending on the system where it's built. As far as the user authorization is concerned, it currently uses PolicyKit on linux and Authorization Services on Mac OSX, and a Windows backend will eventually be written, too. At the communication layer, the library uses D-Bus on every supported platform. @section kauth_concepts Concepts There are a few concepts to understand when using the library. Much of those are carried from underlying APIs such as PolicyKit, so if you know something about them there shouldn't be problems. An action is a single task that needs to be done by the application. You refer to an action using an action identifier, which is a string in reverse domain name syntax (to avoid duplicates). For example, if the date/time control center module needs to change the date, it would need an action like "org.kde.datatime.change". If your application has to perform more than one privileged task, you should configure more than one action. This allows system administrators to fine tune the policies that allow users to perform your actions. The authorization is the process that is executed to decide if a user can perform an action or not. In order to execute the helper as root, the user has to be authorized. For example, on linux, che policykit backend will look at the policykit policy database to see what requirements the user has to meet in order to execute the action you requested. The policy set for that action could allow or deny that user, or could say the user has to authenticate in order to gain the authorization. The authentication is the process that allows the system to know that the person is in front of the console is who he says to be. If an action can be allowed or not depending on the user's identity, it has to be proved by entering a password or any other identification data the system requires. A typical session with the authorization API is like this: - The user want to perform some privileged task - The application asks the system if the user is authorized. - The system asks the user to authenticate, if needed, and reply the application. - The application uses some system-provided mechanism to execute the helper's code as the root user. Previously, you had to set the setuid bit to do this, but we have something cool called "dbus activation" that doesn't require the setuid bit and is much more flexible. - The helper code, immediately after starting, checks if the caller is authorized to do what it asks. If not the helper immediately exits! - If the caller is authorized, the helper executes the task and exits. - The application receives data back from the helper. All these steps are managed by the library. Following sections will focus on how to write the helper to implement your actions and how to call the helper from the application. @section kauth_helper Writing the helper tool The first thing you need to do before writing anything is to decide what actions you need to implement. Every action needs to be identified by a string in the reverse domain name syntax. This helps to avoid duplicates. An example of action id is "org.kde.datetime.change" or "org.kde.ksysguard.killprocess". Action names can only contain lowercase letters and dots (not as the first or last char). You also need an identifier for your helper. An application using the KDE auth api can implement and use more than one helper, implementing different actions. An helper is uniquely identified in the system context with a string. It, again, is in reverse domain name syntax to avoid duplicates. A common approach is to call the helper like the common prefix of your action names. For example, the Date/Time kcm module could use a helper called "org.kde.datetime", to perform actions like "org.kde.datetime.changedate" and "org.kde.datetime.changetime". This naming convention simplifies the implementation of the helper. From the code point of view, the helper is implemented as a QObject subclass. Every action is implemented by a public slot. In the example/ directory in the source code tree you find a complete example. Let's look at that. The helper.h file declares the class that implements the helper. It looks like: @snippet helper.h helper_declaration The slot names are the last part of the action name, without the helper's ID if it's a prefix, with all the dots replaced by underscores. In this case, the helper ID is "org.kde.kf5auth.example", so those three slots implement the actions "org.kde.kf5auth.example.read", "org.kde.kf5auth.example.write" and "org.kde.kf5auth.example.longaction". The helper ID doesn't have to appear at the beginning of the action name, but it's good practice. If you want to extend MyHelper to implement also a different action like "org.kde.datetime.changetime", since the helper ID doesn't match you'll have to implement a slot called org_kde_datetime_changetime(). The slot's signature is fixed: the return type is ActionReply, a class that allows you to return results, error codes and custom data to the application when your action has finished to run. Please note that due to QMetaObject being picky about namespaces, you NEED to declare the return type as ActionReply and not KAuth::ActionReply. So the using declaration is compulsory The QVariantMap object that comes as argument contains custom data coming from the application. Let's look at the read action implementation. Its purpose is to read files: @snippet helper.cpp helper_read_action First, the code creates a default reply object. The default constructor creates a reply that reports success. Then it gets the filename parameter from the argument QVariantMap, that has previously been set by the application, before calling the helper. If it fails to open the file, it creates an ActionReply object that notifies that some error has happened in the helper, then set the error code to that returned by QFile and returns. If there is no error, it reads the file. The contents are put into the reply.data() object, which is another QVariantMap. It will be sent back to the application with the reply. Because this class will be compiled into a standalone executable, we need a main() function and some code to initialize everything: you don't have to write it. Instead, you use the KAUTH_HELPER_MAIN() macro that will take care of everything. It's used like this: @snippet helper.cpp helper_main The first parameter is the string containing the helper identifier. Please note that you need to use this same string in the application's code to tell the library which helper to call, so please stay away from typos, because we don't have any way to detect them. The second parameter is the name of the helper's class. Your helper, if complex, can be composed of a lot of source files, but the important thing is to include this macro in at least one of them. To build the helper, KDE macros provide a function named kauth_install_helper_files(). Use it in your cmake file like this: @code add_executable( your sources...) target_link_libraries( your libraries...) install(TARGETS DESTINATION ${KAUTH_HELPER_INSTALL_DIR}) kauth_install_helper_files( ) @endcode The first argument is the cmake target name for the helper executable, which you have to build and install separately. Make sure to INSTALL THE HELPER IN ${KAUTH_HELPER_INSTALL_DIR}, otherwise kauth_install_helper_files will not work. The second argument is the helper id. Please be sure to don't misspell it, and to not quote it. The user parameter is the user that the helper has to be run as. It usually is root, but some actions could require less strict permissions, so you should use the right user where possible (for example the user apache if you have to mess with apache settings). Note that the target created by this macro already links to libkauth and QtCore. @section kauth_actions Action registration To be able to authorize the actions, they have to be added to the policy database. To do this in a cross-platform way, we provide a cmake macro. It looks like: @code kauth_install_actions( ) @endcode The action definition file describes which actions are implemented by your code and which default security options they should have. It is a common text file in ini format, with one section for each action and some parameters. The definition for the read action is: @verbatim [org.kde.kf5auth.example.read] Name=Read action Description=Read action description Policy=auth_admin Persistence=session @endverbatim The name parameter is a text describing the action for who reads the file. The description parameter is the message shown to the user in the authentication dialog. It should be a finite phrase. The policy attribute specify the default rule that the user must satisfy to be authorized. Possible values are: - yes: the action should be always allowed - no: the action should be always denied - auth_self: the user should authenticate as itself - auth_admin: the user should authenticate as an administrator user The persistence attribute is optional. It says how long an authorization should be retained for that action. The values could be: - session: the authorization persists until the user logs-out - always: the authorization will persist indefinitely @section kauth_app Calling the helper from the application Once the helper is ready, we need to call it from the main application. In examples/client.cpp you can see how this is done. To create a reference to an action, an object of type Action has to be created. Every Action object refers to an action by its action id. Two objects with the same action id will act on the same action. With an Action object, you can authorize and execute the action. To execute an action you need to retrieve an ExecuteJob, which is a standard KJob that you can run synchronously or asynchronously. See the KJob documentation (from KCoreAddons) for more details. The piece of code that calls the action of the previous example is: @snippet client.cpp client_how_to_call_helper First of all, it creates the action object specifying the action id. Then it loads the filename (we want to read a forbidden file) into the arguments() QVariantMap, which will be directly passed to the helper in the read() slot's parameter. This example code uses a synchronous call to execute the action and retrieve the reply. If the reply succeeded, the reply data is retrieved from the returned QVariantMap object. Please note that you have to explicitly set the helper ID to the action: this is done for added safety, to prevent the caller from accidentally invoking a helper, and also because KAuth actions may be used without a helper attached (the default). @section kauth_async Asynchronous calls, data reporting, and action termination For a more advanced example, we look at the action "org.kde.kf5auth.example.longaction" in the example helper. This is an action that takes a long time to execute, so we need some features: - The helper needs to regularly send data to the application, to inform about the execution status. - The application needs to be able to stop the action execution if the user stops it or close the application. The example code follows: @snippet helper.cpp helper_longaction In this example, the action is only waiting a "long" time using a loop, but we can see some interesting line. The progress status is sent to the application using the HelperSupport::progressStep() method. When this method is called, the HelperProxy associated with this action will emit the progressStep() signal, reporting back the data to the application. There are two overloads of these methods and corresponding signals. The one used here takes an integer. Its meaning is application dependent, so you can use it as a sort of percentage. The other overload takes a QVariantMap object that is directly passed to the app. In this way, you can report to the application all the custom data you want. In this example code, the loop exits when the HelperSupport::isStopped() returns true. This happens when the application calls the HelperProxy::stopAction() method on the correponding action object. The stopAction() method, this way, asks the helper to stop the action execution. It's up to the helper to obbey to this request, and if it does so, it should return from the slot, _not_ exit. @section kauth_other Other features It doesn't happen very frequently that you code something that doesn't require some debugging, and you'll need some tool, even a basic one, to debug your helper code as well. For this reason, the KDE Authorization library provides a message handler for the Qt debugging system. This means that every call to qDebug() & co. will be reported to the application, and printed using the same qt debugging system, with the same debug level. If, in the helper code, you write something like: @code qDebug() << "I'm in the helper"; @endcode You'll see something like this in the application's output: @verbatim Debug message from the helper: I'm in the helper @endverbatim Remember that the debug level is preserved, so if you use qFatal() you won't only abort the helper (which isn't suggested anyway), but also the application. */ namespace KAuth { class ActionReplyData; /** -* @brief Class that encapsulates a reply coming from the helper after executing -* an action -* -* An instance of ActionReply is returned every time you execute an action with -* the Action class. You get the reply directly from the Action::execute() method -* or indirectly as a parameter of the ActionWatcher::actionPerformed() signal. -* -* ActionReply objects can contain both data from a successful action or an error -* indicator. In case of success, the errorCode() is ActionReply::NoError (zero) -* and the type() is ActionReply::Success. The data() method returns a -* QVariantMap object that may contain custom data sent back by the helper. -* -* In case of errors coming from the library, the type() is -* ActionReply::KAuthError. In this case, errorCode() will always be one of the -* predefined errors from the ActionReply::Error enum. An error reply of -* KAuthError type always contains an empty data() object. For some kind of -* errors you could get a human-readable description with errorDescription(). -* -* If, instead, the helper itself has to report some errors occurred during the -* action execution, the type() will be (and has to be) ActionReply::HelperError. -* In this case the data() object can contain custom data from the helper, and -* the errorCode() and errorDescription() values are application-dependent. -* -* In the helper, to create an action reply object you have two choices: using -* the constructor, or the predefined replies. For example, to create a -* successful reply you can use the default constructor but to create a helper -* error reply, instead of writing ActionReply(ActionReply::HelperError) -* you could use the more convenient ActionReply::HelperErrorReply -* constant. -* -* You should not use the predefined error replies to create and return new -* errors. Replies with the KAuthError type are intended to be returned by the -* library only. However, you can use them for comparisons. -* -* To quickly check for success or failure of an action, you can use succeeded() -* or failed(). -* -* @since 4.4 -*/ + * @brief Class that encapsulates a reply coming from the helper after executing + * an action + * + * Helper applications will return this to describe the result of the action. + * + * Callers should access the reply though the KAuth::ExecuteJob job. + * + * @since 4.4 + */ class KAUTH_EXPORT ActionReply { public: /** * Enumeration of the different kinds of replies. */ enum Type { KAuthErrorType, ///< An error reply generated by the library itself. HelperErrorType, ///< An error reply generated by the helper. SuccessType ///< The action has been completed successfully }; static const ActionReply SuccessReply(); ///< An empty successful reply. Same as using the default constructor static const ActionReply HelperErrorReply(); ///< An empty reply with type() == HelperError and errorCode() == -1 static const ActionReply HelperErrorReply(int error); ///< An empty reply with type() == HelperError and error is set to the passed value static const ActionReply NoResponderReply(); ///< errorCode() == NoResponder static const ActionReply NoSuchActionReply(); ///< errorCode() == NoSuchAction static const ActionReply InvalidActionReply(); ///< errorCode() == InvalidAction static const ActionReply AuthorizationDeniedReply(); ///< errorCode() == AuthorizationDenied static const ActionReply UserCancelledReply(); ///< errorCode() == UserCancelled static const ActionReply HelperBusyReply(); ///< errorCode() == HelperBusy static const ActionReply AlreadyStartedReply(); ///< errorCode() == AlreadyStartedError static const ActionReply DBusErrorReply(); ///< errorCode() == DBusError /** * The enumeration of the possible values of errorCode() when type() is ActionReply::KAuthError */ enum Error { NoError = 0, ///< No error. NoResponderError, ///< The helper responder object hasn't been set. This shouldn't happen if you use the KAUTH_HELPER macro in the helper source NoSuchActionError, ///< The action you tried to execute doesn't exist. InvalidActionError, ///< You tried to execute an invalid action object AuthorizationDeniedError, ///< You don't have the authorization to execute the action UserCancelledError, ///< Action execution has been cancelled by the user HelperBusyError, ///< The helper is busy executing another action (or group of actions). Try later AlreadyStartedError, ///< The action was already started and is currently running DBusError, ///< An error from D-Bus occurred BackendError ///< The underlying backend reported an error }; /// Default constructor. Sets type() to Success and errorCode() to zero. ActionReply(); /** * @brief Constructor to directly set the type. * * This constructor directly sets the reply type. You shouldn't need to * directly call this constructor, because you can use the more convenient * predefined replies constants. You also shouldn't create a reply with * the KAuthError type because it's reserved for errors coming from the * library. * * @param type The type of the new reply */ ActionReply(Type type); /** * @brief Constructor that creates a KAuthError reply with a specified error code. * Do not use outside the library. * * This constructor is for internal use only, since it creates a reply * with KAuthError type, which is reserved for errors coming from the library. * * @param errorCode The error code of the new reply */ ActionReply(int errorCode); /// Copy constructor ActionReply(const ActionReply &reply); /// Virtual destructor virtual ~ActionReply(); /** * @brief Sets the custom data to send back to the application * * In the helper's code you can use this function to set an QVariantMap * with custom data that will be sent back to the application. * * @param data The new QVariantMap object. */ void setData(const QVariantMap &data); /** * @brief Returns the custom data coming from the helper. * * This method is used to get the object that contains the custom * data coming from the helper. In the helper's code, you can set it * using setData() or the convenience method addData(). * * @return The data coming from (or that will be sent by) the helper */ QVariantMap data() const; /** * @brief Convenience method to add some data to the reply. * * This method adds the pair @c key/value to the QVariantMap used to * report back custom data to the application. * * Use this method if you don't want to create a new QVariantMap only to * add a new entry. * * @param key The new entry's key * @param value The value of the new entry */ void addData(const QString &key, const QVariant &value); /// Returns the reply's type Type type() const; /** * @brief Sets the reply type * * Every time you create an action reply, you implicitly set a type. * Default constructed replies or ActionReply::SuccessReply have * type() == Success. * ActionReply::HelperErrorReply has type() == HelperError. * Predefined error replies have type() == KAuthError. * * This means you rarely need to change the type after the creation, * but if you need to, don't set it to KAuthError, because it's reserved * for errors coming from the library. * * @param type The new reply type */ void setType(Type type); /// Returns true if type() == Success bool succeeded() const; /// Returns true if type() != Success bool failed() const; /** * @brief Returns the error code of an error reply * * The error code returned is one of the values in the ActionReply::Error * enumeration if type() == KAuthError, or is totally application-dependent if * type() == HelperError. It also should be zero for successful replies. * * @return The reply error code */ int error() const; /** * @brief Returns the error code of an error reply * * The error code returned is one of the values in the ActionReply::Error * enumeration if type() == KAuthError. * Result is only valid if the type() == HelperError * * @return The reply error code */ Error errorCode() const; /** * @brief Sets the error code of an error reply * * If you're setting the error code in the helper because * you need to return an error to the application, please make sure * you already have set the type to HelperError, either by calling * setType() or by creating the reply in the right way. * * If the type is Success when you call this method, it will become KAuthError * * @param error The new reply error code */ void setError(int error); /** * @brief Sets the error code of an error reply * * @see * If you're setting the error code in the helper, use setError(int) * * If the type is Success when you call this method, it will become KAuthError * * @param errorCode The new reply error code */ void setErrorCode(Error errorCode); /** * @brief Gets a human-readble description of the error, if available * * Currently, replies of type KAuthError rarely report an error description. * This situation could change in the future. * * By now, you can use this method for custom errors of type HelperError. * * @return The error human-readable description */ QString errorDescription() const; /** * @brief Sets a human-readble description of the error * * Call this method from the helper if you want to send back a description for * a custom error. Note that this method doesn't affect the errorCode in any way * * @param error The new error description */ void setErrorDescription(const QString &error); /** * @brief Serialize the reply into a QByteArray. * * This is a convenience method used internally to sent the reply to a remote peer. * To recreate the reply, use deserialize() * * @return A QByteArray representation of this reply */ QByteArray serialized() const; /** * @brief Deserialize a reply from a QByteArray * * This method returns a reply from a QByteArray obtained from * the serialized() method. * * @param data A QByteArray obtained with serialized() */ static ActionReply deserialize(const QByteArray &data); /// Assignment operator ActionReply &operator=(const ActionReply &reply); /** * @brief Comparison operator * * This operator checks if the type and the error code of two replies are the same. * It doesn't compare the data or the error descriptions, so be careful. * * The suggested use it to compare a reply agains one of the predefined error replies: * @code * if(reply == ActionReply::HelperBusyReply) { * // Do something... * } * @endcode * * Note that you can do it also by compare errorCode() with the relative enumeration value. */ bool operator==(const ActionReply &reply) const; /** * @brief Negated comparison operator * * See the operator==() for an important notice. */ bool operator!=(const ActionReply &reply) const; /// Output streaming operator for QDataStream friend QDataStream &operator<<(QDataStream &, const ActionReply &); /// Input streaming operator for QDataStream friend QDataStream &operator>>(QDataStream &, ActionReply &); private: QSharedDataPointer d; }; } // namespace Auth Q_DECLARE_METATYPE(KAuth::ActionReply) #endif diff --git a/src/kauthexecutejob.h b/src/kauthexecutejob.h index 691c1c2..0c4bb8b 100644 --- a/src/kauthexecutejob.h +++ b/src/kauthexecutejob.h @@ -1,118 +1,138 @@ /* * Copyright (C) 2012 Dario Freddi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . */ -#ifndef ASYNC_ACTION_H -#define ASYNC_ACTION_H +#ifndef EXECUTE_JOB_H +#define EXECUTE_JOB_H #include #include #include "kauthaction.h" #include "kauthactionreply.h" namespace KAuth { /** - * @brief Job executing an Action + * @brief Job for executing an Action + * + * To run the action synchonously use KJob::exec() and check the return code for + * success. + * + * For longer tasks connect KJob::result(KJob*) and any other signals such as + * percent(KJob*, unsigned long) and newData(const QVariantMap &) then run start(). + * + * To check for authentiation success or problems connect to + * statusChanged(KAuth::Action::AuthStatus status) signal. + * + * Use data() to get the return result of the action. + * * @since 5.0 */ class KAUTH_EXPORT ExecuteJob : public KJob { Q_OBJECT ExecuteJob(const KAuth::Action &action, KAuth::Action::ExecutionMode mode, QObject *parent); friend class Action; class Private; Private *const d; Q_PRIVATE_SLOT(d, void doExecuteAction()) Q_PRIVATE_SLOT(d, void doAuthorizeAction()) Q_PRIVATE_SLOT(d, void actionPerformedSlot(const QString &action, const KAuth::ActionReply &reply)) Q_PRIVATE_SLOT(d, void progressStepSlot(const QString &action, int i)) Q_PRIVATE_SLOT(d, void progressStepSlot(const QString &action, const QVariantMap &data)) Q_PRIVATE_SLOT(d, void statusChangedSlot(const QString &action, KAuth::Action::AuthStatus status)) public: /// Virtual destructor virtual ~ExecuteJob(); /** * Starts the job asynchronously. * @see KJob::result * @see newData * @see statusChanged */ void start() Q_DECL_OVERRIDE; /** * @returns the action associated with this job */ Action action() const; /** - * @returns the data sent by the helper + * Use this to get the data set in the action by + * HelperSupport::progressStep(QVariant) or returned at the end of the + * action. + * + * This function is particularly useful once the job has completed. During + * execution, simply read the data in the newData signal. + * + * @see ExecuteJob::newData + * @returns the data set by the helper */ QVariantMap data() const; public Q_SLOTS: /** * Attempts to halt the execution of the action associated with this job. * You should listen to the finished and result signals to work out whether * halting was successful (as long running operations can also take time * to shut down cleanly). * @see HelperSupport::isStopped() * @see KJob::result * @see KJob::finished * @return Always returns true */ bool kill(KillVerbosity verbosity = Quietly); Q_SIGNALS: /** * @brief Signal emitted by the helper to notify the action's progress * * This signal is emitted every time the helper's code calls the * HelperSupport::progressStep(QVariantMap) method. This is useful to let the * helper notify the execution status of a long action, also providing * some data, for example if you want to achieve some sort of progressive loading. * The meaning of the data passed here is totally application-dependent. * If you only need to pass some percentage, you can use the other signal that * pass an int. * * @param data The progress data from the helper */ void newData(const QVariantMap &data); /** * @brief Signal emitted when the authentication status changes * @param status the the new authentication status */ void statusChanged(KAuth::Action::AuthStatus status); private: Q_DISABLE_COPY(ExecuteJob) }; } // namespace Auth #endif diff --git a/src/kauthhelpersupport.h b/src/kauthhelpersupport.h index dfcd153..00f41d8 100644 --- a/src/kauthhelpersupport.h +++ b/src/kauthhelpersupport.h @@ -1,106 +1,108 @@ /* * Copyright (C) 2008 Nicola Gigante * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . */ #ifndef HELPER_SUPPORT_H #define HELPER_SUPPORT_H #include #include #include #define KAUTH_HELPER_MAIN(ID, HelperClass) \ int main(int argc, char **argv) { return KAuth::HelperSupport::helperMain(argc, argv, ID, new HelperClass()); } namespace KAuth { /** * @brief Support class with some KAUTH_EXPORT methods useful to the helper's code * * This class provides the API to write the helper tool that executes your actions. * You don't create instances of HelperSupport. Instead, you use its KAUTH_EXPORT methods. * * This them you can notify the application of progress in your action's execution * and you can check if the application asked you to terminate it. * * @since 4.4 */ namespace HelperSupport { /** * @brief Send a progressStep signal to the caller application * * You can use this method to notify progress information about the - * action execution. When you call this method, the ActionWatcher - * object associated with the current action will emit the progressStep(int) + * action execution. When you call this method, the KAuth::ExecuteJob + * object associated with the current action will emit the KJob::percent(KJob*, unsigned long) * signal. The meaning of the integer passed here is totally application dependent, * but you'll want to use it as a sort of percentage. * If you need to be more expressive, use the other overload which takes a QVariantMap * * @param step The progress indicator */ KAUTH_EXPORT void progressStep(int step); /** * @brief Send a progressStep signal to the caller application * * You can use this method to notify progress information about the -* action execution. When you call this method, the ActionWatcher +* action execution. When you call this method, the KAuth::ExecuteJob * object associated with the current action will emit the progressStep(QVariantMap) * signal. The meaning of the data passed here is totally application dependent. +* * If you only need a simple percentage value, use the other overload which takes an int. * * @param data The progress data */ KAUTH_EXPORT void progressStep(const QVariantMap &data); /** * @brief Check if the caller asked the helper to stop the execution * * This method will return true if the helper has been asked to stop the * execution of the current action. If this happens, your helper should * return (NOT exit). The meaning of the data you return in this case is * application-dependent. + * * It's good practice to check it regularly if you have a long-running action * * @see ExecuteJob::kill * @return true if the helper has been asked to stop, false otherwise */ KAUTH_EXPORT bool isStopped(); /** * @brief Method that implements the main function of the helper tool. Do not call directly * * This method is called in the proper way by the code generated by the KAUTH_HELPER_MAIN(), * which creates a main() function for the helper tool. * macro. You shouldn't call this method directly. * * @param argc The argc parameter from the main() function. * @param argv The argv parameter from the main() function. * @param id The helper ID as passed to the macro * @param responder The responder object for the helper. The macro passes a default-constructed, * heap-allocated object of the class specified as the last macro parameter */ KAUTH_EXPORT int helperMain(int argc, char **argv, const char *id, QObject *responder); } // namespace HelperSupport } // namespace Auth #endif