signon: make Accounts optional

The new "signon" provider only depends on lib[g]signon-glib. It uses
gSSO if found, else UOA. Instead of pulling parameters and the
identity via libaccounts-glib, the user of SyncEvolution now has to
ensure that the identity exists and pass all relevant parameters
in the "signon:" username.

This does not have to be user-friendly, so the machine-readable
GVariant text dump format is used to pass all parameters.
This commit is contained in:
Patrick Ohly 2014-05-21 13:58:17 +02:00
parent 026903b57d
commit 42daad6034
5 changed files with 196 additions and 31 deletions

View File

@ -1,8 +1,8 @@
Google CalDAV/CardDAV via OAuth2
================================
Setup
-----
Setup with gSSO and Accounts
----------------------------
SyncEvolution needs an active account for Google. Services for CardDAV
and CalDAV can be used, but are not required if the provider was
@ -10,7 +10,8 @@ already configured to ask for CardDAV and CalDAV access.
SyncEvolution source code ships with example .provider, .service, and
.service-type files in the src/backends/signon/accounts
directory. These files work with gSSO.
directory. These files work with gSSO and (when compiled for that
instead ) with UOA.
When compiling, it is possible to insert valid client ID+secret into
these files using the --with-google-client-* configure options. The
@ -41,6 +42,31 @@ It is not possible to create signond identities via command line
tools. SyncEvolution will create those automatically and store them
for the provider so that it can be reused in future operations.
Setup with gSSO and without Accounts
====================================
An identity must be created once:
$ gsso-example --create-identity=google-for-syncevolution --identity-method=oauth --identity-realms=google.com
Identity stored with id 3
$ ID=3
On normal Linux desktops, access control is based on executable file
paths. On such a system grant SyncEvolution access to the new identity with:
$ gsso-example --add-context=$ID --system-context=<path>/syncevolution --application-context=
$ gsso-example --add-context=$ID --system-context=<path>/libexec/syncevo-dbus-server --application-context=
$ gsso-example --add-context=$ID --system-context=<path>/libexec/syncevo-dbus-helper --application-context=
$ gsso-example --add-context=$ID --system-context=<path>/libexec/syncevo-local-sync --application-context=
On Tizen, set a Smack label: --system-context=User --application-context=
Then use
"username=signon:
{'identity': <uint32 $ID>, 'method': <'oauth'>, 'mechanism': <'oauth2'>, 'session': <{'TokenHost': <'accounts.google.com'>, 'ForceClientAuthViaRequestBody': <true>, 'Scope': <'email https://www.googleapis.com/auth/carddav'>, 'UiPolicy': <uint32 0>, 'ClientId': <'73652887053-2ciia00v5fseed7s0sudggdu3oaoo2re.apps.googleusercontent.com'>, 'AuthPath': <'/o/oauth2/auth'>, 'ResponseType': <'code'>, 'AuthHost': <'accounts.google.com'>, 'ClientSecret': <'2YDYzyI6HWSJFd5dOqz0uZGj'>, 'Realms': <['google.com']>, 'RedirectUri': <'http://localhost'>, 'TokenPath': <'/o/oauth2/token'>}> }"
in the commands below. This is the information that SyncEvolution
would normally find in the accounts system.
Usage
-----

View File

