Hello Buteo team!

The UI design for using Accounts+SSO in the open source MeeGo UX is
still under debate. But we wanted to use at least SSO as part of the UX
to share credentials between apps, so I came up with a patch that does
that without depending on Accounts.

This was done before you agreed to implement feature "[FEA] SyncML
credentials stored in Accounts&SSO" [1], which to my knowledge hasn't
been completed yet.

I see this patch as a stop-gap measure that can be removed and/or
deprecated once a full solution is in place both in Buteo and the UX. In
the meantime, please consider including it because we have code which is
meant to use this feature.

The patch was meant to be as unintrusive as possible and therefore adds
neither a new API nor a new XML config option. It merely interprets a
special value for "Username" and then passes the real credentials at the
place where sync plugins expect them, the profile instance passed to the
plugins.

As before, patch attached and in a git repo [2].

[1] http://bugs.meego.com/show_bug.cgi?id=8027
[2] "shared-credentials" branch,
    http://meego.gitorious.org/~pohly/meego-middleware/pohlys-buteo-syncfw

-- 
Best Regards

Patrick Ohly
Senior Software Engineer

Intel GmbH
Open Source Technology Center               
Pützstr. 5                              Phone: +49-228-2493652
53129 Bonn
Germany
>From 2dce4bbc7c66d3f044174adb49bb07d43a1e8109 Mon Sep 17 00:00:00 2001
From: Patrick Ohly <[email protected]>
Date: Fri, 12 Nov 2010 15:57:14 +0100
Subject: [PATCH 1/2] client plugins: added automatic retrieval of credentials from SSO

If the "Username" value is of the format "sso-provider=<name>", then
the msyncd will automatically retrieve the real username and password
from SSO. The identity is found via its caption string, which must
equal "<name>".

This additional lookup is completely transparent to client
plugins. The profile which is passed to init() will have "Username"
and "Key" set to the values from SSO. If credentials retrieval fails,
an error is created without ever calling init().

The SSO interaction happens in the calling application thread. That
is easier because signal/slot connections then don't have to work
across threads (the work is done by the ClientThread instance, which
remains on the application thread).
---
 libsyncpluginmgr/ClientPlugin.h |    3 +
 msyncd/ClientThread.cpp         |   86 +++++++++++++++++++++++++++++++++-----
 msyncd/ClientThread.h           |   34 ++++++++++++++-
 msyncd/msyncd.pro               |    3 +-
 4 files changed, 111 insertions(+), 15 deletions(-)

diff --git a/libsyncpluginmgr/ClientPlugin.h b/libsyncpluginmgr/ClientPlugin.h
index b9b44fb..61e0b56 100644
--- a/libsyncpluginmgr/ClientPlugin.h
+++ b/libsyncpluginmgr/ClientPlugin.h
@@ -63,6 +63,9 @@ public:
      */
     virtual bool startSync() = 0;
 
+    /*! \brief access to profile owned and used by this instance
+     */
+    SyncProfile &profile() { return iProfile; }
 
 protected:
 
diff --git a/msyncd/ClientThread.cpp b/msyncd/ClientThread.cpp
index 935666e..8992961 100644
--- a/msyncd/ClientThread.cpp
+++ b/msyncd/ClientThread.cpp
@@ -29,6 +29,9 @@ using namespace Buteo;
 
 ClientThread::ClientThread()
  : iClientPlugin( 0 ),
+   iIdentity(NULL),
+   iService(NULL),
+   iSession(NULL),
    iRunning(false)
 {
     FUNCTION_CALL_TRACE;
@@ -37,6 +40,10 @@ ClientThread::ClientThread()
 ClientThread::~ClientThread()
 {
     FUNCTION_CALL_TRACE;
+    if (iSession) {
+        iIdentity->destroySession(iSession);
+    }
+    delete iIdentity;
 }
 
 QString ClientThread::getProfileName() const
@@ -78,11 +85,30 @@ bool ClientThread::startThread( ClientPlugin* aClientPlugin )
     }
 
     iClientPlugin = aClientPlugin;
+    if (iClientPlugin == 0)
+    {
+        LOG_CRITICAL("Client plugin is NULL");
+        return false;
+    }
 
-    // Move to client thread
-    iClientPlugin->moveToThread( this );
-
-    start();
+    SyncProfile &profile = iClientPlugin->profile();
+    const QString prefix("sso-provider=");
+    QString username = profile.key("Username");
+    if (username.startsWith(prefix)) {
+        // Look up real username/password in SSO first,
+        // before starting sync. This is better done
+        // in the application thread, because this is where
+        // this instance lives.
+        iProvider = username.mid(prefix.size());
+        iService = new SignOn::AuthService(this);
+        connect(iService, SIGNAL(identities(const QList<IdentityInfo> &)),
+                this, SLOT(identities(const QList<IdentityInfo> &)));
+        iService->queryIdentities();
+    } else {
+        // Move to client thread
+        iClientPlugin->moveToThread( this );
+        start();
+    }
 
     return true;
 }
