signon  8.41
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  bool allOk = true;
185  qint32 dataCounter = 0;
186  if (!(data.keys().empty())) {
187  QMapIterator<QString, QVariant> it(data);
188  while (it.hasNext()) {
189  it.next();
190 
191  QByteArray array;
192  QDataStream stream(&array, QIODevice::WriteOnly);
193  stream << it.value();
194 
195  dataCounter += it.key().size() +array.size();
196  if (dataCounter >= SSO_MAX_TOKEN_STORAGE) {
197  BLAME() << "storing data max size exceeded";
198  allOk = false;
199  break;
200  }
201  /* Key/value insert/replace/delete */
202  QSqlQuery query = newQuery();
203  if (it.value().isValid() && !it.value().isNull()) {
204  TRACE() << "insert";
205  query.prepare(S(
206  "INSERT OR REPLACE INTO STORE "
207  "(identity_id, method_id, key, value) "
208  "VALUES(:id, :method, :key, :value)"));
209  query.bindValue(S(":value"), array);
210  } else {
211  TRACE() << "remove";
212  query.prepare(S(
213  "DELETE FROM STORE WHERE identity_id = :id "
214  "AND method_id = :method "
215  "AND key = :key"));
216 
217  }
218  query.bindValue(S(":id"), id);
219  query.bindValue(S(":method"), method);
220  query.bindValue(S(":key"), it.key());
221  exec(query);
222  if (errorOccurred()) {
223  allOk = false;
224  break;
225  }
226  }
227  }
228 
229  if (allOk && commit()) {
230  TRACE() << "Data insertion ok.";
231  return true;
232  }
233  rollback();
234  TRACE() << "Data insertion failed.";
235  return false;
236 }
237 
238 bool SecretsDB::removeData(quint32 id, quint32 method)
239 {
240  TRACE();
241 
242  if (!startTransaction()) {
243  TRACE() << "Could not start transaction. Error removing data.";
244  return false;
245  }
246 
247  QSqlQuery q = newQuery();
248  if (method == 0) {
249  q.prepare(S("DELETE FROM STORE WHERE identity_id = :id"));
250  } else {
251  q.prepare(S("DELETE FROM STORE WHERE identity_id = :id "
252  "AND method_id = :method"));
253  q.bindValue(S(":method"), method);
254  }
255  q.bindValue(S(":id"), id);
256  exec(q);
257  if (!errorOccurred() && commit()) {
258  TRACE() << "Data removal ok.";
259  return true;
260  } else {
261  rollback();
262  TRACE() << "Data removal failed.";
263  return false;
264  }
265 }
266 
268  AbstractSecretsStorage(parent)
269 {
270 }
271 
273 {
274  close();
275 }
276 
277 bool DefaultSecretsStorage::initialize(const QVariantMap &configuration)
278 {
279  if (isOpen()) {
280  TRACE() << "Initializing open DB; closing first...";
281  close();
282  }
283 
284  QString name = configuration.value(QLatin1String("name")).toString();
285 
286  m_secretsDB = new SecretsDB(name);
287  if (!m_secretsDB->init()) {
288  setLastError(m_secretsDB->lastError());
289  delete m_secretsDB;
290  m_secretsDB = 0;
291  return false;
292  }
293 
294  setIsOpen(true);
295  return true;
296 }
297 
299 {
300  if (m_secretsDB != 0) {
301  QString connectionName = m_secretsDB->connectionName();
302  delete m_secretsDB;
303  QSqlDatabase::removeDatabase(connectionName);
304  m_secretsDB = 0;
305  }
307 }
308 
310 {
311  RETURN_IF_NOT_OPEN(false);
312 
313  return m_secretsDB->clear();
314 }
315 
317  const QString &username,
318  const QString &password)
319 {
320  RETURN_IF_NOT_OPEN(false);
321 
322  return m_secretsDB->updateCredentials(id, username, password);
323 }
324 
326 {
327  RETURN_IF_NOT_OPEN(false);
328 
329  return m_secretsDB->removeCredentials(id);
330 }
331 
333  QString &username,
334  QString &password)
335 {
336  RETURN_IF_NOT_OPEN(false);
337 
338  return m_secretsDB->loadCredentials(id, username, password);
339 }
340 
341 QVariantMap DefaultSecretsStorage::loadData(quint32 id, quint32 method)
342 {
343  RETURN_IF_NOT_OPEN(QVariantMap());
344 
345  return m_secretsDB->loadData(id, method);
346 }
347 
348 bool DefaultSecretsStorage::storeData(quint32 id, quint32 method,
349  const QVariantMap &data)
350 {
351  RETURN_IF_NOT_OPEN(false);
352 
353  return m_secretsDB->storeData(id, method, data);
354 }
355 
356 bool DefaultSecretsStorage::removeData(quint32 id, quint32 method)
357 {
358  RETURN_IF_NOT_OPEN(false);
359 
360  return m_secretsDB->removeData(id, method);
361 }