signon: revert accidental inclusion in master branch

The code wasn't ready and got pushed as part of some other change.
This commit is contained in:
Patrick Ohly 2013-08-02 22:02:03 +02:00
parent c0212c4585
commit b7fa64f15c
35 changed files with 506 additions and 1973 deletions

View File

@ -154,11 +154,7 @@ ActiveSyncSource::Databases ActiveSyncSource::getDatabases()
{
Databases result;
// do a scan if username is set
UserIdentity identity = m_context->getSyncUser();
if (identity.m_provider != USER_IDENTITY_PLAIN_TEXT) {
throwError(StringPrintf("%s: only the 'user:<account ID in gconf>' format is supported by ActiveSync", identity.toString().c_str()));
}
const std::string &account = identity.m_identity;
std::string account = m_context->getSyncUsername();
if (!account.empty()) {
@ -195,12 +191,7 @@ std::string ActiveSyncSource::lookupFolder(std::string folder) {
void ActiveSyncSource::open()
{
// extract account ID and throw error if missing
UserIdentity identity = m_context->getSyncUser();
if (identity.m_provider != USER_IDENTITY_PLAIN_TEXT) {
throwError(StringPrintf("%s: only the 'user:<account ID in gconf>' format is supported by ActiveSync", identity.toString().c_str()));
}
const std::string &username = identity.m_identity;
std::string username = m_context->getSyncUsername();
std::string folder = getDatabaseID();
SE_LOG_DEBUG(NULL,
"using eas sync account %s from config %s with folder %s",

View File

@ -195,7 +195,7 @@ static TestingSyncSource *createEASSource(const ClientTestConfig::createsource_t
// up sharing change tracking with source A.
if (!isSourceA) {
ActiveSyncSource *eassource = static_cast<ActiveSyncSource *>(res.get());
std::string account = eassource->getSyncConfig().getSyncUser().toString();
std::string account = eassource->getSyncConfig().getSyncUsername();
account += "_B";
eassource->getSyncConfig().setSyncUsername(account, true);
}

View File

@ -26,7 +26,6 @@ using namespace std;
#include "config.h"
#include "EvolutionSyncSource.h"
#include <syncevo/IdentityProvider.h>
#ifdef ENABLE_EBOOK
@ -243,24 +242,22 @@ void EvolutionContactSource::open()
// users are not expected to configure an authentication method,
// so pick one automatically if the user indicated that he wants authentication
// by setting user or password
UserIdentity identity = getUser();
InitStateString passwd = getPassword();
if (identity.wasSet() || passwd.wasSet()) {
std::string user = getUser(),
passwd = getPassword();
if (!user.empty() || !passwd.empty()) {
GList *authmethod;
if (!e_book_get_supported_auth_methods(m_addressbook, &authmethod, gerror)) {
throwError("getting authentication methods", gerror);
}
while (authmethod) {
// map identity + password to plain username/password credentials
Credentials cred = IdentityProviderCredentials(identity, passwd);
const char *method = (const char *)authmethod->data;
SE_LOG_DEBUG(getDisplayName(), "trying authentication method \"%s\", user %s, password %s",
method,
identity.wasSet() ? "configured" : "not configured",
passwd.wasSet() ? "configured" : "not configured");
!user.empty() ? "configured" : "not configured",
!passwd.empty() ? "configured" : "not configured");
if (e_book_authenticate_user(m_addressbook,
cred.m_username.c_str(),
cred.m_password.c_str(),
user.c_str(),
passwd.c_str(),
method,
gerror)) {
SE_LOG_DEBUG(getDisplayName(), "authentication succeeded");

View File

@ -52,7 +52,7 @@
SE_BEGIN_CXX
#define OBC_SERVICE "org.openobex.client" // obexd < 0.47
#define OBC_SERVICE_NEW "org.bluez.obex.client" // obexd >= 0.47, including 0.48 (with yet another slight API change!)
#define OBC_SERVICE_NEW "org.bluez.obex.client" // obexd >= 0.47
#define OBC_SERVICE_NEW5 "org.bluez.obex" // obexd in Bluez 5.0
#define OBC_CLIENT_INTERFACE "org.openobex.Client"
#define OBC_CLIENT_INTERFACE_NEW "org.bluez.obex.Client"
@ -517,18 +517,10 @@ boost::shared_ptr<PullAll> PbapSession::startPullAll(PullData pullData)
break;
}
bool pullAllWithFiltersFallback = false;
if (m_obexAPI == OBEXD_OLD ||
m_obexAPI == OBEXD_NEW) {
try {
GDBusCXX::DBusClientCall0(*m_session, "SetFilter")(filter);
GDBusCXX::DBusClientCall0(*m_session, "SetFormat")(format);
} catch (...) {
// Ignore failure, can happen with 0.48. Instead send filter together
// with PullAll method call.
Exception::handle(HANDLE_EXCEPTION_NO_ERROR);
pullAllWithFiltersFallback = true;
}
GDBusCXX::DBusClientCall0(*m_session, "SetFilter")(filter);
GDBusCXX::DBusClientCall0(*m_session, "SetFormat")(format);
}
boost::shared_ptr<PullAll> state(new PullAll);
@ -544,13 +536,8 @@ boost::shared_ptr<PullAll> PbapSession::startPullAll(PullData pullData)
SE_LOG_DEBUG(NULL, "Created temporary file for PullAll %s", state->m_tmpFile.filename().c_str());
GDBusCXX::DBusClientCall1<std::pair<GDBusCXX::DBusObject_t, Params> > pullall(*m_session, "PullAll");
std::pair<GDBusCXX::DBusObject_t, Params> tuple =
pullAllWithFiltersFallback ?
// 0.48
GDBusCXX::DBusClientCall1<std::pair<GDBusCXX::DBusObject_t, Params> >(*m_session, "PullAll")(state->m_tmpFile.filename(), currentFilter) :
m_obexAPI == OBEXD_NEW ?
// 0.47
GDBusCXX::DBusClientCall1<std::pair<GDBusCXX::DBusObject_t, Params> >(*m_session, "PullAll")(state->m_tmpFile.filename()) :
// 5.x
GDBusCXX::DBusClientCall2<GDBusCXX::DBusObject_t, Params>(*m_session, "PullAll")(state->m_tmpFile.filename(), currentFilter);
const GDBusCXX::DBusObject_t &transfer = tuple.first;
const Params &properties = tuple.second;
@ -565,7 +552,6 @@ boost::shared_ptr<PullAll> PbapSession::startPullAll(PullData pullData)
state->m_tmpFileOffset = 0;
state->m_session = m_self.lock();
} else {
// < 0.47
GDBusCXX::DBusClientCall1<std::string> pullall(*m_session, "PullAll");
state->m_buffer = pullall();
state->addVCards(0, state->m_buffer);

View File

@ -1,90 +0,0 @@
Google CalDAV/CardDAV via OAuth2
================================
Setup
-----
SyncEvolution needs an active account for Google. Services for CardDAV
and CalDAV can be used, but are not required if the provider was
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.
When compiling, it is possible to insert valid client ID+secret into
these files using the --with-google-client-* configure options. The
example files then get installed into
$docdir/accounts/[providers|services|service_types].
SyncEvolution binaries from syncevolution.org are compiled with valid
client ID+secret. Distributions are encouraged to *not* reuse the same
credentials and register themselves with Google instead. That way,
abuse of the credentials affects less users and the quota available to
all users of the same credentials does not get exhausted as quickly.
Users of the syncevolution.org binaries need to install the
/usr/share/doc/syncevolution/accounts themselves, typically in their
home directory:
cp -a /usr/share/doc/syncevolution/accounts ~/.local/share/accounts
A "google" account needs to be created and enabled:
ag-tool create-account google google-for-syncevolution
ID=`ag-tool list-accounts | grep google-for-syncevolution | sed -e 's/ .*//'`
ag-tool enable-account $ID
Optionally, one can also enable the services:
ag-tool enable-service $ID google-carddav
ag-tool enable-service $ID google-caldav
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.
Usage
-----
OAuth2 authentication with gSSO is enabled by setting username or
databaseUser to a string of the format
gsso:<numeric accountID>[,<service name>]
When used without service name, SyncEvolution will ask for access to
CalDAV and CardDAV only once. When used with service name, it will ask
for access to only CalDAV or CardDAV, but it will have to ask twice
(once for each service).
The base URL for each service currently needs to be given via syncURL:
syncevolution --print-databases \
backend=carddav \
username=gsso:$ID,google-carddav \
syncURL=https://www.googleapis.com/.well-known/carddav
src/syncevolution --print-databases \
backend=caldav \
username=gsso:$ID,google-caldav \
syncURL=https://apidata.googleusercontent.com/caldav/v2
Once that works, follow the "CalDAV and CardDAV" instructions from the
README with the different username and syncURL.
Debugging
---------
Add --daemon=no to the command line to prevent shifting the actual
command executing into syncevo-dbus-server and (from there)
syncevo-dbus-helper.
Warning: gsignond limits access to the identity to the executable which created
it. One has to use different accounts for syncevolution with and without
--daemon=no and another account when using valgrind.
Set SYNCEVOLUTION_DEBUG=1 to see all debug messages and increase the
loglevel to see HTTP requests:
SYNCEVOLUTION_DEBUG=1 syncevolution --daemon=no \
loglevel=4 \
--print-databases \
...

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<service-type id="caldav">
<name>CalDAV</name>
<description>Integrate your events, tasks, memos via CalDAV</description>
</service-type>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<service-type id="carddav">
<name>CardDAV</name>
<description>Integrate your contacts via CardDAV</description>
</service-type>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<service id="google-contacts">
<type>contacts</type>
<name>Google Contacts</name>
<provider>google</provider>
<!-- default settings (account settings have precedence over these) -->
<template>
<group name="auth">
<setting name="method">oauth</setting>
<setting name="mechanism">oauth2</setting>
<group name="oauth">
<group name="oauth2">
<setting name="Scope">email https://www.googleapis.com/auth/calendar</setting>
<!-- workaround for (g)signond token cache issue: use different client IDs for each service and the provider -->
<setting name="ClientId">@GOOGLE_CLIENT_ID_CALDAV@</setting>
<setting name="ClientSecret">@GOOGLE_CLIENT_SECRET_CALDAV@</setting>
</group>
</group>
</group>
</template>
</service>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<service id="google-contacts">
<type>contacts</type>
<name>Google Contacts</name>
<provider>google</provider>
<!-- default settings (account settings have precedence over these) -->
<template>
<group name="auth">
<setting name="method">oauth</setting>
<setting name="mechanism">oauth2</setting>
<group name="oauth">
<group name="oauth2">
<setting name="Scope">email https://www.googleapis.com/auth/carddav</setting>
<!-- workaround for (g)signond token cache issue: use different client IDs for each service and the provider -->
<setting name="ClientId">@GOOGLE_CLIENT_ID_CARDDAV@</setting>
<setting name="ClientSecret">@GOOGLE_CLIENT_SECRET_CARDDAV@</setting>
</group>
</group>
</group>
</template>
</service>

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<provider id="google-contacts">
<name>Google Contacts</name>
<!-- default settings (account settings have precedence over these) -->
<template>
<group name="auth">
<setting name="method">oauth</setting>
<setting name="mechanism">oauth2</setting>
<group name="oauth">
<group name="oauth2">
<setting name="ResponseType">code</setting>
<setting name="AuthHost">accounts.google.com</setting>
<setting name="AuthPath">/o/oauth2/auth</setting>
<setting name="RedirectUri">http://localhost</setting>
<setting name="Scope">email https://www.googleapis.com/auth/carddav https://www.googleapis.com/auth/calendar</setting>
<setting name="TokenHost">accounts.google.com</setting>
<setting name="TokenPath">/o/oauth2/token</setting>
<setting name="ClientId">@GOOGLE_CLIENT_ID@</setting>
<setting name="ClientSecret">@GOOGLE_CLIENT_SECRET@</setting>
<setting type="boolean" name="ForceClientAuthViaRequestBody">true</setting>
</group>
</group>
</group>
</template>
</provider>

View File

@ -1,56 +0,0 @@
PKG_CHECK_MODULES(GSSO, [libgsignon-glib libaccounts-glib], HAVE_GSSO=yes, HAVE_GSSO=no)
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]),
[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")
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/gsso/providergsso.la"
fi
# conditional compilation in make
AM_CONDITIONAL([USE_GSSO], [test "$use_gsso" = "yes"])
AC_ARG_WITH([google-client-id],
[OAuth2 client ID for google.provider],
[GOOGLE_CLIENT_ID=$withval],
[GOOGLE_CLIENT_ID=insert-your-client-id-here])
AC_ARG_WITH([google-client-secret],
[OAuth2 client secret for google.provider],
[GOOGLE_CLIENT_SECRET=$withval],
[GOOGLE_CLIENT_SECRET=insert-your-client-secret-here])
AC_SUBST(GOOGLE_CLIENT_ID)
AC_SUBST(GOOGLE_CLIENT_SECRET)
AC_ARG_WITH([google-client-id-caldav],
[OAuth2 client ID for google-caldav.service],
[GOOGLE_CLIENT_ID_CALDAV=$withval],
[GOOGLE_CLIENT_ID_CALDAV=insert-your-client-id-here])
AC_ARG_WITH([google-client-secret],
[OAuth2 client secret for google-caldav.service],
[GOOGLE_CLIENT_SECRET_CALDAV=$withval],
[GOOGLE_CLIENT_SECRET_CALDAV=insert-your-client-secret-here])
AC_SUBST(GOOGLE_CLIENT_ID_CALDAV)
AC_SUBST(GOOGLE_CLIENT_SECRET_CALDAV)
AC_ARG_WITH([google-client-id-carddav],
[OAuth2 client ID for google-carddav.service],
[GOOGLE_CLIENT_ID_CARDDAV=$withval],
[GOOGLE_CLIENT_ID_CARDDAV=insert-your-client-id-here])
AC_ARG_WITH([google-client-secret],
[OAuth2 client secret for google-carddav.service],
[GOOGLE_CLIENT_SECRET_CARDDAV=$withval],
[GOOGLE_CLIENT_SECRET_CARDDAV=insert-your-client-secret-here])
AC_SUBST(GOOGLE_CLIENT_ID_CARDDAV)
AC_SUBST(GOOGLE_CLIENT_SECRET_CARDDAV)
AC_CONFIG_FILES([
src/backends/signon/accounts/google-caldav.service
src/backends/signon/accounts/google-carddav.service
src/backends/signon/accounts/google.provider
])

View File

@ -1,47 +0,0 @@
dist_noinst_DATA += src/backends/signon/configure-sub.in \
src/backends/signon/README \
$(NONE)
# Distribute the .in file, install the generated one.
accounts_servicesdir = $(docdir)/accounts/services
nodist_accounts_services_DATA = \
src/backends/signon/accounts/google-caldav.service \
src/backends/signon/accounts/google-carddav.service \
$(NONE)
dist_noinst_DATA += $(nodist_accounts_services_DATA:%:%.in)
# Distribute the .in file, install the generated one.
accounts_providersdir = $(docdir)/accounts/providers
nodist_accounts_providers_DATA = \
src/backends/signon/accounts/google.provider \
$(NONE)
dist_noinst_DATA += $(nodist_accounts_providers_DATA:%:%.in)
# Distribute and install the same file.
accounts_service_typesdir = $(docdir)/accounts/service_types
dist_accounts_service_types_DATA = \
src/backends/signon/accounts/caldav.service-type \
src/backends/signon/accounts/carddav.service-type \
$(NONE)
src_backends_signon_lib = src/backends/signon/providergsso.la
MOSTLYCLEANFILES += $(src_backends_signon_lib)
src_backends_signon_providergsso_la_SOURCES = \
src/backends/signon/signon.cpp
if ENABLE_MODULES
src_backends_signon_backenddir = $(BACKENDS_DIRECTORY)
src_backends_signon_backend_LTLIBRARIES = $(src_backends_signon_lib)
src_backends_signon_providergsso_la_SOURCES += \
src/backends/signon/signonRegister.cpp
else
noinst_LTLIBRARIES += $(src_backends_signon_lib)
endif
src_backends_signon_providergsso_la_LIBADD = $(GSSO_LIBS) $(SYNCEVOLUTION_LIBS)
src_backends_signon_providergsso_la_LDFLAGS = -module -avoid-version
src_backends_signon_providergsso_la_CXXFLAGS = $(GSSO_CFLAGS) $(SYNCEVOLUTION_CFLAGS)
src_backends_signon_providergsso_la_CPPFLAGS = -I$(top_srcdir)/test $(BACKEND_CPPFLAGS)
src_backends_signon_providergsso_la_DEPENDENCIES = src/syncevo/libsyncevolution.la

View File

@ -1,334 +0,0 @@
/*
* Copyright (C) 2013 Intel Corporation
*
* This library 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) version 3.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <config.h>
#include <syncevo/IdentityProvider.h>
#ifdef USE_GSSO
#include "libgsignon-glib/signon-auth-service.h"
#include "libgsignon-glib/signon-identity.h"
#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>
#include <syncevo/GLibSupport.h>
#include <pcrecpp.h>
#include <boost/lambda/core.hpp>
SE_GOBJECT_TYPE(SignonAuthService)
SE_GOBJECT_TYPE(SignonAuthSession)
SE_GOBJECT_TYPE(SignonIdentity)
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)
SE_GLIB_TYPE(GHashTable, g_hash_table)
#endif // USE_GSSO
#include <syncevo/declarations.h>
SE_BEGIN_CXX
#ifdef USE_GSSO
typedef GListCXX<AgService, GList, ag_service_unref> ServiceListCXX;
/**
* Simple auto_ptr for GVariant.
*/
class GVariantCXX : boost::noncopyable
{
GVariant *m_var;
public:
/** takes over ownership */
GVariantCXX(GVariant *var = NULL) : m_var(var) {}
~GVariantCXX() { if (m_var) { g_variant_unref(m_var); } }
operator GVariant * () { return m_var; }
GVariantCXX &operator = (GVariant *var) {
if (m_var != var) {
if (m_var) {
g_variant_unref(m_var);
}
m_var = var;
}
return *this;
}
};
// Originally from google-oauth2-example.c, which is also under LGPL
// 2.1, or any later version.
static GVariant *HashTable2Variant(const GHashTable *hashTable) throw ()
{
GVariantBuilder builder;
GHashTableIter iter;
const gchar *key;
GVariant *value;
if (!hashTable) {
return NULL;
}
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
g_hash_table_iter_init(&iter, (GHashTable *)hashTable);
while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) {
g_variant_builder_add(&builder, "{sv}", key, g_variant_ref(value));
}
return g_variant_builder_end(&builder);
}
/**
* The created GHashTable maps strings to GVariants which are
* reference counted, so when adding or setting values, use g_variant_ref_sink(g_variant_new_...()).
*/
static GHashTable *Variant2HashTable(GVariant *variant) throw ()
{
if (!variant) {
return NULL;
}
GHashTable *hashTable;
GVariantIter iter;
GVariant *value;
gchar *key;
hashTable = g_hash_table_new_full(g_str_hash,
g_str_equal,
g_free,
(GDestroyNotify)g_variant_unref);
g_variant_iter_init(&iter, variant);
while (g_variant_iter_next(&iter, "{sv}", &key, &value)) {
g_hash_table_insert(hashTable, g_strdup(key), g_variant_ref(value));
}
return hashTable;
}
class SignonAuthProvider : public AuthProvider
{
SignonAuthSessionCXX m_authSession;
GHashTableCXX m_sessionData;
std::string m_mechanism;
public:
SignonAuthProvider(const SignonAuthSessionCXX &authSession,
const GHashTableCXX &sessionData,
const std::string &mechanism) :
m_authSession(authSession),
m_sessionData(sessionData),
m_mechanism(mechanism)
{}
virtual bool methodIsSupported(AuthMethod method) const { return method == AUTH_METHOD_OAUTH2; }
virtual Credentials getCredentials() const { SE_THROW("only OAuth2 is supported"); }
virtual std::string getOAuth2Bearer(int failedTokens) const
{
SE_LOG_DEBUG(NULL, "retrieving OAuth2 token, attempt %d", failedTokens);
// Retry login if even the refreshed token failed.
g_hash_table_insert(m_sessionData, g_strdup("UiPolicy"),
g_variant_ref_sink(g_variant_new_uint32(failedTokens >= 2 ? SIGNON_POLICY_REQUEST_PASSWORD : 0)));
GVariantCXX resultDataVar;
GErrorCXX gerror;
// Enforce normal reference counting via _ref_sink.
GVariantCXX sessionDataVar(g_variant_ref_sink(HashTable2Variant(m_sessionData)));
PlainGStr buffer(g_variant_print(sessionDataVar, true));
SE_LOG_DEBUG(NULL, "asking for OAuth2 token with method %s, mechanism %s and parameters %s",
signon_auth_session_get_method(m_authSession),
m_mechanism.c_str(),
buffer.get());
#define signon_auth_session_process_async_finish signon_auth_session_process_finish
SYNCEVO_GLIB_CALL_SYNC(resultDataVar, gerror, signon_auth_session_process_async,
m_authSession, sessionDataVar, m_mechanism.c_str(), NULL);
buffer.reset(resultDataVar ? g_variant_print(resultDataVar, true) : NULL);
SE_LOG_DEBUG(NULL, "OAuth2 token result: %s, %s",
buffer ? buffer.get() : "<<null>>",
gerror ? gerror->message : "???");
if (!resultDataVar || gerror) {
SE_THROW_EXCEPTION_STATUS(StatusException,
StringPrintf("could not obtain OAuth2 token: %s", gerror ? gerror->message : "???"),
STATUS_FORBIDDEN);
}
GHashTableCXX resultData(Variant2HashTable(resultDataVar), TRANSFER_REF);
GVariant *tokenVar = static_cast<GVariant *>(g_hash_table_lookup(resultData, (gpointer)"AccessToken"));
if (!tokenVar) {
SE_THROW("no AccessToken in OAuth2 response");
}
const char *token = g_variant_get_string(tokenVar, NULL);
if (!token) {
SE_THROW("AccessToken did not contain a string value");
}
return token;
}
virtual std::string getUsername() const { return ""; }
};
#endif // USE_GSSO
class StoreIdentityData
{
public:
StoreIdentityData() : m_running(true) {}
bool m_running;
guint32 m_id;
GErrorCXX m_gerror;
};
static void StoreIdentityCB(SignonIdentity *self,
guint32 id,
const GError *error,
gpointer userData)
{
StoreIdentityData *data = reinterpret_cast<StoreIdentityData *>(userData);
data->m_running = false;
data->m_id = id;
data->m_gerror = error;
}
boost::shared_ptr<AuthProvider> createGSSOAuthProvider(const InitStateString &username,
const InitStateString &password)
{
// Returning NULL if not enabled...
boost::shared_ptr<AuthProvider> provider;
#ifdef USE_GSSO
// Split username into <account ID> and <service name>.
// Be flexible and allow leading/trailing white space.
// Comma is optional.
static const pcrecpp::RE re("^\\s*(\\d+)\\s*,?\\s*(.*)\\s*$");
AgAccountId accountID;
std::string serviceName;
if (!re.FullMatch(username, &accountID, &serviceName)) {
SE_THROW(StringPrintf("username must have the format gsso:<account ID>,<service name>: %s",
username.c_str()));
}
SE_LOG_DEBUG(NULL, "looking up account ID %d and service '%s'",
accountID,
serviceName.c_str());
AgManagerCXX manager(ag_manager_new(), TRANSFER_REF);
GErrorCXX gerror;
AgAccountCXX account(ag_manager_load_account(manager, accountID, gerror), TRANSFER_REF);
if (!account) {
gerror.throwError(StringPrintf("loading account with ID %d from %s failed",
accountID,
username.c_str()));
}
if (!ag_account_get_enabled(account)) {
SE_THROW(StringPrintf("account with ID %d from %s is disabled, refusing to use it",
accountID,
username.c_str()));
}
AgAccountServiceCXX accountService;
if (serviceName.empty()) {
accountService = AgAccountServiceCXX::steal(ag_account_service_new(account, NULL));
} else {
ServiceListCXX services(ag_account_list_enabled_services(account));
BOOST_FOREACH (AgService *service, services) {
const char *name = ag_service_get_name(service);
SE_LOG_DEBUG(NULL, "enabled service: %s", name);
if (serviceName == name) {
accountService = AgAccountServiceCXX::steal(ag_account_service_new(account, service));
// Do *not* select the service for reading/writing properties.
// AgAccountService does this internally, and when we create
// a new identity below, we want it to be shared by all
// services so that the user only needs to log in once.
// ag_account_select_service(account, service);
break;
}
}
}
if (!accountService) {
SE_THROW(StringPrintf("service '%s' in account with ID %d not found or not enabled",
serviceName.c_str(),
accountID));
}
AgAuthDataCXX authData(ag_account_service_get_auth_data(accountService), TRANSFER_REF);
// SignonAuthServiceCXX authService(signon_auth_service_new(), TRANSFER_REF);
guint signonID = ag_auth_data_get_credentials_id(authData);
const char *method = ag_auth_data_get_method(authData);
const char *mechanism = ag_auth_data_get_mechanism(authData);
// Check that the service has a credentials ID. If not, create it and
// store its ID permanently.
if (!signonID) {
SE_LOG_DEBUG(NULL, "have to create signon identity");
SignonIdentityCXX identity(signon_identity_new(NULL), TRANSFER_REF);
boost::shared_ptr<SignonIdentityInfo> identityInfo(signon_identity_info_new(), signon_identity_info_free);
signon_identity_info_set_caption(identityInfo.get(),
StringPrintf("created by SyncEvolution for account #%d and service %s",
accountID,
serviceName.empty() ? "<<none>>" : serviceName.c_str()).c_str());
const gchar *mechanisms[] = { mechanism ? mechanism : "*", NULL };
signon_identity_info_set_method(identityInfo.get(), method, mechanisms);
StoreIdentityData data;
signon_identity_store_credentials_with_info(identity, identityInfo.get(),
StoreIdentityCB, &data);
GRunWhile(boost::lambda::var(data.m_running));
if (!data.m_id || data.m_gerror) {
SE_THROW(StringPrintf("failed to create signon identity: %s",
data.m_gerror ? data.m_gerror->message : "???"));
}
// Now store in account.
static const char CREDENTIALS_ID[] = "CredentialsId";
ag_account_set_variant(account, CREDENTIALS_ID, g_variant_new_uint32(data.m_id));
#define ag_account_store_async_finish ag_account_store_finish
gboolean res;
SYNCEVO_GLIB_CALL_SYNC(res, gerror, ag_account_store_async,
account, NULL);
if (!res) {
gerror.throwError("failed to store account");
}
authData = AgAuthDataCXX::steal(ag_account_service_get_auth_data(accountService));
signonID = ag_auth_data_get_credentials_id(authData);
if (!signonID) {
SE_THROW("still no signonID?!");
}
method = ag_auth_data_get_method(authData);
mechanism = ag_auth_data_get_mechanism(authData);
}
GVariantCXX sessionDataVar(ag_auth_data_get_login_parameters(authData, NULL));
GHashTableCXX sessionData(Variant2HashTable(sessionDataVar), TRANSFER_REF);
SignonIdentityCXX identity(signon_identity_new_from_db(signonID, NULL), TRANSFER_REF);
SE_LOG_DEBUG(NULL, "using signond identity %d", signonID);
SignonAuthSessionCXX authSession(signon_identity_create_session(identity, method, gerror), TRANSFER_REF);
// TODO (?): retrieve start URL from account system
provider.reset(new SignonAuthProvider(authSession, sessionData, mechanism));
#endif // USE_GSSO
return provider;
}
SE_END_CXX

View File

@ -1,34 +0,0 @@
/*
* Copyright (C) 2013 Intel Corporation
*
* This library 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) version 3.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef INCL_SYNC_EVOLUTION_SIGNON_AUTH_PROVIDER
# define INCL_SYNC_EVOLUTION_SIGNON_AUTH_PROVIDER
#include <syncevo/util.h>
#include <boost/shared_ptr.hpp>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
class AuthProvider;
boost::shared_ptr<AuthProvider> createGSSOAuthProvider(const InitStateString &username,
const InitStateString &password);
SE_END_CXX
#endif

View File

@ -1,54 +0,0 @@
/*
* Copyright (C) 2013 Intel Corporation
*
* This library 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) version 3.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <config.h>
#include "signon.h"
#include <syncevo/IdentityProvider.h>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
static class gSSOProvider : public IdentityProvider
{
public:
gSSOProvider() :
// This uses "gsso" at the moment. The advantage of that is that if
// gSSO and UOA were installed in parallel, the user could choose which
// 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.
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.")
{}
virtual boost::shared_ptr<AuthProvider> create(const InitStateString &username,
const InitStateString &password)
{
boost::shared_ptr<AuthProvider> provider = createGSSOAuthProvider(username, password);
return provider;
}
} gsso;
SE_END_CXX

View File

@ -22,7 +22,6 @@
#include <syncevo/LogRedirect.h>
#include <syncevo/SmartPtr.h>
#include <syncevo/SuspendFlags.h>
#include <syncevo/IdentityProvider.h>
#include <sstream>
@ -191,7 +190,6 @@ std::string Status2String(const ne_status *status)
Session::Session(const boost::shared_ptr<Settings> &settings) :
m_forceAuthorizationOnce(false),
m_credentialsSent(false),
m_oauthTokenRejections(0),
m_settings(settings),
m_debugging(false),
m_session(NULL),
@ -295,17 +293,9 @@ boost::shared_ptr<Session> Session::create(const boost::shared_ptr<Settings> &se
int Session::getCredentials(void *userdata, const char *realm, int attempt, char *username, char *password) throw()
{
try {
Session *session = static_cast<Session *>(userdata);
boost::shared_ptr<AuthProvider> authProvider = session->m_settings->getAuthProvider();
if (authProvider && authProvider->methodIsSupported(AuthProvider::AUTH_METHOD_OAUTH2)) {
// We have to fail here because we cannot provide neon
// with a username/password combination. Instead we rely
// on the "retry request" mechanism to resend the request
// with a fresh token.
SE_LOG_DEBUG(NULL, "giving up on request, try again with new OAuth2 token");
return 1;
} else if (!attempt) {
if (!attempt) {
// try again with credentials
Session *session = static_cast<Session *>(userdata);
std::string user, pw;
session->m_settings->getCredentials(realm, user, pw);
SyncEvo::Strncpy(username, user.c_str(), NE_ABUFSIZ);
@ -324,10 +314,11 @@ int Session::getCredentials(void *userdata, const char *realm, int attempt, char
}
}
void Session::forceAuthorization(const boost::shared_ptr<AuthProvider> &authProvider)
void Session::forceAuthorization(const std::string &username, const std::string &password)
{
m_forceAuthorizationOnce = true;
m_authProvider = authProvider;
m_forceUsername = username;
m_forcePassword = password;
}
void Session::preSendHook(ne_request *req, void *userdata, ne_buffer *header) throw()
@ -346,28 +337,15 @@ void Session::preSend(ne_request *req, ne_buffer *header)
SE_THROW("internal error: startOperation() not called");
}
// Only do this once when using normal username/password.
// Always do it when using OAuth2.
bool useOAuth2 = m_authProvider && m_authProvider->methodIsSupported(AuthProvider::AUTH_METHOD_OAUTH2);
if (m_forceAuthorizationOnce || useOAuth2) {
if (m_forceAuthorizationOnce) {
// only do this once
m_forceAuthorizationOnce = false;
bool haveAuthorizationHeader = boost::starts_with(header->data, "Authorization:") ||
strstr(header->data, "\nAuthorization:");
if (useOAuth2) {
if (haveAuthorizationHeader) {
SE_THROW("internal error: already have Authorization header when about to add OAuth2");
}
// Token was obtained by Session::run().
SE_LOG_DEBUG(NULL, "using OAuth2 token '%s' to authenticate", m_oauth2Bearer.c_str());
m_credentialsSent = true;
// SmartPtr<char *> blob(ne_base64((const unsigned char *)m_oauth2Bearer.c_str(), m_oauth2Bearer.size()));
ne_buffer_concat(header, "Authorization: Bearer ", m_oauth2Bearer.c_str() /* blob.get() */, "\r\n", NULL);
} else if (m_uri.m_scheme == "https") {
if (m_uri.m_scheme == "https") {
// append "Authorization: Basic" header if not present already
if (haveAuthorizationHeader) {
Credentials creds = m_authProvider->getCredentials();
std::string credentials = creds.m_username + ":" + creds.m_password;
if (!boost::starts_with(header->data, "Authorization:") &&
!strstr(header->data, "\nAuthorization:")) {
std::string credentials = m_forceUsername + ":" + m_forcePassword;
SmartPtr<char *> blob(ne_base64((const unsigned char *)credentials.c_str(), credentials.size()));
ne_buffer_concat(header, "Authorization: Basic ", blob.get(), "\r\n", NULL);
}
@ -441,7 +419,6 @@ void Session::propfindURI(const std::string &path, int depth,
boost::shared_ptr<ne_propfind_handler> handler;
int error;
checkAuthorization();
handler = boost::shared_ptr<ne_propfind_handler>(ne_propfind_create(m_session, path.c_str(), depth),
PropFindDeleter());
if (props != NULL) {
@ -623,32 +600,11 @@ bool Session::checkError(int error, int code, const ne_status *status, const str
SE_LOG_DEBUG(NULL, "credentials accepted");
m_settings->setCredentialsOkay(true);
}
m_oauthTokenRejections = 0;
return true;
}
break;
case NE_AUTH: {
// Retry OAuth2-based request if we still have a valid token.
bool useOAuth2 = m_authProvider && m_authProvider->methodIsSupported(AuthProvider::AUTH_METHOD_OAUTH2);
if (useOAuth2) {
// Try again with new token? Need to restore the counter,
// because it is relevant for getOAuth2Bearer() in preSend().
if (m_oauthTokenRejections < 2) {
if (!m_oauth2Bearer.empty() && m_credentialsSent) {
SE_LOG_DEBUG(NULL, "discarding used and rejected OAuth2 token '%s'", m_oauth2Bearer.c_str());
m_oauthTokenRejections++;
m_oauth2Bearer.clear();
} else {
SE_LOG_DEBUG(NULL, "OAuth2 token '%s' not used?!", m_oauth2Bearer.c_str());
}
retry = true;
SE_LOG_DEBUG(NULL, "OAuth2 retry after %d failed tokens", m_oauthTokenRejections);
} else {
SE_LOG_DEBUG(NULL, "too many failed OAuth2 tokens, giving up");
}
}
case NE_AUTH:
// tell caller what kind of transport error occurred
code = STATUS_UNAUTHORIZED;
descr = StringPrintf("%s: Neon error code %d = NE_AUTH, HTTP status %d: %s",
@ -656,7 +612,6 @@ bool Session::checkError(int error, int code, const ne_status *status, const str
error, code,
ne_get_error(m_session));
break;
}
case NE_ERROR:
if (code) {
descr = StringPrintf("%s: Neon error code %d: %s",
@ -926,49 +881,20 @@ static int ne_accept_2xx(void *userdata, ne_request *req, const ne_status *st)
}
#endif
void Session::checkAuthorization()
{
bool useOAuth2 = m_authProvider && m_authProvider->methodIsSupported(AuthProvider::AUTH_METHOD_OAUTH2);
if (useOAuth2 &&
m_oauth2Bearer.empty()) {
// Count the number of times we asked for new tokens. This helps
// the provider determine whether the token that it returns are valid.
try {
m_oauth2Bearer = m_authProvider->getOAuth2Bearer(m_oauthTokenRejections);
SE_LOG_DEBUG(NULL, "got new OAuth2 token '%s' for next request", m_oauth2Bearer.c_str());
} catch (...) {
std::string explanation;
Exception::handle(explanation);
// Treat all errors as fatal authentication errors.
// Our caller will abort immediately.
SE_THROW_EXCEPTION_STATUS(FatalException,
StringPrintf("logging into remote service failed: %s", explanation.c_str()),
STATUS_FORBIDDEN);
}
}
}
bool Session::run(Request &request, const std::set<int> *expectedCodes)
bool Request::run(const std::set<int> *expectedCodes)
{
int error;
// Check for authorization while we still can.
checkAuthorization();
std::string *result = request.getResult();
ne_request *req = request.getRequest();
if (result) {
result->clear();
ne_add_response_body_reader(req, ne_accept_2xx,
Request::addResultData, &request);
error = ne_request_dispatch(req);
if (m_result) {
m_result->clear();
ne_add_response_body_reader(m_req, ne_accept_2xx,
addResultData, this);
error = ne_request_dispatch(m_req);
} else {
error = ne_xml_dispatch_request(req, request.getParser()->get());
error = ne_xml_dispatch_request(m_req, m_parser->get());
}
return checkError(error, request.getStatus()->code, request.getStatus(),
request.getResponseHeader("Location"),
expectedCodes);
return checkError(error, expectedCodes);
}
int Request::addResultData(void *userdata, const char *buf, size_t len)
@ -978,6 +904,12 @@ int Request::addResultData(void *userdata, const char *buf, size_t len)
return 0;
}
bool Request::checkError(int error, const std::set<int> *expectedCodes)
{
return m_session.checkError(error, getStatus()->code, getStatus(), getResponseHeader("Location"),
expectedCodes);
}
}
SE_END_CXX

View File

@ -25,13 +25,10 @@ using namespace std;
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <syncevo/util.h>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
class AuthProvider;
namespace Neon {
#if 0
}
@ -42,21 +39,6 @@ std::string features();
class Request;
/**
* Throwing this will stop all further attempts to use the
* remote service.
*/
class FatalException : public StatusException
{
public:
FatalException(const std::string &file,
int line,
const std::string &what,
SyncMLStatus status) :
StatusException(file, line, what, status)
{}
};
class Settings {
public:
/**
@ -87,12 +69,6 @@ class Settings {
std::string &username,
std::string &password) = 0;
/**
* Grant access to AuthProvider. In addition to plain username/password
* in getCredentials, this one here might also be used for OAuth2.
*/
virtual boost::shared_ptr<AuthProvider> getAuthProvider() = 0;
/**
* Google returns a 401 error even if the credentials
* are valid. It seems to use that to throttle request
@ -233,10 +209,10 @@ class Session {
static boost::shared_ptr<Session> m_cachedSession;
bool m_forceAuthorizationOnce;
boost::shared_ptr<AuthProvider> m_authProvider;
std::string m_forceUsername, m_forcePassword;
/**
* Count how often a request was sent with credentials.
* Remember whether a request was sent with credentials.
* If the request succeeds, we assume that the credentials
* were okay. A bit fuzzy because forcing authorization
* might succeed despite invalid credentials if the
@ -244,21 +220,6 @@ class Session {
*/
bool m_credentialsSent;
/**
* Count the number of consecutive times that an OAuth2 token
* failed to get accepted. This can happen when the current one
* expired and needs to be refreshed or we need re-authorization
* by the user.
*/
int m_oauthTokenRejections;
/**
* Cached token for OAuth2. Obtained before starting the request in run(),
* used in preSend(), invalidated when it caused an authentication error
* in checkError().
*/
std::string m_oauth2Bearer;
/**
* current operation; used for debugging output
*/
@ -334,44 +295,12 @@ class Session {
*/
void startOperation(const string &operation, const Timespec &deadline);
/**
* Run one attempt to execute the request. May be called multiple times.
*
* Uses checkError() underneath to detect fatal errors and throw
* exceptions.
*
* @return result of Session::checkError()
*/
bool run(Request &request, const std::set<int> *expectedCodes);
/**
* to be called after each operation which might have produced debugging output by neon;
* automatically called by checkError()
*/
void flush();
ne_session *getSession() const { return m_session; }
/**
* Force next request in this session to have Basic authorization
* (when username/password are provided by AuthProvider) or all
* requests to use OAuth2 authentication.
*/
void forceAuthorization(const boost::shared_ptr<AuthProvider> &authProvider);
private:
boost::shared_ptr<Settings> m_settings;
bool m_debugging;
ne_session *m_session;
URI m_uri;
std::string m_proxyURL;
/** time when last successul request completed, maintained by checkError() */
Timespec m_lastRequestEnd;
/** number of times a request was sent, maintained by startOperation(), the credentials callback, and checkError() */
int m_attempt;
void checkAuthorization();
/**
* throw error if error code indicates failure;
* pass additional status code from a request whenever possible
@ -386,11 +315,31 @@ class Session {
*
* @return true for success, false if retry needed (only if deadline not empty);
* errors reported via exceptions
*/
*/
bool checkError(int error, int code = 0, const ne_status *status = NULL,
const string &location = "",
const std::set<int> *expectedCodes = NULL);
ne_session *getSession() const { return m_session; }
/**
* force next request in this session to have Basic authorization
* with the given username/password (which may be invalid to
* trigger real authorization)
*/
void forceAuthorization(const std::string &username, const std::string &password);
private:
boost::shared_ptr<Settings> m_settings;
bool m_debugging;
ne_session *m_session;
URI m_uri;
std::string m_proxyURL;
/** time when last successul request completed, maintained by checkError() */
Timespec m_lastRequestEnd;
/** number of times a request was sent, maintained by startOperation(), the credentials callback, and checkError() */
int m_attempt;
/** ne_set_server_auth() callback */
static int getCredentials(void *userdata, const char *realm, int attempt, char *username, char *password) throw();
@ -577,9 +526,13 @@ class Request
}
/**
* Execute the request. See Session::run().
* Execute the request. May only be called once per request. Uses
* Session::checkError() underneath to detect fatal errors and throw
* exceptions.
*
* @return result of Session::checkError()
*/
bool run(const std::set<int> *expectedCodes = NULL) { return m_session.run(*this, expectedCodes); }
bool run(const std::set<int> *expectedCodes = NULL);
std::string getResponseHeader(const std::string &name) {
const char *value = ne_get_response_header(m_req, name.c_str());
@ -588,13 +541,6 @@ class Request
int getStatusCode() { return ne_get_status(m_req)->code; }
const ne_status *getStatus() { return ne_get_status(m_req); }
ne_request *getRequest() const { return m_req; }
std::string *getResult() const { return m_result; }
XMLParser *getParser() const { return m_parser; }
/** ne_block_reader implementation */
static int addResultData(void *userdata, const char *buf, size_t len);
private:
// buffers for string (copied by ne_request_create(),
// but due to a bug in neon, our method string is still used
@ -605,6 +551,12 @@ class Request
ne_request *m_req;
std::string *m_result;
XMLParser *m_parser;
/** ne_block_reader implementation */
static int addResultData(void *userdata, const char *buf, size_t len);
/** throw error if error code *or* current status indicates failure */
bool checkError(int error, const std::set<int> *expectedCodes = NULL);
};
/** thrown for 301 HTTP status */

View File

@ -12,7 +12,6 @@
#include <boost/scoped_ptr.hpp>
#include <syncevo/LogRedirect.h>
#include <syncevo/IdentityProvider.h>
#include <boost/assign.hpp>
@ -61,10 +60,8 @@ public:
// check source config first
if (m_sourceConfig) {
url = m_sourceConfig->getDatabaseID();
if (url.find("%u") != url.npos) {
std::string username = getUsername();
boost::replace_all(url, "%u", Neon::URI::escape(username));
}
std::string username = m_sourceConfig->getUser();
boost::replace_all(url, "%u", Neon::URI::escape(username));
}
// fall back to sync context
@ -73,10 +70,8 @@ public:
if (!urls.empty()) {
url = urls.front();
if (url.find("%u") != url.npos) {
std::string username = getUsername();
boost::replace_all(url, "%u", Neon::URI::escape(username));
}
std::string username = m_context->getSyncUsername();
boost::replace_all(url, "%u", Neon::URI::escape(username));
}
}
@ -132,14 +127,22 @@ public:
virtual void getCredentials(const std::string &realm,
std::string &username,
std::string &password);
virtual boost::shared_ptr<AuthProvider> getAuthProvider();
std::string getUsername()
std::string &password)
{
lookupAuthProvider();
return m_authProvider->getUsername();
// prefer source config if anything is set there
if (m_sourceConfig) {
username = m_sourceConfig->getUser();
password = m_sourceConfig->getPassword();
if (!username.empty() || !password.empty()) {
return;
}
}
// fall back to context
if (m_context) {
username = m_context->getSyncUsername();
password = m_context->getSyncPassword();
}
}
virtual bool getCredentialsOkay() { return m_credentialsOkay; }
@ -163,52 +166,8 @@ public:
private:
void initializeFlags(const std::string &url);
boost::shared_ptr<AuthProvider> m_authProvider;
void lookupAuthProvider();
};
void ContextSettings::getCredentials(const std::string &realm,
std::string &username,
std::string &password)
{
lookupAuthProvider();
Credentials creds = m_authProvider->getCredentials();
username = creds.m_username;
password = creds.m_password;
}
boost::shared_ptr<AuthProvider> ContextSettings::getAuthProvider()
{
lookupAuthProvider();
return m_authProvider;
}
void ContextSettings::lookupAuthProvider()
{
if (m_authProvider) {
return;
}
UserIdentity identity;
InitStateString password;
// prefer source config if anything is set there
if (m_sourceConfig) {
identity = m_sourceConfig->getUser();
password = m_sourceConfig->getPassword();
}
// fall back to context
if (m_context && !identity.wasSet() && !password.wasSet()) {
identity = m_context->getSyncUser();
password = m_context->getSyncPassword();
}
// lookup actual authentication method instead of assuming username/password
m_authProvider = AuthProvider::create(identity, password);
}
void ContextSettings::initializeFlags(const std::string &url)
{
bool googleUpdate = false,
@ -547,8 +506,10 @@ void WebDAVSource::contactServer()
m_contextSettings->setURL(database);
// start talking to host defined by m_settings->getURL()
m_session = Neon::Session::create(m_settings);
// force authentication via username/password or OAuth2
m_session->forceAuthorization(m_settings->getAuthProvider());
// force authentication
std::string user, pw;
m_settings->getCredentials("", user, pw);
m_session->forceAuthorization(user, pw);
return;
}
@ -593,8 +554,6 @@ void WebDAVSource::contactServer()
SE_LOG_DEBUG(NULL, "%s WebDAV capabilities: %s",
m_session->getURL().c_str(),
Flags2String(caps, descr).c_str());
} catch (const Neon::FatalException &ex) {
throw;
} catch (...) {
Exception::handle();
}
@ -613,8 +572,8 @@ bool WebDAVSource::findCollections(const boost::function<bool (const std::string
(timeoutSeconds <= 0 ||
retrySeconds <= 0) ? "resending disabled" : "resending allowed");
boost::shared_ptr<AuthProvider> authProvider = m_contextSettings->getAuthProvider();
std::string username = authProvider->getUsername();
std::string username, password;
m_contextSettings->getCredentials("", username, password);
// If no URL was configured, then try DNS SRV lookup.
// syncevo-webdav-lookup and at least one of the tools
@ -845,22 +804,15 @@ bool WebDAVSource::findCollections(const boost::function<bool (const std::string
// relevant for debugging.
try {
SE_LOG_DEBUG(NULL, "debugging: read all WebDAV properties of %s", path.c_str());
// Use OAuth2, if available.
boost::shared_ptr<AuthProvider> authProvider = m_settings->getAuthProvider();
if (authProvider->methodIsSupported(AuthProvider::AUTH_METHOD_OAUTH2)) {
m_session->forceAuthorization(authProvider);
}
Neon::Session::PropfindPropCallback_t callback =
boost::bind(&WebDAVSource::openPropCallback,
this, _1, _2, _3, _4);
m_session->propfindProp(path, 0, NULL, callback, Timespec());
} catch (const Neon::FatalException &ex) {
throw;
} catch (...) {
handleException(HANDLE_EXCEPTION_NO_ERROR);
}
}
// Now ask for some specific properties of interest for us.
// Using CALDAV:allprop would be nice, but doesn't seem to
// be possible with Neon.
@ -877,17 +829,19 @@ bool WebDAVSource::findCollections(const boost::function<bool (const std::string
// <unauthenticated/>
// </current-user-principal>
//
// We send valid credentials here, using Basic authorization,
// if configured to use credentials instead of something like OAuth2.
// We send valid credentials here, using Basic authorization.
// The rationale is that this cuts down on the number of
// requests for https while still being secure. For
// http, our Neon wrapper is smart enough to ignore our request.
// http the setup already is insecure if the transport
// isn't trusted (sends PIM data as plain text).
//
// See also:
// http://tools.ietf.org/html/rfc4918#appendix-E
// http://lists.w3.org/Archives/Public/w3c-dist-auth/2005OctDec/0243.html
// http://thread.gmane.org/gmane.comp.web.webdav.neon.general/717/focus=719
m_session->forceAuthorization(m_settings->getAuthProvider());
std::string user, pw;
m_settings->getCredentials("", user, pw);
m_session->forceAuthorization(user, pw);
m_davProps.clear();
// Avoid asking for CardDAV properties when only using CalDAV
// and vice versa, to avoid breaking both when the server is only
@ -937,8 +891,6 @@ bool WebDAVSource::findCollections(const boost::function<bool (const std::string
getContent() == "VCARD" ? carddav : caldav,
callback, deadline);
success = true;
} catch (const Neon::FatalException &ex) {
throw;
} catch (const Neon::RedirectException &ex) {
// follow to new location
Neon::URI next = Neon::URI::parse(ex.getLocation(), true);

View File

@ -257,7 +257,7 @@ public:
sc->setDatabaseID(database);
}
if (!sc->getUser().wasSet() && !m_evoUser.empty()) {
sc->setUsername(m_evoUser);
sc->setUser(m_evoUser);
}
if (!sc->getPassword().wasSet() && !m_evoPassword.empty()) {
sc->setPassword(m_evoPassword);

View File

@ -178,10 +178,20 @@ void ReadOperations::getNamedConfig(const std::string &configName,
dbusConfig = getLocalConfig(configName);
DBusUserInterface ui(dbusConfig->getKeyring());
//try to check password and read password from gnome keyring if possible
PasswordConfigProperty::checkPasswords(ui, *dbusConfig,
// Keep usernames as they are, but retrieve passwords for the D-Bus client.
PasswordConfigProperty::CHECK_PASSWORD_ALL & ~PasswordConfigProperty::CHECK_PASSWORD_RESOLVE_USERNAME,
dbusConfig->getSyncSources());
ConfigPropertyRegistry& registry = SyncConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(ui, configName, *dbusConfig->getProperties());
}
list<string> configuredSources = dbusConfig->getSyncSources();
BOOST_FOREACH(const string &sourceName, configuredSources) {
ConfigPropertyRegistry& registry = SyncSourceConfig::getRegistry();
SyncSourceNodes sourceNodes = dbusConfig->getSyncSourceNodes(sourceName);
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(ui, configName, *dbusConfig->getProperties(),
sourceName, sourceNodes.getProperties());
}
}
syncConfig = dbusConfig.get();
}

View File

@ -311,38 +311,13 @@ void Session::setNamedConfig(const std::string &configName,
for ( it = sourceFilters.begin(); it != sourceFilters.end(); it++ ) {
from->setConfigFilter(false, it->first, it->second);
}
// We need no interactive user interface, but we do need to handle
// storing passwords in a keyring here.
// run without dedicated user interface and thus without
// interactive password requests here (not needed)
boost::shared_ptr<SyncContext> syncConfig(new SyncContext(configName));
syncConfig->prepareConfigForWrite();
syncConfig->copy(*from, NULL);
class KeyringUI : public UserInterface {
InitStateString m_keyring;
public:
KeyringUI(const InitStateString &keyring) :
m_keyring(keyring)
{}
// Implement UserInterface.
virtual bool savePassword(const std::string &passwordName,
const std::string &password,
const ConfigPasswordKey &key)
{
return GetSavePasswordSignal()(m_keyring, passwordName, password, key);
}
virtual void readStdin(std::string &content) { SE_THROW("not implemented"); }
virtual std::string askPassword(const std::string &passwordName,
const std::string &descr,
const ConfigPasswordKey &key)
{
SE_THROW("not implemented");
return "";
}
} ui(syncConfig->getKeyring());
syncConfig->preFlush(ui);
syncConfig->preFlush(syncConfig->getUserInterfaceNonNull());
syncConfig->flush();
m_setConfig = true;
}

View File

@ -51,7 +51,6 @@ using namespace std;
#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>
#include <boost/range.hpp>
#include <boost/assign/list_of.hpp>
#include <fstream>
#include <syncevo/declarations.h>
@ -655,6 +654,30 @@ public:
}
};
void Cmdline::checkSyncPasswords(SyncContext &context)
{
ConfigPropertyRegistry& registry = SyncConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(context.getUserInterfaceNonNull(),
context.getConfigName(),
*context.getProperties());
}
}
void Cmdline::checkSourcePasswords(SyncContext &context,
const std::string &sourceName,
SyncSourceNodes &nodes)
{
ConfigPropertyRegistry &registry = SyncSourceConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(context.getUserInterfaceNonNull(),
context.getConfigName(),
*context.getProperties(),
sourceName,
nodes.getProperties());
}
}
static void ShowLUID(SyncSourceLogging *logging, const std::string &luid)
{
string description;
@ -807,10 +830,8 @@ bool Cmdline::run() {
if (source.get() != NULL) {
if (!m_server.empty() && nodes) {
// ensure that we have passwords for this config
PasswordConfigProperty::checkPasswords(context->getUserInterfaceNonNull(),
*context,
PasswordConfigProperty::CHECK_PASSWORD_ALL,
boost::assign::list_of(sourceName));
checkSyncPasswords(*context);
checkSourcePasswords(*context, sourceName, *nodes);
}
(this->*operation)(source.get(), header);
} else {
@ -1363,10 +1384,8 @@ bool Cmdline::run() {
sysync::TSyError err;
#define CHECK_ERROR(_op) if (err) { SE_THROW_EXCEPTION_STATUS(StatusException, string(source->getName()) + ": " + (_op), SyncMLStatus(err)); }
PasswordConfigProperty::checkPasswords(context->getUserInterfaceNonNull(),
*context,
PasswordConfigProperty::CHECK_PASSWORD_ALL,
boost::assign::list_of(source->getName()));
checkSyncPasswords(*context);
checkSourcePasswords(*context, source->getName(), sourceNodes);
source->setNeedChanges(false);
source->open();
const SyncSource::Operations &ops = source->getOperations();

View File

@ -334,6 +334,11 @@ protected:
* @return encoded luid of inserted item
*/
CmdlineLUID insertItem(SyncSourceRaw *source, const std::string &luid, const std::string &data);
static void checkSyncPasswords(SyncContext &context);
static void checkSourcePasswords(SyncContext &context,
const std::string &sourceName,
SyncSourceNodes &nodes);
};

View File

@ -28,7 +28,6 @@
#include <syncevo/ConfigFilter.h>
#include <syncevo/UserInterface.h>
#include <syncevo/SyncML.h>
#include <syncevo/SyncConfig.h>
#include <synthesis/engine_defs.h>
#include <gdbus-cxx-bridge.h>
@ -124,12 +123,6 @@ namespace GDBusCXX {
GDBusCXX::dbus_member<SyncEvo::ConfigPasswordKey, std::string, &SyncEvo::ConfigPasswordKey::authtype,
GDBusCXX::dbus_member_single<SyncEvo::ConfigPasswordKey, unsigned int, &SyncEvo::ConfigPasswordKey::port> > > > > > > >
{};
template <> struct dbus_traits<SyncEvo::UserIdentity> :
public dbus_struct_traits<SyncEvo::UserIdentity,
GDBusCXX::dbus_member<SyncEvo::UserIdentity, SyncEvo::InitStateString, &SyncEvo::UserIdentity::m_provider,
GDBusCXX::dbus_member_single<SyncEvo::UserIdentity, SyncEvo::InitStateString, &SyncEvo::UserIdentity::m_identity> > >
{};
}
#endif // INCL_SYNCEVO_DBUS_TRAITS

View File

@ -42,7 +42,6 @@ typedef void *GMainLoop;
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/function_traits.hpp>
#include <boost/utility/value_init.hpp>
#include <boost/lambda/lambda.hpp>
#include <iterator>
#include <memory>
@ -1034,7 +1033,9 @@ template<> class GAsyncReadyDoneCXX<void>
SYNCEVO_GLIB_CALL_ASYNC(_prepare, \
GAsyncReadyDoneCXX<boost::function<typeof(_prepare ## _finish)>::result_type>::createCB(_res, _gerror, done), \
_args); \
GRunWhile(! boost::lambda::var(done)); \
while (!done) { \
g_main_context_iteration(NULL, true); \
} \
} while (false); \

View File

@ -1,116 +0,0 @@
/*
* Copyright (C) 2013 Intel Corporation
*
* This library 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) version 3.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <syncevo/IdentityProvider.h>
#include <syncevo/SyncConfig.h>
#include <algorithm>
SE_BEGIN_CXX
const char USER_IDENTITY_PLAIN_TEXT[] = "user";
const char USER_IDENTITY_SYNC_CONFIG[] = "id";
Credentials IdentityProviderCredentials(const UserIdentity &identity,
const InitStateString &password)
{
Credentials cred;
if (identity.m_provider == USER_IDENTITY_PLAIN_TEXT) {
cred.m_username = identity.m_identity;
cred.m_password = password;
} else {
// We could use the gSSO password plugin to request
// username/password. But it is uncertain whether that is useful,
// therefore that is not implemented at the moment.
SE_THROW(StringPrintf("%s: need username+password as credentials", identity.toString().c_str()));
}
return cred;
}
class CredentialsProvider : public AuthProvider
{
Credentials m_creds;
public:
CredentialsProvider(const std::string &username,
const std::string &password)
{
m_creds.m_username = username;
m_creds.m_password = password;
}
virtual bool methodIsSupported(AuthMethod method) const { return method == AUTH_METHOD_CREDENTIALS; }
virtual Credentials getCredentials() const { return m_creds; }
virtual std::string getOAuth2Bearer(int failedTokens) const { SE_THROW("OAuth2 not supported"); return ""; }
virtual std::string getUsername() const { return m_creds.m_username; }
};
boost::shared_ptr<AuthProvider> AuthProvider::create(const UserIdentity &identity,
const InitStateString &password)
{
boost::shared_ptr<AuthProvider> authProvider;
if (identity.m_provider == USER_IDENTITY_PLAIN_TEXT) {
authProvider.reset(new CredentialsProvider(identity.m_identity, password));
} else {
BOOST_FOREACH (IdentityProvider *idProvider, IdentityProvider::getRegistry()) {
if (boost::iequals(idProvider->m_key, identity.m_provider)) {
authProvider = idProvider->create(identity.m_identity, password);
if (!authProvider) {
SE_THROW(StringPrintf("identity provider for '%s' is disabled in this installation",
identity.m_provider.c_str()));
}
break;
}
}
if (!authProvider) {
SE_THROW(StringPrintf("unknown identity provider '%s' in '%s'",
identity.m_provider.c_str(),
identity.toString().c_str()));
}
}
return authProvider;
}
std::list<IdentityProvider *> &IdentityProvider::getRegistry()
{
static std::list<IdentityProvider *> providers;
return providers;
}
IdentityProvider::IdentityProvider(const std::string &key,
const std::string &descr) :
m_key(key),
m_descr(descr)
{
getRegistry().push_back(this);
}
IdentityProvider::~IdentityProvider()
{
getRegistry().erase(std::find(getRegistry().begin(),
getRegistry().end(),
this));
}
SE_END_CXX

View File

@ -1,149 +0,0 @@
/*
* Copyright (C) 2013 Intel Corporation
*
* This library 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) version 3.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef INCL_SYNC_EVOLUTION_IDENTITY_PROVIDER
# define INCL_SYNC_EVOLUTION_IDENTITY_PROVIDER
#include <syncevo/util.h>
#include <string>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
extern const char USER_IDENTITY_PLAIN_TEXT[];
extern const char USER_IDENTITY_SYNC_CONFIG[];
struct UserIdentity; // from SyncConfig.h
struct Credentials
{
std::string m_username;
std::string m_password;
};
/**
* Returns username/password for an identity. The password is the
* string configured for it inside SyncEvolution. It may be empty and/or unset if
* the plain text password comes from the identity provider.
*
* If the credentials cannot be retrieved, an error is thrown, so don't use this
* in cases where a different authentication method might also work.
*/
Credentials IdentityProviderCredentials(const UserIdentity &identity,
const InitStateString &password);
/**
* Supports multiple different ways of authorizing the user.
* Actual implementations are IdentityProvider specific.
*/
class AuthProvider
{
public:
/**
* Creates an AuthProvider matching the identity.m_provider value
* or throws an exception if that fails. Never returns NULL.
*/
static boost::shared_ptr<AuthProvider> create(const UserIdentity &identity,
const InitStateString &password);
enum AuthMethod {
AUTH_METHOD_NONE,
AUTH_METHOD_CREDENTIALS,
AUTH_METHOD_OAUTH2,
AUTH_METHOD_MAX
};
/**
* Returns true if the given method is supported and currently possible.
*/
virtual bool methodIsSupported(AuthMethod method) const = 0;
/**
* Returns username/password credentials. Throws an error if not supported.
*/
virtual Credentials getCredentials() const = 0;
/**
* Returns the 'Bearer b64token' string required for logging into
* services supporting OAuth2 or throws an exception when we don't
* have a valid token. Internally this will refresh tokens
* automatically.
*
* See http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-20#section-2.1
*
* An application should:
* - request a token and try to use it
* - in case of failure try to request a token again, and try to use it
* again (in case the first token has expired just before using it)
* - if the second token also fails, request a third token with full
* re-authentication as above.
* - if that fails, then give up.
*
* To achieve that, the caller must count how often he got a token that
* did not work.
*
* @param failedTokens zero when asking for initial token, one for refresh, two for full re-authorization
*
* @return a base64 encoded token, ready to be used in "Authorization: Bearer %s"
*/
virtual std::string getOAuth2Bearer(int failedTokens) const = 0;
/**
* Returns username at the remote service. Works for
* username/password credentials and may be made to work for
* OAuth2. At the moment, code should not depend on it when using
* OAuth2.
*/
virtual std::string getUsername() const = 0;
};
/**
* Instantiating this class adds a new provider.
* Deleting it removes it.
*/
class IdentityProvider
{
public:
/** returns NULL if disabled, valid AuthProvider if possible, and throws error if something goes wrong */
virtual boost::shared_ptr<AuthProvider> create(const InitStateString &username,
const InitStateString &password) = 0;
/**
* All known providers.
*/
static std::list<IdentityProvider *> &getRegistry();
/**
* @param key short, unique word without colons used to select this provider
* in an identity string, for example "gsso"
* @param descr one or more lines describing the provider and its syntax,
* for example
* "gsso:<account ID>\n"
* " authentication using libgsignond + libaccounts\n"
*/
IdentityProvider(const std::string &key,
const std::string &descr);
virtual ~IdentityProvider();
const std::string m_key;
const std::string m_descr;
};
SE_END_CXX
#endif

View File

@ -212,8 +212,8 @@ void LocalTransportAgent::onChildConnect(const GDBusCXX::DBusConnectionPtr &conn
m_server->getRootPath()),
static_cast<std::string>(m_server->getLogDir()),
m_server->getDoLogging(),
std::make_pair(m_server->getSyncUser(),
m_server->getSyncPassword()),
StringPair(m_server->getSyncUsername(),
m_server->getSyncPassword()),
m_server->getConfigProps(),
sources,
boost::bind(&LocalTransportAgent::storeReplyMsg, m_self, _1, _2, _3));
@ -724,7 +724,7 @@ class LocalTransportAgentChild : public TransportAgent
const StringPair &serverConfig, // config name + root path
const std::string &serverLogDir,
bool serverDoLogging,
const std::pair<UserIdentity, InitStateString> &serverSyncCredentials,
const StringPair &serverSyncCredentials,
const FullProps &serverConfigProps,
const LocalTransportChild::ActiveSources_t &sources,
const LocalTransportChild::ReplyPtr &reply)
@ -771,10 +771,10 @@ class LocalTransportAgentChild : public TransportAgent
// will end up in our LocalTransportContext::askPassword()
// implementation above, which will pass the question to
// the local sync parent.
if (serverSyncCredentials.first.wasSet()) {
m_client->setSyncUsername(serverSyncCredentials.first.toString(), true);
if (!serverSyncCredentials.first.empty()) {
m_client->setSyncUsername(serverSyncCredentials.first, true);
}
if (serverSyncCredentials.second.wasSet()) {
if (!serverSyncCredentials.second.empty()) {
m_client->setSyncPassword(serverSyncCredentials.second, true);
}

View File

@ -33,7 +33,6 @@
#include <syncevo/Cmdline.h>
#include <syncevo/lcs.h>
#include <syncevo/ThreadSupport.h>
#include <syncevo/IdentityProvider.h>
#include <test.h>
#include <synthesis/timeutil.h>
@ -109,33 +108,6 @@ PropertySpecifier PropertySpecifier::StringToPropSpec(const std::string &spec, i
return res;
}
UserIdentity UserIdentity::fromString(const InitStateString &idString)
{
UserIdentity id;
if (idString.wasSet()) {
size_t off = idString.find(':');
if (off != idString.npos) {
id.m_provider = idString.substr(0, off);
id.m_identity = idString.substr(off + 1);
} else {
id.m_provider = InitStateString(USER_IDENTITY_PLAIN_TEXT, false);
id.m_identity = idString;
}
}
return id;
}
InitStateString UserIdentity::toString() const
{
std::string str;
if (m_provider.wasSet()) {
str += m_provider;
str += ':';
}
str += m_identity;
return InitStateString(str, m_provider.wasSet() || m_identity.wasSet());
}
std::string PropertySpecifier::toString()
{
std::string res;
@ -340,7 +312,6 @@ void SyncConfig::makeEphemeral()
}
SyncConfig::SyncConfig(const string &peer,
Layout treeLayout,
boost::shared_ptr<ConfigTree> tree,
const string &redirectPeerRootPath) :
m_layout(SHARED_LAYOUT),
@ -363,20 +334,11 @@ SyncConfig::SyncConfig(const string &peer,
m_peer;
if (tree.get() != NULL) {
// existing tree points into simple configuration
m_tree = tree;
m_layout = treeLayout;
if (treeLayout == SHARED_LAYOUT) {
// Use the standard paths, same as below.
splitConfigString(m_peer, m_peerPath, m_contextPath);
if (!m_peerPath.empty()) {
m_peerPath = m_contextPath + "/peers/" + m_peerPath;
}
} else {
// Existing tree points into simple configuration,
// use empty paths.
m_peerPath =
m_contextPath = "";
}
m_layout = HTTP_SERVER_LAYOUT;
m_peerPath =
m_contextPath = "";
} else {
// search for configuration in various places...
root = getOldRoot();
@ -845,7 +807,7 @@ boost::shared_ptr<SyncConfig> SyncConfig::createPeerTemplate(const string &serve
}
boost::shared_ptr<ConfigTree> tree(new SingleFileConfigTree(templateConfig));
boost::shared_ptr<SyncConfig> config(new SyncConfig(server, SyncConfig::HTTP_SERVER_LAYOUT, tree));
boost::shared_ptr<SyncConfig> config(new SyncConfig(server, tree));
boost::shared_ptr<PersistentSyncSourceConfig> source;
config->setDefaults(false);
@ -948,7 +910,7 @@ void SyncConfig::preFlush(UserInterface &ui)
/* save password in the global config node */
ConfigPropertyRegistry& registry = getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->savePassword(ui, *this);
prop->savePassword(ui, m_peer, *getProperties());
}
/** grep each source and save their password */
@ -959,7 +921,8 @@ void SyncConfig::preFlush(UserInterface &ui)
SyncSourceNodes sourceNodes = getSyncSourceNodes(sourceName);
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->savePassword(ui, *this, sourceName);
prop->savePassword(ui, m_peer, *getProperties(),
sourceName, sourceNodes.getProperties());
}
}
}
@ -1222,62 +1185,14 @@ static ConfigProperty syncPropDevID("deviceId",
static ConfigProperty syncPropUsername("username",
"user name used for authorization with the SyncML server",
"");
static class SyncPasswordConfigProperty : public PasswordConfigProperty
{
public:
SyncPasswordConfigProperty() :
PasswordConfigProperty("password",
"password used for authorization with the peer;\n"
"in addition to specifying it directly as plain text, it can\n"
"also be read from the standard input or from an environment\n"
"variable of your choice::\n\n"
" plain text : password = <insert your password here>\n"
" ask : password = -\n"
" env variable: password = ${<name of environment variable>}\n")
{}
virtual void checkPassword(UserInterface &ui,
SyncConfig &config,
int flags,
const std::string &sourceName = "") const {
PasswordConfigProperty::checkPassword(ui, config, flags, syncPropUsername, sourceName);
}
virtual void savePassword(UserInterface &ui,
SyncConfig &config,
const std::string &sourceName = "") const {
PasswordConfigProperty::savePassword(ui, config, syncPropUsername, sourceName);
}
ConfigPasswordKey getPasswordKey(const string &descr,
const string &serverName,
FilterConfigNode &globalConfigNode,
const string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const {
/**
* Here we use server sync url without protocol prefix and
* user account name as the key in the keyring.
* The URL must not be empty, otherwise we end up
* overwriting the password of some other service just
* because it happens to have the same username.
*/
ConfigPasswordKey key;
key.server = syncPropSyncURL.getProperty(globalConfigNode);
size_t start = key.server.find("://");
/* we don't preserve protocol prefix for it may change */
if (start != key.server.npos) {
key.server = key.server.substr(start + 3);
}
if (key.server.empty()) {
SE_THROW("cannot store password in keyring without a syncURL that identifies the service");
}
key.user = getUsername(syncPropUsername, globalConfigNode);
return key;
}
} syncPropPassword;
static PasswordConfigProperty syncPropPassword("password",
"password used for authorization with the peer;\n"
"in addition to specifying it directly as plain text, it can\n"
"also be read from the standard input or from an environment\n"
"variable of your choice::\n\n"
" plain text : password = <insert your password here>\n"
" ask : password = -\n"
" env variable: password = ${<name of environment variable>}\n");
static BoolConfigProperty syncPropPreventSlowSync("preventSlowSync",
"During a slow sync, the SyncML server must match all items\n"
"of the client with its own items and detect which ones it\n"
@ -1312,46 +1227,11 @@ static ConfigProperty syncPropProxyHost("proxyHost",
"proxy URL (``http://<host>:<port>``)");
static ConfigProperty syncPropProxyUsername("proxyUsername",
"authentication for proxy: username");
static class ProxyPasswordConfigProperty : public PasswordConfigProperty {
public:
ProxyPasswordConfigProperty() :
PasswordConfigProperty("proxyPassword",
"proxy password, can be specified in different ways,\n"
"see SyncML server password for details\n",
"",
"proxy")
{}
/**
* re-implement this function for it is necessary to do a check
* before retrieving proxy password
*/
virtual void checkPassword(UserInterface &ui,
SyncConfig &config,
int flags,
const std::string &sourceName = std::string()) const {
/* if useProxy is set 'true', then check proxypassword */
if (config.getUseProxy()) {
PasswordConfigProperty::checkPassword(ui, config, flags, syncPropProxyUsername, sourceName);
}
}
virtual void savePassword(UserInterface &ui,
SyncConfig &config,
const std::string &sourceName = std::string()) const {
PasswordConfigProperty::savePassword(ui, config, syncPropProxyUsername, sourceName);
}
virtual ConfigPasswordKey getPasswordKey(const std::string &descr,
const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const {
ConfigPasswordKey key;
key.server = syncPropProxyHost.getProperty(globalConfigNode);
key.user = getUsername(syncPropProxyUsername, globalConfigNode);
return key;
}
} syncPropProxyPassword;
static ProxyPasswordConfigProperty syncPropProxyPassword("proxyPassword",
"proxy password, can be specified in different ways,\n"
"see SyncML server password for details\n",
"",
"proxy");
static StringConfigProperty syncPropClientAuthType("clientAuthType",
"- empty or \"md5\" for secure method (recommended)\n"
"- \"basic\" for insecure method\n"
@ -1853,216 +1733,152 @@ ConfigPropertyRegistry &SyncConfig::getRegistry()
return registry;
}
UserIdentity SyncConfig::getSyncUser() const {
InitStateString user = syncPropUsername.getProperty(*getNode(syncPropUsername));
UserIdentity id(UserIdentity::fromString(user));
return id;
}
InitStateString SyncConfig::getSyncUsername() const { return syncPropUsername.getProperty(*getNode(syncPropUsername)); }
void SyncConfig::setSyncUsername(const string &value, bool temporarily) { syncPropUsername.setProperty(*getNode(syncPropUsername), value, temporarily); }
InitStateString SyncConfig::getSyncPassword() const {
return syncPropPassword.getProperty(*getNode(syncPropPassword));
return syncPropPassword.getCachedProperty(*getNode(syncPropPassword), m_cachedPassword);
}
void PasswordConfigProperty::checkPassword(UserInterface &ui,
SyncConfig &config,
int flags,
const ConfigProperty &usernameProperty,
const std::string &sourceName) const
const string &serverName,
FilterConfigNode &globalConfigNode,
const string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const
{
std::string serverName = config.getConfigName();
boost::shared_ptr<FilterConfigNode> globalConfigNode = config.getProperties();
boost::shared_ptr<FilterConfigNode> sourceConfigNode;
if (!sourceName.empty()) {
sourceConfigNode = config.getSyncSourceNodes(sourceName).getNode(*this);
}
FilterConfigNode &configNode = sourceConfigNode ? *sourceConfigNode : *globalConfigNode;
InitStateString username = usernameProperty.getProperty(configNode);
SE_LOG_DEBUG(NULL, "checking password property '%s' in config '%s' with user identity '%s'",
getMainName().c_str(),
serverName.c_str(),
username.c_str());
UserIdentity identity(UserIdentity::fromString(username));
InitStateString passwordToSave;
InitStateString usernameToSave;
if (identity.m_provider == USER_IDENTITY_SYNC_CONFIG) {
const std::string &credConfigName = identity.m_identity;
SE_LOG_INFO(NULL, "using %s/%s from config '%s' as credentials for %s%s%s",
syncPropUsername.getMainName().c_str(),
syncPropPassword.getMainName().c_str(),
credConfigName.c_str(),
serverName.c_str(),
sourceName.empty() ? "" : "/",
sourceName.c_str());
// Actual username/password are stored in a different config. Go find it...
boost::shared_ptr<ConfigTree> tree = config.getConfigTree();
SyncConfig::Layout layout = config.getLayout();
if (layout != SyncConfig::SHARED_LAYOUT) {
SE_THROW(StringPrintf("%s = %s: only supported in configs using the current config storage, please migrate config %s",
usernameProperty.getMainName().c_str(),
username.c_str(),
config.getConfigName().c_str()));
}
boost::shared_ptr<SyncConfig> credConfig(new SyncConfig(credConfigName, layout, tree));
if (!credConfig->exists()) {
SE_THROW(StringPrintf("%s = %s: config '%s' not found, cannot look up credentials",
usernameProperty.getMainName().c_str(),
username.c_str(),
credConfigName.c_str()));
}
syncPropPassword.checkPassword(ui, *credConfig, flags);
// Always store the new values.
passwordToSave = InitStateString(credConfig->getSyncPassword(), true);
usernameToSave = InitStateString(credConfig->getSyncUser().toString(), true);
} else if (identity.m_provider != USER_IDENTITY_PLAIN_TEXT) {
// Can some provider give us the plain text password? Not at the moment,
// so we've got nothing to do here.
string password, passwordSave;
/* if no source config node, then it should only be password in the global config node */
if(sourceConfigNode.get() == NULL) {
password = getProperty(globalConfigNode);
} else {
// Default, internal password handling.
InitStateString password = getProperty(configNode);
password = getProperty(*sourceConfigNode);
}
string descr = getDescr(serverName,*globalConfigNode,sourceName,sourceConfigNode);
if (password == "-") {
ConfigPasswordKey key = getPasswordKey(descr,serverName,*globalConfigNode,sourceName,sourceConfigNode);
passwordToSave = ui.askPassword(getMainName(),descr, key);
} else if(boost::starts_with(password, "${") &&
boost::ends_with(password, "}")) {
string envname = password.substr(2, password.size() - 3);
const char *envval = getenv(envname.c_str());
if (!envval) {
SyncContext::throwError(string("the environment variable '") +
envname +
"' for the '" +
descr +
"' password is not set");
} else {
passwordToSave = envval;
}
string descr = getDescr(serverName,globalConfigNode,sourceName,sourceConfigNode);
if (password == "-") {
ConfigPasswordKey key = getPasswordKey(descr,serverName,globalConfigNode,sourceName,sourceConfigNode);
passwordSave = ui.askPassword(getMainName(),descr, key);
} else if(boost::starts_with(password, "${") &&
boost::ends_with(password, "}")) {
string envname = password.substr(2, password.size() - 3);
const char *envval = getenv(envname.c_str());
if (!envval) {
SyncContext::throwError(string("the environment variable '") +
envname +
"' for the '" +
descr +
"' password is not set");
} else {
passwordSave = envval;
}
}
// If we found a password, then set it in the config node
// temporarily. That way, all following "get password" calls will
// be able to return it without having to make all callers away of
// password handling.
if (passwordToSave.wasSet() && (flags & CHECK_PASSWORD_RESOLVE_PASSWORD)) {
configNode.addFilter(getMainName(), InitStateString(passwordToSave, true));
}
if (usernameToSave.wasSet() && (flags & CHECK_PASSWORD_RESOLVE_USERNAME)) {
configNode.addFilter(usernameProperty.getMainName(), InitStateString(usernameToSave, true));
}
}
void PasswordConfigProperty::checkPasswords(UserInterface &ui,
SyncConfig &config,
int flags,
const std::list<std::string> &sourceNames)
{
ConfigPropertyRegistry& registry = SyncConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(ui, config, flags);
}
BOOST_FOREACH (const std::string &sourceName, sourceNames) {
ConfigPropertyRegistry &registry = SyncSourceConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(ui, config, flags, sourceName);
/* If password is from ui or environment variable, set them in the config node on fly
* Previous impl use temp string to store them, this is not good for expansion in the backend */
if(!passwordSave.empty()) {
if(sourceConfigNode.get() == NULL) {
globalConfigNode.addFilter(getMainName(), InitStateString(passwordSave, true));
} else {
sourceConfigNode->addFilter(getMainName(), InitStateString(passwordSave, true));
}
}
}
std::string PasswordConfigProperty::getUsername(const ConfigProperty &usernameProperty,
const FilterConfigNode &node)
{
InitStateString username = usernameProperty.getProperty(node);
UserIdentity id(UserIdentity::fromString(username));
return id.m_identity;
}
void PasswordConfigProperty::savePassword(UserInterface &ui,
SyncConfig &config,
const ConfigProperty &usernameProperty,
const std::string &sourceName) const
const string &serverName,
FilterConfigNode &globalConfigNode,
const string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const
{
std::string serverName = config.getConfigName();
boost::shared_ptr<FilterConfigNode> globalConfigNode = config.getProperties();
boost::shared_ptr<FilterConfigNode> sourceConfigNode;
if (!sourceName.empty()) {
sourceConfigNode = config.getSyncSourceNodes(sourceName).getNode(*this);
/** here we don't invoke askPassword for this function has different logic from it */
string password;
if(sourceConfigNode.get() == NULL) {
password = getProperty(globalConfigNode);
} else {
password = getProperty(*sourceConfigNode);
}
FilterConfigNode &configNode = sourceConfigNode ? *sourceConfigNode : *globalConfigNode;
InitStateString username = usernameProperty.getProperty(configNode);
// In checkPassword() we retrieve from background storage and store as temporary value.
// Here we use the temporary value and move it in the background storage.
InitStateString password = getProperty(configNode);
if (!password.wasSet()) {
/** if it has been stored or it has no value, do nothing */
if(password == "-" || password == "") {
return;
} else if(boost::starts_with(password, "${") &&
boost::ends_with(password, "}")) {
/** we delay this calculation of environment variable for
* it might be changed in the sync time. */
return;
}
SE_LOG_DEBUG(NULL, "saving password property '%s' in config '%s' with user identity '%s'",
getMainName().c_str(),
serverName.c_str(),
username.c_str());
UserIdentity identity(UserIdentity::fromString(username));
bool updatePassword = false;
InitStateString passwordToSave;
if (identity.m_provider == USER_IDENTITY_SYNC_CONFIG) {
// Store in the other config and unset it here.
updatePassword = true;
const std::string &credConfigName = identity.m_identity;
SE_LOG_INFO(NULL, "setting %s in config '%s' as part of credentials for %s%s%s",
syncPropPassword.getMainName().c_str(),
credConfigName.c_str(),
serverName.c_str(),
sourceName.empty() ? "" : "/",
sourceName.c_str());
// Actual username/password are stored in a different config. Go find it...
boost::shared_ptr<ConfigTree> tree = config.getConfigTree();
SyncConfig::Layout layout = config.getLayout();
if (layout != SyncConfig::SHARED_LAYOUT) {
SE_THROW(StringPrintf("%s = %s: only supported in configs using the current config storage, please migrate config %s",
usernameProperty.getMainName().c_str(),
username.c_str(),
config.getConfigName().c_str()));
}
boost::shared_ptr<SyncConfig> credConfig(new SyncConfig(credConfigName, layout, tree));
if (!credConfig->exists()) {
SE_THROW(StringPrintf("%s = %s: config '%s' not found, cannot look up credentials",
usernameProperty.getMainName().c_str(),
username.c_str(),
credConfigName.c_str()));
}
credConfig->setSyncPassword(password, false);
syncPropPassword.savePassword(ui, *credConfig);
} else if (identity.m_provider != USER_IDENTITY_PLAIN_TEXT) {
// Cannot store passwords in providers.
if (password.wasSet() && !password.empty()) {
SE_THROW(StringPrintf("setting property '%s' not supported for provider '%s' from property '%s'",
getMainName().c_str(),
identity.m_provider.c_str(),
usernameProperty.getMainName().c_str()));
}
} else {
if (password == "-" || password == "" ||
(boost::starts_with(password, "${") && boost::ends_with(password, "}"))) {
// Nothing to do, leave it as is.
string descr = getDescr(serverName,globalConfigNode,sourceName,sourceConfigNode);
ConfigPasswordKey key = getPasswordKey(descr,serverName,globalConfigNode,sourceName,sourceConfigNode);
if(ui.savePassword(getMainName(), password, key)) {
string value = "-";
if(sourceConfigNode.get() == NULL) {
setProperty(globalConfigNode, value);
} else {
string descr = getDescr(serverName,*globalConfigNode,sourceName,sourceConfigNode);
ConfigPasswordKey key = getPasswordKey(descr,serverName,*globalConfigNode,sourceName,sourceConfigNode);
if (ui.savePassword(getMainName(), password, key)) {
passwordToSave = "-";
updatePassword = true;
}
setProperty(*sourceConfigNode,value);
}
}
if (updatePassword) {
setProperty(configNode, passwordToSave);
}
}
void SyncConfig::setSyncPassword(const string &value, bool temporarily) { syncPropPassword.setProperty(*getNode(syncPropPassword), value, temporarily); }
InitStateString PasswordConfigProperty::getCachedProperty(const ConfigNode &node,
const string &cachedPassword)
{
InitStateString password;
if (!cachedPassword.empty()) {
password = InitStateString(cachedPassword, true);
} else {
password = getProperty(node);
}
return password;
}
/**
* remove some unnecessary parts of server URL.
* internal use.
*/
static void purifyServer(string &server)
{
/** here we use server sync url without protocol prefix and
* user account name as the key in the keyring */
size_t start = server.find("://");
/** we don't reserve protocol prefix for it may change*/
if(start != server.npos) {
server = server.substr(start + 3);
}
}
ConfigPasswordKey PasswordConfigProperty::getPasswordKey(const string &descr,
const string &serverName,
FilterConfigNode &globalConfigNode,
const string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const
{
ConfigPasswordKey key;
key.server = syncPropSyncURL.getProperty(globalConfigNode);
purifyServer(key.server);
key.user = syncPropUsername.getProperty(globalConfigNode);
return key;
}
void ProxyPasswordConfigProperty::checkPassword(UserInterface &ui,
const string &serverName,
FilterConfigNode &globalConfigNode,
const string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const
{
/* if useProxy is set 'true', then check proxypassword */
if(syncPropUseProxy.getPropertyValue(globalConfigNode)) {
PasswordConfigProperty::checkPassword(ui, serverName, globalConfigNode, sourceName, sourceConfigNode);
}
}
ConfigPasswordKey ProxyPasswordConfigProperty::getPasswordKey(const string &descr,
const string &serverName,
FilterConfigNode &globalConfigNode,
const string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const
{
ConfigPasswordKey key;
key.server = syncPropProxyHost.getProperty(globalConfigNode);
key.user = syncPropProxyUsername.getProperty(globalConfigNode);
return key;
}
void SyncConfig::setSyncPassword(const string &value, bool temporarily) { m_cachedPassword = ""; syncPropPassword.setProperty(*getNode(syncPropPassword), value, temporarily); }
InitState<bool> SyncConfig::getPreventSlowSync() const {
return syncPropPreventSlowSync.getPropertyValue(*getNode(syncPropPreventSlowSync));
@ -2100,17 +1916,13 @@ InitStateString SyncConfig::getProxyHost() const {
void SyncConfig::setProxyHost(const string &value, bool temporarily) { syncPropProxyHost.setProperty(*getNode(syncPropProxyHost), value, temporarily); }
UserIdentity SyncConfig::getProxyUser() const {
InitStateString username = syncPropProxyUsername.getProperty(*getNode(syncPropProxyUsername));
UserIdentity id(UserIdentity::fromString(username));
return id;
}
InitStateString SyncConfig::getProxyUsername() const { return syncPropProxyUsername.getProperty(*getNode(syncPropProxyUsername)); }
void SyncConfig::setProxyUsername(const string &value, bool temporarily) { syncPropProxyUsername.setProperty(*getNode(syncPropProxyUsername), value, temporarily); }
InitStateString SyncConfig::getProxyPassword() const {
return syncPropProxyPassword.getProperty(*getNode(syncPropProxyPassword));
return syncPropProxyPassword.getCachedProperty(*getNode(syncPropProxyPassword), m_cachedProxyPassword);
}
void SyncConfig::setProxyPassword(const string &value, bool temporarily) { syncPropProxyPassword.setProperty(*getNode(syncPropProxyPassword), value, temporarily); }
void SyncConfig::setProxyPassword(const string &value, bool temporarily) { m_cachedProxyPassword = ""; syncPropProxyPassword.setProperty(*getNode(syncPropProxyPassword), value, temporarily); }
InitState< vector<string> > SyncConfig::getSyncURL() const {
InitStateString s = syncPropSyncURL.getProperty(*getNode(syncPropSyncURL));
vector<string> urls;
@ -2696,52 +2508,7 @@ static ConfigProperty sourcePropUser(Aliases("databaseUser") + "evolutionuser",
"Warning: setting database user/password in cases where it is not\n"
"needed, as for example with local Evolution calendars and addressbooks,\n"
"can cause the Evolution backend to hang.");
static class DatabasePasswordConfigProperty : public PasswordConfigProperty {
public:
DatabasePasswordConfigProperty() :
PasswordConfigProperty(Aliases("databasePassword") + "evolutionpassword", "","", "backend")
{}
virtual void checkPassword(UserInterface &ui,
SyncConfig &config,
int flags,
const std::string &sourceName) const {
PasswordConfigProperty::checkPassword(ui, config, flags, sourcePropUser, sourceName);
}
virtual void savePassword(UserInterface &ui,
SyncConfig &config,
const std::string &sourceName) const {
PasswordConfigProperty::savePassword(ui, config, sourcePropUser, sourceName);
}
virtual ConfigPasswordKey getPasswordKey(const std::string &descr,
const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName = std::string(),
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode=boost::shared_ptr<FilterConfigNode>()) const {
ConfigPasswordKey key;
key.user = getUsername(sourcePropUser, *sourceConfigNode);
std::string configName = SyncConfig::normalizeConfigString(serverName, SyncConfig::NORMALIZE_LONG_FORMAT);
std::string peer, context;
SyncConfig::splitConfigString(configName, peer, context);
key.object = "@";
key.object += context;
key.object += " ";
key.object += sourceName;
key.object += " backend";
return key;
}
virtual const std::string getDescr(const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const {
std::string descr = sourceName;
descr += " ";
descr += ConfigProperty::getDescr();
return descr;
}
} sourcePropPassword;
static DatabasePasswordConfigProperty sourcePropPassword(Aliases("databasePassword") + "evolutionpassword", "","", "backend");
static ConfigProperty sourcePropAdminData(SourceAdminDataName,
"used by the Synthesis library internally; do not modify");
@ -2865,17 +2632,22 @@ SyncSourceNodes::getNode(const ConfigProperty &prop) const
InitStateString SyncSourceConfig::getDatabaseID() const { return sourcePropDatabaseID.getProperty(*getNode(sourcePropDatabaseID)); }
void SyncSourceConfig::setDatabaseID(const string &value, bool temporarily) { sourcePropDatabaseID.setProperty(*getNode(sourcePropDatabaseID), value, temporarily); }
UserIdentity SyncSourceConfig::getUser() const {
InitStateString user = sourcePropUser.getProperty(*getNode(sourcePropUser));
UserIdentity id(UserIdentity::fromString(user));
return id;
}
void SyncSourceConfig::setUsername(const string &value, bool temporarily) { sourcePropUser.setProperty(*getNode(sourcePropUser), value, temporarily); }
InitStateString SyncSourceConfig::getUser() const { return sourcePropUser.getProperty(*getNode(sourcePropUser)); }
void SyncSourceConfig::setUser(const string &value, bool temporarily) { sourcePropUser.setProperty(*getNode(sourcePropUser), value, temporarily); }
InitStateString SyncSourceConfig::getPassword() const {
return sourcePropPassword.getProperty(*getNode(sourcePropPassword));
return sourcePropPassword.getCachedProperty(*getNode(sourcePropPassword), m_cachedPassword);
}
void SyncSourceConfig::setPassword(const string &value, bool temporarily) { sourcePropPassword.setProperty(*getNode(sourcePropPassword), value, temporarily); }
void SyncSourceConfig::checkPassword(UserInterface &ui,
const string &serverName,
FilterConfigNode& globalConfigNode) {
sourcePropPassword.checkPassword(ui, serverName, globalConfigNode, m_name, getNode(sourcePropPassword));
}
void SyncSourceConfig::savePassword(UserInterface &ui,
const string &serverName,
FilterConfigNode& globalConfigNode) {
sourcePropPassword.savePassword(ui, serverName, globalConfigNode, m_name, getNode(sourcePropPassword));
}
void SyncSourceConfig::setPassword(const string &value, bool temporarily) { m_cachedPassword = ""; sourcePropPassword.setProperty(*getNode(sourcePropPassword), value, temporarily); }
InitStateString SyncSourceConfig::getURI() const { return sourcePropURI.getProperty(*getNode(sourcePropURI)); }
InitStateString SyncSourceConfig::getURINonEmpty() const {
InitStateString uri = sourcePropURI.getProperty(*getNode(sourcePropURI));
@ -3012,6 +2784,24 @@ void SyncSourceConfig::setSynthesisID(int value, bool temporarily) {
sourcePropSynthesisID.setProperty(*getNode(sourcePropSynthesisID), value, temporarily);
}
ConfigPasswordKey DatabasePasswordConfigProperty::getPasswordKey(const string &descr,
const string &serverName,
FilterConfigNode &globalConfigNode,
const string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const
{
ConfigPasswordKey key;
key.user = sourcePropUser.getProperty(*sourceConfigNode);
std::string configName = SyncConfig::normalizeConfigString(serverName, SyncConfig::NORMALIZE_LONG_FORMAT);
std::string peer, context;
SyncConfig::splitConfigString(configName, peer, context);
key.object = "@";
key.object += context;
key.object += " ";
key.object += sourceName;
key.object += " backend";
return key;
}
// Used for built-in templates
SyncConfig::TemplateDescription::TemplateDescription (const std::string &name, const std::string &description)

View File

@ -128,60 +128,11 @@ class ConfigTree;
class UserInterface;
class SyncSourceNodes;
class ConstSyncSourceNodes;
class SyncConfig;
struct ConfigPasswordKey;
/** name of the per-source admin data property */
extern const char *const SourceAdminDataName;
struct UserIdentity {
/**
* Defines how information about the user and the
* corresponding authentication method is stored.
*
* The config level itself handles USER_IDENTITY_PLAIN_TEXT (=
* "user", the traditional username/password authentication
* with the password being stored in the config, in a keyring
* or retrieved at runtime) and USER_IDENTITY_SYNC_CONFIG (=
* "id", an indirect lookup mechanism which uses the
* username/password from a different sync config).
*
* Other string values are also allowed in the config. When
* needed, one of the plugable identity providers must
* recognize the string and provide credentials.
*
* Never empty. However, it may be marked as "unset", in which
* case the actual string in the config didn't have a prefix
* and the traditional USER_IDENTITY_PLAIN_TEXT is used as
* fallback.
*/
InitStateString m_provider;
/**
* The actual username (USER_IDENTITY_PLAIN_TEXT) or config name
* (USER_IDENTITY_SYNC_CONFIG).
*
* It is unset if and only if the config had no value for the "username"
* property.
*/
InitStateString m_identity;
/**
* Returns concatenation of provider prefix (only if set) and idendity.
*/
InitStateString toString() const;
/**
* Splits the string as generated by toString().
*/
static UserIdentity fromString(const InitStateString &idString);
/**
* true iff the config had a value.
*/
bool wasSet() const { return m_identity.wasSet(); }
};
/** simplified creation of string lists: InitList("foo") + "bar" + ... */
template<class T> class InitList : public std::list<T> {
public:
@ -334,14 +285,16 @@ class ConfigProperty {
* sourceName and sourceConfigNode might be not set by caller. They only affect
* when checking password for syncsourceconfig
* @param ui user interface
* @param config the peer config in which we are checking the password
* @param flags see PasswordConfigProperty
* @param sourceName the source name for which we need the password, empty if checking a peer password
* @param serverName server name
* @param globalConfigNode the sync global config node for a server
* @param sourceName the source name used for source config properties
* @param sourceConfigNode the config node for the source
*/
virtual void checkPassword(UserInterface &ui,
SyncConfig &config,
int flags,
const std::string &sourceName = std::string()) const {}
const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName = std::string(),
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode = boost::shared_ptr<FilterConfigNode>()) const {}
/**
* Try to save password if a config property wants.
@ -349,8 +302,10 @@ class ConfigProperty {
* function to save the password if necessary
*/
virtual void savePassword(UserInterface &ui,
SyncConfig &config,
const std::string &sourceName = std::string()) const {}
const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName = std::string(),
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode = boost::shared_ptr<FilterConfigNode>()) const {}
/**
* This is used to generate description dynamically according to the context information
@ -716,11 +671,6 @@ class SecondsConfigProperty : public UIntConfigProperty
static bool parseDuration(const std::string &value, std::string &error, unsigned int &seconds);
};
/**
* Supports lookup of password via env variable, interactively, and
* keyring. The keys for the keyring are derived from other properties
* in derived classes.
*/
class PasswordConfigProperty : public ConfigProperty {
public:
PasswordConfigProperty(const std::string &name, const std::string &comment, const std::string &def = std::string(""),const std::string &descr = std::string("")) :
@ -731,64 +681,29 @@ class PasswordConfigProperty : public ConfigProperty {
{}
/**
* Check the password and cache the result. Accessed via base
* ConfigProperty class, must be implemented in derived classes.
* Check the password and cache the result.
*/
virtual void checkPassword(UserInterface &ui,
SyncConfig &config,
int flags,
const std::string &sourceName = std::string()) const = 0;
const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName = "",
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode =
boost::shared_ptr<FilterConfigNode>()) const;
/**
* Move password to storage. Accessed via base ConfigProperty
* class, must be implemented in derived classes.
* It firstly check password and then invoke ui's savePassword
* function to save the password if necessary
*/
virtual void savePassword(UserInterface &ui,
SyncConfig &config,
const std::string &sourceName = std::string()) const = 0;
const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName = "",
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode =
boost::shared_ptr<FilterConfigNode>()) const;
/**
* Utility method, invokes checkPassword() on the properties selected
* via flags and sourceNames array. Optionally updates the properties
* to reflect the actual values which need to be used.
*/
static void checkPasswords(UserInterface &ui,
SyncConfig &config,
int flags,
const std::list<std::string> &sourceNames = std::list<std::string>());
enum {
CHECK_PASSWORD_RESOLVE_USERNAME = 1<<0, ///< temporarily replace username that corresponds to the password
CHECK_PASSWORD_RESOLVE_PASSWORD = 1<<1, ///< temporarily replace password with actual value
CHECK_PASSWORD_SYNC = 1<<2, ///< check sync properties
CHECK_PASSWORD_SOURCE = 1<<3, ///< check source properties
/** Alias for the "check and resolve" everything. */
CHECK_PASSWORD_ALL = CHECK_PASSWORD_RESOLVE_USERNAME|CHECK_PASSWORD_RESOLVE_PASSWORD|CHECK_PASSWORD_SYNC|CHECK_PASSWORD_SOURCE,
CHECK_PASSWORD_FLAG_NONE = 0
};
protected:
/**
* The actual implementation, with additional information
* provided by derived classes.
*/
void checkPassword(UserInterface &ui,
SyncConfig &config,
int flags,
const ConfigProperty &usernameProperty,
const std::string &sourceName = std::string()) const;
void savePassword(UserInterface &ui,
SyncConfig &config,
const ConfigProperty &usernameProperty,
const std::string &sourceName = std::string()) const;
/**
* Get password lookup key for storing or retrieving passwords.
*
* Get password key for storing or retrieving passwords
* The default implemention is for 'password' in global config.
* @param descr decription for password
* @param globalConfigNode the global config node
* @param sourceConfigNode the source config node. It might be empty
@ -798,10 +713,64 @@ class PasswordConfigProperty : public ConfigProperty {
FilterConfigNode &globalConfigNode,
const std::string &sourceName = std::string(),
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode =
boost::shared_ptr<FilterConfigNode>()) const = 0;
boost::shared_ptr<FilterConfigNode>()) const;
static std::string getUsername(const ConfigProperty &usernameProperty,
const FilterConfigNode &node);
/**
* return the cached value if necessary and possible
*/
virtual InitStateString getCachedProperty(const ConfigNode &node,
const std::string &cachedPassword);
};
/**
* A derived ConfigProperty class for the property "proxyPassword"
*/
class ProxyPasswordConfigProperty : public PasswordConfigProperty {
public:
ProxyPasswordConfigProperty(const std::string &name, const std::string &comment, const std::string &def = std::string(""), const std::string &descr = std::string("")) :
PasswordConfigProperty(name,comment,def,descr)
{}
/**
* re-implement this function for it is necessary to do a check
* before retrieving proxy password
*/
virtual void checkPassword(UserInterface &ui,
const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const;
virtual ConfigPasswordKey getPasswordKey(const std::string &descr,
const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName = std::string(),
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode=boost::shared_ptr<FilterConfigNode>()) const;
};
/**
* A derived ConfigProperty class for the property "evolutionpassword"
*/
class DatabasePasswordConfigProperty : public PasswordConfigProperty {
public:
DatabasePasswordConfigProperty(const Aliases &names,
const std::string &comment,
const std::string &def = std::string(""),
const std::string &descr = std::string("")):
PasswordConfigProperty(names,comment,def,descr)
{}
virtual ConfigPasswordKey getPasswordKey(const std::string &descr,
const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName = std::string(),
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode=boost::shared_ptr<FilterConfigNode>()) const;
virtual const std::string getDescr(const std::string &serverName,
FilterConfigNode &globalConfigNode,
const std::string &sourceName,
const boost::shared_ptr<FilterConfigNode> &sourceConfigNode) const {
std::string descr = sourceName;
descr += " ";
descr += ConfigProperty::getDescr();
return descr;
}
};
/**
@ -915,15 +884,6 @@ class ConfigPropertyRegistry : public std::list<const ConfigProperty *> {
*/
class SyncConfig {
public:
enum Layout {
SYNC4J_LAYOUT, /**< .syncj4/evolution/<server>, SyncEvolution <= 0.7.x */
HTTP_SERVER_LAYOUT, /**< .config/syncevolution/<server> with sources
underneath, SyncEvolution <= 0.9.x */
SHARED_LAYOUT /**< .config/syncevolution/<context> containing sources
and peers, with source settings shared by peers,
SyncEvolution >= 1.0 */
};
/**
* Opens the configuration for a specific server,
* searching for the config files in the usual
@ -951,8 +911,6 @@ class SyncConfig {
* searching for it; always uses the
* current layout in that tree
*
* @param layout if tree is given, then this is the layout to be used with it
*
* @param redirectPeerRootPath
* Can be used to redirect the per-peer
* files into a different directory. Only works
@ -960,7 +918,6 @@ class SyncConfig {
* Used by SyncContext for local sync.
*/
SyncConfig(const std::string &peer,
Layout treeLayout = HTTP_SERVER_LAYOUT,
boost::shared_ptr<ConfigTree> tree = boost::shared_ptr<ConfigTree>(),
const std::string &redirectPeerRootPath = "");
@ -1500,7 +1457,7 @@ class SyncConfig {
*/
/**@{*/
virtual UserIdentity getSyncUser() const;
virtual InitStateString getSyncUsername() const;
virtual void setSyncUsername(const std::string &value, bool temporarily = false);
virtual InitStateString getSyncPassword() const;
virtual void setSyncPassword(const std::string &value, bool temporarily = false);
@ -1511,7 +1468,7 @@ class SyncConfig {
virtual void setUseProxy(bool value, bool temporarily = false);
virtual InitStateString getProxyHost() const;
virtual void setProxyHost(const std::string &value, bool temporarily = false);
virtual UserIdentity getProxyUser() const;
virtual InitStateString getProxyUsername() const;
virtual void setProxyUsername(const std::string &value, bool temporarily = false);
virtual InitStateString getProxyPassword() const;
virtual void setProxyPassword(const std::string &value, bool temporarily = false);
@ -1618,18 +1575,21 @@ class SyncConfig {
virtual InitStateString getDevType() const;
/**@}*/
enum Layout {
SYNC4J_LAYOUT, /**< .syncj4/evolution/<server>, SyncEvolution <= 0.7.x */
HTTP_SERVER_LAYOUT, /**< .config/syncevolution/<server> with sources
underneath, SyncEvolution <= 0.9.x */
SHARED_LAYOUT /**< .config/syncevolution/<context> containing sources
and peers, with source settings shared by peers,
SyncEvolution >= 1.0 */
};
/** config versioning; setting is done internally */
int getConfigVersion(ConfigLevel level, ConfigLimit limit) const;
/** file layout used by config */
Layout getLayout() const { return m_layout; }
/**
* The storage for the current config and (if the layout is SHARED_LAYOUT)
* also other configs. May be NULL.
*/
boost::shared_ptr<ConfigTree> getConfigTree() const { return m_tree; }
private:
/**
* scans for peer configurations
@ -1684,6 +1644,8 @@ private:
Layout m_layout;
std::string m_redirectPeerRootPath;
std::string m_cachedPassword;
std::string m_cachedProxyPassword;
ConfigWriteMode m_configWriteMode;
Bool m_ephemeral;
@ -1909,12 +1871,20 @@ class SyncSourceConfig {
/** true if the source config exists with view-specific properties (not just default or shared ones) */
bool exists() const { return m_nodes.exists(); }
virtual UserIdentity getUser() const;
virtual void setUsername(const std::string &value, bool temporarily = false);
virtual InitStateString getUser() const;
virtual void setUser(const std::string &value, bool temporarily = false);
virtual InitStateString getPassword() const;
virtual void setPassword(const std::string &value, bool temporarily = false);
/** same as SyncConfig::checkPassword() but with
* an extra argument globalConfigNode for source config property
* may need global config node to check password */
virtual void checkPassword(UserInterface &ui, const std::string &serverName, FilterConfigNode& globalConfigNode);
/** same as SyncConfig::savePassword() */
virtual void savePassword(UserInterface &ui, const std::string &serverName, FilterConfigNode& globalConfigNode);
/** selects the backend database to use */
virtual InitStateString getDatabaseID() const;
virtual void setDatabaseID(const std::string &value, bool temporarily = false);
@ -1989,6 +1959,7 @@ class SyncSourceConfig {
std::string m_name;
SyncSourceNodes m_nodes;
std::string m_cachedPassword;
};
class SingleFileConfigTree;

View File

@ -28,7 +28,6 @@
#include <syncevo/util.h>
#include <syncevo/SuspendFlags.h>
#include <syncevo/ThreadSupport.h>
#include <syncevo/IdentityProvider.h>
#include <syncevo/SafeConfigNode.h>
#include <syncevo/IniConfigNode.h>
@ -102,7 +101,6 @@ SyncContext::SyncContext(const string &client,
const boost::shared_ptr<TransportAgent> &agent,
bool doLogging) :
SyncConfig(client,
SyncConfig::HTTP_SERVER_LAYOUT,
boost::shared_ptr<ConfigTree>(),
rootPath),
m_server(client),
@ -1519,19 +1517,8 @@ public:
}
return NULL;
}
std::list<std::string> getSourceNames() const;
};
std::list<std::string> SourceList::getSourceNames() const
{
std::list<std::string> sourceNames;
BOOST_FOREACH (SyncSource *source, *this) {
sourceNames.push_back(source->getName());
}
return sourceNames;
}
void unref(SourceList *sourceList)
{
delete sourceList;
@ -1948,7 +1935,7 @@ bool SyncContext::displaySourceProgress(SyncSource &source,
switch (extra1) {
case 401:
// TODO: reset cached password
SE_LOG_INFO(NULL, "authorization failed, check username '%s' and password", getSyncUser().toString().c_str());
SE_LOG_INFO(NULL, "authorization failed, check username '%s' and password", getSyncUsername().c_str());
break;
case 403:
SE_LOG_INFO(source.getDisplayName(), "log in succeeded, but server refuses access - contact server operator");
@ -2881,10 +2868,8 @@ void SyncContext::getConfigXML(string &xml, string &configname)
substTag(xml, "maxmsgsize", std::max(getMaxMsgSize().get(), 10000ul));
substTag(xml, "maxobjsize", std::max(getMaxObjSize().get(), 1024u));
if (m_serverMode) {
UserIdentity id = getSyncUser();
Credentials cred = IdentityProviderCredentials(id, getSyncPassword());
const string &user = cred.m_username;
const string &password = cred.m_password;
const string user = getSyncUsername();
const string password = getSyncPassword();
/*
* Do not check username/pwd if this local sync or over
@ -3295,7 +3280,7 @@ SyncMLStatus SyncContext::sync(SyncReport *report)
try {
// dump some summary information at the beginning of the log
SE_LOG_DEV(NULL, "SyncML server account: %s", getSyncUser().toString().c_str());
SE_LOG_DEV(NULL, "SyncML server account: %s", getSyncUsername().c_str());
SE_LOG_DEV(NULL, "client: SyncEvolution %s for %s", getSwv().c_str(), getDevType().c_str());
SE_LOG_DEV(NULL, "device ID: %s", getDevID().c_str());
SE_LOG_DEV(NULL, "%s", EDSAbiWrapperDebug());
@ -3322,9 +3307,24 @@ SyncMLStatus SyncContext::sync(SyncReport *report)
startLoopThread();
// ask for passwords now
PasswordConfigProperty::checkPasswords(getUserInterfaceNonNull(), *this,
PasswordConfigProperty::CHECK_PASSWORD_ALL,
sourceList.getSourceNames());
/* iterator over all sync and source properties instead of checking
* some specified passwords.
*/
ConfigPropertyRegistry& registry = SyncConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
SE_LOG_DEBUG(NULL, "checking sync password %s", prop->getMainName().c_str());
prop->checkPassword(getUserInterfaceNonNull(), m_server, *getProperties());
}
BOOST_FOREACH(SyncSource *source, sourceList) {
ConfigPropertyRegistry& registry = SyncSourceConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
SE_LOG_DEBUG(NULL, "checking source %s password %s",
source->getName().c_str(),
prop->getMainName().c_str());
prop->checkPassword(getUserInterfaceNonNull(), m_server, *getProperties(),
source->getName(), source->getProperties());
}
}
// open each source - failing now is still safe
// in clients; in servers we wait until the source
@ -3425,11 +3425,7 @@ bool SyncContext::sendSAN(uint16_t version)
bool legacy = version < 12;
/* Should be nonce sent by the server in the preceeding sync session */
string nonce = "SyncEvolution";
UserIdentity id = getSyncUser();
Credentials cred = IdentityProviderCredentials(id, getSyncPassword());
const std::string &user = cred.m_username;
const std::string &password = cred.m_password;
string uauthb64 = san.B64_H(user, password);
string uauthb64 = san.B64_H (getSyncUsername(), getSyncPassword());
/* Client is expected to conduct the sync in the backgroud */
sysync::UI_Mode mode = sysync::UI_not_specified;
@ -3709,12 +3705,8 @@ SyncMLStatus SyncContext::doSync()
}
m_engine.SetStrValue(profile, "serverURI", getUsedSyncURL());
UserIdentity id = getSyncUser();
Credentials cred = IdentityProviderCredentials(id, getSyncPassword());
const std::string &user = cred.m_username;
const std::string &password = cred.m_password;
m_engine.SetStrValue(profile, "serverUser", user);
m_engine.SetStrValue(profile, "serverPassword", password);
m_engine.SetStrValue(profile, "serverUser", getSyncUsername());
m_engine.SetStrValue(profile, "serverPassword", getSyncPassword());
m_engine.SetInt32Value(profile, "encoding",
getWBXML() ? 1 /* WBXML */ : 2 /* XML */);
@ -4299,10 +4291,13 @@ void SyncContext::status()
SourceList sourceList(*this, false);
initSources(sourceList);
PasswordConfigProperty::checkPasswords(getUserInterfaceNonNull(), *this,
// Don't need sync passwords.
PasswordConfigProperty::CHECK_PASSWORD_ALL & ~PasswordConfigProperty::CHECK_PASSWORD_SYNC,
sourceList.getSourceNames());
BOOST_FOREACH(SyncSource *source, sourceList) {
ConfigPropertyRegistry& registry = SyncSourceConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(getUserInterfaceNonNull(), m_server, *getProperties(),
source->getName(), source->getProperties());
}
}
BOOST_FOREACH(SyncSource *source, sourceList) {
source->open();
}
@ -4348,10 +4343,13 @@ void SyncContext::checkStatus(SyncReport &report)
SourceList sourceList(*this, false);
initSources(sourceList);
PasswordConfigProperty::checkPasswords(getUserInterfaceNonNull(), *this,
// Don't need sync passwords.
PasswordConfigProperty::CHECK_PASSWORD_ALL & ~PasswordConfigProperty::CHECK_PASSWORD_SYNC,
sourceList.getSourceNames());
BOOST_FOREACH(SyncSource *source, sourceList) {
ConfigPropertyRegistry& registry = SyncSourceConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(getUserInterfaceNonNull(), m_server, *getProperties(),
source->getName(), source->getProperties());
}
}
BOOST_FOREACH(SyncSource *source, sourceList) {
source->open();
}
@ -4427,10 +4425,14 @@ void SyncContext::restore(const string &dirname, RestoreDatabase database)
sourceList.accessSession(dirname.c_str());
Logger::instance().setLevel(Logger::INFO);
initSources(sourceList);
PasswordConfigProperty::checkPasswords(getUserInterfaceNonNull(), *this,
// Don't need sync passwords.
PasswordConfigProperty::CHECK_PASSWORD_ALL & ~PasswordConfigProperty::CHECK_PASSWORD_SYNC,
sourceList.getSourceNames());
BOOST_FOREACH(SyncSource *source, sourceList) {
ConfigPropertyRegistry& registry = SyncSourceConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(getUserInterfaceNonNull(), m_server, *getProperties(),
source->getName(), source->getProperties());
}
}
string datadump = database == DATABASE_BEFORE_SYNC ? "before" : "after";
BOOST_FOREACH(SyncSource *source, sourceList) {

View File

@ -33,7 +33,6 @@ using namespace sysync;
#include <syncevo/SyncContext.h>
#include <syncevo/SyncSource.h>
#include <syncevo/IdentityProvider.h>
#include <sstream>
@ -314,10 +313,8 @@ TSyError SyncEvolution_Session_Login( CContext sContext, cAppCharP sUsername, ap
return LOCERR_WRONGUSAGE;
}
TSyError res = DB_Forbidden;
UserIdentity id = sc->getSyncUser();
Credentials cred = IdentityProviderCredentials(id, sc->getSyncPassword());
const std::string &user = cred.m_username;
const std::string &password = cred.m_password;
string user = sc->getSyncUsername();
string password = sc->getSyncPassword();
if (user.empty() && password.empty()) {
// nothing to check, accept peer

View File

@ -19,7 +19,6 @@
#include <syncevo/TransportAgent.h>
#include <syncevo/SyncConfig.h>
#include <syncevo/IdentityProvider.h>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
@ -33,9 +32,8 @@ void HTTPTransportAgent::setConfig(SyncConfig &config)
{
if (config.getUseProxy()) {
setProxy(config.getProxyHost());
UserIdentity identity = config.getProxyUser();
Credentials cred = IdentityProviderCredentials(identity, config.getProxyPassword());
setProxyAuth(cred.m_username, cred.m_password);
setProxyAuth(config.getProxyUsername(),
config.getProxyPassword());
}
setUserAgent(config.getUserAgent());
setSSL(config.findSSLServerCertificate(),

View File

@ -22,9 +22,6 @@ src_syncevo_sources = \
src/syncevo/EDSClient.h \
src/syncevo/EDSClient.cpp \
\
src/syncevo/IdentityProvider.h \
src/syncevo/IdentityProvider.cpp \
\
src/syncevo/ConfigTree.h \
src/syncevo/ConfigFilter.h \
src/syncevo/ConfigFilter.cpp \
@ -165,7 +162,6 @@ src_syncevo_libsyncevolution_include_HEADERS = \
src/syncevo/SafeConfigNode.h \
src/syncevo/SyncConfig.h \
src/syncevo/SyncSource.h \
src/syncevo/IdentityProvider.h \
src/syncevo/util.h \
src/syncevo/BoostHelper.h \
src/syncevo/SuspendFlags.h \

View File

@ -3934,80 +3934,6 @@ class TestMultipleConfigs(unittest.TestCase, DBusUtil):
self.assertEqual(config[""]["defaultPeer"], "foobar_peer")
self.assertNotEqual(config[""]["deviceId"], "shared-device-identifier")
def testCredentials(self):
"""TestMultipleConfigs.testCredentials - test storing username/password"""
self.setupConfigs()
# Store username/password in config 'foo' without using keyring.
# We want this test to work in all cases.
self.setUpSession("foo")
config = self.session.GetConfig(False, utf8_strings=True)
config[""]["keyring"] = "no"
config[""]["username"] = "john"
config[""]["password"] = "doe-pwd"
self.session.SetConfig(True, False, config)
self.session.Detach()
# Retrieve username/password.
self.setUpSession("foo")
config = self.session.GetConfig(False, utf8_strings=True)
self.assertEqual(config[""]["username"], "john")
self.assertEqual(config[""]["password"], "doe-pwd")
self.session.Detach()
# Re-use credentials in second config.
self.setUpSession("bar")
config = self.session.GetConfig(False, utf8_strings=True)
config[""]["username"] = "id:foo"
self.assertNotIn("password", config[""])
self.session.SetConfig(True, False, config)
self.session.Detach()
# We expect to see the actual "username" value from "bar"
# here, not the one from "foo". When running a sync, the
# credentials from "foo" must be used, which needs to be
# tested in configurations used by the nightly testing.
#
# The "password" however should come from "foo", to allow the
# user to edit it. This is consistent with showing the
# password after retrieving it from a keyring.
self.setUpSession("bar")
config = self.session.GetConfig(False, utf8_strings=True)
self.assertEqual(config[""]["username"], "id:foo")
self.assertEqual(config[""]["password"], "doe-pwd")
# When changing passwords via "bar", the actual writes need
# to go to "foo".
config[""]["password"] = "doe-pwd-2"
self.session.SetConfig(True, False, config)
self.session.Detach()
# Check in "foo".
self.setUpSession("foo")
config = self.session.GetConfig(False, utf8_strings=True)
self.assertEqual(config[""]["username"], "john")
self.assertEqual(config[""]["password"], "doe-pwd-2")
self.session.Detach()
# Check in "bar".
self.setUpSession("bar")
config = self.session.GetConfig(False, utf8_strings=True)
self.assertEqual(config[""]["username"], "id:foo")
self.assertEqual(config[""]["password"], "doe-pwd-2")
# Now try it also with proxy and database crdentials.
config[""]["proxyUsername"] = "id:foo"
config[""]["useProxy"] = "1"
config["source/calendar"]["databaseUser"] = "id:foo"
self.session.SetConfig(True, False, config)
config = self.session.GetConfig(False, utf8_strings=True)
self.assertEqual(config[""]["proxyUsername"], "id:foo")
self.assertEqual(config[""]["proxyPassword"], "doe-pwd-2")
self.assertEqual(config["source/calendar"]["databaseUser"], "id:foo")
self.assertEqual(config["source/calendar"]["databasePassword"], "doe-pwd-2")
class TestLocalSync(unittest.TestCase, DBusUtil):
"""Tests involving local sync."""