@ -1,47 +1,97 @@
PKG_CHECK_MODULES(GSSO, [libgsignon-glib >= 2.0 libaccounts-glib], HAVE_GSSO=yes, HAVE_GSSO=no)
PKG_CHECK_MODULES(GSSO, [libgsignon-glib >= 2.0], HAVE_GSSO=yes, HAVE_GSSO=no)
PKG_CHECK_MODULES(UOA, [libsignon-glib >= 1.7], HAVE_UOA=yes, HAVE_UOA=no)
PKG_CHECK_MODULES(ACCOUNTS, [libaccounts-glib >= 1.8], HAVE_ACCOUNTS=yes, HAVE_ACCOUNTS=no)
def_gsso="no"
def_uoa="no"
def_signon="no"
if test "$HAVE_GSSO" = "yes" && test "$HAVE_ACCOUNTS" = "yes"; then
def_gsso="yes"
fi
if test "$HAVE_UOA" = "yes" && test "$HAVE_ACCOUNTS" = "yes"; then
def_uoa="yes"
fi
if test "$HAVE_GSSO" = "yes"; then
DEFAULT_SIGNON="GSSO"
def_signon="yes"
elif test "$HAVE_UOA" = "yes"; then
DEFAULT_SIGNON="UOA"
def_signon="yes"
fi
AC_SUBST(DEFAULT_SIGNON)
# The backends could be compiled independently. However, the XML example files
# for accounts only get created once and the backends require conflicting values
# in them.
#
# Also, when compiling without dynamic module loading, signonRegister.cpp will
# only be compiled once and thus can activate only one of the actual signon
# implementations.
AC_ARG_ENABLE(gsso,
AS_HELP_STRING([--enable-gsso],
[enables or disables support for the gSSO single-sign-on system; default is on if development files are available]),
[enables or disables support for the gSSO + Accounts single-sign-on system; default is on if development files are available]),
[enable_gsso="$enableval"
test "$enable_gsso" = "yes" || test "$enable_gsso" = "no" || AC_MSG_ERROR([invalid value for --enable-gsso: $enable_gsso])
test "$enable_gsso" = "no" || test "$HAVE_GSSO" = "yes" || AC_MSG_ERROR([required pkg(s) not found that are needed for --enable-gsso])],
enable_gsso="$HAVE_GSSO")
test "$enable_gsso" = "no" || (test "$HAVE_GSSO" = "yes" && test "$HAVE_ACCOUNTS" = "yes" ) || AC_MSG_ERROR([required pkg(s) not found that are needed for --enable-gsso])],
enable_gsso="$def_gsso")
if test $enable_gsso = "yes"; then
AC_DEFINE(USE_GSSO, 1, [use gSSO])
# link into static executables, similar to a SyncSource
SYNCSOURCES="$SYNCSOURCES src/backends/signon/providergsso.la"
GOOGLE_METHOD="oauth"
GOOGLE_MECHANISM="oauth2"
AC_SUBST(GOOGLE_METHOD)
AC_SUBST(GOOGLE_MECHANISM)
if test "$enable_static" = "yes"; then
AC_DEFINE(STATIC_GSSO, 1, [activate gsso])
fi
fi
# conditional compilation in make
AM_CONDITIONAL([USE_GSSO], [test "$enable_gsso" = "yes"])
PKG_CHECK_MODULES(UOA, [libsignon-glib >= 1.7 libaccounts-glib >= 1.8], HAVE_UOA=yes, HAVE_UOA=no)
AC_ARG_ENABLE(uoa,
AS_HELP_STRING([--enable-uoa],
[enables or disables support for the UOA single-sign-on system; default is on if development files are available]),
[enable_uoa="$enableval"
test "$enable_uoa" = "yes" || test "$enable_uoa" = "no" || AC_MSG_ERROR([invalid value for --enable-uoa: $enable_uoa])
test "$enable_uoa" = "no" || test "$HAVE_UOA" = "yes" || AC_MSG_ERROR([required pkg(s) not found that are needed for --enable-uoa])],
enable_uoa="$HAVE_UOA")
test "$enable_uoa" = "no" || (test "$HAVE_UOA" = "yes" && test "$HAVE_ACCOUNTS" = "yes" ) || AC_MSG_ERROR([required pkg(s) not found that are needed for --enable-uoa])],
enable_uoa="$def_uoa")
if test $enable_uoa = "yes"; then
AC_DEFINE(USE_UOA, 1, [use UOA])
# link into static executables, similar to a SyncSource
SYNCSOURCES="$SYNCSOURCES src/backends/signon/provideruoa.la"
GOOGLE_METHOD="oauth2"
GOOGLE_MECHANISM="web_server"
AC_SUBST(GOOGLE_METHOD)
AC_SUBST(GOOGLE_MECHANISM)
if test "$enable_static" = "yes"; then
AC_DEFINE(STATIC_UOA, 1, [activate gsso])
fi
fi
# conditional compilation in make
AM_CONDITIONAL([USE_UOA], [test "$enable_uoa" = "yes"])
AC_ARG_ENABLE(signon,
AS_HELP_STRING([--enable-sign],
[enables or disables support for the UOA or gSSO single-sign-on system without Accounts; default is on if development files are available]),
[enable_signon="$enableval"
test "$enable_signon" = "yes" || test "$enable_signon" = "no" || AC_MSG_ERROR([invalid value for --enable-signon: $enable_signon])
test "$enable_signon" = "no" || test "$HAVE_UOA" = "yes" || test "$HAVE_GSSO" = "yes" || AC_MSG_ERROR([required pkg(s) not found that are needed for --enable-signon])],
enable_signon="$def_signon")
if test $enable_signon = "yes"; then
# link into static executables, similar to a SyncSource
SYNCSOURCES="$SYNCSOURCES src/backends/signon/providersignon.la"
AC_SUBST(GOOGLE_METHOD)
AC_SUBST(GOOGLE_MECHANISM)
if test "$enable_static" = "yes"; then
AC_DEFINE(STATIC_SIGNON, 1, [activate gsso])
fi
fi
# conditional compilation in make
AM_CONDITIONAL([USE_SIGNON], [test "$enable_signon" = "yes"])
AC_ARG_WITH([google-client-id],
AS_HELP_STRING([--with-google-client-id=...], [OAuth2 client ID for google.provider]),
[GOOGLE_CLIENT_ID=$withval],

View File

@ -31,6 +31,9 @@ endif
if USE_UOA
src_backends_signon_libs += src/backends/signon/provideruoa.la
endif
if USE_SIGNON
src_backends_signon_libs += src/backends/signon/providersignon.la
endif
MOSTLYCLEANFILES += $(src_backends_signon_libs)
src_backends_signon_common_sources = \
@ -50,24 +53,32 @@ endif
src_backends_signon_common_libadd = $(SYNCEVOLUTION_LIBS)
src_backends_signon_common_ldflags = -module -avoid-version
src_backends_signon_common_cxxflags = $(SYNCEVOLUTION_CFLAGS)
src_backends_signon_common_cppflags = -I$(top_srcdir)/test $(BACKEND_CPPFLAGS)
src_backends_signon_common_cppflags = -DUSE_SIGNON -I$(top_srcdir)/test $(BACKEND_CPPFLAGS)
src_backends_signon_common_dependencies = src/syncevo/libsyncevolution.la
if USE_GSSO
src_backends_signon_providergsso_la_SOURCES = $(src_backends_signon_common_sources)
src_backends_signon_providergsso_la_LIBADD = $(GSSO_LIBS) $(src_backends_signon_common_libadd)
src_backends_signon_providergsso_la_LIBADD = $(GSSO_LIBS) $(ACCOUNTS_LIBS) $(src_backends_signon_common_libadd)
src_backends_signon_providergsso_la_LDFLAGS = $(src_backends_signon_common_ldflags)
src_backends_signon_providergsso_la_CXXFLAGS = $(GSSO_CFLAGS) $(src_backends_signon_common_cxxflags)
src_backends_signon_providergsso_la_CPPFLAGS = $(src_backends_signon_common_cppflags)
src_backends_signon_providergsso_la_CXXFLAGS = $(GSSO_CFLAGS) $(ACCOUNTS_CFLAGS) $(src_backends_signon_common_cxxflags)
src_backends_signon_providergsso_la_CPPFLAGS = -DUSE_GSSO -DUSE_ACCOUNTS $(src_backends_signon_common_cppflags)
src_backends_signon_providergsso_la_DEPENDENCIES = $(src_backends_signon_common_dependencies)
endif
if USE_UOA
src_backends_signon_provideruoa_la_SOURCES = $(src_backends_signon_common_sources)
src_backends_signon_provideruoa_la_LIBADD = $(UOA_LIBS) $(src_backends_signon_common_libadd)
src_backends_signon_provideruoa_la_LIBADD = $(UOA_LIBS) $(ACCOUNTS_LIBS) $(src_backends_signon_common_libadd)
src_backends_signon_provideruoa_la_LDFLAGS = $(src_backends_signon_common_ldflags)
src_backends_signon_provideruoa_la_CXXFLAGS = $(UOA_CFLAGS) $(src_backends_signon_common_cxxflags)
src_backends_signon_provideruoa_la_CPPFLAGS = $(src_backends_signon_common_cppflags)
src_backends_signon_provideruoa_la_CXXFLAGS = $(UOA_CFLAGS) $(ACCOUNTS_CFLAGS) $(src_backends_signon_common_cxxflags)
src_backends_signon_provideruoa_la_CPPFLAGS = -DUSE_UOA -DUSE_ACCOUNTS $(src_backends_signon_common_cppflags)
src_backends_signon_provideruoa_la_DEPENDENCIES = $(src_backends_signon_common_dependencies)
endif
if USE_SIGNON
src_backends_signon_providersignon_la_SOURCES = $(src_backends_signon_common_sources)
src_backends_signon_providersignon_la_LIBADD = $($(DEFAULT_SIGNON)_LIBS) $(ACCOUNTS_LIBS) $(src_backends_signon_common_libadd)
src_backends_signon_providersignon_la_LDFLAGS = $(src_backends_signon_common_ldflags)
src_backends_signon_providersignon_la_CXXFLAGS = $($(DEFAULT_SIGNON)_CFLAGS) $(ACCOUNTS_CFLAGS) $(src_backends_signon_common_cxxflags)
src_backends_signon_providersignon_la_CPPFLAGS = -DUSE_$(DEFAULT_SIGNON) $(src_backends_signon_common_cppflags)
src_backends_signon_providersignon_la_DEPENDENCIES = $(src_backends_signon_common_dependencies)
endif

View File

@ -21,9 +21,7 @@
#include <syncevo/IdentityProvider.h>
#define USE_SIGNON (defined USE_GSSO || defined USE_UOA)
#if USE_SIGNON
#ifdef USE_SIGNON
#ifdef USE_GSSO
#include "libgsignon-glib/signon-auth-service.h"
#include "libgsignon-glib/signon-identity.h"
@ -31,11 +29,13 @@
#include "libsignon-glib/signon-auth-service.h"
#include "libsignon-glib/signon-identity.h"
#endif // USE_GSSO
#ifdef USE_ACCOUNTS
#include "libaccounts-glib/ag-account.h"
#include "libaccounts-glib/ag-account-service.h"
#include <libaccounts-glib/ag-auth-data.h>
#include <libaccounts-glib/ag-service.h>
#include <libaccounts-glib/ag-manager.h>
#endif
#include <syncevo/GLibSupport.h>
#include <pcrecpp.h>
@ -45,11 +45,15 @@
SE_GOBJECT_TYPE(SignonAuthService)
SE_GOBJECT_TYPE(SignonAuthSession)
SE_GOBJECT_TYPE(SignonIdentity)
#ifdef USE_ACCOUNTS
SE_GOBJECT_TYPE(AgAccount)
SE_GOBJECT_TYPE(AgAccountService)
SE_GOBJECT_TYPE(AgManager)
SE_GLIB_TYPE(AgService, ag_service)
SE_GLIB_TYPE(AgAuthData, ag_auth_data)
#endif
SE_GLIB_TYPE(GHashTable, g_hash_table)
#endif // USE_SIGNON
@ -59,7 +63,9 @@ SE_BEGIN_CXX
#ifdef USE_SIGNON
#ifdef USE_ACCOUNTS
typedef GListCXX<AgService, GList, ag_service_unref> ServiceListCXX;
#endif
/**
* Simple auto_ptr for GVariant.
@ -196,6 +202,7 @@ public:
virtual std::string getUsername() const { return ""; }
};
#ifdef USE_ACCOUNTS
class StoreIdentityData
{
public:
@ -343,6 +350,69 @@ boost::shared_ptr<AuthProvider> createSignonAuthProvider(const InitStateString &
return provider;
}
#else // USE_ACCOUNTS
boost::shared_ptr<AuthProvider> createSignonAuthProvider(const InitStateString &username,
const InitStateString &password)
{
// Expected content of parameter GVariant.
boost::shared_ptr<GVariantType> hashtype(g_variant_type_new("a{sv}"), g_variant_type_free);
// 'username' is the part after signon: which we can parse directly.
GErrorCXX gerror;
GVariantCXX parametersVar(g_variant_parse(hashtype.get(), username.c_str(), NULL, NULL, gerror));
if (!parametersVar) {
gerror.throwError(SE_HERE, "parsing 'signon:' username");
}
GHashTableCXX parameters(Variant2HashTable(parametersVar), TRANSFER_REF);
// Extract the values that we expect in the parameters hash.
guint32 signonID;
const char *method;
const char *mechanism;
GVariant *value;
value = (GVariant *)g_hash_table_lookup(parameters, "identity");
if (!value ||
!g_variant_type_equal(G_VARIANT_TYPE_UINT32, g_variant_get_type(value))) {
SE_THROW("need 'identity: <numeric ID>' in 'signon:' parameters");
}
signonID = g_variant_get_uint32(value);
value = (GVariant *)g_hash_table_lookup(parameters, "method");
if (!value ||
!g_variant_type_equal(G_VARIANT_TYPE_STRING, g_variant_get_type(value))) {
SE_THROW("need 'method: <string>' in 'signon:' parameters");
}
method = g_variant_get_string(value, NULL);
value = (GVariant *)g_hash_table_lookup(parameters, "mechanism");
if (!value ||
!g_variant_type_equal(G_VARIANT_TYPE_STRING, g_variant_get_type(value))) {
SE_THROW("need 'mechanism: <string>' in 'signon:' parameters");
}
mechanism = g_variant_get_string(value, NULL);
value = (GVariant *)g_hash_table_lookup(parameters, "session");
if (!value ||
!g_variant_type_equal(hashtype.get(), g_variant_get_type(value))) {
SE_THROW("need 'session: <hash>' in 'signon:' parameters");
}
GHashTableCXX sessionData(Variant2HashTable(value), TRANSFER_REF);
SE_LOG_DEBUG(NULL, "using identity %u, method %s, mechanism %s",
signonID, method, mechanism);
SignonIdentityCXX identity(signon_identity_new_from_db(signonID), TRANSFER_REF);
SE_LOG_DEBUG(NULL, "using signond identity %d", signonID);
SignonAuthSessionCXX authSession(signon_identity_create_session(identity, method, gerror), TRANSFER_REF);
boost::shared_ptr<AuthProvider> provider(new SignonAuthProvider(authSession, sessionData, mechanism));
return provider;
}
#endif // USE_ACCOUNTS
#endif // USE_SIGNON
SE_END_CXX

View File

@ -26,7 +26,7 @@
#include <syncevo/declarations.h>
SE_BEGIN_CXX
#if defined(USE_GSSO) || defined(USE_UOA)
#if defined(USE_GSSO) || defined(USE_UOA) || defined(USE_SIGNON) || defined(STATIC_GSSO) || defined(STATIC_UOA) || defined(STATIC_SIGNON)
static class SignonProvider : public IdentityProvider
{
public:
@ -36,35 +36,43 @@ public:
// one to use. If it turns out that the two will never be installed at the
// same time, then this perhaps should be "signon" instead, which then would
// pick either a gSSO or UAO backend depending on which is available.
#ifdef USE_GSSO
#if defined(USE_GSSO) || defined(STATIC_GSSO)
IdentityProvider("gsso",
"gsso:<numeric account ID>[,<service name>]\n"
" Authentication using libgsignond + libaccounts,\n"
" using an account created and managed with libaccounts.\n"
" The service name is optional. If not given, the\n"
" settings from the account will be used.")
#elif defined USE_UOA
#elif defined(USE_UOA) || defined(STATIC_UOA)
IdentityProvider("uoa",
"uoa:<numeric account ID>[,<service name>]\n"
" Authentication using libsignon + libaccounts,\n"
" using an account created and managed with libaccounts.\n"
" The service name is optional. If not given, the\n"
" settings from the account will be used.")
#endif // USE_GSSO
#elif defined(USE_SIGNON) || defined(STATIC_SIGNON)
IdentityProvider("signon",
"signon:<parameters>]\n"
" Authentication using libgsignond with an identity created\n"
" before calling SyncEvolution. The <parameters> string is a\n"
" GVariant text dump suitable for g_variant_parse() (see\n"
" https://developer.gnome.org/glib/stable/gvariant-text.html).\n"
" It must contain a hash with keys 'identity', 'method', \n"
" 'session' and 'mechanism'. The first two values are used for\n"
" signon_identity_create_session(), the last one for\n"
" signon_auth_session_process_async().\n")
#endif
{}
virtual boost::shared_ptr<AuthProvider> create(const InitStateString &username,
const InitStateString &password)
{
// Returning NULL if not enabled...
boost::shared_ptr<AuthProvider> provider;
#if (defined USE_GSSO || defined USE_UOA)
provider = createSignonAuthProvider(username, password);
#endif
return provider;
}
} gsso;
#endif // USE_GSSO || USE_UOA
#endif // one signon-based provider enabled
SE_END_CXX