Hello all,

Here's the remote widget api as it is after review at Tokamak.... still 
something wrong? Speak now, or forever hold your peace.

Regards,
Rob
/**
Overview:
Most of the new api is focussed on plasma shells, to allow them to access remote widgets and allow them controll over the security aspects. The one class every shell should at least use is AuthorizationManager, which allows shells the type of security they want. There are a couple of 'presets' (e.g. allow no remote access at all, always do pin pairing for untrusted clients etc.), but you can define custom behavior by implementing an AuthorizationInterface and setting that with AuthorizationManager.
After setting AuthorizationManager up, always call lock(); so plasma plugins can't change security stuff themselves.
The other class most plasma shells will probably want to use is AccessManager, which allows access to remote plasma widgets. The reason this is not in Applet like Applet::load is that accessing a remote plasmoid is an asynchronous operation (it returns a PlasmoidAccessJob) and so wouldn't be very consistent.
The AccessManager also allows the shell to be notified of new plasmoids that are published through zeroconf, or get a list of plasmoids that are published through zeroconf.
The way the origin of access attempts is identified is through a class called Identity, which handles all message signing/verifying. We basically identify any incoming connection by a hash of their public key. AuthorizationManager takes care of actually checking if a certain identity has to be allowed or denied access to a certain service. I will discuss with Dario about integrating this with PolicyKit, which seems like a good idea, but it will probably still be abstracted by AuthorizationRule so that won't affect the API much.
Besides some minor additions to the api in for example service and dataengine, the rest of the work is mostly in private classes. Most of it is build on top of Plasma::Service, so for example accessing a remote dataengine actually returns a DataEngine subclass that uses a remote published Plasma::Service to fetch updates and what not.
*/

/**
 * @class AccessManager plasma/accessmanager.h <Plasma/AccessManager>
 *
 * @short Allows access to remote Plasma::Service, Plasma::DataEngine and Plasma::Applet classes.
 *
 * This manager provides a way to access a plasmoid that is hosted on another machine. It also
 * provides a mechanism to discover services announced to the network through zeroconf or
 * bluetooth.
 * All access function are asynchronous. The services need to be accessed over the network, and
 * might even have to be authorized by the user of that machine first. All access functions
 * therefore return a job that can be monitored to see when the service is ready for use.
 *
 * @since 4.4?
 */
class PLASMA_EXPORT AccessManager : public QObject
{
    Q_OBJECT

    public:
        /**
         * Singleton pattern accessor.
         */
        static AccessManager *self();

        /**
         * Access a native plasmoid hosted on another machine.
         * @param location the location of the remote plasmoids. Exmples of valid urls:
         * plasma://ip:port/!/resourceName
         * zeroconf://PlasmoidName
         * @returns a job that can be used to track when a remote plasmoid is ready for use, and to
         * obtain the applet when the package is sent over.
         */
	AccessAppletJob *accessRemoteApplet(const KUrl &location) const;

        /**
         * @returns a map mapping service names to the plasmoid's metadata.
         */
        QList<PackageMetadata> remoteApplet() const;

    Q_SIGNALS:
        /**
         * fires when a PlasmoidAccessJob is finished.
         */
        void finished(Plasma::AccessAppletJob *);

        /**
         * fires when a new plasmoid is announced on the network.
         */
        void remoteAppletAnnounced(Plasma::PackageMetadata metadata);

        /**
         * fires when an announced plasmoid disappears from the network.
         */
        void remoteAppletUnannounced(Plasma::PackageMetadata metadata);

    private:
        AccessManager();
        ~AccessManager();

        AccessManagerPrivate * const d;

        Q_PRIVATE_SLOT(d, void slotJobFinished(KJob*))
        Q_PRIVATE_SLOT(d, void slotAddService(DNSSD::RemoteService::Ptr service))
        Q_PRIVATE_SLOT(d, void slotRemoveService(DNSSD::RemoteService::Ptr service))

        friend class AccessManagerPrivate;
        friend class AccessManagerSingleton;
};

/**
 * @class AuthorizationInterface plasma/authorizationinterface.h <Plasma/AuthorizationInterface>
 *
 * @short Allows authorization of access to plasma services.
 *
 * This class is only needed when you create a plasma shell, and none of the presets for 
 * AuthorizationManager matches your use case. When you implement it and register it
 * with the AuthorizationManager class, it allows you to respond to incoming service access
 * attempts. Whenever a message is received that does not match any of the AuthorizationRules,
 * AuthorizationManager creates a new rule matching it, and passes it to the authorize function.
 * Change the rule from Unspecified to something else like Allow or Deny to continue processing the
 * message.
 *
 * @since 4.4?
 */