@@ -98,18 +124,12 @@ void ClientThread::run()
 {
     FUNCTION_CALL_TRACE;
 
-    if (iClientPlugin == 0)
-    {
-        LOG_CRITICAL("Client plugin is NULL");
-        return;
-    }
-
     if( !iClientPlugin->init() ) {
         LOG_DEBUG( "Could not initialize client plugin:" << iClientPlugin->getPluginName() );
         emit initError( getProfileName(), "", 0 );
         return;
     }
-
+    
     if( !iClientPlugin->startSync() ) {
         LOG_DEBUG( "Could not start client plugin:" << iClientPlugin->getPluginName() );
         emit initError( getProfileName(), "", 0 );
@@ -132,7 +152,6 @@ void ClientThread::run()
 
 }
 
-
 SyncResults ClientThread::getSyncResults()
 {
     FUNCTION_CALL_TRACE;
@@ -140,3 +159,46 @@ SyncResults ClientThread::getSyncResults()
 	return iSyncResults;
 }
 
+void ClientThread::identities(const QList<SignOn::IdentityInfo> &identityList)
+{
+    FUNCTION_CALL_TRACE;
+
+    for (int i = 0; i < identityList.size(); ++i) {
+        const SignOn::IdentityInfo &info = identityList.at(i);
+        if (info.caption() == iProvider) {
+            iIdentity = SignOn::Identity::existingIdentity(info.id(), this);
+            // Setup an authentication session using the "password" method
+            iSession =
+                iIdentity->createSession(QLatin1String("password"));
+            connect(iSession, SIGNAL(response(const SessionData &)),
+                    this, SLOT(identityResponse(const SessionData &)));
+            connect(iSession, SIGNAL(error(const Error &)),
+                    this, SLOT(identityError(const Error &)));
+            // Get the password!
+            iSession->process(SessionData(), QLatin1String("password"));
+            return;
+        }
+    }
+    emit initError( getProfileName(), "credentials not found in SSO", 0 );
+}
+
+void ClientThread::identityResponse(const SignOn::SessionData &sessionData)
+{
+    FUNCTION_CALL_TRACE;
+
+    // temporarily set real username/password, then invoke client
+    SyncProfile &profile = iClientPlugin->profile();
+    profile.setKey("Username", sessionData.UserName());
+    profile.setKey("Password", sessionData.Secret());
+
+    // delayed starting of thread
+    iClientPlugin->moveToThread( this );
+    start();
+}
+
+void ClientThread::identityError(const SignOn::Error &err)
+{
+    FUNCTION_CALL_TRACE;
+
+    emit initError( getProfileName(), err.message(), 0 );
+}
diff --git a/msyncd/ClientThread.h b/msyncd/ClientThread.h
index 5f84fa6..b4915f8 100644
--- a/msyncd/ClientThread.h
+++ b/msyncd/ClientThread.h
@@ -27,8 +27,15 @@
 #include <QMutex>
 #include <SyncResults.h>
 
+#include "SignOn/AuthService"
+#include "SignOn/Identity"
+
 namespace Buteo {
 
+// temporary workaround for old libsignon-qt: declares signal without
+// namespace, so app must do the same
+using namespace SignOn;
+
 class ClientPlugin;
     
 /*! \brief Thread for client plugins
@@ -102,16 +109,39 @@ private:
 
     SyncResults iSyncResults;
     
-    bool iRunning;
+    SignOn::Identity *iIdentity;
+    SignOn::AuthService *iService;
+    SignOn::AuthSession *iSession;
+    QString iProvider;
     
+    bool iRunning;
+
     mutable QMutex iMutex;
 
 #ifdef SYNCFW_UNIT_TESTS
     friend class ClientThreadTest;
 #endif
 
+    /*!
+     * \brief invokes iClientPlugin->startSync()
+     *
+     * It should be called when profile is ready for use, with
+     * credentials set in the Username/Password keys.  It is called
+     * either in run() or, if the Username key starts with the
+     * "sso-provider=" prefix, after retrieving the credentials from
+     * SSO (queryIdentities() -> identities() -> session ->
+     * identityResponse() -> startSync()).
+     *
+     * @return true for success (run thread), else failure (running
+     * thread is no longer necessary)
+     */
+    bool startSync();
 
-
+private slots:
+    // temporary: IdentityInfo without SignOn:: namespace
+    void identities(const QList<IdentityInfo> &identityList);
+    void identityResponse(const SessionData &session);
+    void identityError(const Error &error);
 };
 
 }
