signon  8.42
credentialsdb.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) 2009-2010 Nokia Corporation.
6  * Copyright (C) 2012 Canonical Ltd.
7  *
8  * Contact: Aurel Popirtac <ext-aurel.popirtac@nokia.com>
9  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * version 2.1 as published by the Free Software Foundation.
14  *
15  * This library is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25 
26 #include "credentialsdb.h"
27 #include "credentialsdb_p.h"
28 #include "signond-common.h"
29 #include "signonsessioncoretools.h"
30 
31 #define INIT_ERROR() ErrorMonitor errorMonitor(this)
32 #define RETURN_IF_NO_SECRETS_DB(retval) \
33  if (!isSecretsDBOpen()) { \
34  TRACE() << "Secrets DB is not available"; \
35  _lastError = noSecretsDB; return retval; \
36  }
37 
38 #define S(s) QLatin1String(s)
39 
40 namespace SignonDaemonNS {
41 
42 static const QString driver = QLatin1String("QSQLITE");
43 
45  QString &username,
46  QString &password) const
47 {
48  QHash<quint32, AuthCache>::const_iterator i;
49 
50  i = m_cache.find(id);
51  if (i == m_cache.end()) return false;
52 
53  username = i->m_username;
54  password = i->m_password;
55  return true;
56 }
57 
58 QVariantMap SecretsCache::lookupData(quint32 id, quint32 method) const
59 {
60  return m_cache.value(id).m_blobData.value(method);
61 }
62 
64  const QString &username,
65  const QString &password,
66  bool storePassword)
67 {
68  if (id == 0) return;
69 
70  AuthCache &credentials = m_cache[id];
71  credentials.m_username = username;
72  credentials.m_password = password;
73  credentials.m_storePassword = storePassword;
74 }
75 
76 void SecretsCache::updateData(quint32 id, quint32 method,
77  const QVariantMap &data)
78 {
79  if (id == 0) return;
80 
81  AuthCache &credentials = m_cache[id];
82  credentials.m_blobData[method] = data;
83 }
84 
85 void
86 SecretsCache::storeToDB(SignOn::AbstractSecretsStorage *secretsStorage) const
87 {
88  if (m_cache.isEmpty()) return;
89 
90  TRACE() << "Storing cached credentials into permanent storage";
91 
92  QHash<quint32, AuthCache>::const_iterator i;
93  for (i = m_cache.constBegin();
94  i != m_cache.constEnd();
95  i++) {
96  quint32 id = i.key();
97  const AuthCache &cache = i.value();
98 
99  /* Store the credentials */
100  QString password = cache.m_storePassword ?
101  cache.m_password : QString();
102  if (!cache.m_username.isEmpty() || !password.isEmpty()) {
103  secretsStorage->updateCredentials(id,
104  cache.m_username,
105  password);
106  }
107 
108  /* Store any binary blobs */
109  QHash<quint32, QVariantMap>::const_iterator j;
110  for (j = cache.m_blobData.constBegin();
111  j != cache.m_blobData.constEnd();
112  j++) {
113  quint32 method = j.key();
114  secretsStorage->storeData(id, method, j.value());
115  }
116  }
117 }
118 
120 {
121  m_cache.clear();
122 }
123 
124 SqlDatabase::SqlDatabase(const QString &databaseName,
125  const QString &connectionName,
126  int version):
127  m_lastError(SignOn::CredentialsDBError()),
128  m_version(version),
129  m_database(QSqlDatabase::addDatabase(driver, connectionName))
130 
131 {
132  TRACE() << "Supported Drivers:" << this->supportedDrivers();
133  TRACE() << "DATABASE NAME [" << databaseName << "]";
134 
135  m_database.setDatabaseName(databaseName);
136 }
137 
139 {
140  m_database.commit();
141  m_database.close();
142 }
143 
145 {
146  if (!connect())
147  return false;
148 
149  TRACE() << "Database connection succeeded.";
150 
151  if (!hasTables()) {
152  TRACE() << "Creating SQL table structure...";
153  if (!createTables())
154  return false;
155 
157  BLAME() << "Failed to set database version to: " << m_version
158  << ".This could lead to data loss.";
159  } else {
160  TRACE() << "SQL table structure already created...";
161  // check the DB version
162  QSqlQuery q = exec(S("PRAGMA user_version"));
163  int oldVersion = q.first() ? q.value(0).toInt() : 0;
164  if (oldVersion < m_version)
165  updateDB(oldVersion);
166  }
167 
168  return true;
169 }
170 
171 bool SqlDatabase::updateDB(int version)
172 {
173  TRACE() << "Update DB from version " << version << " to " << m_version;
174  exec(QString::fromLatin1("PRAGMA user_version = %1").arg(m_version));
175  return true;
176 }
177 
179 {
180  if (!m_database.open()) {
181  TRACE() << "Could not open database connection.\n";
182  setLastError(m_database.lastError());
183  return false;
184  }
185  return true;
186 }
187 
189 {
190  m_database.close();
191 }
192 
194 {
195  return m_database.transaction();
196 }
197 
199 {
200  return m_database.commit();
201 }
202 
204 {
205  if (!m_database.rollback())
206  TRACE() << "Rollback failed, db data integrity could be compromised.";
207 }
208 
209 QSqlQuery SqlDatabase::exec(const QString &queryStr)
210 {
211  QSqlQuery query(QString(), m_database);
212 
213  if (!query.prepare(queryStr))
214  TRACE() << "Query prepare warning: " << query.lastQuery();
215 
216  if (!query.exec()) {
217  TRACE() << "Query exec error: " << query.lastQuery();
218  setLastError(query.lastError());
219  TRACE() << errorInfo(query.lastError());
220  } else
221  m_lastError.clear();
222 
223  return query;
224 }
225 
226 QSqlQuery SqlDatabase::exec(QSqlQuery &query)
227 {
228 
229  if (!query.exec()) {
230  TRACE() << "Query exec error: " << query.lastQuery();
231  setLastError(query.lastError());
232  TRACE() << errorInfo(query.lastError());
233  } else
234  m_lastError.clear();
235 
236  return query;
237 }
238 
239 
240 bool SqlDatabase::transactionalExec(const QStringList &queryList)
241 {
242  if (!startTransaction()) {
243  setLastError(m_database.lastError());
244  TRACE() << "Could not start transaction";
245  return false;
246  }
247 
248  bool allOk = true;
249  foreach (QString queryStr, queryList) {
250  TRACE() << QString::fromLatin1("TRANSACT Query [%1]").arg(queryStr);
251  QSqlQuery query = exec(queryStr);
252 
253  if (errorOccurred()) {
254  allOk = false;
255  break;
256  }
257  }
258 
259  if (allOk && commit()) {
260  TRACE() << "Commit SUCCEEDED.";
261  return true;
262  } else {
263  rollback();
264  }
265 
266  TRACE() << "Transactional exec FAILED!";
267  return false;
268 }
269 
270 SignOn::CredentialsDBError SqlDatabase::lastError() const
271 {
272  return m_lastError;
273 }
274 
275 void SqlDatabase::setLastError(const QSqlError &sqlError)
276 {
277  if (sqlError.isValid()) {
278  if (sqlError.type() == QSqlError::ConnectionError) {
279  m_lastError.setType(SignOn::CredentialsDBError::ConnectionError);
280  } else {
281  m_lastError.setType(SignOn::CredentialsDBError::StatementError);
282  }
283  m_lastError.setText(sqlError.text());
284  } else {
285  m_lastError.clear();
286  }
287 }
288 
289 QString SqlDatabase::errorInfo(const QSqlError &error)
290 {
291  if (!error.isValid())
292  return QLatin1String("SQL Error invalid.");
293 
294  QString text;
295  QTextStream stream(&text);
296  stream << "SQL error description:";
297  stream << "\n\tType: ";
298 
299  const char *errType;
300  switch (error.type()) {
301  case QSqlError::NoError: errType = "NoError"; break;
302  case QSqlError::ConnectionError: errType = "ConnectionError"; break;
303  case QSqlError::StatementError: errType = "StatementError"; break;
304  case QSqlError::TransactionError: errType = "TransactionError"; break;
306  /* fall trough */
307  default: errType = "UnknownError";
308  }
309  stream << errType;
310  stream << "\n\tDatabase text: " << error.databaseText();
311  stream << "\n\tDriver text: " << error.driverText();
312  stream << "\n\tNumber: " << error.number();
313 
314  return text;
315 }
316 
317 QStringList SqlDatabase::queryList(const QString &query_str)
318 {
319  QSqlQuery query(QString(), m_database);
320  if (!query.prepare(query_str))
321  TRACE() << "Query prepare warning: " << query.lastQuery();
322  return queryList(query);
323 }
324 
325 QStringList SqlDatabase::queryList(QSqlQuery &q)
326 {
327  QStringList list;
328  QSqlQuery query = exec(q);
329  if (errorOccurred()) return list;
330  while (query.next()) {
331  list.append(query.value(0).toString());
332  }
333  query.clear();
334  return list;
335 }
336 
337 QStringList MetaDataDB::tableUpdates2()
338 {
339  QStringList tableUpdates = QStringList()
340  << QString::fromLatin1(
341  "CREATE TABLE OWNER"
342  "(rowid INTEGER PRIMARY KEY AUTOINCREMENT,"
343  "identity_id INTEGER CONSTRAINT fk_identity_id REFERENCES CREDENTIALS(id) ON DELETE CASCADE,"
344  "token_id INTEGER CONSTRAINT fk_token_id REFERENCES TOKENS(id) ON DELETE CASCADE)")
345  //added triggers for OWNER
346  << QString::fromLatin1(
347  // Foreign Key Preventing insert
348  "CREATE TRIGGER fki_OWNER_token_id_TOKENS_id "
349  "BEFORE INSERT ON [OWNER] "
350  "FOR EACH ROW BEGIN "
351  " SELECT RAISE(ROLLBACK, 'insert on table OWNER violates foreign key constraint fki_OWNER_token_id_TOKENS_id') "
352  " WHERE NEW.token_id IS NOT NULL AND (SELECT id FROM TOKENS WHERE id = NEW.token_id) IS NULL; "
353  "END; "
354  )
355  << QString::fromLatin1(
356  // Foreign key preventing update
357  "CREATE TRIGGER fku_OWNER_token_id_TOKENS_id "
358  "BEFORE UPDATE ON [OWNER] "
359  "FOR EACH ROW BEGIN "
360  " SELECT RAISE(ROLLBACK, 'update on table OWNER violates foreign key constraint fku_OWNER_token_id_TOKENS_id') "
361  " WHERE NEW.token_id IS NOT NULL AND (SELECT id FROM TOKENS WHERE id = NEW.token_id) IS NULL; "
362  "END; "
363  )
364  << QString::fromLatin1(
365  // Cascading Delete
366  "CREATE TRIGGER fkdc_OWNER_token_id_TOKENS_id "
367  "BEFORE DELETE ON TOKENS "
368  "FOR EACH ROW BEGIN "
369  " DELETE FROM OWNER WHERE OWNER.token_id = OLD.id; "
370  "END; "
371  );
372 
373  return tableUpdates;
374 }
375 
377 {
378  /* !!! Foreign keys support seems to be disabled, for the moment... */
379  QStringList createTableQuery = QStringList()
380  << QString::fromLatin1(
381  "CREATE TABLE CREDENTIALS"
382  "(id INTEGER PRIMARY KEY AUTOINCREMENT,"
383  "caption TEXT,"
384  "username TEXT,"
385  "flags INTEGER,"
386  "type INTEGER)")
387  << QString::fromLatin1(
388  "CREATE TABLE METHODS"
389  "(id INTEGER PRIMARY KEY AUTOINCREMENT,"
390  "method TEXT UNIQUE)")
391  << QString::fromLatin1(
392  "CREATE TABLE MECHANISMS"
393  "(id INTEGER PRIMARY KEY AUTOINCREMENT,"
394  "mechanism TEXT UNIQUE)")
395  << QString::fromLatin1(
396  "CREATE TABLE TOKENS"
397  "(id INTEGER PRIMARY KEY AUTOINCREMENT,"
398  "token TEXT UNIQUE)")
399  << QString::fromLatin1(
400  "CREATE TABLE REALMS"
401  "(identity_id INTEGER CONSTRAINT fk_identity_id REFERENCES CREDENTIALS(id) ON DELETE CASCADE,"
402  "realm TEXT,"
403  "hostname TEXT,"
404  "PRIMARY KEY (identity_id, realm, hostname))")
405  << QString::fromLatin1(
406  "CREATE TABLE ACL"
407  "(rowid INTEGER PRIMARY KEY AUTOINCREMENT,"
408  "identity_id INTEGER CONSTRAINT fk_identity_id REFERENCES CREDENTIALS(id) ON DELETE CASCADE,"
409  "method_id INTEGER CONSTRAINT fk_method_id REFERENCES METHODS(id) ON DELETE CASCADE,"
410  "mechanism_id INTEGER CONSTRAINT fk_mechanism_id REFERENCES MECHANISMS(id) ON DELETE CASCADE,"
411  "token_id INTEGER CONSTRAINT fk_token_id REFERENCES TOKENS(id) ON DELETE CASCADE)")
412  << QString::fromLatin1(
413  "CREATE TABLE REFS"
414  "(identity_id INTEGER CONSTRAINT fk_identity_id REFERENCES CREDENTIALS(id) ON DELETE CASCADE,"
415  "token_id INTEGER CONSTRAINT fk_token_id REFERENCES TOKENS(id) ON DELETE CASCADE,"
416  "ref TEXT,"
417  "PRIMARY KEY (identity_id, token_id, ref))")
418 
419 /*
420 * triggers generated with
421 * http://www.rcs-comp.com/site/index.php/view/Utilities-SQLite_foreign_key_trigger_generator
422 */
423  //insert triggers to force foreign keys support
424  << QString::fromLatin1(
425  // Foreign Key Preventing insert
426  "CREATE TRIGGER fki_REALMS_identity_id_CREDENTIALS_id "
427  "BEFORE INSERT ON [REALMS] "
428  "FOR EACH ROW BEGIN "
429  " SELECT RAISE(ROLLBACK, 'insert on table REALMS violates foreign key constraint fki_REALMS_identity_id_CREDENTIALS_id') "
430  " WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM CREDENTIALS WHERE id = NEW.identity_id) IS NULL; "
431  "END; "
432  )
433  << QString::fromLatin1(
434  // Foreign key preventing update
435  "CREATE TRIGGER fku_REALMS_identity_id_CREDENTIALS_id "
436  "BEFORE UPDATE ON [REALMS] "
437  "FOR EACH ROW BEGIN "
438  " SELECT RAISE(ROLLBACK, 'update on table REALMS violates foreign key constraint fku_REALMS_identity_id_CREDENTIALS_id') "
439  " WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM CREDENTIALS WHERE id = NEW.identity_id) IS NULL; "
440  "END; "
441  )
442  << QString::fromLatin1(
443  // Cascading Delete
444  "CREATE TRIGGER fkdc_REALMS_identity_id_CREDENTIALS_id "
445  "BEFORE DELETE ON CREDENTIALS "
446  "FOR EACH ROW BEGIN "
447  " DELETE FROM REALMS WHERE REALMS.identity_id = OLD.id; "
448  "END; "
449  )
450  << QString::fromLatin1(
451  // Foreign Key Preventing insert
452  "CREATE TRIGGER fki_ACL_identity_id_CREDENTIALS_id "
453  "BEFORE INSERT ON [ACL] "
454  "FOR EACH ROW BEGIN "
455  " SELECT RAISE(ROLLBACK, 'insert on table ACL violates foreign key constraint fki_ACL_identity_id_CREDENTIALS_id') "
456  " WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM CREDENTIALS WHERE id = NEW.identity_id) IS NULL; "
457  "END;"
458  )
459  << QString::fromLatin1(
460  // Foreign key preventing update
461  "CREATE TRIGGER fku_ACL_identity_id_CREDENTIALS_id "
462  "BEFORE UPDATE ON [ACL] "
463  "FOR EACH ROW BEGIN "
464  " SELECT RAISE(ROLLBACK, 'update on table ACL violates foreign key constraint fku_ACL_identity_id_CREDENTIALS_id') "
465  " WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM CREDENTIALS WHERE id = NEW.identity_id) IS NULL; "
466  "END; "
467  )
468  << QString::fromLatin1(
469  // Cascading Delete
470  "CREATE TRIGGER fkdc_ACL_identity_id_CREDENTIALS_id "
471  "BEFORE DELETE ON CREDENTIALS "
472  "FOR EACH ROW BEGIN "
473  " DELETE FROM ACL WHERE ACL.identity_id = OLD.id; "
474  "END; "
475  )
476  << QString::fromLatin1(
477  // Foreign Key Preventing insert
478  "CREATE TRIGGER fki_ACL_method_id_METHODS_id "
479  "BEFORE INSERT ON [ACL] "
480  "FOR EACH ROW BEGIN "
481  " SELECT RAISE(ROLLBACK, 'insert on table ACL violates foreign key constraint fki_ACL_method_id_METHODS_id') "
482  " WHERE NEW.method_id IS NOT NULL AND (SELECT id FROM METHODS WHERE id = NEW.method_id) IS NULL; "
483  "END; "
484  )
485  << QString::fromLatin1(
486  // Foreign key preventing update
487  "CREATE TRIGGER fku_ACL_method_id_METHODS_id "
488  "BEFORE UPDATE ON [ACL] "
489  "FOR EACH ROW BEGIN "
490  " SELECT RAISE(ROLLBACK, 'update on table ACL violates foreign key constraint fku_ACL_method_id_METHODS_id') "
491  " WHERE NEW.method_id IS NOT NULL AND (SELECT id FROM METHODS WHERE id = NEW.method_id) IS NULL; "
492  "END; "
493  )
494  << QString::fromLatin1(
495  // Cascading Delete
496  "CREATE TRIGGER fkdc_ACL_method_id_METHODS_id "
497  "BEFORE DELETE ON METHODS "
498  "FOR EACH ROW BEGIN "
499  " DELETE FROM ACL WHERE ACL.method_id = OLD.id; "
500  "END; "
501  )
502  << QString::fromLatin1(
503  // Foreign Key Preventing insert
504  "CREATE TRIGGER fki_ACL_mechanism_id_MECHANISMS_id "
505  "BEFORE INSERT ON [ACL] "
506  "FOR EACH ROW BEGIN "
507  " SELECT RAISE(ROLLBACK, 'insert on table ACL violates foreign key constraint fki_ACL_mechanism_id_MECHANISMS_id') "
508  " WHERE NEW.mechanism_id IS NOT NULL AND (SELECT id FROM MECHANISMS WHERE id = NEW.mechanism_id) IS NULL; "
509  "END; "
510  )
511  << QString::fromLatin1(
512  // Foreign key preventing update
513  "CREATE TRIGGER fku_ACL_mechanism_id_MECHANISMS_id "
514  "BEFORE UPDATE ON [ACL] "
515  "FOR EACH ROW BEGIN "
516  " SELECT RAISE(ROLLBACK, 'update on table ACL violates foreign key constraint fku_ACL_mechanism_id_MECHANISMS_id') "
517  " WHERE NEW.mechanism_id IS NOT NULL AND (SELECT id FROM MECHANISMS WHERE id = NEW.mechanism_id) IS NULL; "
518  "END; "
519  )
520  << QString::fromLatin1(
521  // Cascading Delete
522  "CREATE TRIGGER fkdc_ACL_mechanism_id_MECHANISMS_id "
523  "BEFORE DELETE ON MECHANISMS "
524  "FOR EACH ROW BEGIN "
525  " DELETE FROM ACL WHERE ACL.mechanism_id = OLD.id; "
526  "END; "
527  )
528  << QString::fromLatin1(
529  // Foreign Key Preventing insert
530  "CREATE TRIGGER fki_ACL_token_id_TOKENS_id "
531  "BEFORE INSERT ON [ACL] "
532  "FOR EACH ROW BEGIN "
533  " SELECT RAISE(ROLLBACK, 'insert on table ACL violates foreign key constraint fki_ACL_token_id_TOKENS_id') "
534  " WHERE NEW.token_id IS NOT NULL AND (SELECT id FROM TOKENS WHERE id = NEW.token_id) IS NULL; "
535  "END; "
536  )
537  << QString::fromLatin1(
538  // Foreign key preventing update
539  "CREATE TRIGGER fku_ACL_token_id_TOKENS_id "
540  "BEFORE UPDATE ON [ACL] "
541  "FOR EACH ROW BEGIN "
542  " SELECT RAISE(ROLLBACK, 'update on table ACL violates foreign key constraint fku_ACL_token_id_TOKENS_id') "
543  " WHERE NEW.token_id IS NOT NULL AND (SELECT id FROM TOKENS WHERE id = NEW.token_id) IS NULL; "
544  "END; "
545  )
546  << QString::fromLatin1(
547  // Cascading Delete
548  "CREATE TRIGGER fkdc_ACL_token_id_TOKENS_id "
549  "BEFORE DELETE ON TOKENS "
550  "FOR EACH ROW BEGIN "
551  " DELETE FROM ACL WHERE ACL.token_id = OLD.id; "
552  "END; "
553  )
554  << QString::fromLatin1(
555  // Foreign Key Preventing insert
556  "CREATE TRIGGER fki_REFS_identity_id_CREDENTIALS_id "
557  "BEFORE INSERT ON [REFS] "
558  "FOR EACH ROW BEGIN "
559  " SELECT RAISE(ROLLBACK, 'insert on table REFS violates foreign key constraint fki_REFS_identity_id_CREDENTIALS_id') "
560  " WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM CREDENTIALS WHERE id = NEW.identity_id) IS NULL; "
561  "END; "
562  )
563  << QString::fromLatin1(
564  // Foreign key preventing update
565  "CREATE TRIGGER fku_REFS_identity_id_CREDENTIALS_id "
566  "BEFORE UPDATE ON [REFS] "
567  "FOR EACH ROW BEGIN "
568  " SELECT RAISE(ROLLBACK, 'update on table REFS violates foreign key constraint fku_REFS_identity_id_CREDENTIALS_id') "
569  " WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM CREDENTIALS WHERE id = NEW.identity_id) IS NULL; "
570  "END; "
571  )
572  << QString::fromLatin1(
573  // Cascading Delete
574  "CREATE TRIGGER fkdc_REFS_identity_id_CREDENTIALS_id "
575  "BEFORE DELETE ON CREDENTIALS "
576  "FOR EACH ROW BEGIN "
577  " DELETE FROM REFS WHERE REFS.identity_id = OLD.id; "
578  "END; "
579  )
580  << QString::fromLatin1(
581  // Foreign Key Preventing insert
582  "CREATE TRIGGER fki_REFS_token_id_TOKENS_id "
583  "BEFORE INSERT ON [REFS] "
584  "FOR EACH ROW BEGIN "
585  " SELECT RAISE(ROLLBACK, 'insert on table REFS violates foreign key constraint fki_REFS_token_id_TOKENS_id') "
586  " WHERE NEW.token_id IS NOT NULL AND (SELECT id FROM TOKENS WHERE id = NEW.token_id) IS NULL; "
587  "END; "
588  )
589  << QString::fromLatin1(
590  // Foreign key preventing update
591  "CREATE TRIGGER fku_REFS_token_id_TOKENS_id "
592  "BEFORE UPDATE ON [REFS] "
593  "FOR EACH ROW BEGIN "
594  " SELECT RAISE(ROLLBACK, 'update on table REFS violates foreign key constraint fku_REFS_token_id_TOKENS_id') "
595  " WHERE NEW.token_id IS NOT NULL AND (SELECT id FROM TOKENS WHERE id = NEW.token_id) IS NULL; "
596  "END; "
597  )
598  << QString::fromLatin1(
599  // Cascading Delete
600  "CREATE TRIGGER fkdc_REFS_token_id_TOKENS_id "
601  "BEFORE DELETE ON TOKENS "
602  "FOR EACH ROW BEGIN "
603  " DELETE FROM REFS WHERE REFS.token_id = OLD.id; "
604  "END; "
605  );
606 /*
607 end of generated code
608 */
609  //insert table updates
610  createTableQuery << tableUpdates2();
611 
612  foreach (QString createTable, createTableQuery) {
613  QSqlQuery query = exec(createTable);
614  if (lastError().isValid()) {
615  TRACE() << "Error occurred while creating the database.";
616  return false;
617  }
618  query.clear();
619  commit();
620  }
621  TRACE() << "Creation successful";
622 
623  return true;
624 }
625 
626 bool MetaDataDB::updateDB(int version)
627 {
628  if (version == m_version)
629  return true;
630 
631  if (version < 1) {
632  TRACE() << "Upgrading from version < 1 not supported. Clearing DB";
633  QString fileName = m_database.databaseName();
634  QString connectionName = m_database.connectionName();
635  m_database.close();
636  QFile::remove(fileName);
637  m_database = QSqlDatabase(QSqlDatabase::addDatabase(driver,
638  connectionName));
639  m_database.setDatabaseName(fileName);
640  if (!connect())
641  return false;
642 
643  if (!createTables())
644  return false;
645  }
646 
647  //convert from 1 to 2
648  if (version == 1) {
649  QStringList createTableQuery = tableUpdates2();
650  foreach (QString createTable, createTableQuery) {
651  QSqlQuery query = exec(createTable);
652  if (lastError().isValid()) {
653  TRACE() << "Error occurred while inseting new tables.";
654  return false;
655  }
656  query.clear();
657  commit();
658  }
659  TRACE() << "Table insert successful";
660 
661  //populate owner table from acl
662  QSqlQuery ownerInsert = exec(S("INSERT OR IGNORE INTO OWNER "
663  "(identity_id, token_id) "
664  " SELECT identity_id, token_id FROM ACL"));
665  if (!commit()){
666  BLAME() << "Table copy failed.";
667  rollback();
668  }
669 
670  } else {
671  return false;
672  }
673 
674  return SqlDatabase::updateDB(version);
675 }
676 
677 QStringList MetaDataDB::methods(const quint32 id, const QString &securityToken)
678 {
679  QStringList list;
680  if (securityToken.isEmpty()) {
681  list = queryList(
682  QString::fromLatin1("SELECT DISTINCT METHODS.method FROM "
683  "( ACL JOIN METHODS ON ACL.method_id = METHODS.id ) "
684  "WHERE ACL.identity_id = '%1'").arg(id)
685  );
686  return list;
687  }
688  QSqlQuery q = newQuery();
689  q.prepare(S("SELECT DISTINCT METHODS.method FROM "
690  "( ACL JOIN METHODS ON ACL.method_id = METHODS.id) "
691  "WHERE ACL.identity_id = :id AND ACL.token_id = "
692  "(SELECT id FROM TOKENS where token = :token)"));
693  q.bindValue(S(":id"), id);
694  q.bindValue(S(":token"), securityToken);
695  list = queryList(q);
696 
697  return list;
698 }
699 
700 quint32 MetaDataDB::methodId(const QString &method)
701 {
702  TRACE() << "method:" << method;
703 
704  QSqlQuery q = newQuery();
705  q.prepare(S("SELECT id FROM METHODS WHERE method = :method"));
706  q.bindValue(S(":method"), method);
707  exec(q);
708  if (!q.first()) {
709  TRACE() << "No result or invalid method query.";
710  return 0;
711  }
712 
713  return q.value(0).toUInt();
714 }
715 
717 {
718  QString query_str;
719 
720  query_str = QString::fromLatin1(
721  "SELECT caption, username, flags, type "
722  "FROM credentials WHERE id = %1").arg(id);
723  QSqlQuery query = exec(query_str);
724 
725  if (!query.first()) {
726  TRACE() << "No result or invalid credentials query.";
727  return SignonIdentityInfo();
728  }
729 
730  QString caption = query.value(0).toString();
731  QString username = query.value(1).toString();
732  int flags = query.value(2).toInt();
733  bool savePassword = flags & RememberPassword;
734  bool validated = flags & Validated;
735  bool isUserNameSecret = flags & UserNameIsSecret;
736  if (isUserNameSecret) username = QString();
737  int type = query.value(3).toInt();
738 
739  query.clear();
740  QStringList realms = queryList(
741  QString::fromLatin1("SELECT realm FROM REALMS "
742  "WHERE identity_id = %1").arg(id));
743 
744  QStringList ownerTokens = queryList(
745  QString::fromLatin1("SELECT token FROM TOKENS "
746  "WHERE id IN "
747  "(SELECT token_id FROM OWNER WHERE identity_id = '%1' )")
748  .arg(id));
749 
750  query_str = QString::fromLatin1("SELECT token FROM TOKENS "
751  "WHERE id IN "
752  "(SELECT token_id FROM ACL WHERE identity_id = '%1' )")
753  .arg(id);
754  query = exec(query_str);
755  QStringList securityTokens;
756  while (query.next()) {
757  securityTokens.append(query.value(0).toString());
758  }
759  query.clear();
761  query_str = QString::fromLatin1(
762  "SELECT DISTINCT ACL.method_id, METHODS.method FROM "
763  "( ACL JOIN METHODS ON ACL.method_id = METHODS.id ) "
764  "WHERE ACL.identity_id = '%1'").arg(id);
765  query = exec(query_str);
766  while (query.next()) {
767  QStringList mechanisms = queryList(
768  QString::fromLatin1("SELECT DISTINCT MECHANISMS.mechanism FROM "
769  "( MECHANISMS JOIN ACL "
770  "ON ACL.mechanism_id = MECHANISMS.id ) "
771  "WHERE ACL.method_id = '%1' AND ACL.identity_id = '%2' ")
772  .arg(query.value(0).toInt()).arg(id));
773  methods.insert(query.value(1).toString(), mechanisms);
774  }
775  query.clear();
776 
777  int refCount = 0;
778  //TODO query for refcount
779 
780  SignonIdentityInfo info =
781  SignonIdentityInfo(id, username, QString(), savePassword,
782  caption, methods, realms, securityTokens,
783  ownerTokens,
784  type, refCount, validated);
785  info.setUserNameSecret(isUserNameSecret);
786  return info;
787 }
788 
789 QList<SignonIdentityInfo> MetaDataDB::identities(const QMap<QString,
790  QString> &filter)
791 {
792  TRACE();
793  Q_UNUSED(filter)
794  QList<SignonIdentityInfo> result;
795 
796  QString queryStr(QString::fromLatin1("SELECT id FROM credentials"));
797 
798  // TODO - process filtering step here !!!
799 
800  queryStr += QString::fromLatin1(" ORDER BY id");
801 
802  QSqlQuery query = exec(queryStr);
803  if (errorOccurred()) {
804  TRACE() << "Error occurred while fetching credentials from database.";
805  return result;
806  }
807 
808  while (query.next()) {
809  SignonIdentityInfo info = identity(query.value(0).toUInt());
810  if (errorOccurred())
811  break;
812  result << info;
813  }
814 
815  query.clear();
816  return result;
817 }
818 
820 {
821  if (!startTransaction()) {
822  TRACE() << "Could not start transaction. Error inserting credentials.";
823  return 0;
824  }
825 
826  quint32 id = updateCredentials(info);
827  if (id == 0) {
828  rollback();
829  return 0;
830  }
831 
832  /* Methods inserts */
833  insertMethods(info.methods());
834 
835  if (!updateRealms(id, info.realms(), info.isNew())) {
836  TRACE() << "Error in updating realms";
837  rollback();
838  return 0;
839  }
840 
841  /* Security tokens insert */
842  foreach (QString token, info.accessControlList()) {
843  QSqlQuery tokenInsert = newQuery();
844  tokenInsert.prepare(S("INSERT OR IGNORE INTO TOKENS (token) "
845  "VALUES ( :token )"));
846  tokenInsert.bindValue(S(":token"), token);
847  exec(tokenInsert);
848  }
849 
850  foreach (QString token, info.ownerList()) {
851  if (!token.isEmpty()) {
852  QSqlQuery tokenInsert = newQuery();
853  tokenInsert.prepare(S("INSERT OR IGNORE INTO TOKENS (token) "
854  "VALUES ( :token )"));
855  tokenInsert.bindValue(S(":token"), token);
856  exec(tokenInsert);
857  }
858  }
859 
860  if (!info.isNew()) {
861  //remove acl
862  QString queryStr = QString::fromLatin1(
863  "DELETE FROM ACL WHERE "
864  "identity_id = '%1'")
865  .arg(info.id());
866  QSqlQuery insertQuery = exec(queryStr);
867  insertQuery.clear();
868  //remove owner
869  queryStr = QString::fromLatin1(
870  "DELETE FROM OWNER WHERE "
871  "identity_id = '%1'")
872  .arg(info.id());
873  insertQuery = exec(queryStr);
874  insertQuery.clear();
875  }
876 
877  /* ACL insert, this will do basically identity level ACL */
878  QMapIterator<QString, QStringList> it(info.methods());
879  while (it.hasNext()) {
880  it.next();
881  if (!info.accessControlList().isEmpty()) {
882  foreach (QString token, info.accessControlList()) {
883  foreach (QString mech, it.value()) {
884  QSqlQuery aclInsert = newQuery();
885  aclInsert.prepare(S("INSERT OR REPLACE INTO ACL "
886  "(identity_id, method_id, mechanism_id, token_id) "
887  "VALUES ( :id, "
888  "( SELECT id FROM METHODS WHERE method = :method ),"
889  "( SELECT id FROM MECHANISMS WHERE mechanism= :mech ), "
890  "( SELECT id FROM TOKENS WHERE token = :token ))"));
891  aclInsert.bindValue(S(":id"), id);
892  aclInsert.bindValue(S(":method"), it.key());
893  aclInsert.bindValue(S(":mech"), mech);
894  aclInsert.bindValue(S(":token"), token);
895  exec(aclInsert);
896  }
897  //insert entires for empty mechs list
898  if (it.value().isEmpty()) {
899  QSqlQuery aclInsert = newQuery();
900  aclInsert.prepare(S("INSERT OR REPLACE INTO ACL (identity_id, method_id, token_id) "
901  "VALUES ( :id, "
902  "( SELECT id FROM METHODS WHERE method = :method ),"
903  "( SELECT id FROM TOKENS WHERE token = :token ))"));
904  aclInsert.bindValue(S(":id"), id);
905  aclInsert.bindValue(S(":method"), it.key());
906  aclInsert.bindValue(S(":token"), token);
907  exec(aclInsert);
908  }
909  }
910  } else {
911  foreach (QString mech, it.value()) {
912  QSqlQuery aclInsert = newQuery();
913  aclInsert.prepare(S("INSERT OR REPLACE INTO ACL "
914  "(identity_id, method_id, mechanism_id) "
915  "VALUES ( :id, "
916  "( SELECT id FROM METHODS WHERE method = :method ),"
917  "( SELECT id FROM MECHANISMS WHERE mechanism= :mech )"
918  ")"));
919  aclInsert.bindValue(S(":id"), id);
920  aclInsert.bindValue(S(":method"), it.key());
921  aclInsert.bindValue(S(":mech"), mech);
922  exec(aclInsert);
923  }
924  //insert entires for empty mechs list
925  if (it.value().isEmpty()) {
926  QSqlQuery aclInsert = newQuery();
927  aclInsert.prepare(S("INSERT OR REPLACE INTO ACL (identity_id, method_id) "
928  "VALUES ( :id, "
929  "( SELECT id FROM METHODS WHERE method = :method )"
930  ")"));
931  aclInsert.bindValue(S(":id"), id);
932  aclInsert.bindValue(S(":method"), it.key());
933  exec(aclInsert);
934  }
935  }
936  }
937  //insert acl in case where methods are missing
938  if (info.methods().isEmpty()) {
939  foreach (QString token, info.accessControlList()) {
940  QSqlQuery aclInsert = newQuery();
941  aclInsert.prepare(S("INSERT OR REPLACE INTO ACL "
942  "(identity_id, token_id) "
943  "VALUES ( :id, "
944  "( SELECT id FROM TOKENS WHERE token = :token ))"));
945  aclInsert.bindValue(S(":id"), id);
946  aclInsert.bindValue(S(":token"), token);
947  exec(aclInsert);
948  }
949  }
950 
951  //insert owner list
952  foreach (QString token, info.ownerList()) {
953  if (!token.isEmpty()) {
954  QSqlQuery ownerInsert = newQuery();
955  ownerInsert.prepare(S("INSERT OR REPLACE INTO OWNER "
956  "(identity_id, token_id) "
957  "VALUES ( :id, "
958  "( SELECT id FROM TOKENS WHERE token = :token ))"));
959  ownerInsert.bindValue(S(":id"), id);
960  ownerInsert.bindValue(S(":token"), token);
961  exec(ownerInsert);
962  }
963  }
964 
965  if (commit()) {
966  return id;
967  } else {
968  rollback();
969  TRACE() << "Credentials insertion failed.";
970  return 0;
971  }
972 }
973 
974 bool MetaDataDB::removeIdentity(const quint32 id)
975 {
976  TRACE();
977 
978  QStringList queries = QStringList()
979  << QString::fromLatin1(
980  "DELETE FROM CREDENTIALS WHERE id = %1").arg(id)
981  << QString::fromLatin1(
982  "DELETE FROM ACL WHERE identity_id = %1").arg(id)
983  << QString::fromLatin1(
984  "DELETE FROM REALMS WHERE identity_id = %1").arg(id)
985  << QString::fromLatin1(
986  "DELETE FROM owner WHERE identity_id = %1").arg(id);
987 
988  return transactionalExec(queries);
989 }
990 
992 {
993  TRACE();
994 
995  QStringList clearCommands = QStringList()
996  << QLatin1String("DELETE FROM CREDENTIALS")
997  << QLatin1String("DELETE FROM METHODS")
998  << QLatin1String("DELETE FROM MECHANISMS")
999  << QLatin1String("DELETE FROM ACL")
1000  << QLatin1String("DELETE FROM REALMS")
1001  << QLatin1String("DELETE FROM TOKENS")
1002  << QLatin1String("DELETE FROM OWNER");
1003 
1004  return transactionalExec(clearCommands);
1005 }
1006 
1007 QStringList MetaDataDB::accessControlList(const quint32 identityId)
1008 {
1009  return queryList(QString::fromLatin1("SELECT token FROM TOKENS "
1010  "WHERE id IN "
1011  "(SELECT token_id FROM ACL WHERE identity_id = '%1' )")
1012  .arg(identityId));
1013 }
1014 
1015 QStringList MetaDataDB::ownerList(const quint32 identityId)
1016 {
1017  return queryList(QString::fromLatin1("SELECT token FROM TOKENS "
1018  "WHERE id IN "
1019  "(SELECT token_id FROM OWNER WHERE identity_id = '%1' )")
1020  .arg(identityId));
1021 }
1022 
1023 bool MetaDataDB::addReference(const quint32 id,
1024  const QString &token,
1025  const QString &reference)
1026 {
1027  if (!startTransaction()) {
1028  TRACE() << "Could not start transaction. Error inserting data.";
1029  return false;
1030  }
1031 
1032  TRACE() << "Storing:" << id << ", " << token << ", " << reference;
1033  /* Data insert */
1034  bool allOk = true;
1035 
1036  /* Security token insert */
1037  QSqlQuery tokenInsert = newQuery();
1038  tokenInsert.prepare(S("INSERT OR IGNORE INTO TOKENS (token) "
1039  "VALUES ( :token )"));
1040  tokenInsert.bindValue(S(":token"), token);
1041  exec(tokenInsert);
1042  if (errorOccurred()) {
1043  allOk = false;
1044  }
1045 
1046  QSqlQuery refsInsert = newQuery();
1047  refsInsert.prepare(S("INSERT OR REPLACE INTO REFS "
1048  "(identity_id, token_id, ref) "
1049  "VALUES ( :id, "
1050  "( SELECT id FROM TOKENS WHERE token = :token ),"
1051  ":reference"
1052  ")"));
1053  refsInsert.bindValue(S(":id"), id);
1054  refsInsert.bindValue(S(":token"), token);
1055  refsInsert.bindValue(S(":reference"), reference);
1056  exec(refsInsert);
1057  if (errorOccurred()) {
1058  allOk = false;
1059  }
1060 
1061  if (allOk && commit()) {
1062  TRACE() << "Data insertion ok.";
1063  return true;
1064  }
1065  rollback();
1066  TRACE() << "Data insertion failed.";
1067  return false;
1068 }
1069 
1070 bool MetaDataDB::removeReference(const quint32 id,
1071  const QString &token,
1072  const QString &reference)
1073 {
1074  TRACE() << "Removing:" << id << ", " << token << ", " << reference;
1075  //check that there is references
1076  QStringList refs = references(id, token);
1077  if (refs.isEmpty())
1078  return false;
1079  if (!reference.isNull() && !refs.contains(reference))
1080  return false;
1081 
1082  if (!startTransaction()) {
1083  TRACE() << "Could not start transaction. Error removing data.";
1084  return false;
1085  }
1086 
1087  bool allOk = true;
1088  QSqlQuery refsDelete = newQuery();
1089 
1090  if (reference.isEmpty()) {
1091  refsDelete.prepare(S("DELETE FROM REFS "
1092  "WHERE identity_id = :id AND "
1093  "token_id = ( SELECT id FROM TOKENS WHERE token = :token )"));
1094  refsDelete.bindValue(S(":id"), id);
1095  refsDelete.bindValue(S(":token"), token);
1096  } else {
1097  refsDelete.prepare(S("DELETE FROM REFS "
1098  "WHERE identity_id = :id AND "
1099  "token_id = ( SELECT id FROM TOKENS WHERE token = :token ) "
1100  "AND ref = :ref"));
1101  refsDelete.bindValue(S(":id"), id);
1102  refsDelete.bindValue(S(":token"), token);
1103  refsDelete.bindValue(S(":ref"), reference);
1104  }
1105 
1106  exec(refsDelete);
1107  if (errorOccurred()) {
1108  allOk = false;
1109  }
1110 
1111  if (allOk && commit()) {
1112  TRACE() << "Data delete ok.";
1113  return true;
1114  }
1115  rollback();
1116  TRACE() << "Data delete failed.";
1117  return false;
1118 }
1119 
1120 QStringList MetaDataDB::references(const quint32 id, const QString &token)
1121 {
1122  if (token.isEmpty())
1123  return queryList(QString::fromLatin1("SELECT ref FROM REFS "
1124  "WHERE identity_id = '%1'")
1125  .arg(id));
1126  QSqlQuery q = newQuery();
1127  q.prepare(S("SELECT ref FROM REFS "
1128  "WHERE identity_id = :id AND "
1129  "token_id = (SELECT id FROM TOKENS WHERE token = :token )"));
1130  q.bindValue(S(":id"), id);
1131  q.bindValue(S(":token"), token);
1132  return queryList(q);
1133 }
1134 
1135 bool MetaDataDB::insertMethods(QMap<QString, QStringList> methods)
1136 {
1137  bool allOk = true;
1138 
1139  if (methods.isEmpty()) return false;
1140  //insert (unique) method names
1141  QMapIterator<QString, QStringList> it(methods);
1142  while (it.hasNext()) {
1143  it.next();
1144  QSqlQuery methodInsert = newQuery();
1145  methodInsert.prepare(S("INSERT OR IGNORE INTO METHODS (method) "
1146  "VALUES( :method )"));
1147  methodInsert.bindValue(S(":method"), it.key());
1148  exec(methodInsert);
1149  if (errorOccurred()) allOk = false;
1150  //insert (unique) mechanism names
1151  foreach (QString mech, it.value()) {
1152  QSqlQuery mechInsert = newQuery();
1153  mechInsert.prepare(S("INSERT OR IGNORE INTO MECHANISMS (mechanism) "
1154  "VALUES( :mech )"));
1155  mechInsert.bindValue(S(":mech"), mech);
1156  exec(mechInsert);
1157  if (errorOccurred()) allOk = false;
1158  }
1159  }
1160  return allOk;
1161 }
1162 
1163 quint32 MetaDataDB::insertMethod(const QString &method, bool *ok)
1164 {
1165  QSqlQuery q = newQuery();
1166  q.prepare(S("INSERT INTO METHODS (method) VALUES(:method)"));
1167  q.bindValue(S(":method"), method);
1168  exec(q);
1169 
1170  if (errorOccurred()) {
1171  if (ok != 0) *ok = false;
1172  return 0;
1173  }
1174  return q.lastInsertId().toUInt(ok);
1175 }
1176 
1177 quint32 MetaDataDB::updateCredentials(const SignonIdentityInfo &info)
1178 {
1179  quint32 id;
1180  QSqlQuery q = newQuery();
1181 
1182  int flags = 0;
1183  if (info.validated()) flags |= Validated;
1184  if (info.storePassword()) flags |= RememberPassword;
1185  if (info.isUserNameSecret()) flags |= UserNameIsSecret;
1186 
1187  if (!info.isNew()) {
1188  TRACE() << "UPDATE:" << info.id() ;
1189  q.prepare(S("UPDATE CREDENTIALS SET caption = :caption, "
1190  "username = :username, "
1191  "flags = :flags, "
1192  "type = :type WHERE id = :id"));
1193  q.bindValue(S(":id"), info.id());
1194  } else {
1195  TRACE() << "INSERT:" << info.id();
1196  q.prepare(S("INSERT INTO CREDENTIALS "
1197  "(caption, username, flags, type) "
1198  "VALUES(:caption, :username, :flags, :type)"));
1199  }
1200  q.bindValue(S(":username"),
1201  info.isUserNameSecret() ? QString() : info.userName());
1202  q.bindValue(S(":caption"), info.caption());
1203  q.bindValue(S(":flags"), flags);
1204  q.bindValue(S(":type"), info.type());
1205  exec(q);
1206  if (errorOccurred()) {
1207  TRACE() << "Error occurred while updating crendentials";
1208  return 0;
1209  }
1210 
1211  if (info.isNew()) {
1212  /* Fetch id of the inserted credentials */
1213  QVariant idVariant = q.lastInsertId();
1214  if (!idVariant.isValid()) {
1215  TRACE() << "Error occurred while inserting crendentials";
1216  return 0;
1217  }
1218  id = idVariant.toUInt();
1219  } else {
1220  id = info.id() ;
1221  }
1222 
1223  return id;
1224 }
1225 
1226 bool MetaDataDB::updateRealms(quint32 id, const QStringList &realms, bool isNew)
1227 {
1228  QString queryStr;
1229 
1230  if (!isNew) {
1231  //remove realms list
1232  queryStr = QString::fromLatin1(
1233  "DELETE FROM REALMS WHERE identity_id = '%1'")
1234  .arg(id);
1235  exec(queryStr);
1236  }
1237 
1238  /* Realms insert */
1239  QSqlQuery q = newQuery();
1240  q.prepare(S("INSERT OR IGNORE INTO REALMS (identity_id, realm) "
1241  "VALUES (:id, :realm)"));
1242  foreach (QString realm, realms) {
1243  q.bindValue(S(":id"), id);
1244  q.bindValue(S(":realm"), realm);
1245  exec(q);
1246  if (errorOccurred()) return false;
1247  }
1248  return true;
1249 }
1250 
1251 /* Error monitor class */
1252 
1254 {
1255  db->_lastError.setType(SignOn::CredentialsDBError::NoError);
1256  db->metaDataDB->clearError();
1257  if (db->secretsStorage != 0)
1258  db->secretsStorage->clearError();
1259  _db = db;
1260 }
1261 
1262 CredentialsDB::ErrorMonitor::~ErrorMonitor()
1263 {
1264  /* If there's an error set on the CredentialsDB, just let it be and return.
1265  * If not, take the error from the SqlDatabase objects, if any.
1266  */
1267  if (_db->_lastError.isValid())
1268  return;
1269 
1270  if (_db->secretsStorage != 0 &&
1271  _db->secretsStorage->lastError().isValid()) {
1272  _db->_lastError = _db->secretsStorage->lastError();
1273  return;
1274  }
1275 
1276  _db->_lastError = _db->metaDataDB->lastError();
1277 }
1278 
1279 /* ------- CredentialsDB implementation ------- */
1280 
1281 CredentialsDB::CredentialsDB(const QString &metaDataDbName,
1282  SignOn::AbstractSecretsStorage *secretsStorage):
1283  secretsStorage(secretsStorage),
1284  m_secretsCache(new SecretsCache),
1285  metaDataDB(new MetaDataDB(metaDataDbName))
1286 {
1287  noSecretsDB = SignOn::CredentialsDBError(
1288  QLatin1String("Secrets DB not opened"),
1289  SignOn::CredentialsDBError::ConnectionError);
1290 }
1291 
1293 {
1294  TRACE();
1295 
1296  delete m_secretsCache;
1297 
1298  if (metaDataDB) {
1299  QString connectionName = metaDataDB->connectionName();
1300  delete metaDataDB;
1301  QSqlDatabase::removeDatabase(connectionName);
1302  }
1303 }
1304 
1306 {
1307  return metaDataDB->init();
1308 }
1309 
1310 bool CredentialsDB::openSecretsDB(const QString &secretsDbName)
1311 {
1312  QVariantMap configuration;
1313  configuration.insert(QLatin1String("name"), secretsDbName);
1314  if (!secretsStorage->initialize(configuration)) {
1315  TRACE() << "SecretsStorage initialization failed: " <<
1316  secretsStorage->lastError().text();
1317  return false;
1318  }
1319 
1320  m_secretsCache->storeToDB(secretsStorage);
1321  m_secretsCache->clear();
1322  return true;
1323 }
1324 
1326 {
1327  return secretsStorage != 0 && secretsStorage->isOpen();
1328 }
1329 
1331 {
1332  if (secretsStorage != 0) secretsStorage->close();
1333 }
1334 
1335 SignOn::CredentialsDBError CredentialsDB::lastError() const
1336 {
1337  return _lastError;
1338 }
1339 
1340 QStringList CredentialsDB::methods(const quint32 id,
1341  const QString &securityToken)
1342 {
1343  INIT_ERROR();
1344  return metaDataDB->methods(id, securityToken);
1345 }
1346 
1347 bool CredentialsDB::checkPassword(const quint32 id,
1348  const QString &username,
1349  const QString &password)
1350 {
1351  INIT_ERROR();
1352  RETURN_IF_NO_SECRETS_DB(false);
1353  SignonIdentityInfo info = metaDataDB->identity(id);
1354  if (info.isUserNameSecret()) {
1355  return secretsStorage->checkPassword(id, username, password);
1356  } else {
1357  return username == info.userName() &&
1358  secretsStorage->checkPassword(id, QString(), password);
1359  }
1360 }
1361 
1363  bool queryPassword)
1364 {
1365  TRACE() << "id:" << id << "queryPassword:" << queryPassword;
1366  INIT_ERROR();
1367  SignonIdentityInfo info = metaDataDB->identity(id);
1368  if (queryPassword && !info.isNew()) {
1369  QString username, password;
1370  if (info.storePassword() && isSecretsDBOpen()) {
1371  TRACE() << "Loading credentials from DB.";
1372  secretsStorage->loadCredentials(id, username, password);
1373  } else {
1374  TRACE() << "Looking up credentials from cache.";
1375  m_secretsCache->lookupCredentials(id, username, password);
1376  }
1377  if (info.isUserNameSecret())
1378  info.setUserName(username);
1379  info.setPassword(password);
1380 
1381 #ifdef DEBUG_ENABLED
1382  if (password.isEmpty()) {
1383  TRACE() << "Password is empty";
1384  }
1385 #endif
1386  }
1387  return info;
1388 }
1389 
1390 QList<SignonIdentityInfo>
1391 CredentialsDB::credentials(const QMap<QString, QString> &filter)
1392 {
1393  INIT_ERROR();
1394  return metaDataDB->identities(filter);
1395 }
1396 
1398 {
1399  SignonIdentityInfo newInfo = info;
1400  if (!info.isNew())
1401  newInfo.setNew();
1402  return updateCredentials(newInfo);
1403 }
1404 
1406 {
1407  INIT_ERROR();
1408  quint32 id = metaDataDB->updateIdentity(info);
1409  if (id == 0) return id;
1410 
1411  QString password = info.password();
1412  QString userName;
1413  if (info.isUserNameSecret())
1414  userName = info.userName();
1415 
1416  if (info.storePassword() && isSecretsDBOpen()) {
1417  secretsStorage->updateCredentials(id, userName, password);
1418  } else {
1419  /* Cache username and password in memory */
1420  m_secretsCache->updateCredentials(id, userName, password,
1421  info.storePassword());
1422  }
1423 
1424  return id;
1425 }
1426 
1427 bool CredentialsDB::removeCredentials(const quint32 id)
1428 {
1429  INIT_ERROR();
1430 
1431  /* We don't allow removing the credentials if the secrets DB is not
1432  * available */
1433  RETURN_IF_NO_SECRETS_DB(false);
1434 
1435  return secretsStorage->removeCredentials(id) &&
1436  metaDataDB->removeIdentity(id);
1437 }
1438 
1440 {
1441  TRACE();
1442 
1443  INIT_ERROR();
1444 
1445  /* We don't allow clearing the DB if the secrets DB is not available */
1446  RETURN_IF_NO_SECRETS_DB(false);
1447 
1448  return secretsStorage->clear() && metaDataDB->clear();
1449 }
1450 
1451 QVariantMap CredentialsDB::loadData(const quint32 id, const QString &method)
1452 {
1453  TRACE() << "Loading:" << id << "," << method;
1454 
1455  INIT_ERROR();
1456  if (id == 0) return QVariantMap();
1457 
1458  quint32 methodId = metaDataDB->methodId(method);
1459  if (methodId == 0) return QVariantMap();
1460 
1461  if (isSecretsDBOpen()) {
1462  return secretsStorage->loadData(id, methodId);
1463  } else {
1464  TRACE() << "Looking up data from cache";
1465  return m_secretsCache->lookupData(id, methodId);
1466  }
1467 }
1468 
1469 bool CredentialsDB::storeData(const quint32 id, const QString &method,
1470  const QVariantMap &data)
1471 {
1472  TRACE() << "Storing:" << id << "," << method;
1473 
1474  INIT_ERROR();
1475  if (id == 0) return false;
1476 
1477  quint32 methodId = metaDataDB->methodId(method);
1478  if (methodId == 0) {
1479  bool ok = false;
1480  methodId = metaDataDB->insertMethod(method, &ok);
1481  if (!ok)
1482  return false;
1483  }
1484 
1485  if (isSecretsDBOpen()) {
1486  return secretsStorage->storeData(id, methodId, data);
1487  } else {
1488  TRACE() << "Storing data into cache";
1489  m_secretsCache->updateData(id, methodId, data);
1490  return true;
1491  }
1492 }
1493 
1494 bool CredentialsDB::removeData(const quint32 id, const QString &method)
1495 {
1496  TRACE() << "Removing:" << id << "," << method;
1497 
1498  INIT_ERROR();
1499  RETURN_IF_NO_SECRETS_DB(false);
1500  if (id == 0) return false;
1501 
1502  quint32 methodId;
1503  if (!method.isEmpty()) {
1504  methodId = metaDataDB->methodId(method);
1505  if (methodId == 0) return false;
1506  } else {
1507  methodId = 0;
1508  }
1509 
1510  return secretsStorage->removeData(id, methodId);
1511 }
1512 
1513 QStringList CredentialsDB::accessControlList(const quint32 identityId)
1514 {
1515  INIT_ERROR();
1516  return metaDataDB->accessControlList(identityId);
1517 }
1518 
1519 QStringList CredentialsDB::ownerList(const quint32 identityId)
1520 {
1521  INIT_ERROR();
1522  return metaDataDB->ownerList(identityId);
1523 }
1524 
1525 QString CredentialsDB::credentialsOwnerSecurityToken(const quint32 identityId)
1526 {
1527  //return first owner token
1528  QStringList owners = ownerList(identityId);
1529  return owners.count() ? owners.at(0) : QString();
1530 }
1531 
1532 bool CredentialsDB::addReference(const quint32 id,
1533  const QString &token,
1534  const QString &reference)
1535 {
1536  INIT_ERROR();
1537  return metaDataDB->addReference(id, token, reference);
1538 }
1539 
1540 bool CredentialsDB::removeReference(const quint32 id,
1541  const QString &token,
1542  const QString &reference)
1543 {
1544  INIT_ERROR();
1545  return metaDataDB->removeReference(id, token, reference);
1546 }
1547 
1548 QStringList CredentialsDB::references(const quint32 id, const QString &token)
1549 {
1550  INIT_ERROR();
1551  return metaDataDB->references(id, token);
1552 }
1553 
1554 } //namespace SignonDaemonNS