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