Optionally return error message from wxSecretStore::IsOk()
This allows to give at least some explanation about why the secrets can't be stored to the user, e.g. they could search for a message such as The name org.freedesktop.secrets was not provided by any .service files to find out that gnome-keyring package needs to be installed on their system. Note that this change means that under Unix an attempt to connect to the secret service is now made when wxSecretStore is constructed and not just when it's used for the first time, as before.
This commit is contained in:
@@ -78,6 +78,8 @@ private:
|
||||
class wxSecretStoreImpl : public wxRefCounter
|
||||
{
|
||||
public:
|
||||
virtual bool IsOk(wxString* WXUNUSED(errmsg)) const { return true; }
|
||||
|
||||
virtual bool Save(const wxString& service,
|
||||
const wxString& username,
|
||||
const wxSecretValueImpl& password,
|
||||
|
@@ -126,8 +126,9 @@ public:
|
||||
~wxSecretStore();
|
||||
|
||||
|
||||
// Check if this object is valid.
|
||||
bool IsOk() const { return m_impl != NULL; }
|
||||
// Check if this object is valid, i.e. can be used, and optionally fill in
|
||||
// the provided error message string if it isn't.
|
||||
bool IsOk(wxString* errmsg = NULL) const;
|
||||
|
||||
|
||||
// Store a username/password combination.
|
||||
|
@@ -166,14 +166,16 @@ public:
|
||||
Example of storing credentials using this class:
|
||||
@code
|
||||
wxSecretStore store = wxSecretStore::GetDefault();
|
||||
if ( store.IsOk() )
|
||||
wxString errmsg;
|
||||
if ( store.IsOk(&errmsg) )
|
||||
{
|
||||
if ( !store.Save("MyApp/MyService", username, password) )
|
||||
wxLogWarning("Failed to save credentials to the system secret store.");
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogWarning("This system doesn't support storing passwords securely.");
|
||||
wxLogWarning("This system doesn't support storing passwords securely "
|
||||
"(%s).", errmsg);
|
||||
}
|
||||
@endcode
|
||||
|
||||
@@ -205,9 +207,13 @@ public:
|
||||
static wxSecretStore GetDefault();
|
||||
|
||||
/**
|
||||
Check if this object is valid.
|
||||
Check if this object can actually be used.
|
||||
|
||||
@param errmsg If not @NULL, this parameter is filled with a
|
||||
user-readable error message explaining why the secret store can't
|
||||
be used (this argument is new since wxWidgets 3.1.4)
|
||||
*/
|
||||
bool IsOk() const;
|
||||
bool IsOk(wxString* errmsg = NULL) const;
|
||||
|
||||
/**
|
||||
Store a username/password combination.
|
||||
|
@@ -196,9 +196,11 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
wxSecretStore store = wxSecretStore::GetDefault();
|
||||
if ( !store.IsOk() )
|
||||
wxString errmsg;
|
||||
if ( !store.IsOk(&errmsg) )
|
||||
{
|
||||
wxFprintf(stderr, "Failed to create default secret store.\n");
|
||||
wxFprintf(stderr, "Failed to create default secret store (%s)\n",
|
||||
errmsg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@@ -162,6 +162,19 @@ wxSecretStore::~wxSecretStore()
|
||||
// Methods forwarded to wxSecretStoreImpl
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
wxSecretStore::IsOk(wxString* errmsg) const
|
||||
{
|
||||
if ( !m_impl )
|
||||
{
|
||||
if ( errmsg )
|
||||
*errmsg = _("Not available for this platform");
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_impl->IsOk(errmsg);
|
||||
}
|
||||
|
||||
bool
|
||||
wxSecretStore::Save(const wxString& service,
|
||||
const wxString& user,
|
||||
|
@@ -119,6 +119,52 @@ private:
|
||||
SecretValue* const m_value;
|
||||
};
|
||||
|
||||
// Dummy implementation used when secret service is not available.
|
||||
class wxSecretStoreNotAvailableImpl : public wxSecretStoreImpl
|
||||
{
|
||||
public:
|
||||
explicit wxSecretStoreNotAvailableImpl(const wxString& error)
|
||||
: m_error(error)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool IsOk(wxString* errmsg) const wxOVERRIDE
|
||||
{
|
||||
if ( errmsg )
|
||||
*errmsg = m_error;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool Save(const wxString& WXUNUSED(service),
|
||||
const wxString& WXUNUSED(user),
|
||||
const wxSecretValueImpl& WXUNUSED(secret),
|
||||
wxString& errmsg) wxOVERRIDE
|
||||
{
|
||||
errmsg = m_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool Load(const wxString& WXUNUSED(service),
|
||||
wxString* WXUNUSED(user),
|
||||
wxSecretValueImpl** WXUNUSED(secret),
|
||||
wxString& errmsg) const wxOVERRIDE
|
||||
{
|
||||
errmsg = m_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool Delete(const wxString& WXUNUSED(service),
|
||||
wxString& errmsg) wxOVERRIDE
|
||||
{
|
||||
errmsg = m_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const wxString m_error;
|
||||
};
|
||||
|
||||
// This implementation uses synchronous libsecret functions which is supposed
|
||||
// to be a bad idea, but doesn't seem to be a big deal in practice and as there
|
||||
// is no simple way to implement asynchronous API under the other platforms, it
|
||||
@@ -127,6 +173,25 @@ private:
|
||||
class wxSecretStoreLibSecretImpl : public wxSecretStoreImpl
|
||||
{
|
||||
public:
|
||||
static wxSecretStoreLibSecretImpl* Create(wxString& errmsg)
|
||||
{
|
||||
wxGtkError error;
|
||||
SecretService* const service = secret_service_get_sync
|
||||
(
|
||||
SECRET_SERVICE_OPEN_SESSION,
|
||||
NULL, // No cancellation
|
||||
error.Out()
|
||||
);
|
||||
if ( !service )
|
||||
{
|
||||
errmsg = error.GetMessage();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This passes ownership of service to the new object.
|
||||
return new wxSecretStoreLibSecretImpl(service);
|
||||
}
|
||||
|
||||
virtual bool Save(const wxString& service,
|
||||
const wxString& user,
|
||||
const wxSecretValueImpl& secret,
|
||||
@@ -142,7 +207,7 @@ public:
|
||||
wxGtkError error;
|
||||
if ( !secret_service_store_sync
|
||||
(
|
||||
NULL, // Default service
|
||||
m_service,
|
||||
GetSchema(),
|
||||
BuildAttributes(service, user),
|
||||
SECRET_COLLECTION_DEFAULT,
|
||||
@@ -167,7 +232,7 @@ public:
|
||||
wxGtkError error;
|
||||
GList* const found = secret_service_search_sync
|
||||
(
|
||||
NULL, // Default service
|
||||
m_service,
|
||||
GetSchema(),
|
||||
BuildAttributes(service),
|
||||
static_cast<SecretSearchFlags>
|
||||
@@ -211,7 +276,7 @@ public:
|
||||
wxGtkError error;
|
||||
if ( !secret_service_clear_sync
|
||||
(
|
||||
NULL, // Default service
|
||||
m_service,
|
||||
GetSchema(),
|
||||
BuildAttributes(service),
|
||||
NULL, // Can't be cancelled
|
||||
@@ -279,6 +344,15 @@ private:
|
||||
NULL
|
||||
));
|
||||
}
|
||||
|
||||
// Ctor is private, Create() should be used for creating objects of this
|
||||
// class.
|
||||
explicit wxSecretStoreLibSecretImpl(SecretService* service)
|
||||
: m_service(service)
|
||||
{
|
||||
}
|
||||
|
||||
wxGtkObject<SecretService> m_service;
|
||||
};
|
||||
|
||||
const char* wxSecretStoreLibSecretImpl::FIELD_SERVICE = "service";
|
||||
@@ -299,8 +373,17 @@ wxSecretValueImpl* wxSecretValue::NewImpl(size_t size, const void *data)
|
||||
/* static */
|
||||
wxSecretStore wxSecretStore::GetDefault()
|
||||
{
|
||||
// There is only a single store under Windows anyhow.
|
||||
return wxSecretStore(new wxSecretStoreLibSecretImpl());
|
||||
// Try to create the real implementation.
|
||||
wxString errmsg;
|
||||
wxSecretStoreImpl* impl = wxSecretStoreLibSecretImpl::Create(errmsg);
|
||||
if ( !impl )
|
||||
{
|
||||
// But if we failed, fall back to a dummy one, so that we could at
|
||||
// least return the error to the code using this class.
|
||||
impl = new wxSecretStoreNotAvailableImpl(errmsg);
|
||||
}
|
||||
|
||||
return wxSecretStore(impl);
|
||||
}
|
||||
|
||||
#endif // wxUSE_SECRETSTORE
|
||||
|
Reference in New Issue
Block a user