class PLASMA_EXPORT AuthorizationInterface
{
    public:
        virtual ~AuthorizationInterface();

        /**
         * implement this function to respond to an incoming request that doesn't match any rule.
	 * ERVIN: this doesn't return bool, because this is async... we might want to ask the user for
	 * confirmation first and don't want to block plasma all the time waiting for that. Same for
	 * clientPinRequest: we want to pop up a dialog and allow the user to specify a password, without
	 * blocking all of plasma
         * @param rule a new AuthorizationRule matching an incoming operation. Call setRules on this
         * rule to allow/deny the operation.
         */
        virtual void authorizationRequest(AuthorizationRule *rule) = 0;

        /**
         * Implement this function to respond to an outgoing connection that needs a password to
         * connect succesfully. As a response to this you'll probably want to show a dialog.
         * @param request a ClientPinRequest where you can call setPin on to set the pin for the
         * outgoing connection.
         */
        virtual void clientPinRequest(ClientPinRequest *request) = 0;

    protected:
        AuthorizationInterface();
};

/**
 * @class AuthorizationManager plasma/authorizationmanager.h <Plasma/AccessManager>
 *
 * @short Allows authorization of access to plasma services.
 *
 * This is the class where every message to or from another machine passes through. 
 * It's responsibilities are:
 * - creating/keeping a public/private key pair for message signing.
 * - testing whether or not the sender is allowed to access the requested resource by testing the
 *   request to a set of rules.
 * - allowing the shell to respond to a remote request that doesn't match any of the 
 *   rules that are in effect.
 * Besides internal use in libplasma, the only moment you'll need to access this class is when you
 * implement a plasma shell. 
 *
 * @since 4.4?
 */
class PLASMA_EXPORT AuthorizationManager : public QObject
{
    Q_OBJECT
    public:
        enum AuthorizationPolicy {
            DenyAll = 0,  /** < Don't allow any incoming connections */
	    TrustedOnly = 1, /** < Only allow connections from trusted machines */
            PinPairing = 2, /**< Standard PIN pairing for untrusted connections */        
            Custom = 256 /** < Specify a custom AuthorizationInterface */
        };
        
        /**
         * Singleton pattern accessor.
         */
        static AuthorizationManager *self();

        /**
         * Set a policy used for authorizing incoming connections. You can either use one of the
         * included policies, Default is to deny all incoming connections.
         */
        void setAuthorizationPolicy(AuthorizationPolicy policy);
        
        /**
         * Register an implementation of AuthorizationInterface. Use this to make your shell
         * handle authorization requests.
         */
        void setAuthorizationInterface(AuthorizationInterface *interface);

    private:
        AuthorizationManager();
        ~AuthorizationManager();

        AuthorizationManagerPrivate *const d;

        Q_PRIVATE_SLOT(d, void loadRules())

        friend class AuthorizationManagerSingleton;
        friend class AuthorizationRule;
        friend class Applet;
        friend class DataEngine;
        friend class GetSource;
        friend class Identity;
        friend class Package;
        friend class PlasmoidServiceJob;
        friend class RemoteService;
        friend class RemoteServiceJob;
        friend class ServiceProvider;
};

/**
 * @class AuthorizationRule plasma/authorizationrule.h <Plasma/AuthorizationRule>
 *
 * @short Defines a rule indicating whether or not a certain service can be accessed by a certain
 * machine.
 *
 * Rules allow you to have control over which computers are allowed to access which
 * services. Everytime a message get's in, AuthorizationManager validates it's sender, and then
 * checks it's list of rules for rules matching the sender and/or the service. If no rules match,
 * or all matching rules have the value Unspecified, AuthorizationManager will create a new rule
 * for this message, and invoke authorize on your shells implementation of AuthorizationInterface.
 * Here, you can change that rule to either allow or deny that request.
 * This class can be used to specify different types of rules:
 * - Rules matching only a user
 * - Rules matching only a service
 * - Rules matching both a service, and a user.
 * A more specific rule always takes precedence over a more global rule: so if for example you have
 * a rule for "myAwesomeService" specifying Deny, and a rule for "myAwesomeService" in combination
 * with "130.42.120.146" as caller specifying Allow, only 130.42.120.146 can access
 * myAwesomeService.
 * By setting the PinRequired flag in setRules in an AuthorizationInterface implementation, you
 * trigger Pin pairing (user will be asked to enter the same password on both machines).
 *
 * @since 4.4?
 */
