signon  8.58
default-secrets-storage.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /*
00003  * This file is part of signon
00004  *
00005  * Copyright (C) 2011 Canonical Ltd.
00006  *
00007  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public License
00011  * version 2.1 as published by the Free Software Foundation.
00012  *
00013  * This library is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00021  * 02110-1301 USA
00022  */
00023 
00024 #include "default-secrets-storage.h"
00025 #include "signond-common.h"
00026 
00027 #define RETURN_IF_NOT_OPEN(retval) \
00028     if (!isOpen()) { \
00029         TRACE() << "Secrets DB is not available"; \
00030         SignOn::CredentialsDBError error(QLatin1String("Not open"), \
00031                                          SignOn::CredentialsDBError::NotOpen); \
00032         setLastError(error); return retval; \
00033     }
00034 
00035 #define S(s) QLatin1String(s)
00036 
00037 using namespace SignonDaemonNS;
00038 
00039 bool SecretsDB::createTables()
00040 {
00041     QStringList createTableQuery = QStringList()
00042         <<  QString::fromLatin1(
00043             "CREATE TABLE CREDENTIALS"
00044             "(id INTEGER NOT NULL UNIQUE,"
00045             "username TEXT,"
00046             "password TEXT,"
00047             "PRIMARY KEY (id))")
00048         <<  QString::fromLatin1(
00049             "CREATE TABLE STORE"
00050             "(identity_id INTEGER,"
00051             "method_id INTEGER,"
00052             "key TEXT,"
00053             "value BLOB,"
00054             "PRIMARY KEY (identity_id, method_id, key))")
00055 
00056         << QString::fromLatin1(
00057             // Cascading Delete
00058             "CREATE TRIGGER tg_delete_credentials "
00059             "BEFORE DELETE ON CREDENTIALS "
00060             "FOR EACH ROW BEGIN "
00061             "    DELETE FROM STORE WHERE STORE.identity_id = OLD.id; "
00062             "END; "
00063         );
00064 
00065    foreach (QString createTable, createTableQuery) {
00066         QSqlQuery query = exec(createTable);
00067         if (lastError().isValid()) {
00068             TRACE() << "Error occurred while creating the database.";
00069             return false;
00070         }
00071         query.clear();
00072         commit();
00073     }
00074     return true;
00075 }
00076 
00077 bool SecretsDB::clear()
00078 {
00079     TRACE();
00080 
00081     QStringList clearCommands = QStringList()
00082         << QLatin1String("DELETE FROM CREDENTIALS")
00083         << QLatin1String("DELETE FROM STORE");
00084 
00085     return transactionalExec(clearCommands);
00086 }
00087 
00088 bool SecretsDB::updateCredentials(const quint32 id,
00089                                   const QString &username,
00090                                   const QString &password)
00091 {
00092     if (!startTransaction()) {
00093         TRACE() << "Could not start transaction. Error inserting credentials.";
00094         return false;
00095     }
00096     QSqlQuery query = newQuery();
00097 
00098     TRACE() << "INSERT:" << id;
00099     query.prepare(S("INSERT OR REPLACE INTO CREDENTIALS "
00100                     "(id, username, password) "
00101                     "VALUES(:id, :username, :password)"));
00102 
00103     query.bindValue(S(":id"), id);
00104     query.bindValue(S(":username"), username);
00105     query.bindValue(S(":password"), password);
00106 
00107     exec(query);
00108 
00109     if (errorOccurred()) {
00110         rollback();
00111         TRACE() << "Error occurred while storing crendentials";
00112         return false;
00113     }
00114     return commit();
00115 }
00116 
00117 bool SecretsDB::removeCredentials(const quint32 id)
00118 {
00119     TRACE();
00120 
00121     QStringList queries = QStringList()
00122         << QString::fromLatin1(
00123             "DELETE FROM CREDENTIALS WHERE id = %1").arg(id)
00124         << QString::fromLatin1(
00125             "DELETE FROM STORE WHERE identity_id = %1").arg(id);
00126 
00127     return transactionalExec(queries);
00128 }
00129 
00130 bool SecretsDB::loadCredentials(const quint32 id,
00131                                 QString &username,
00132                                 QString &password)
00133 {
00134     TRACE();
00135 
00136     QString queryStr =
00137         QString::fromLatin1("SELECT username, password FROM credentials "
00138                             "WHERE id = %1").arg(id);
00139     QSqlQuery query = exec(queryStr);
00140     if (!query.first()) {
00141         TRACE() << "No result or invalid credentials query.";
00142         return false;
00143     }
00144 
00145     username = query.value(0).toString();
00146     password = query.value(1).toString();
00147     return true;
00148 }
00149 
00150 QVariantMap SecretsDB::loadData(quint32 id, quint32 method)
00151 {
00152     TRACE();
00153 
00154     QSqlQuery q = newQuery();
00155     q.prepare(S("SELECT key, value "
00156                 "FROM STORE WHERE identity_id = :id AND method_id = :method"));
00157     q.bindValue(S(":id"), id);
00158     q.bindValue(S(":method"), method);
00159     exec(q);
00160     if (errorOccurred())
00161         return QVariantMap();
00162 
00163     QVariantMap result;
00164     while (q.next()) {
00165         QByteArray array;
00166         array = q.value(1).toByteArray();
00167         QDataStream stream(array);
00168         QVariant data;
00169         stream >> data;
00170         result.insert(q.value(0).toString(), data);
00171     }
00172     return result;
00173 }
00174 
00175 bool SecretsDB::storeData(quint32 id, quint32 method, const QVariantMap &data)
00176 {
00177     TRACE();
00178 
00179     if (!startTransaction()) {
00180         TRACE() << "Could not start transaction. Error inserting data.";
00181         return false;
00182     }
00183 
00184     /* first, remove existing data */
00185     QSqlQuery q = newQuery();
00186     q.prepare(S("DELETE FROM STORE WHERE identity_id = :id "
00187                 "AND method_id = :method"));
00188     q.bindValue(S(":id"), id);
00189     q.bindValue(S(":method"), method);
00190     exec(q);
00191     if (errorOccurred()) {
00192         rollback();
00193         TRACE() << "Data removal failed.";
00194         return false;
00195     }
00196 
00197     bool allOk = true;
00198     qint32 dataCounter = 0;
00199     if (!(data.keys().empty())) {
00200         QMapIterator<QString, QVariant> it(data);
00201         while (it.hasNext()) {
00202             it.next();
00203 
00204             QByteArray array;
00205             QDataStream stream(&array, QIODevice::WriteOnly);
00206             stream << it.value();
00207 
00208             dataCounter += it.key().size() +array.size();
00209             if (dataCounter >= SSO_MAX_TOKEN_STORAGE) {
00210                 BLAME() << "storing data max size exceeded";
00211                 allOk = false;
00212                 break;
00213             }
00214             /* Key/value insert/replace/delete */
00215             QSqlQuery query = newQuery();
00216             if (!it.value().isValid() || it.value().isNull()) {
00217                 continue;
00218             }
00219             TRACE() << "insert";
00220             query.prepare(S(
00221                 "INSERT OR REPLACE INTO STORE "
00222                 "(identity_id, method_id, key, value) "
00223                 "VALUES(:id, :method, :key, :value)"));
00224             query.bindValue(S(":value"), array);
00225             query.bindValue(S(":id"), id);
00226             query.bindValue(S(":method"), method);
00227             query.bindValue(S(":key"), it.key());
00228             exec(query);
00229             if (errorOccurred()) {
00230                 allOk = false;
00231                 break;
00232             }
00233         }
00234     }
00235 
00236     if (allOk && commit()) {
00237         TRACE() << "Data insertion ok.";
00238         return true;
00239     }
00240     rollback();
00241     TRACE() << "Data insertion failed.";
00242     return false;
00243 }
00244 
00245 bool SecretsDB::removeData(quint32 id, quint32 method)
00246 {
00247     TRACE();
00248 
00249     if (!startTransaction()) {
00250         TRACE() << "Could not start transaction. Error removing data.";
00251         return false;
00252     }
00253 
00254     QSqlQuery q = newQuery();
00255     if (method == 0) {
00256         q.prepare(S("DELETE FROM STORE WHERE identity_id = :id"));
00257     } else {
00258         q.prepare(S("DELETE FROM STORE WHERE identity_id = :id "
00259                     "AND method_id = :method"));
00260         q.bindValue(S(":method"), method);
00261     }
00262     q.bindValue(S(":id"), id);
00263     exec(q);
00264     if (!errorOccurred() && commit()) {
00265         TRACE() << "Data removal ok.";
00266         return true;
00267     } else {
00268         rollback();
00269         TRACE() << "Data removal failed.";
00270         return false;
00271     }
00272 }
00273 
00274 DefaultSecretsStorage::DefaultSecretsStorage(QObject *parent):
00275     AbstractSecretsStorage(parent),
00276     m_secretsDB(0)
00277 {
00278 }
00279 
00280 DefaultSecretsStorage::~DefaultSecretsStorage()
00281 {
00282     close();
00283 }
00284 
00285 bool DefaultSecretsStorage::initialize(const QVariantMap &configuration)
00286 {
00287     if (isOpen()) {
00288         TRACE() << "Initializing open DB; closing first...";
00289         close();
00290     }
00291 
00292     QString name; // force deep copy / detach
00293     name.append(configuration.value(QLatin1String("name")).toString());
00294 
00295     m_secretsDB = new SecretsDB(name);
00296     if (!m_secretsDB->init()) {
00297         setLastError(m_secretsDB->lastError());
00298         delete m_secretsDB;
00299         m_secretsDB = 0;
00300         return false;
00301     }
00302 
00303     m_secretsDBConnectionName.clear();
00304     m_secretsDBConnectionName.append(m_secretsDB->connectionName());
00305     setIsOpen(true);
00306     return true;
00307 }
00308 
00309 bool DefaultSecretsStorage::close()
00310 {
00311     if (m_secretsDB != 0) {
00312         delete m_secretsDB;
00313         QSqlDatabase::removeDatabase(m_secretsDBConnectionName);
00314         m_secretsDB = 0;
00315     }
00316     return AbstractSecretsStorage::close();
00317 }
00318 
00319 bool DefaultSecretsStorage::clear()
00320 {
00321     RETURN_IF_NOT_OPEN(false);
00322 
00323     return m_secretsDB->clear();
00324 }
00325 
00326 bool DefaultSecretsStorage::updateCredentials(const quint32 id,
00327                                               const QString &username,
00328                                               const QString &password)
00329 {
00330     RETURN_IF_NOT_OPEN(false);
00331 
00332     return m_secretsDB->updateCredentials(id, username, password);
00333 }
00334 
00335 bool DefaultSecretsStorage::removeCredentials(const quint32 id)
00336 {
00337     RETURN_IF_NOT_OPEN(false);
00338 
00339     return m_secretsDB->removeCredentials(id);
00340 }
00341 
00342 bool DefaultSecretsStorage::loadCredentials(const quint32 id,
00343                                             QString &username,
00344                                             QString &password)
00345 {
00346     RETURN_IF_NOT_OPEN(false);
00347 
00348     return m_secretsDB->loadCredentials(id, username, password);
00349 }
00350 
00351 QVariantMap DefaultSecretsStorage::loadData(quint32 id, quint32 method)
00352 {
00353     RETURN_IF_NOT_OPEN(QVariantMap());
00354 
00355     return m_secretsDB->loadData(id, method);
00356 }
00357 
00358 bool DefaultSecretsStorage::storeData(quint32 id, quint32 method,
00359                                       const QVariantMap &data)
00360 {
00361     RETURN_IF_NOT_OPEN(false);
00362 
00363     return m_secretsDB->storeData(id, method, data);
00364 }
00365 
00366 bool DefaultSecretsStorage::removeData(quint32 id, quint32 method)
00367 {
00368     RETURN_IF_NOT_OPEN(false);
00369 
00370     return m_secretsDB->removeData(id, method);
00371 }