signon  8.42
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  m_pInfo->setPassword(secret);
441  m_pInfo->setStorePassword(storeSecret);
442  m_id = storeCredentials(*m_pInfo);
443 
444  if (m_id == SIGNOND_NEW_IDENTITY) {
445  sendErrorReply(SIGNOND_STORE_FAILED_ERR_NAME,
446  SIGNOND_STORE_FAILED_ERR_STR);
447  }
448 
449  return m_id;
450 }
451 
453 {
455  if (db == NULL) {
456  BLAME() << "NULL database handler object.";
457  return SIGNOND_NEW_IDENTITY;
458  }
459 
460  bool newIdentity = info.isNew();
461 
462  if (newIdentity)
463  m_id = db->insertCredentials(info);
464  else
465  db->updateCredentials(info);
466 
467  if (db->errorOccurred()) {
468  if (newIdentity)
469  m_id = SIGNOND_NEW_IDENTITY;
470 
471  TRACE() << "Error occurred while inserting/updating credentials.";
472  } else {
473  if (m_pInfo) {
474  delete m_pInfo;
475  m_pInfo = NULL;
476  }
477  m_pSignonDaemon->identityStored(this);
478 
479  TRACE() << "FRESH, JUST STORED CREDENTIALS ID:" << m_id;
480  emit infoUpdated((int)SignOn::IdentityDataUpdated);
481  }
482  return m_id;
483 }
484 
485 void SignonIdentity::queryUiSlot(QDBusPendingCallWatcher *call)
486 {
487  TRACE();
488  setAutoDestruct(true);
489 
490  QDBusMessage errReply;
491  QDBusPendingReply<QVariantMap> reply;
492  if (call != NULL) {
493  reply = *call;
494  call->deleteLater();
495  }
496  QVariantMap resultParameters;
497  if (!reply.isError() && reply.count()) {
498  resultParameters = reply.argumentAt<0>();
499  } else {
500  errReply =
501  m_message.createErrorReply(
502  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_NAME,
503  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_STR);
504  SIGNOND_BUS.send(errReply);
505  return;
506  }
507 
508  if (!resultParameters.contains(SSOUI_KEY_ERROR)) {
509  //no reply code
510  errReply = m_message.createErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
511  SIGNOND_INTERNAL_SERVER_ERR_STR);
512  SIGNOND_BUS.send(errReply);
513  return;
514  }
515 
516  int errorCode = resultParameters.value(SSOUI_KEY_ERROR).toInt();
517  TRACE() << "error: " << errorCode;
518  if (errorCode != QUERY_ERROR_NONE) {
519  if (errorCode == QUERY_ERROR_CANCELED)
520  errReply =
521  m_message.createErrorReply(
522  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_NAME,
523  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_STR);
524  else
525  errReply =
526  m_message.createErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
527  QString(QLatin1String("signon-ui call returned error %1")).
528  arg(errorCode));
529 
530  SIGNOND_BUS.send(errReply);
531  return;
532  }
533 
534  if (resultParameters.contains(SSOUI_KEY_PASSWORD)) {
535  CredentialsDB *db =
537  if (db == NULL) {
538  BLAME() << "NULL database handler object.";
539  errReply = m_message.createErrorReply(SIGNOND_STORE_FAILED_ERR_NAME,
540  SIGNOND_STORE_FAILED_ERR_STR);
541  SIGNOND_BUS.send(errReply);
542  return;
543  }
544 
545  //store new password
546  if (m_pInfo) {
547  m_pInfo->setPassword(resultParameters[SSOUI_KEY_PASSWORD].toString());
548 
549  quint32 ret = db->updateCredentials(*m_pInfo);
550  delete m_pInfo;
551  m_pInfo = NULL;
552  if (ret != SIGNOND_NEW_IDENTITY) {
553  QDBusMessage dbusreply = m_message.createReply();
554  dbusreply << quint32(m_id);
555  SIGNOND_BUS.send(dbusreply);
556  return;
557  } else{
558  BLAME() << "Error during update";
559  }
560  }
561  }
562 
563  //this should not happen, return error
564  errReply = m_message.createErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
565  SIGNOND_INTERNAL_SERVER_ERR_STR);
566  SIGNOND_BUS.send(errReply);
567  return;
568 }
569 
570 void SignonIdentity::verifyUiSlot(QDBusPendingCallWatcher *call)
571 {
572  TRACE();
573  setAutoDestruct(true);
574 
575  QDBusMessage errReply;
576  QDBusPendingReply<QVariantMap> reply;
577  if (call != NULL) {
578  reply = *call;
579  call->deleteLater();
580  }
581  QVariantMap resultParameters;
582  if (!reply.isError() && reply.count()) {
583  resultParameters = reply.argumentAt<0>();
584  } else {
585  errReply =
586  m_message.createErrorReply(
587  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_NAME,
588  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_STR);
589  SIGNOND_BUS.send(errReply);
590  return;
591  }
592 
593  if (!resultParameters.contains(SSOUI_KEY_ERROR)) {
594  //no reply code
595  errReply = m_message.createErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
596  SIGNOND_INTERNAL_SERVER_ERR_STR);
597  SIGNOND_BUS.send(errReply);
598  return;
599  }
600 
601  int errorCode = resultParameters.value(SSOUI_KEY_ERROR).toInt();
602  TRACE() << "error: " << errorCode;
603  if (errorCode != QUERY_ERROR_NONE) {
604  if (errorCode == QUERY_ERROR_CANCELED)
605  errReply = m_message.createErrorReply(
606  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_NAME,
607  SIGNOND_IDENTITY_OPERATION_CANCELED_ERR_STR);
608  else if (errorCode == QUERY_ERROR_FORGOT_PASSWORD)
609  errReply = m_message.createErrorReply(
610  SIGNOND_FORGOT_PASSWORD_ERR_NAME,
611  SIGNOND_FORGOT_PASSWORD_ERR_STR);
612  else
613  errReply = m_message.createErrorReply(
614  SIGNOND_INTERNAL_SERVER_ERR_NAME,
615  QString(QLatin1String("signon-ui call "
616  "returned error %1")).
617  arg(errorCode));
618 
619  SIGNOND_BUS.send(errReply);
620  return;
621  }
622 
623  if (resultParameters.contains(SSOUI_KEY_PASSWORD)) {
624  CredentialsDB *db =
626  if (db == NULL) {
627  BLAME() << "NULL database handler object.";
628  errReply = m_message.createErrorReply(SIGNOND_STORE_FAILED_ERR_NAME,
629  SIGNOND_STORE_FAILED_ERR_STR);
630  SIGNOND_BUS.send(errReply);
631  return;
632  }
633 
634  //compare passwords
635  if (m_pInfo) {
636  bool ret =
637  m_pInfo->password() == resultParameters[SSOUI_KEY_PASSWORD].
638  toString();
639 
640  if (!ret && resultParameters.contains(SSOUI_KEY_CONFIRMCOUNT)) {
641  int count = resultParameters[SSOUI_KEY_CONFIRMCOUNT].toInt();
642  TRACE() << "retry count:" << count;
643  if (count > 0) { //retry
644  resultParameters[SSOUI_KEY_CONFIRMCOUNT] = (count-1);
645  resultParameters[SSOUI_KEY_MESSAGEID] =
646  QUERY_MESSAGE_NOT_AUTHORIZED;
647  queryUserPassword(resultParameters);
648  return;
649  } else {
650  //TODO show error note here if needed
651  }
652  }
653  delete m_pInfo;
654  m_pInfo = NULL;
655  QDBusMessage dbusreply = m_message.createReply();
656  dbusreply << ret;
657  SIGNOND_BUS.send(dbusreply);
658  return;
659  }
660  }
661  //this should not happen, return error
662  errReply = m_message.createErrorReply(SIGNOND_INTERNAL_SERVER_ERR_NAME,
663  SIGNOND_INTERNAL_SERVER_ERR_STR);
664  SIGNOND_BUS.send(errReply);
665  return;
666 }
667 
668 } //namespace SignonDaemonNS