signon  8.41
signonidentity.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of signon
3  *
4  * Copyright (C) 2009-2010 Nokia Corporation.
5  * Copyright (C) 2011 Intel Corporation.
6  *
7  * Contact: Aurel Popirtac <ext-aurel.popirtac@nokia.com>
8  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
9  * Contact: Jussi Laako <jussi.laako@linux.intel.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 <iostream>
27 #include <QVariantMap>
28 
29 #include "signond-common.h"
30 #include "signonidentity.h"
31 #include "signonui_interface.h"
32 #include "SignOn/uisessiondata.h"
33 #include "SignOn/uisessiondata_priv.h"
34 #include "signoncommon.h"
35 
37 #include "signonidentityadaptor.h"
38 
39 #define SIGNON_RETURN_IF_CAM_UNAVAILABLE(_ret_arg_) do { \
40  if (!(CredentialsAccessManager::instance()->credentialsSystemOpened())) { \
41  sendErrorReply(internalServerErrName, \
42  internalServerErrStr + \
43  QLatin1String("Could not access Signon Database."));\
44  return _ret_arg_; \
45  } \
46  } while(0)
47 
48 namespace SignonDaemonNS {
49 
50 const QString internalServerErrName = SIGNOND_INTERNAL_SERVER_ERR_NAME;
51 const QString internalServerErrStr = SIGNOND_INTERNAL_SERVER_ERR_STR;
52 
53 SignonIdentity::SignonIdentity(quint32 id, int timeout,
54  SignonDaemon *parent):
55  SignonDisposable(timeout, parent),
56  m_pInfo(NULL),
57  m_pSignonDaemon(parent),
58  m_registered(false)
59 {
60  m_id = id;
61 
62  /*
63  * creation of unique name for the given identity
64  * */
65  static quint32 incr = 0;
66  QString objectName = SIGNOND_DAEMON_OBJECTPATH + QLatin1String("/Identity_")
67  + QString::number(incr++, 16);
68  setObjectName(objectName);
69 
70  m_signonui = new SignonUiAdaptor(
73  SIGNOND_BUS,
74  this);
75 }
76 
77 SignonIdentity::~SignonIdentity()
78 {
79  if (m_registered)
80  {
81  emit unregistered();
82  QDBusConnection connection = SIGNOND_BUS;
83  connection.unregisterObject(objectName());
84  }
85 
86  if (credentialsStored())
87  m_pSignonDaemon->m_storedIdentities.remove(m_id);
88  else
89  m_pSignonDaemon->m_unstoredIdentities.remove(objectName());
90 
91  delete m_signonui;
92 }
93 
94 bool SignonIdentity::init()
95 {
96  QDBusConnection connection = SIGNOND_BUS;
97 
98  if (!connection.isConnected()) {
99  QDBusError err = connection.lastError();
100  TRACE() << "Connection cannot be established:" <<
101  err.errorString(err.type()) ;
102  return false;
103  }
104 
105  QDBusConnection::RegisterOptions registerOptions =
106  QDBusConnection::ExportAllContents;
107 
108  (void)new SignonIdentityAdaptor(this);
109  registerOptions = QDBusConnection::ExportAdaptors;
110 
111  if (!connection.registerObject(objectName(), this, registerOptions)) {
112  TRACE() << "Object cannot be registered: " << objectName();
113  return false;
114  }
115 
116  return (m_registered = true);
117 }
118 
120 {
121  SignonIdentity *identity =
122  new SignonIdentity(id, parent->identityTimeout(), parent);
123 
124  if (!identity->init()) {
125  TRACE() << "The created identity is invalid and will be deleted.\n";
126  delete identity;
127  return NULL;
128  }
129 
130  return identity;
131 }
132 
134 {
135  if (m_registered)
136  {
137  emit unregistered();
138  QDBusConnection connection = SIGNOND_BUS;
139  connection.unregisterObject(objectName());
140  m_registered = false;
141  }
142 
143  deleteLater();
144 }
145 
146 SignonIdentityInfo SignonIdentity::queryInfo(bool &ok, bool queryPassword)
147 {
148  ok = true;
149 
150  bool needLoadFromDB = true;
151  if (m_pInfo) {
152  needLoadFromDB = false;
153  if (queryPassword && m_pInfo->password().isEmpty()) {
154  needLoadFromDB = true;
155  }
156  }
157 
158  if (needLoadFromDB) {
159  if (m_pInfo != 0) {
160  delete m_pInfo;
161  }
162 
163  CredentialsDB *db =
165  m_pInfo = new SignonIdentityInfo(db->credentials(m_id, queryPassword));
166 
167  if (db->lastError().isValid()) {
168  ok = false;
169  delete m_pInfo;
170  m_pInfo = NULL;
171  return SignonIdentityInfo();
172  }
173  }
174 
175  /* Make sure that we clear the password, if the caller doesn't need it */
176  SignonIdentityInfo info = *m_pInfo;
177  if (!queryPassword) {
178  info.setPassword(QString());
179  }
180  return info;
181 }
182 
183 bool SignonIdentity::addReference(const QString &reference)
184 {
185  TRACE() << "addReference: " << reference;
186 
188 
190  if (db == NULL) {
191  BLAME() << "NULL database handler object.";
192  return false;
193  }
194  QString appId =
196  (static_cast<QDBusContext>(*this)).message());
197  keepInUse();
198  return db->addReference(m_id, appId, reference);
199 }
200 
201 bool SignonIdentity::removeReference(const QString &reference)
202 {
203  TRACE() << "removeReference: " << reference;
204 
206 
208  if (db == NULL) {
209  BLAME() << "NULL database handler object.";
210  return false;
211  }
212  QString appId =
214  (static_cast<QDBusContext>(*this)).message());
215  keepInUse();
216  return db->removeReference(m_id, appId, reference);
217 }
218 
219 quint32 SignonIdentity::requestCredentialsUpdate(const QString &displayMessage)
220 {
221  SIGNON_RETURN_IF_CAM_UNAVAILABLE(SIGNOND_NEW_IDENTITY);
222 
223  bool ok;
224  SignonIdentityInfo info = queryInfo(ok, false);
225 
226  if (!ok) {
227  BLAME() << "Identity not found.";
228  sendErrorReply(SIGNOND_IDENTITY_NOT_FOUND_ERR_NAME,
229  SIGNOND_IDENTITY_NOT_FOUND_ERR_STR);
230  return SIGNOND_NEW_IDENTITY;
231  }
232  if (!info.storePassword()) {
233  BLAME() << "Password cannot be stored.";
234  sendErrorReply(SIGNOND_STORE_FAILED_ERR_NAME,
235  SIGNOND_STORE_FAILED_ERR_STR);
236  return SIGNOND_NEW_IDENTITY;
237  }
238 
239  //delay dbus reply, ui interaction might take long time to complete
240  setDelayedReply(true);
241  m_message = message();
242 
243  //create ui request to ask password
244  QVariantMap uiRequest;
245  uiRequest.insert(SSOUI_KEY_QUERYPASSWORD, true);
246  uiRequest.insert(SSOUI_KEY_USERNAME, info.userName());
247  uiRequest.insert(SSOUI_KEY_MESSAGE, displayMessage);
248  uiRequest.insert(SSOUI_KEY_CAPTION, info.caption());
249 
250  TRACE() << "Waiting for reply from signon-ui";
251  QDBusPendingCallWatcher *watcher =
252  new QDBusPendingCallWatcher(m_signonui->queryDialog(uiRequest), this);
253  connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
254  this, SLOT(queryUiSlot(QDBusPendingCallWatcher*)));
255 
256  setAutoDestruct(false);
257  return 0;
258 }
259 
261 {
262  TRACE() << "QUERYING INFO";
263 
264  SIGNON_RETURN_IF_CAM_UNAVAILABLE(QVariantMap());
265 
266  bool ok;
267  SignonIdentityInfo info = queryInfo(ok, false);
268 
269  if (!ok) {
270  TRACE();
271  sendErrorReply(SIGNOND_CREDENTIALS_NOT_AVAILABLE_ERR_NAME,
272  SIGNOND_CREDENTIALS_NOT_AVAILABLE_ERR_STR +
273  QLatin1String("Database querying error occurred."));
274  return QVariantMap();
275  }
276 
277  if (info.isNew()) {
278  TRACE();
279  sendErrorReply(SIGNOND_IDENTITY_NOT_FOUND_ERR_NAME,
280  SIGNOND_IDENTITY_NOT_FOUND_ERR_STR);
281  return QVariantMap();
282  }
283 
284  keepInUse();
285  return info.toMap();
286 }
287 
288 void SignonIdentity::queryUserPassword(const QVariantMap &params) {
289  TRACE() << "Waiting for reply from signon-ui";
290  QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
291  m_signonui->queryDialog(params), this);
292  connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
293  SLOT(verifyUiSlot(QDBusPendingCallWatcher*)));
294 
295  setAutoDestruct(false);
296 }
297 
298 bool SignonIdentity::verifyUser(const QVariantMap &params)
299 {
301 
302  bool ok;
303  SignonIdentityInfo info = queryInfo(ok, true);
304 
305  if (!ok) {
306  BLAME() << "Identity not found.";
307  sendErrorReply(SIGNOND_IDENTITY_NOT_FOUND_ERR_NAME,
308  SIGNOND_IDENTITY_NOT_FOUND_ERR_STR);
309  return false;
310  }
311  if (!info.storePassword() || info.password().isEmpty()) {
312  BLAME() << "Password is not stored.";
313  sendErrorReply(SIGNOND_CREDENTIALS_NOT_AVAILABLE_ERR_NAME,
314  SIGNOND_CREDENTIALS_NOT_AVAILABLE_ERR_STR);
315  return false;
316  }
317 
318  //delay dbus reply, ui interaction might take long time to complete
319  setDelayedReply(true);
320  m_message = message();
321 
322  //create ui request to ask password
323  QVariantMap uiRequest;
324  uiRequest.unite(params);
325  uiRequest.insert(SSOUI_KEY_QUERYPASSWORD, true);
326  uiRequest.insert(SSOUI_KEY_USERNAME, info.userName());
327  uiRequest.insert(SSOUI_KEY_CAPTION, info.caption());
328 
329  queryUserPassword(uiRequest);
330  return false;
331 }
332 
333 bool SignonIdentity::verifySecret(const QString &secret)
334 {
336 
337  bool ok;
338  queryInfo(ok);
339  if (!ok) {
340  TRACE();
341  sendErrorReply(SIGNOND_CREDENTIALS_NOT_AVAILABLE_ERR_NAME,
342  SIGNOND_CREDENTIALS_NOT_AVAILABLE_ERR_STR +
343  QLatin1String("Database querying error occurred."));
344  return false;
345  }
346 
348  bool ret = db->checkPassword(m_pInfo->id(), m_pInfo->userName(), secret);
349 
350  keepInUse();
351  return ret;
352 }
353 
355 {
357 
359  if ((db == 0) || !db->removeCredentials(m_id)) {
360  TRACE() << "Error occurred while inserting/updating credentials.";
361  sendErrorReply(SIGNOND_REMOVE_FAILED_ERR_NAME,
362  SIGNOND_REMOVE_FAILED_ERR_STR +
363  QLatin1String("Database error occurred."));
364  return;
365  }
366  emit infoUpdated((int)SignOn::IdentityRemoved);
367  keepInUse();
368 }
369 
371 {
372  TRACE() << "Signout request. Identity ID: " << id();
373  /*
374  * - If the identity is stored (thus registered here)
375  * signal 'sign out' to all identities subsribed to this object,
376  * otherwise the only identity subscribed to this is the newly
377  * created client side identity, which called this method.
378  * - This is just a safety check, as the client identity - if it is a new
379  * one - should not inform server side to sign out.
380  */
381  if (id() != SIGNOND_NEW_IDENTITY) {
382  //clear stored sessiondata
383  CredentialsDB *db =
385  if ((db == 0) || !db->removeData(m_id)) {
386  TRACE() << "clear data failed";
387  }
388 
389  emit infoUpdated((int)SignOn::IdentitySignedOut);
390  }
391  keepInUse();
392  return true;
393 }
394 
395 quint32 SignonIdentity::store(const QVariantMap &info)
396 {
397  keepInUse();
398  SIGNON_RETURN_IF_CAM_UNAVAILABLE(SIGNOND_NEW_IDENTITY);
399 
400  QString secret = info.value(SIGNOND_IDENTITY_INFO_SECRET).toString();
401  QString appId =
403  (static_cast<QDBusContext>(*this)).message());
404 
405  bool storeSecret = info.value(SIGNOND_IDENTITY_INFO_STORESECRET).toBool();
406  QVariant container = info.value(SIGNOND_IDENTITY_INFO_AUTHMETHODS);
407  MethodMap methods =
408  qdbus_cast<MethodMap>(container.value<QDBusArgument>());
409 
410  //Add creator to owner list if it has AID
411  QStringList ownerList =
412  info.value(SIGNOND_IDENTITY_INFO_OWNER).toStringList();
413  if (!appId.isNull())
414  ownerList.append(appId);
415 
416  if (m_pInfo == 0) {
417  m_pInfo = new SignonIdentityInfo(info);
418  m_pInfo->setMethods(methods);
419  m_pInfo->setOwnerList(ownerList);
420  } else {
421  QString userName =
422  info.value(SIGNOND_IDENTITY_INFO_USERNAME).toString();
423  QString caption =
424  info.value(SIGNOND_IDENTITY_INFO_CAPTION).toString();
425  QStringList realms =
426  info.value(SIGNOND_IDENTITY_INFO_REALMS).toStringList();
427  QStringList accessControlList =
428  info.value(SIGNOND_IDENTITY_INFO_ACL).toStringList();
429  int type = info.value(SIGNOND_IDENTITY_INFO_TYPE).toInt();
430 
431  m_pInfo->setUserName(userName);
432  m_pInfo->setCaption(caption);
433  m_pInfo->setMethods(methods);
434  m_pInfo->setRealms(realms);
435  m_pInfo->setAccessControlList(accessControlList);
436  m_pInfo->setOwnerList(ownerList);
437  m_pInfo->setType(type);
438  }
439 
440  if (storeSecret) {
441  m_pInfo->setPassword(secret);
442  } else {
443  m_pInfo->setPassword(QString());
444  }
445  m_id = storeCredentials(*m_pInfo, storeSecret);
446 
447  if (m_id == SIGNOND_NEW_IDENTITY) {
448  sendErrorReply(SIGNOND_STORE_FAILED_ERR_NAME,
449  SIGNOND_STORE_FAILED_ERR_STR);
450  }
451 
452  return m_id;
453 }
454 
456  bool storeSecret)
457 {
459  if (db == NULL) {
460  BLAME() << "NULL database handler object.";
461  return SIGNOND_NEW_IDENTITY;
462  }
463 
464  bool newIdentity = info.isNew();
465 
466  if (newIdentity)
467  m_id = db->insertCredentials(info, storeSecret);
468  else
469  db->updateCredentials(info, storeSecret);
470 
471  if (db->errorOccurred()) {
472  if (newIdentity)
473  m_id = SIGNOND_NEW_IDENTITY;
474 
475  TRACE() << "Error occurred while inserting/updating credentials.";
476  } else {
477  if (m_pInfo) {
478  delete m_pInfo;
479  m_pInfo = NULL;
480  }
481  m_pSignonDaemon->identityStored(this);
482 
483  //If secrets db is not available cache auth. data.
484  if (!db->isSecretsDBOpen()) {
485  AuthCache *cache = new AuthCache;
486  cache->setUsername(info.userName());
487  cache->setPassword(info.password());
490  cache);
491  }
492  TRACE() << "FRESH, JUST STORED CREDENTIALS ID:" << m_id;
493  emit infoUpdated((int)SignOn::IdentityDataUpdated);
494  }
495  return m_id;
496 }
497 
498 void SignonIdentity::queryUiSlot(QDBusPendingCallWatcher *call)
499 {
500  TRACE();
501  setAutoDestruct(true);
502 
503  QDBusMessage errReply;
504  QDBusPendingReply<QVariantMap> reply;
505  if (call != NULL) {
506  reply = *call;
507  call->deleteLater();
508  }
509  QVariantMap resultParameters;
510  if (!reply.isError() && reply.count()) {
511  resultParameters = reply.argumentAt<0>();
512  } else {
513  errReply =
514  m_message.createErrorReply(
515  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_NAME,
516  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_STR);
517  SIGNOND_BUS.send(errReply);
518  return;
519  }
520 
521  if (!resultParameters.contains(SSOUI_KEY_ERROR)) {
522  //no reply code
523  errReply = m_message.createErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
524  SIGNOND_INTERNAL_SERVER_ERR_STR);
525  SIGNOND_BUS.send(errReply);
526  return;
527  }
528 
529  int errorCode = resultParameters.value(SSOUI_KEY_ERROR).toInt();
530  TRACE() << "error: " << errorCode;
531  if (errorCode != QUERY_ERROR_NONE) {
532  if (errorCode == QUERY_ERROR_CANCELED)
533  errReply =
534  m_message.createErrorReply(
535  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_NAME,
536  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_STR);
537  else
538  errReply =
539  m_message.createErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
540  QString(QLatin1String("signon-ui call returned error %1")).
541  arg(errorCode));
542 
543  SIGNOND_BUS.send(errReply);
544  return;
545  }
546 
547  if (resultParameters.contains(SSOUI_KEY_PASSWORD)) {
548  CredentialsDB *db =
550  if (db == NULL) {
551  BLAME() << "NULL database handler object.";
552  errReply = m_message.createErrorReply(SIGNOND_STORE_FAILED_ERR_NAME,
553  SIGNOND_STORE_FAILED_ERR_STR);
554  SIGNOND_BUS.send(errReply);
555  return;
556  }
557 
558  //store new password
559  if (m_pInfo) {
560  m_pInfo->setPassword(resultParameters[SSOUI_KEY_PASSWORD].toString());
561 
562  quint32 ret = db->updateCredentials(*m_pInfo, true);
563  delete m_pInfo;
564  m_pInfo = NULL;
565  if (ret != SIGNOND_NEW_IDENTITY) {
566  QDBusMessage dbusreply = m_message.createReply();
567  dbusreply << quint32(m_id);
568  SIGNOND_BUS.send(dbusreply);
569  return;
570  } else{
571  BLAME() << "Error during update";
572  }
573  }
574  }
575 
576  //this should not happen, return error
577  errReply = m_message.createErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
578  SIGNOND_INTERNAL_SERVER_ERR_STR);
579  SIGNOND_BUS.send(errReply);
580  return;
581 }
582 
583 void SignonIdentity::verifyUiSlot(QDBusPendingCallWatcher *call)
584 {
585  TRACE();
586  setAutoDestruct(true);
587 
588  QDBusMessage errReply;
589  QDBusPendingReply<QVariantMap> reply;
590  if (call != NULL) {
591  reply = *call;
592  call->deleteLater();
593  }
594  QVariantMap resultParameters;
595  if (!reply.isError() && reply.count()) {
596  resultParameters = reply.argumentAt<0>();
597  } else {
598  errReply =
599  m_message.createErrorReply(
600  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_NAME,
601  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_STR);
602  SIGNOND_BUS.send(errReply);
603  return;
604  }
605 
606  if (!resultParameters.contains(SSOUI_KEY_ERROR)) {
607  //no reply code
608  errReply = m_message.createErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
609  SIGNOND_INTERNAL_SERVER_ERR_STR);
610  SIGNOND_BUS.send(errReply);
611  return;
612  }
613 
614  int errorCode = resultParameters.value(SSOUI_KEY_ERROR).toInt();
615  TRACE() << "error: " << errorCode;
616  if (errorCode != QUERY_ERROR_NONE) {
617  if (errorCode == QUERY_ERROR_CANCELED)
618  errReply = m_message.createErrorReply(
619  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_NAME,
620  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_STR);
621  else if (errorCode == QUERY_ERROR_FORGOT_PASSWORD)
622  errReply = m_message.createErrorReply(
623  SIGNOND_FORGOT_PASSWORD_ERR_NAME,
624  SIGNOND_FORGOT_PASSWORD_ERR_STR);
625  else
626  errReply = m_message.createErrorReply(
627  SIGNOND_INTERNAL_SERVER_ERR_NAME,
628  QString(QLatin1String("signon-ui call "
629  "returned error %1")).
630  arg(errorCode));
631 
632  SIGNOND_BUS.send(errReply);
633  return;
634  }
635 
636  if (resultParameters.contains(SSOUI_KEY_PASSWORD)) {
637  CredentialsDB *db =
639  if (db == NULL) {
640  BLAME() << "NULL database handler object.";
641  errReply = m_message.createErrorReply(SIGNOND_STORE_FAILED_ERR_NAME,
642  SIGNOND_STORE_FAILED_ERR_STR);
643  SIGNOND_BUS.send(errReply);
644  return;
645  }
646 
647  //compare passwords
648  if (m_pInfo) {
649  bool ret =
650  m_pInfo->password() == resultParameters[SSOUI_KEY_PASSWORD].
651  toString();
652 
653  if (!ret && resultParameters.contains(SSOUI_KEY_CONFIRMCOUNT)) {
654  int count = resultParameters[SSOUI_KEY_CONFIRMCOUNT].toInt();
655  TRACE() << "retry count:" << count;
656  if (count > 0) { //retry
657  resultParameters[SSOUI_KEY_CONFIRMCOUNT] = (count-1);
658  resultParameters[SSOUI_KEY_MESSAGEID] =
659  QUERY_MESSAGE_NOT_AUTHORIZED;
660  queryUserPassword(resultParameters);
661  return;
662  } else {
663  //TODO show error note here if needed
664  }
665  }
666  delete m_pInfo;
667  m_pInfo = NULL;
668  QDBusMessage dbusreply = m_message.createReply();
669  dbusreply << ret;
670  SIGNOND_BUS.send(dbusreply);
671  return;
672  }
673  }
674  //this should not happen, return error
675  errReply = m_message.createErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
676  SIGNOND_INTERNAL_SERVER_ERR_STR);
677  SIGNOND_BUS.send(errReply);
678  return;
679 }
680 
681 } //namespace SignonDaemonNS