class PLASMA_EXPORT AuthorizationRule : public QObject
{
    Q_OBJECT
    public:
        ~AuthorizationRule();
        
	enum Policy {
	    Deny = 0,           /**< access for messages matching this rule is denied. */
	    Allow = 1,          /**< access for messages matching this rule is allowed. */
	    PinRequired = 2,   /**< specify that the user will need to enter a pin at both sides */
	}
	
	enum Persistence {
	    Transient = 0,
	    Persistent = 1
	}
	
	enum Target {
	    Default = 0,
	    AllUsers = 1,
	    AllServices = 2
	}
	Q_DECLARE_FLAGS(Targets, Target)
	
        /**
         * @returns a friendly and i18n'd description of the current rule, useful for creating a
         * GUI to allow editing rules, or asking permission for an access attempt.
         */
        QString description() const;

        /**
         * @param rules the flags describing this rule.
         */
        void setPolicy(Policy policy);

        /**
         * @returns the flags describing this rule.
         */
	Policy policy();
	
	/**
         * @param rules the flags describing this rule.
         */
        void setPersistence(Persistence persistence);

        /**
         * @returns the flags describing this rule.
         */
	Persistence persistence();
	
	/**
         * @param rules the flags describing this rule.
         */
        void setTargets(Targets targets);

        /**
         * @returns the flags describing this rule.
         */
	Targets targets();

        /**
         * @param pin set pin for pin pairing. You'll need to call this bevore setting the rule.
         */
        void setPin(const QString &pin);

        /**
         * @returns the pin for pin pairing.
         */
        QString pin() const;

        /**
         * @returns the identity of the caller.
         */
        Identity identity() const;

        /**
         * @returns the name of the service this rule applies to.
         */
        QString serviceName() const;

    private:
        AuthorizationRule(const QString &serviceName, Identity identity);

        AuthorizationRulePrivate * const d;

        friend class AuthorizationManager;
        friend class AuthorizationManagerPrivate;
        friend class ServiceProvider;
        friend class GetSource;
        friend class PlasmoidServiceJob;
};

/**
 * @class ClientPinRequest plasma/clientpinrequest.h <Plasma/ClientPinRequest>
 *
 * describes an outgoing connection.
 *
 * @since 4.4?
 */
class PLASMA_EXPORT ClientPinRequest : public QObject
{
    Q_OBJECT
    public:
        /**
         * @returns nice i18n'ed description of this outgoing connection.
         */
        QString description() const;

        /**
         * @param pin set a pin for pin pairing.
         */
        void setPin(const QString &pin);

        /**
         * @returns the pin for pin pairing.
         */
        QString pin() const;

    private:
        ClientPinRequest(RemoteService *service);
        ~ClientPinRequest();
        
        ClientPinRequestPrivate * const d;

        friend class RemoteService;

};

/**
 * @class PlasmoidAccessJob plasma/plasmoidaccessjob.h <Plasma/PlasmoidAccessJob>
 *
 * @short This class is used for asynchronously accessing an applet published on a remote system.
 * After calling AccessManager::accessPlasmoid, monitor this job to track when the remote applet
 * is ready to be used, and to obtain the service when finished.
 */
class PLASMA_EXPORT AccessAppletJob : public KJob
{
    Q_OBJECT

public:
    ~AccessAppletJob();
    
    Applet *applet() const;

protected:
    /**
     * Default constructor
     *
     * @arg location the location of the service
     * @arg parent the parent object for this service
     */
    AccessAppletJob(const KUrl &location, QObject *parent = 0);

    void start();

private:
    AccessAppletJob();
    
    Q_PRIVATE_SLOT(d, void slotPackageDownloaded(Plasma::ServiceJob*))
    Q_PRIVATE_SLOT(d, void slotStart())
    Q_PRIVATE_SLOT(d, void slotServiceReady(Plasma::Service*))
    Q_PRIVATE_SLOT(d, void slotTimeout())

    PlasmoidAccessJobPrivate * const d;
    
    friend class AccessManager;
    friend class AccessManagerPrivate;
    friend class PlasmoidAccessJobPrivate;
};

namespace Plasma {
  enum TrustLevel {
        InvalidCredentials,
	UnknownCredentials,
	ValidCredentials,
	TrustedCredentials,
	UltimateCredentials
    }
    
}

