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:
parent
c0212c4585
commit
b7fa64f15c
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 \
|
||||
...
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||
])
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 ®istry = 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();
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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); \
|
||||
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ®istry = 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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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."""
|
||||
|
||||
|
|
Loading…
Reference in New Issue