signon  8.58
default-secrets-storage.cpp
Go to the documentation of this file.
1 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of signon
4  *
5  * Copyright (C) 2011 Canonical Ltd.
6  *
7  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * version 2.1 as published by the Free Software Foundation.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  */
23 
25 #include "signond-common.h"
26 
27 #define RETURN_IF_NOT_OPEN(retval) \
28  if (!isOpen()) { \
29  TRACE() << "Secrets DB is not available"; \
30  SignOn::CredentialsDBError error(QLatin1String("Not open"), \
31  SignOn::CredentialsDBError::NotOpen); \
32  setLastError(error); return retval; \
33  }
34 
35 #define S(s) QLatin1String(s)
36 
37 using namespace SignonDaemonNS;
38 
40 {
41  QStringList createTableQuery = QStringList()
42  << QString::fromLatin1(
43  "CREATE TABLE CREDENTIALS"
44  "(id INTEGER NOT NULL UNIQUE,"
45  "username TEXT,"
46  "password TEXT,"
47  "PRIMARY KEY (id))")
48  << QString::fromLatin1(
49  "CREATE TABLE STORE"
50  "(identity_id INTEGER,"
51  "method_id INTEGER,"
52  "key TEXT,"
53  "value BLOB,"
54  "PRIMARY KEY (identity_id, method_id, key))")
55 
56  << QString::fromLatin1(
57  // Cascading Delete
58  "CREATE TRIGGER tg_delete_credentials "
59  "BEFORE DELETE ON CREDENTIALS "
60  "FOR EACH ROW BEGIN "
61  " DELETE FROM STORE WHERE STORE.identity_id = OLD.id; "
62  "END; "
63  );
64 
65  foreach (QString createTable, createTableQuery) {
66  QSqlQuery query = exec(createTable);
67  if (lastError().isValid()) {
68  TRACE() << "Error occurred while creating the database.";
69  return false;
70  }
71  query.clear();
72  commit();
73  }
74  return true;
75 }
76 
78 {
79  TRACE();
80 
81  QStringList clearCommands = QStringList()
82  << QLatin1String("DELETE FROM CREDENTIALS")
83  << QLatin1String("DELETE FROM STORE");
84 
85  return transactionalExec(clearCommands);
86 }
87 
88 bool SecretsDB::updateCredentials(const quint32 id,
89  const QString &username,
90  const QString &password)
91 {
92  if (!startTransaction()) {
93  TRACE() << "Could not start transaction. Error inserting credentials.";
94  return false;
95  }
96  QSqlQuery query = newQuery();
97 
98  TRACE() << "INSERT:" << id;
99  query.prepare(S("INSERT OR REPLACE INTO CREDENTIALS "
100  "(id, username, password) "
101  "VALUES(:id, :username, :password)"));
102 
103  query.bindValue(S(":id"), id);
104  query.bindValue(S(":username"), username);
105  query.bindValue(S(":password"), password);
106 
107  exec(query);
108 
109  if (errorOccurred()) {
110  rollback();
111  TRACE() << "Error occurred while storing crendentials";
112  return false;
113  }
114  return commit();
115 }
116 
117 bool SecretsDB::removeCredentials(const quint32 id)
118 {
119  TRACE();
120 
121  QStringList queries = QStringList()
122  << QString::fromLatin1(
123  "DELETE FROM CREDENTIALS WHERE id = %1").arg(id)
124  << QString::fromLatin1(
125  "DELETE FROM STORE WHERE identity_id = %1").arg(id);
126 
127  return transactionalExec(queries);
128 }
129 
130 bool SecretsDB::loadCredentials(const quint32 id,
131  QString &username,
132  QString &password)
133 {
134  TRACE();
135 
136  QString queryStr =
137  QString::fromLatin1("SELECT username, password FROM credentials "
138  "WHERE id = %1").arg(id);
139  QSqlQuery query = exec(queryStr);
140  if (!query.first()) {
141  TRACE() << "No result or invalid credentials query.";
142  return false;
143  }
144 
145  username = query.value(0).toString();
146  password = query.value(1).toString();
147  return true;
148 }
149 
150 QVariantMap SecretsDB::loadData(quint32 id, quint32 method)
151 {
152  TRACE();
153 
154  QSqlQuery q = newQuery();
155  q.prepare(S("SELECT key, value "
156  "FROM STORE WHERE identity_id = :id AND method_id = :method"));
157  q.bindValue(S(":id"), id);
158  q.bindValue(S(":method"), method);
159  exec(q);
160  if (errorOccurred())
161  return QVariantMap();
162 
163  QVariantMap result;
164  while (q.next()) {
165  QByteArray array;
166  array = q.value(1).toByteArray();
167  QDataStream stream(array);
168  QVariant data;
169  stream >> data;
170  result.insert(q.value(0).toString(), data);
171  }
172  return result;
173 }
174 
175 bool SecretsDB::storeData(quint32 id, quint32 method, const QVariantMap &data)
176 {
177  TRACE();
178 
179  if (!startTransaction()) {
180  TRACE() << "Could not start transaction. Error inserting data.";
181  return false;
182  }
183 
184  /* first, remove existing data */
185  QSqlQuery q = newQuery();
186  q.prepare(S("DELETE FROM STORE WHERE identity_id = :id "
187  "AND method_id = :method"));
188  q.bindValue(S(":id"), id);
189  q.bindValue(S(":method"), method);
190  exec(q);
191  if (errorOccurred()) {
192  rollback();
193  TRACE() << "Data removal failed.";
194  return false;
195  }
196 
197  bool allOk = true;
198  qint32 dataCounter = 0;
199  if (!(data.keys().empty())) {
200  QMapIterator<QString, QVariant> it(data);
201  while (it.hasNext()) {
202  it.next();
203 
204  QByteArray array;
205  QDataStream stream(&array, QIODevice::WriteOnly);
206  stream << it.value();
207 
208  dataCounter += it.key().size() +array.size();
209  if (dataCounter >= SSO_MAX_TOKEN_STORAGE) {
210  BLAME() << "storing data max size exceeded";
211  allOk = false;
212  break;
213  }
214  /* Key/value insert/replace/delete */
215  QSqlQuery query = newQuery();
216  if (!it.value().isValid() || it.value().isNull()) {
217  continue;
218  }
219  TRACE() << "insert";
220  query.prepare(S(
221  "INSERT OR REPLACE INTO STORE "
222  "(identity_id, method_id, key, value) "
223  "VALUES(:id, :method, :key, :value)"));
224  query.bindValue(S(":value"), array);
225  query.bindValue(S(":id"), id);
226  query.bindValue(S(":method"), method);
227  query.bindValue(S(":key"), it.key());
228  exec(query);
229  if (errorOccurred()) {
230  allOk = false;
231  break;
232  }
233  }
234  }
235 
236  if (allOk && commit()) {
237  TRACE() << "Data insertion ok.";
238  return true;
239  }
240  rollback();
241  TRACE() << "Data insertion failed.";
242  return false;
243 }
244 
245 bool SecretsDB::removeData(quint32 id, quint32 method)
246 {
247  TRACE();
248 
249  if (!startTransaction()) {
250  TRACE() << "Could not start transaction. Error removing data.";
251  return false;
252  }
253 
254  QSqlQuery q = newQuery();
255  if (method == 0) {
256  q.prepare(S("DELETE FROM STORE WHERE identity_id = :id"));
257  } else {
258  q.prepare(S("DELETE FROM STORE WHERE identity_id = :id "
259  "AND method_id = :method"));
260  q.bindValue(S(":method"), method);
261  }
262  q.bindValue(S(":id"), id);
263  exec(q);
264  if (!errorOccurred() && commit()) {
265  TRACE() << "Data removal ok.";
266  return true;
267  } else {
268  rollback();
269  TRACE() << "Data removal failed.";
270  return false;
271  }
272 }
273 
275  AbstractSecretsStorage(parent),
276  m_secretsDB(0)
277 {
278 }
279 
281 {
282  close();
283 }
284 
285 bool DefaultSecretsStorage::initialize(const QVariantMap &configuration)
286 {
287  if (isOpen()) {
288  TRACE() << "Initializing open DB; closing first...";
289  close();
290  }
291 
292  QString name; // force deep copy / detach
293  name.append(configuration.value(QLatin1String("name")).toString());
294 
295  m_secretsDB = new SecretsDB(name);
296  if (!m_secretsDB->init()) {
297  setLastError(m_secretsDB->lastError());
298  delete m_secretsDB;
299  m_secretsDB = 0;
300  return false;
301  }
302 
303  m_secretsDBConnectionName.clear();
304  m_secretsDBConnectionName.append(m_secretsDB->connectionName());
305  setIsOpen(true);
306  return true;
307 }
308 
310 {
311  if (m_secretsDB != 0) {
312  delete m_secretsDB;
313  QSqlDatabase::removeDatabase(m_secretsDBConnectionName);
314  m_secretsDB = 0;
315  }
316  return AbstractSecretsStorage::close();
317 }
318 
320 {
321  RETURN_IF_NOT_OPEN(false);
322 
323  return m_secretsDB->clear();
324 }
325 
327  const QString &username,
328  const QString &password)
329 {
330  RETURN_IF_NOT_OPEN(false);
331 
332  return m_secretsDB->updateCredentials(id, username, password);
333 }
334 
336 {
337  RETURN_IF_NOT_OPEN(false);
338 
339  return m_secretsDB->removeCredentials(id);
340 }
341 
343  QString &username,
344  QString &password)
345 {
346  RETURN_IF_NOT_OPEN(false);
347 
348  return m_secretsDB->loadCredentials(id, username, password);
349 }
350 
351 QVariantMap DefaultSecretsStorage::loadData(quint32 id, quint32 method)
352 {
353  RETURN_IF_NOT_OPEN(QVariantMap());
354 
355  return m_secretsDB->loadData(id, method);
356 }
357 
358 bool DefaultSecretsStorage::storeData(quint32 id, quint32 method,
359  const QVariantMap &data)
360 {
361  RETURN_IF_NOT_OPEN(false);
362 
363  return m_secretsDB->storeData(id, method, data);
364 }
365 
366 bool DefaultSecretsStorage::removeData(quint32 id, quint32 method)
367 {
368  RETURN_IF_NOT_OPEN(false);
369 
370  return m_secretsDB->removeData(id, method);
371 }
QSqlQuery exec(const QString &query)
Executes a specific database query.
bool updateCredentials(const quint32 id, const QString &username, const QString &password)
bool removeData(quint32 id, quint32 method)
bool updateCredentials(const quint32 id, const QString &username, const QString &password)
bool removeData(quint32 id, quint32 method)
QVariantMap loadData(quint32 id, quint32 method)
bool storeData(quint32 id, quint32 method, const QVariantMap &data)
#define BLAME()
Definition: debug.h:32
bool storeData(quint32 id, quint32 method, const QVariantMap &data)
#define SSO_MAX_TOKEN_STORAGE
Definition: credentialsdb.h:40
SignOn::CredentialsDBError lastError() const
bool initialize(const QVariantMap &configuration)
bool removeCredentials(const quint32 id)
bool init()
Connects to the DB and if necessary creates the tables.
QVariantMap loadData(quint32 id, quint32 method)
bool loadCredentials(const quint32 id, QString &username, QString &password)
#define TRACE()
Definition: debug.h:28
bool transactionalExec(const QStringList &queryList)
Executes a specific database set of queryes (INSERTs, UPDATEs, DELETEs) in a transaction context (No ...
QSqlQuery newQuery() const
bool loadCredentials(const quint32 id, QString &username, QString &password)
QString connectionName() const
#define S(s)
#define RETURN_IF_NOT_OPEN(retval)