/**
 * @class Identity plasma/identity.h <Plasma/Identity>
 *
 * This class encapsules someone's identity.
 * It contains a unique id that identifies the machine an incoming connection is coming from, it's
 * name (which is not necesarily unique and/or trusted), a public key used to validate messages
 * coming from the machine with this identity, and in the future the possibility to determine
 * whether or not this identity can be trusted based on mechanisms different then pin pairing, e.g.
 * a signature of the key that can be verified by a gpg trusted key.
 */
class Identity
{
public:
    
    /**
     * Default constructor.
     */
    Identity();

    /**
     * Copy constructor.
     */
    Identity(const Identity &other);
    
    ~Identity();
    
    Identity &operator=(const Identity &other);

    /**
     * Create a new identity with a new set of random public/private keys.
     */
    static Identity createIdentity(const QString &name);
    
    /**
     * @return whether or not this identity can be trusted based on e.g. having the key signed with
     * a trusted GPG key (not yet implemented) or having the key in a designated folder on disk.
     * If this function returns false, your shell should always at least instatiate
     * pin pairing before allowing a connection from an untrusted source
     * (AuthorizationRule::PinRequired flag should be set on the rule with setRules).
     * TODO: should indicate a level of trust instead of just true/false
     */
    TrustLevel trustLevel() const;

    /**
     * @return whether or not this is a null identity.
     */
    bool isValid() const;

    /**
     * @return the name of this identity. There's however no guarantee that if the name returns e.g.
     * "Santa Claus", this message is actually from Mr. Claus, except if isTrusted is true.
     */
    QString name() const;

    /**
     * @return an id to identify this identity. I use a Hash of the public key as ID. This way we
     * don't have to send the complete public key with every message.
     */
    QString id() const;

    /**
     * @return wheter or not @p signature is correct for @p message.
     */
    bool isValidSignature(const QByteArray &signature, const QByteArray &message);

    /**
     * @return whether or not this identity can be used for signing a message (whether or not it
     * includes a public key)
     */
    bool canSign() const;

    /**
     * @return the signature for the message.
     */
    QByteArray signMessage(const QByteArray &message);

    /**
     * @return a Identity stripped from any private key, so you can be sure it is save to send to
     * somebody.
     */
    Identity toPublicIdentity() const;
    
    friend QDataStream &operator<<(QDataStream &, const Identity &);
    friend QDataStream &operator>>(QDataStream &, Identity &);

private:
    Identity(const QString &id, const QString &name, const QString &key,
             bool privateKey = false);
             
    IdentityPrivate *const d;
    
    friend class AuthorizationManagerPrivate;
    friend class IdentityPrivate;
};

/**
 * Streaming operators for sending/storing identities.
 */
QDataStream &operator<<(QDataStream &, const Identity &);
QDataStream &operator>>(QDataStream &, Identity &);

Additions to Plasma::DataEngine:
  public:

        /**
         * @param methods ways to announce this engine on the network.
         */
        void publish(PublicationMethods methods, const QString &name);

        /**
         * remove this engine from the network.
         */
        void unpublish(const QString &name = QString());

        /**
         * @return whether or not this engine is published.
         */
        bool isPublished() const;

Additions to Plasma::Service
  public:
	/**
	* Used to access a service from an url. Always check for the signal serviceReady() that fires
	* when this service is actually ready for use.
	*/
	static Service *access(const KUrl &url, QObject *parent = 0);
	
       /**
	* Publish this service on the network.
	* @param name The name under which this service get's published.
	* @param methods The methods we use for publication.
	* @param metadata A package metadata object which will be used to publish metadata in
	* zeroconf's textdata.
	*/
	void publish(AnnouncementMethods methods, const QString &name,
		     /* PackageMetadata metadata = PackageMetadata() */);

	void unpublish(const QString &name = QString());

	bool isPublished() const;
	
  Q_SIGNALS:
	/**
	* Emitted when this service is ready for use
	*/
	void serviceReady(Plasma::Service *service);
	
Additions to PackageMetadata
  public:
	QString remoteLocation() const;
	void setRemoteLocation(const QString &);
    
Additions to Applet
  public:
        void publish(Plasma::AnnouncementMethods method);

        void unpublish(const QString &name = QString());

        bool isPublished() const;
	
Additions to Package:
  public:
        void publish(Plasma::AnnouncementMethods method);

        void unpublish(const QString &name = QString());

        bool isPublished() const;
_______________________________________________
Plasma-devel mailing list
Plasma-devel@kde.org
https://mail.kde.org/mailman/listinfo/plasma-devel

Reply via email to