diff --git a/msyncd/msyncd.pro b/msyncd/msyncd.pro
index 426cf99..3c2724a 100644
--- a/msyncd/msyncd.pro
+++ b/msyncd/msyncd.pro
@@ -26,7 +26,8 @@ INCLUDEPATH += . \
     /usr/include/accounts-qt \
     /usr/include/iphbd/ \
     
-PKGCONFIG += dbus-1
+PKGCONFIG += dbus-1 libsignon-qt
+
 QMAKE_LIBDIR_QT += ../libsyncprofile/
 
 LIBS += -L../libsyncpluginmgr \
-- 
1.7.2.3

>From 97fc306c693769f94eb3215fd0b076b297822e75 Mon Sep 17 00:00:00 2001
From: Patrick Ohly <[email protected]>
Date: Wed, 8 Dec 2010 10:30:38 +0100
Subject: [PATCH 2/2] shared credentials: adapted to signon-qt API changes

Signals/slots now use explicit SignOn namespace and Error is passed
by value.
---
 msyncd/ClientThread.cpp |   16 ++++++++--------
 msyncd/ClientThread.h   |   11 +++--------
 2 files changed, 11 insertions(+), 16 deletions(-)

diff --git a/msyncd/ClientThread.cpp b/msyncd/ClientThread.cpp
index 8992961..88cf1fe 100644
--- a/msyncd/ClientThread.cpp
+++ b/msyncd/ClientThread.cpp
@@ -101,8 +101,8 @@ bool ClientThread::startThread( ClientPlugin* aClientPlugin )
         // this instance lives.
         iProvider = username.mid(prefix.size());
         iService = new SignOn::AuthService(this);
-        connect(iService, SIGNAL(identities(const QList<IdentityInfo> &)),
-                this, SLOT(identities(const QList<IdentityInfo> &)));
+        connect(iService, SIGNAL(identities(const QList<SignOn::IdentityInfo> &)),
+                this, SLOT(identities(const QList<SignOn::IdentityInfo> &)));
         iService->queryIdentities();
     } else {
         // Move to client thread
@@ -170,12 +170,12 @@ void ClientThread::identities(const QList<SignOn::IdentityInfo> &identityList)
             // Setup an authentication session using the "password" method
             iSession =
                 iIdentity->createSession(QLatin1String("password"));
-            connect(iSession, SIGNAL(response(const SessionData &)),
-                    this, SLOT(identityResponse(const SessionData &)));
-            connect(iSession, SIGNAL(error(const Error &)),
-                    this, SLOT(identityError(const Error &)));
+            connect(iSession, SIGNAL(response(const SignOn::SessionData &)),
+                    this, SLOT(identityResponse(const SignOn::SessionData &)));
+            connect(iSession, SIGNAL(error(SignOn::Error)),
+                    this, SLOT(identityError(SignOn::Error)));
             // Get the password!
-            iSession->process(SessionData(), QLatin1String("password"));
+            iSession->process(SignOn::SessionData(), QLatin1String("password"));
             return;
         }
     }
@@ -196,7 +196,7 @@ void ClientThread::identityResponse(const SignOn::SessionData &sessionData)
     start();
 }
 
-void ClientThread::identityError(const SignOn::Error &err)
+void ClientThread::identityError(SignOn::Error err)
 {
     FUNCTION_CALL_TRACE;
 
diff --git a/msyncd/ClientThread.h b/msyncd/ClientThread.h
index b4915f8..5f2ca2a 100644
--- a/msyncd/ClientThread.h
+++ b/msyncd/ClientThread.h
@@ -32,10 +32,6 @@
 
 namespace Buteo {
 
-// temporary workaround for old libsignon-qt: declares signal without
-// namespace, so app must do the same
-using namespace SignOn;
-
 class ClientPlugin;
     
 /*! \brief Thread for client plugins
@@ -138,10 +134,9 @@ private:
     bool startSync();
 
 private slots:
-    // temporary: IdentityInfo without SignOn:: namespace
-    void identities(const QList<IdentityInfo> &identityList);
-    void identityResponse(const SessionData &session);
-    void identityError(const Error &error);
+    void identities(const QList<SignOn::IdentityInfo> &identityList);
+    void identityResponse(const SignOn::SessionData &session);
+    void identityError(SignOn::Error error);
 };
 
 }
-- 
1.7.2.3

_______________________________________________
MeeGo-dev mailing list
[email protected]
http://lists.meego.com/listinfo/meego-dev

Reply via email to