signon  8.42
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 {
277 }
278 
280 {
281  close();
282 }
283 
284 bool DefaultSecretsStorage::initialize(const QVariantMap &configuration)
285 {
286  if (isOpen()) {
287  TRACE() << "Initializing open DB; closing first...";
288  close();
289  }
290 
291  QString name = configuration.value(QLatin1String("name")).toString();
292 
293  m_secretsDB = new SecretsDB(name);
294  if (!m_secretsDB->init()) {
295  setLastError(m_secretsDB->lastError());
296  delete m_secretsDB;
297  m_secretsDB = 0;
298  return false;
299  }
300 
301  setIsOpen(true);
302  return true;
303 }
304 
306 {
307  if (m_secretsDB != 0) {
308  QString connectionName = m_secretsDB->connectionName();
309  delete m_secretsDB;
310  QSqlDatabase::removeDatabase(connectionName);
311  m_secretsDB = 0;
312  }
314 }
315 
317 {
318  RETURN_IF_NOT_OPEN(false);
319 
320  return m_secretsDB->clear();
321 }
322 
324  const QString &username,
325  const QString &password)
326 {
327  RETURN_IF_NOT_OPEN(false);
328 
329  return m_secretsDB->updateCredentials(id, username, password);
330 }
331 
333 {
334  RETURN_IF_NOT_OPEN(false);
335 
336  return m_secretsDB->removeCredentials(id);
337 }
338 
340  QString &username,
341  QString &password)
342 {
343  RETURN_IF_NOT_OPEN(false);
344 
345  return m_secretsDB->loadCredentials(id, username, password);
346 }
347 
348 QVariantMap DefaultSecretsStorage::loadData(quint32 id, quint32 method)
349 {
350  RETURN_IF_NOT_OPEN(QVariantMap());
351 
352  return m_secretsDB->loadData(id, method);
353 }
354 
355 bool DefaultSecretsStorage::storeData(quint32 id, quint32 method,
356  const QVariantMap &data)
357 {
358  RETURN_IF_NOT_OPEN(false);
359 
360  return m_secretsDB->storeData(id, method, data);
361 }
362 
363 bool DefaultSecretsStorage::removeData(quint32 id, quint32 method)
364 {
365  RETURN_IF_NOT_OPEN(false);
366 
367  return m_secretsDB->removeData(id, method);
368 }