code style polishing
[accounts-sso:signon.git] / src / signond / signonsessioncore.cpp
1 /*
2  * This file is part of signon
3  *
4  * Copyright (C) 2009-2010 Nokia Corporation.
5  *
6  * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1 as published by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  */
22
23 #include "signond-common.h"
24 #include "signonauthsession.h"
25 #include "signonidentityinfo.h"
26 #include "signonidentity.h"
27 #include "signonauthsessionadaptor.h"
28 #include "signonui_interface.h"
29 #include "accesscontrolmanager.h"
30
31 #include "SignOn/uisessiondata_priv.h"
32 #include "SignOn/authpluginif.h"
33 #include "SignOn/signonerror.h"
34
35 #define MAX_IDLE_TIME SIGNOND_MAX_IDLE_TIME
36 /*
37  * the watchdog searches for idle sessions with period of half of idle timeout
38  * */
39 #define IDLE_WATCHDOG_TIMEOUT SIGNOND_MAX_IDLE_TIME * 500
40
41 #define SSO_KEY_USERNAME QLatin1String("UserName")
42 #define SSO_KEY_PASSWORD QLatin1String("Secret")
43 #define SSO_KEY_CAPTION QLatin1String("Caption")
44 #define SSO_KEY_KEEPALIVE QLatin1String("KeepAlive")
45
46 using namespace SignonDaemonNS;
47 using namespace SignOnCrypto;
48
49 /*
50  * cache of session queues, as was mentined they cannot be static
51  * */
52 QMap<QString, SignonSessionCore *> sessionsOfStoredCredentials;
53 /*
54  * List of "zero" authsessions, needed for global signout
55  * */
56 QList<SignonSessionCore *> sessionsOfNonStoredCredentials;
57 /*
58  * cache of session queues: we have to serialize of requests done for a given identity
59  * in order to prevent parallel UI sessions for the same username/password
60  * */
61 QMap<quint32, QQueue<SignonSessionCore *> > queuesOfRequestsByIdentity;
62
63 static QVariantMap filterVariantMap(const QVariantMap &other)
64 {
65     QVariantMap result;
66
67     foreach(QString key, other.keys()) {
68         if (!other.value(key).isNull() && other.value(key).isValid())
69             result.insert(key, other.value(key));
70     }
71
72     return result;
73 }
74
75 static QString sessionName(const quint32 id, const QString &method)
76 {
77    return QString::number(id) + QLatin1String("+") + method;
78 }
79
80 static pid_t pidOfContext(const QDBusConnection &connection, const QDBusMessage &message)
81 {
82     return connection.interface()->servicePid(message.service()).value();
83 }
84
85 SignonSessionCore::SignonSessionCore(quint32 id,
86                                      const QString &method,
87                                      int timeout,
88                                      SignonDaemon *parent)
89     : SignonDisposable(timeout, parent),
90       m_id(id),
91       m_method(method),
92       m_passwordUpdate(QString()),
93       m_queryCredsUiDisplayed(false),
94       m_refCount(0)
95 {
96     m_signonui = NULL;
97     m_watcher = NULL;
98
99     if (!(m_encryptor = new Encryptor))
100         qFatal("Cannot allocate memory for encryptor");
101
102     m_signonui = new SignonUiAdaptor(
103                                     SIGNON_UI_SERVICE,
104                                     SIGNON_UI_DAEMON_OBJECTPATH,
105                                     QDBusConnection::sessionBus());
106
107     connect(CredentialsAccessManager::instance(),
108             SIGNAL(credentialsSystemReady()),
109             SLOT(credentialsSystemReady()));
110
111     setAutoDestruct(false);
112
113 }
114
115 SignonSessionCore::~SignonSessionCore()
116 {
117     TRACE();
118     AuthCoreCache::instance()->authSessionDestroyed(
119         AuthCoreCache::CacheId(m_id, m_method));
120
121     if (queuesOfRequestsByIdentity.contains(m_id)) {
122         QQueue <SignonSessionCore *> queue = queuesOfRequestsByIdentity[m_id];
123         queue.removeAll(this);
124     }
125
126     delete m_plugin;
127     delete m_watcher;
128     delete m_signonui;
129     delete m_encryptor;
130
131     m_plugin = NULL;
132     m_signonui = NULL;
133     m_watcher = NULL;
134 }
135
136 SignonSessionCore *SignonSessionCore::sessionCore(const quint32 id, const QString &method, SignonDaemon *parent)
137 {
138     QString objectName;
139     QString key = sessionName(id, method);
140
141     if (id) {
142         if (sessionsOfStoredCredentials.contains(key)) {
143             return sessionsOfStoredCredentials.value(key);
144         }
145     }
146
147     SignonSessionCore *ssc = new SignonSessionCore(id, method, parent->authSessionTimeout(), parent);
148
149     if (ssc->setupPlugin() == false) {
150         TRACE() << "The resulted object is corrupted and has to be deleted";
151         delete ssc;
152         return NULL;
153     }
154
155     if (id)
156         sessionsOfStoredCredentials.insert(key, ssc);
157     else
158         sessionsOfNonStoredCredentials.append(ssc);
159
160     TRACE() << "The new session is created :" << key;
161     return ssc;
162 }
163
164 quint32 SignonSessionCore::id() const
165 {
166     TRACE();
167     keepInUse();
168     return m_id;
169 }
170
171 QString SignonSessionCore::method() const
172 {
173     TRACE();
174     keepInUse();
175     return m_method;
176 }
177
178 bool SignonSessionCore::setupPlugin()
179 {
180     m_plugin = PluginProxy::createNewPluginProxy(m_method);
181
182     if (!m_plugin) {
183         TRACE() << "Plugin of type " << m_method << " cannot be found";
184         return false;
185     }
186
187     connect(m_plugin,
188             SIGNAL(processResultReply(const QString&, const QVariantMap&)),
189             this,
190             SLOT(processResultReply(const QString&, const QVariantMap&)),
191             Qt::DirectConnection);
192
193     connect(m_plugin,
194             SIGNAL(processStore(const QString&, const QVariantMap&)),
195             this,
196             SLOT(processStore(const QString&, const QVariantMap&)),
197             Qt::DirectConnection);
198
199     connect(m_plugin,
200             SIGNAL(processUiRequest(const QString&, const QVariantMap&)),
201             this,
202             SLOT(processUiRequest(const QString&, const QVariantMap&)),
203             Qt::DirectConnection);
204
205     connect(m_plugin,
206             SIGNAL(processRefreshRequest(const QString&, const QVariantMap&)),
207             this,
208             SLOT(processRefreshRequest(const QString&, const QVariantMap&)),
209             Qt::DirectConnection);
210
211     connect(m_plugin,
212             SIGNAL(processError(const QString&, int, const QString&)),
213             this,
214             SLOT(processError(const QString&, int, const QString&)),
215             Qt::DirectConnection);
216
217     connect(m_plugin,
218             SIGNAL(stateChanged(const QString&, int, const QString&)),
219             this,
220             SLOT(stateChangedSlot(const QString&, int, const QString&)),
221             Qt::DirectConnection);
222
223     return true;
224 }
225
226 void SignonSessionCore::stopAllAuthSessions()
227 {
228     foreach (QQueue<SignonSessionCore *> queue, queuesOfRequestsByIdentity)
229         queue.clear();
230
231     queuesOfRequestsByIdentity.clear();
232
233     qDeleteAll(sessionsOfStoredCredentials);
234     sessionsOfStoredCredentials.clear();
235
236     qDeleteAll(sessionsOfNonStoredCredentials);
237     sessionsOfNonStoredCredentials.clear();
238 }
239
240 QStringList SignonSessionCore::loadedPluginMethods(const QString &method)
241 {
242     foreach (SignonSessionCore *corePtr, sessionsOfStoredCredentials) {
243         if (corePtr->method() == method)
244             return corePtr->queryAvailableMechanisms(QStringList());
245     }
246
247     foreach (SignonSessionCore *corePtr, sessionsOfNonStoredCredentials) {
248         if (corePtr->method() == method)
249             return corePtr->queryAvailableMechanisms(QStringList());
250     }
251
252     return QStringList();
253 }
254
255 QStringList SignonSessionCore::queryAvailableMechanisms(const QStringList &wantedMechanisms)
256 {
257     keepInUse();
258
259     if (!wantedMechanisms.size())
260         return m_plugin->mechanisms();
261
262     return m_plugin->mechanisms().toSet().intersect(wantedMechanisms.toSet()).toList();
263 }
264
265 void SignonSessionCore::process(const QDBusConnection &connection,
266                                  const QDBusMessage &message,
267                                  const QVariantMap &sessionDataVa,
268                                  const QString &mechanism,
269                                  const QString &cancelKey)
270 {
271     keepInUse();
272     if (m_encryptor->isVariantMapEncrypted(sessionDataVa)) {
273         pid_t pid = pidOfContext(connection, message);
274         QVariantMap decodedData(m_encryptor->
275                                 decodeVariantMap(sessionDataVa, pid));
276         if (m_encryptor->status() != Encryptor::Ok) {
277             replyError(connection,
278                        message,
279                        Error::EncryptionFailure,
280                        QString::fromLatin1("Failed to decrypt incoming message"));
281             return;
282         }
283         m_listOfRequests.enqueue(RequestData(connection,
284                                              message,
285                                              decodedData,
286                                              mechanism,
287                                              cancelKey));
288     } else {
289         m_listOfRequests.enqueue(RequestData(connection,
290                                              message,
291                                              sessionDataVa,
292                                              mechanism,
293                                              cancelKey));
294     }
295
296     addToQueueOfRequestsByIdentity(m_id, this);
297
298     if (CredentialsAccessManager::instance()->isCredentialsSystemReady()) {
299         wakeUpQueueOfRequestsByIdentity(m_id);
300     }
301 }
302
303 void SignonSessionCore::cancel(const QString &cancelKey)
304 {
305     TRACE();
306
307     int requestIndex;
308     for (requestIndex = 0; requestIndex < m_listOfRequests.size(); requestIndex++) {
309         if (m_listOfRequests.at(requestIndex).m_cancelKey == cancelKey)
310             break;
311     }
312
313     TRACE() << "The request is found with index " << requestIndex;
314     bool requestIsInProcessing = false;
315
316     if (requestIndex < m_listOfRequests.size()) {
317         if (requestIndex == 0) {
318             if (queuesOfRequestsByIdentity.contains(m_id)) {
319                 QQueue<SignonSessionCore *> queue = queuesOfRequestsByIdentity[m_id];
320
321                 if (queue.head() == this)
322                     requestIsInProcessing = true;
323                 else {
324                     queue.removeOne(this);
325                 }
326             }
327         }
328
329         if (requestIsInProcessing) {
330
331             m_canceled = cancelKey;
332             m_plugin->cancel();
333
334             if (m_watcher && !m_watcher->isFinished()) {
335                 m_signonui->cancelUiRequest(cancelKey);
336                 delete m_watcher;
337                 m_watcher = 0;
338             }
339         }
340
341         /*
342         * We must let to the m_listOfRequests to have the canceled request data
343         * in order to delay the next request execution until the actual cancelation
344         * will happen. We will know about that precisely: plugin must reply via
345         * resultSlot or via errorSlot.
346         * */
347         RequestData rd((requestIsInProcessing ?
348                        m_listOfRequests.head() :
349                        m_listOfRequests.takeAt(requestIndex)));
350         QDBusMessage errReply = rd.m_msg.createErrorReply(SIGNOND_SESSION_CANCELED_ERR_NAME,
351                                                          SIGNOND_SESSION_CANCELED_ERR_STR);
352         rd.m_conn.send(errReply);
353         TRACE() << "Size of the queue is " << m_listOfRequests.size();
354     }
355 }
356
357 void SignonSessionCore::setId(quint32 id)
358 {
359     keepInUse();
360
361     if (m_id == id)
362         return;
363
364     QString key;
365
366     if (id == 0) {
367         key = sessionName(m_id, m_method);
368         sessionsOfNonStoredCredentials.append(sessionsOfStoredCredentials.take(key));
369     } else {
370         key = sessionName(id, m_method);
371         if (sessionsOfStoredCredentials.contains(key)) {
372             qCritical() << "attempt to assign existing id";
373             return;
374         }
375
376         sessionsOfNonStoredCredentials.removeOne(this);
377         sessionsOfStoredCredentials[key] = this;
378     }
379     m_id = id;
380 }
381
382 void SignonSessionCore::startProcess()
383 {
384     TRACE() << "the number of requests is : " << m_listOfRequests.length();
385
386     if (m_listOfRequests.length() == 0) {
387         BLAME() << "problems in data queueing: no request to be processed!!!!";
388         return;
389     }
390
391     keepInUse();
392
393     RequestData data = m_listOfRequests.head();
394     QVariantMap parameters = data.m_params;
395
396     if (data.m_cancelKey == m_canceled) {
397
398         //no need to send cancel error as it is sent already
399         m_listOfRequests.removeFirst();
400         removeFromQueueOfRequestsByIdentity(m_id, this);
401
402         m_canceled = QString();
403         return;
404     }
405
406     if (m_id) {
407         CredentialsDB *db = CredentialsAccessManager::instance()->credentialsDB();
408         Q_ASSERT(db != 0);
409
410         SignonIdentityInfo info = db->credentials(m_id);
411         if (info.id() != SIGNOND_NEW_IDENTITY) {
412
413             if (!parameters.contains(SSO_KEY_PASSWORD)) {
414                 //If secrets db not available attempt loading data from cache
415                 if (db->isSecretsDBOpen()) {
416                     parameters[SSO_KEY_PASSWORD] = info.password();
417                 }
418
419                 /* Temporary fix - keep it until session core refactoring is complete and auth cache
420                  * will be dumped in the secrets db. */
421                 if (parameters[SSO_KEY_PASSWORD].toString().isEmpty()) {
422                     AuthCache *cache = AuthCoreCache::instance()->data(info.id());
423                     if (cache != 0) {
424                         TRACE() << "Using cached secret.";
425                         parameters[SSO_KEY_PASSWORD] = cache->password();
426                     } else {
427                         TRACE() << "Secrets storage not available and authentication "
428                                    "cache is empty - if SSO requires a password, "
429                                    "auth. will fail.";
430                     }
431                 }
432             }
433             //database overrules over sessiondata for validated username,
434             //so that identity cannot be misused
435             if (info.validated() || !parameters.contains(SSO_KEY_USERNAME)) {
436                 parameters[SSO_KEY_USERNAME] = info.userName();
437             }
438
439             pid_t pid = pidOfContext(data.m_conn, data.m_msg);
440             QSet<QString> clientTokenSet = AccessControlManager::accessTokens(pid).toSet();
441             QSet<QString> identityAclTokenSet = info.accessControlList().toSet();
442             QSet<QString> paramsTokenSet = clientTokenSet.intersect(identityAclTokenSet);
443
444             if (!paramsTokenSet.isEmpty()) {
445                 QStringList tokenList = paramsTokenSet.toList();
446                 parameters[SSO_ACCESS_CONTROL_TOKENS] = tokenList;
447             }
448         } else {
449             BLAME() << "Error occurred while getting data from credentials database.";
450         }
451
452         QVariantMap storedParams;
453         if (db->isSecretsDBOpen()) {
454             storedParams = db->loadData(m_id, m_method);
455         }
456         /* Temporary fix - keep it until session core refactoring is complete and auth cache
457          * will be dumped in the secrets db. */
458         if (storedParams.isEmpty()) {
459             AuthCache *cache = AuthCoreCache::instance()->data(info.id());
460             if (cache != 0) {
461                 TRACE() << "Using cached BLOB data.";
462                 storedParams = cache->blobData();
463             }
464         }
465
466         //parameters will overwrite any common keys on stored params
467         parameters = mergeVariantMaps(storedParams, parameters);
468     }
469
470     if (parameters.contains(SSOUI_KEY_UIPOLICY)
471         && parameters[SSOUI_KEY_UIPOLICY] == RequestPasswordPolicy) {
472
473         TRACE() << "The request contains demand to show sign-in dialog no matter of what: removing password field from parameters";
474         parameters.remove(SSO_KEY_PASSWORD);
475     }
476
477     /* Temporary caching, if credentials are valid
478      * this data will be effectively cached */
479     m_tmpUsername = parameters[SSO_KEY_USERNAME].toString();
480     m_tmpPassword = parameters[SSO_KEY_PASSWORD].toString();
481
482     if (parameters.contains(SSO_KEY_KEEPALIVE)) {
483         setAutoDestruct(!parameters[SSO_KEY_KEEPALIVE].toBool());
484     }
485
486     if (!m_plugin->process(data.m_cancelKey, parameters, data.m_mechanism)) {
487         QDBusMessage errReply = data.m_msg.createErrorReply(SIGNOND_RUNTIME_ERR_NAME,
488                                                             SIGNOND_RUNTIME_ERR_STR);
489         data.m_conn.send(errReply);
490         m_listOfRequests.removeFirst();
491
492         removeFromQueueOfRequestsByIdentity(m_id, this);
493     } else
494         stateChangedSlot(data.m_cancelKey, SignOn::SessionStarted, QLatin1String("The request is started successfully"));
495 }
496
497 void SignonSessionCore::replyError(const QDBusConnection &conn, const QDBusMessage &msg, int err, const QString &message)
498 {
499     keepInUse();
500
501     QString errName;
502     QString errMessage;
503
504     if (err == Error::EncryptionFailure) {
505         errName = SIGNOND_ENCRYPTION_FAILED_ERR_NAME;
506         errMessage = SIGNOND_ENCRYPTION_FAILED_ERR_STR;
507     } else if(err < Error::AuthSessionErr) {
508         BLAME() << "Deprecated error code: " << err;
509         if (message.isEmpty())
510             errMessage = SIGNOND_UNKNOWN_ERR_STR;
511         else
512             errMessage = message;
513         errName = SIGNOND_UNKNOWN_ERR_NAME;
514     } else if (err < Error::UserErr) {
515         switch(err) {
516             case Error::MechanismNotAvailable:
517                 errName = SIGNOND_MECHANISM_NOT_AVAILABLE_ERR_NAME;
518                 errMessage = SIGNOND_MECHANISM_NOT_AVAILABLE_ERR_STR;
519                 break;
520             case Error::MissingData:
521                 errName = SIGNOND_MISSING_DATA_ERR_NAME;
522                 errMessage = SIGNOND_MISSING_DATA_ERR_STR;
523                 break;
524             case Error::InvalidCredentials:
525                 errName = SIGNOND_INVALID_CREDENTIALS_ERR_NAME;
526                 errMessage = SIGNOND_INVALID_CREDENTIALS_ERR_STR;
527                 break;
528             case Error::NotAuthorized:
529                 errName = SIGNOND_NOT_AUTHORIZED_ERR_NAME;
530                 errMessage = SIGNOND_NOT_AUTHORIZED_ERR_STR;
531                 break;
532             case Error::WrongState:
533                 errName = SIGNOND_WRONG_STATE_ERR_NAME;
534                 errMessage = SIGNOND_WRONG_STATE_ERR_STR;
535                 break;
536             case Error::OperationNotSupported:
537                 errName = SIGNOND_OPERATION_NOT_SUPPORTED_ERR_NAME;
538                 errMessage = SIGNOND_OPERATION_NOT_SUPPORTED_ERR_STR;
539                 break;
540             case Error::NoConnection:
541                 errName = SIGNOND_NO_CONNECTION_ERR_NAME;
542                 errMessage = SIGNOND_NO_CONNECTION_ERR_STR;
543                 break;
544             case Error::Network:
545                 errName = SIGNOND_NETWORK_ERR_NAME;
546                 errMessage = SIGNOND_NETWORK_ERR_STR;
547                 break;
548             case Error::Ssl:
549                 errName = SIGNOND_SSL_ERR_NAME;
550                 errMessage = SIGNOND_SSL_ERR_STR;
551                 break;
552             case Error::Runtime:
553                 errName = SIGNOND_RUNTIME_ERR_NAME;
554                 errMessage = SIGNOND_RUNTIME_ERR_STR;
555                 break;
556             case Error::SessionCanceled:
557                 errName = SIGNOND_SESSION_CANCELED_ERR_NAME;
558                 errMessage = SIGNOND_SESSION_CANCELED_ERR_STR;
559                 break;
560             case Error::TimedOut:
561                 errName = SIGNOND_TIMED_OUT_ERR_NAME;
562                 errMessage = SIGNOND_TIMED_OUT_ERR_STR;
563                 break;
564             case Error::UserInteraction:
565                 errName = SIGNOND_USER_INTERACTION_ERR_NAME;
566                 errMessage = SIGNOND_USER_INTERACTION_ERR_STR;
567                 break;
568             case Error::OperationFailed:
569                 errName = SIGNOND_OPERATION_FAILED_ERR_NAME;
570                 errMessage = SIGNOND_OPERATION_FAILED_ERR_STR;
571                 break;
572             case Error::EncryptionFailed:
573                 errName = SIGNOND_ENCRYPTION_FAILED_ERR_NAME;
574                 errMessage = SIGNOND_ENCRYPTION_FAILED_ERR_STR;
575                 break;
576             case Error::TOSNotAccepted:
577                 errName = SIGNOND_TOS_NOT_ACCEPTED_ERR_NAME;
578                 errMessage = SIGNOND_TOS_NOT_ACCEPTED_ERR_STR;
579                 break;
580             case Error::ForgotPassword:
581                 errName = SIGNOND_FORGOT_PASSWORD_ERR_NAME;
582                 errMessage = SIGNOND_FORGOT_PASSWORD_ERR_STR;
583                 break;
584             case Error::IncorrectDate:
585                 errName = SIGNOND_INCORRECT_DATE_ERR_NAME;
586                 errMessage = SIGNOND_INCORRECT_DATE_ERR_STR;
587                 break;
588             default:
589                 if (message.isEmpty())
590                     errMessage = SIGNOND_UNKNOWN_ERR_STR;
591                 else
592                     errMessage = message;
593                 errName = SIGNOND_UNKNOWN_ERR_NAME;
594                 break;
595         };
596     } else {
597         errName = SIGNOND_USER_ERROR_ERR_NAME;
598         errMessage = (QString::fromLatin1("%1:%2")).arg(err).arg(message);
599     }
600
601     QDBusMessage errReply;
602     errReply = msg.createErrorReply(errName, ( message.isEmpty() ? errMessage : message ));
603     conn.send(errReply);
604 }
605
606 void SignonSessionCore::processStoreOperation(const StoreOperation &operation)
607 {
608     TRACE() << "Processing store operation.";
609     CredentialsDB *db = CredentialsAccessManager::instance()->credentialsDB();
610     Q_ASSERT(db != 0);
611
612     if (operation.m_storeType != StoreOperation::Blob) {
613
614         SignonIdentityInfo info = operation.m_info;
615
616         QVariantMap data = operation.m_credsData;
617
618         //allow update only for not validated username
619         if (!info.validated()
620                 && data.contains(SSO_KEY_USERNAME)
621                 && !data[SSO_KEY_USERNAME].toString().isEmpty())
622             info.setUserName(data[SSO_KEY_USERNAME].toString());
623
624         if (!m_passwordUpdate.isEmpty())
625             info.setPassword(m_passwordUpdate);
626
627         if (data.contains(SSO_KEY_PASSWORD)
628             && !data[SSO_KEY_PASSWORD].toString().isEmpty())
629             info.setPassword(data[SSO_KEY_PASSWORD].toString());
630
631         info.setValidated(true);
632
633         if (!(db->updateCredentials(info))) {
634             BLAME() << "Error occured while updating credentials.";
635         }
636
637     } else {
638         TRACE() << "Processing --- StoreOperation::Blob";
639
640         if(!db->storeData(m_id,
641                           operation.m_authMethod,
642                           operation.m_blobData)) {
643             BLAME() << "Error occured while storing data.";
644         }
645     }
646 }
647
648 void SignonSessionCore::processResultReply(const QString &cancelKey, const QVariantMap &data)
649 {
650     TRACE();
651
652     keepInUse();
653
654     if (!m_listOfRequests.size())
655         return;
656
657     RequestData rd = m_listOfRequests.dequeue();
658
659     if (cancelKey != m_canceled) {
660         QVariantList arguments;
661         QVariantMap filteredData = filterVariantMap(data);
662
663         CredentialsAccessManager *camManager = CredentialsAccessManager::instance();
664         CredentialsDB *db = camManager->credentialsDB();
665         Q_ASSERT(db != 0);
666
667         //put temporary password from ui interaction into result if plugin didn't return new password
668         if (!filteredData.contains(SSO_KEY_PASSWORD) && !m_tmpPassword.isEmpty()) {
669             filteredData[SSO_KEY_PASSWORD] = m_tmpPassword;
670             m_tmpPassword.clear();
671         }
672
673         //update database entry
674         if (m_id != SIGNOND_NEW_IDENTITY) {
675             SignonIdentityInfo info = db->credentials(m_id);
676
677             StoreOperation storeOp(StoreOperation::Credentials);
678             storeOp.m_credsData = filteredData;
679             storeOp.m_info = info;
680
681             /* If the credentials are validated, the secrets db is not available and
682              * not authorized keys are available inform the CAM about the situation. */
683             if (info.validated() && !db->isSecretsDBOpen()) {
684                 /* Send the storage not available event only if the curent result
685                  * processing is following a previous signon UI query. This is
686                  * to avoid unexpected UI pop-ups.
687                  */
688                 if (m_queryCredsUiDisplayed) {
689                     m_storeQueue.enqueue(storeOp);
690
691                     SecureStorageEvent *event =
692                         new SecureStorageEvent(
693                             (QEvent::Type)SIGNON_SECURE_STORAGE_NOT_AVAILABLE);
694
695                     event->m_sender = static_cast<QObject *>(this);
696
697                     QCoreApplication::postEvent(
698                         CredentialsAccessManager::instance(),
699                         event,
700                         Qt::HighEventPriority);
701                 }
702             } else {
703                 processStoreOperation(storeOp);
704             }
705         }
706
707         /* If secrets db not available cache credentials for this session core.
708          * Avoid creating an invalid caching record - cache only if the password
709          * is not empty. */
710         if (!db->isSecretsDBOpen() && !m_tmpPassword.isEmpty()) {
711             AuthCache *cache = new AuthCache;
712             cache->setUsername(m_tmpUsername);
713             cache->setPassword(m_tmpPassword);
714             m_tmpUsername.clear();
715             m_tmpPassword.clear();
716             AuthCoreCache::instance()->insert(
717                 AuthCoreCache::CacheId(m_id, m_method), cache);
718         }
719
720         //remove secret field from output
721         if (m_method != QLatin1String("password")
722             && filteredData.contains(SSO_KEY_PASSWORD))
723             filteredData.remove(SSO_KEY_PASSWORD);
724
725         pid_t pid = pidOfContext(rd.m_conn, rd.m_msg);
726         QVariantMap encodedData(m_encryptor->
727                                 encodeVariantMap(filteredData, pid));
728         if (m_encryptor->status() != Encryptor::Ok) {
729             replyError(rd.m_conn,
730                        rd.m_msg,
731                        Error::EncryptionFailure,
732                        QString::fromLatin1("Failed to encrypt outgoing message"));
733         } else {
734             encodedData = filterVariantMap(encodedData);
735             arguments << encodedData;
736             rd.m_conn.send(rd.m_msg.createReply(arguments));
737         }
738
739         m_canceled = QString();
740
741         if (m_watcher && !m_watcher->isFinished()) {
742             m_signonui->cancelUiRequest(rd.m_cancelKey);
743             delete m_watcher;
744             m_watcher = 0;
745         }
746         m_queryCredsUiDisplayed = false;
747     }
748     m_canceled = QString();
749     removeFromQueueOfRequestsByIdentity(m_id, this);
750 }
751
752 void SignonSessionCore::processStore(const QString &cancelKey, const QVariantMap &data)
753 {
754     Q_UNUSED(cancelKey);
755     TRACE();
756
757     keepInUse();
758     m_passwordUpdate.clear();
759     if (m_id == SIGNOND_NEW_IDENTITY) {
760         BLAME() << "Cannot store without identity";
761         return;
762     }
763     QVariantMap filteredData = data;
764     //do not store username or password
765     filteredData.remove(SSO_KEY_PASSWORD);
766     filteredData.remove(SSO_KEY_USERNAME);
767     filteredData.remove(SSO_ACCESS_CONTROL_TOKENS);
768
769     //store data into db
770     CredentialsDB *db = CredentialsAccessManager::instance()->credentialsDB();
771     Q_ASSERT(db != NULL);
772
773     StoreOperation storeOp(StoreOperation::Blob);
774     storeOp.m_blobData = filteredData;
775     storeOp.m_authMethod = m_method;
776
777     /* If the credentials are validated, the secrets db is not available and
778      * not authorized keys are available inform the CAM about the situation. */
779     SignonIdentityInfo info = db->credentials(m_id);
780     if (info.validated() && !db->isSecretsDBOpen()) {
781         /* Send the storage not available event only if the curent store
782          * processing is following a previous signon UI query. This is to avoid
783          * unexpected UI pop-ups.
784          */
785         if (m_queryCredsUiDisplayed) {
786             TRACE() << "Secure storage not available. Queueing store operations.";
787             m_storeQueue.enqueue(storeOp);
788
789             SecureStorageEvent *event =
790                 new SecureStorageEvent(
791                     (QEvent::Type)SIGNON_SECURE_STORAGE_NOT_AVAILABLE);
792             event->m_sender = static_cast<QObject *>(this);
793
794             QCoreApplication::postEvent(
795                 CredentialsAccessManager::instance(),
796                 event,
797                 Qt::HighEventPriority);
798         }
799     } else {
800         processStoreOperation(storeOp);
801     }
802
803     /* If secrets db not available cache credentials for this session core.
804      * Avoid creating an invalid caching record - cache only if the BLOB data
805      * is not empty. */
806     if (!db->isSecretsDBOpen() && !data.isEmpty()) {
807         TRACE() << "Caching BLOB authentication data.";
808         AuthCache *cache = new AuthCache;
809         cache->setBlobData(data);
810         AuthCoreCache::instance()->insert(
811             AuthCoreCache::CacheId(m_id, m_method), cache);
812     }
813     m_queryCredsUiDisplayed = false;
814
815     return;
816 }
817
818 void SignonSessionCore::processUiRequest(const QString &cancelKey, const QVariantMap &data)
819 {
820     TRACE();
821
822     keepInUse();
823
824     if (cancelKey != m_canceled && m_listOfRequests.size()) {
825         QString uiRequestId = m_listOfRequests.head().m_cancelKey;
826
827         if (m_watcher) {
828             if (!m_watcher->isFinished())
829                 m_signonui->cancelUiRequest(uiRequestId);
830
831             delete m_watcher;
832             m_watcher = 0;
833         }
834
835         m_listOfRequests.head().m_params = filterVariantMap(data);
836         m_listOfRequests.head().m_params[SSOUI_KEY_REQUESTID] = uiRequestId;
837
838         if (m_id == SIGNOND_NEW_IDENTITY)
839             m_listOfRequests.head().m_params[SSOUI_KEY_STORED_IDENTITY] = false;
840         else
841             m_listOfRequests.head().m_params[SSOUI_KEY_STORED_IDENTITY] = true;
842
843         CredentialsAccessManager *camManager = CredentialsAccessManager::instance();
844         CredentialsDB *db = camManager->credentialsDB();
845         Q_ASSERT(db != 0);
846
847         //check that we have caption
848         if (!data.contains(SSO_KEY_CAPTION)) {
849             TRACE() << "Caption missing";
850             if (m_id != SIGNOND_NEW_IDENTITY) {
851                 SignonIdentityInfo info = db->credentials(m_id);
852                 m_listOfRequests.head().m_params.insert(SSO_KEY_CAPTION, info.caption());
853                 TRACE() << "Got caption: " << info.caption();
854             }
855         }
856
857         /*
858          * Check the secure storage status, if any issues are encountered signal
859          * this to the signon ui. */
860         if (!db->isSecretsDBOpen()) {
861             TRACE();
862
863             //If there are no keys available
864             if (!camManager->keysAvailable()) {
865                 TRACE() << "Secrets DB not available."
866                         << "CAM has no keys available. Informing signon-ui.";
867                 m_listOfRequests.head().m_params[
868                     SSOUI_KEY_STORAGE_KEYS_UNAVAILABLE] = true;
869             }
870         }
871
872         m_watcher = new QDBusPendingCallWatcher(m_signonui->queryDialog(m_listOfRequests.head().m_params),
873                                                 this);
874         connect(m_watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(queryUiSlot(QDBusPendingCallWatcher*)));
875     }
876 }
877
878 void SignonSessionCore::processRefreshRequest(const QString &cancelKey, const QVariantMap &data)
879 {
880     TRACE();
881
882     keepInUse();
883
884     if (cancelKey != m_canceled && m_listOfRequests.size()) {
885         QString uiRequestId = m_listOfRequests.head().m_cancelKey;
886
887         if (m_watcher) {
888             if (!m_watcher->isFinished())
889                 m_signonui->cancelUiRequest(uiRequestId);
890
891             delete m_watcher;
892             m_watcher = 0;
893         }
894
895         m_listOfRequests.head().m_params = filterVariantMap(data);
896         m_watcher = new QDBusPendingCallWatcher(m_signonui->refreshDialog(m_listOfRequests.head().m_params),
897                                                 this);
898         connect(m_watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(queryUiSlot(QDBusPendingCallWatcher*)));
899     }
900 }
901
902 void SignonSessionCore::processError(const QString &cancelKey, int err, const QString &message)
903 {
904     TRACE();
905     keepInUse();
906     m_tmpUsername.clear();
907     m_tmpPassword.clear();
908
909     if (!m_listOfRequests.size())
910         return;
911
912     RequestData rd = m_listOfRequests.dequeue();
913
914
915     if (cancelKey != m_canceled) {
916         replyError(rd.m_conn, rd.m_msg, err, message);
917
918         if (m_watcher && !m_watcher->isFinished()) {
919             m_signonui->cancelUiRequest(rd.m_cancelKey);
920             delete m_watcher;
921             m_watcher = 0;
922         }
923     }
924
925     m_canceled = QString();
926     removeFromQueueOfRequestsByIdentity(m_id, this);
927 }
928
929 void SignonSessionCore::stateChangedSlot(const QString &cancelKey, int state, const QString &message)
930 {
931     if (cancelKey != m_canceled && m_listOfRequests.size()) {
932         RequestData rd = m_listOfRequests.head();
933         emit stateChanged(rd.m_cancelKey, (int)state, message);
934     }
935
936     keepInUse();
937 }
938
939 void SignonSessionCore::childEvent(QChildEvent *ce)
940 {
941     if (ce->added())
942         keepInUse();
943     else if (ce->removed())
944         SignonDisposable::destroyUnused();
945 }
946
947 void SignonSessionCore::customEvent(QEvent *event)
948 {
949     TRACE() << "Custom event received.";
950     if (event->type() == SIGNON_SECURE_STORAGE_AVAILABLE) {
951         TRACE() << "Secure storage is available.";
952         AuthCoreCache::instance()->clear();
953
954         TRACE() << "Processing queued stored operations.";
955         while (!m_storeQueue.empty()) {
956             processStoreOperation(m_storeQueue.dequeue());
957         }
958     } else if (event->type() == SIGNON_SECURE_STORAGE_NOT_AVAILABLE) {
959         TRACE() << "Secure storage still not available. "
960                    "Clearing storage operation queue.";
961         m_storeQueue.clear();
962     }
963
964     QObject::customEvent(event);
965 }
966
967 void SignonSessionCore::queryUiSlot(QDBusPendingCallWatcher *call)
968 {
969     keepInUse();
970
971     QDBusPendingReply<QVariantMap> reply = *call;
972     bool isRequestToRefresh = false;
973     Q_ASSERT_X( m_listOfRequests.size() != 0, __func__, "queue of requests is empty");
974
975     if (!reply.isError() && reply.count()) {
976         QVariantMap resultParameters = reply.argumentAt<0>();
977         if (resultParameters.contains(SSOUI_KEY_REFRESH)) {
978             isRequestToRefresh = true;
979             resultParameters.remove(SSOUI_KEY_REFRESH);
980         }
981
982         m_listOfRequests.head().m_params = resultParameters;
983
984         /* If the query ui was canceled or any other error occurred
985          * do not set this flag to true. */
986         if (resultParameters.contains(SSOUI_KEY_ERROR)
987             && (resultParameters[SSOUI_KEY_ERROR] == QUERY_ERROR_CANCELED)) {
988
989             m_queryCredsUiDisplayed = false;
990         } else {
991             m_queryCredsUiDisplayed = true;
992         }
993     } else {
994         m_listOfRequests.head().m_params.insert(SSOUI_KEY_ERROR, (int)SignOn::QUERY_ERROR_NO_SIGNONUI);
995     }
996
997     if (m_listOfRequests.head().m_cancelKey != m_canceled) {
998         /* Temporary caching, if credentials are valid
999          * this data will be effectively cached */
1000         m_tmpUsername = m_listOfRequests.head().m_params.value(
1001             SSO_KEY_USERNAME, QVariant()).toString();
1002         m_tmpPassword = m_listOfRequests.head().m_params.value(
1003             SSO_KEY_PASSWORD, QVariant()).toString();
1004
1005         if (isRequestToRefresh) {
1006             TRACE() << "REFRESH IS REQUIRED";
1007
1008             m_listOfRequests.head().m_params.remove(SSOUI_KEY_REFRESH);
1009             m_plugin->processRefresh(m_listOfRequests.head().m_cancelKey,
1010                                      m_listOfRequests.head().m_params);
1011         } else {
1012             if (m_listOfRequests.head().m_params.contains(SSO_KEY_PASSWORD))
1013                 m_passwordUpdate = m_listOfRequests.head().m_params[SSO_KEY_PASSWORD].toString();
1014             m_plugin->processUi(m_listOfRequests.head().m_cancelKey,
1015                                 m_listOfRequests.head().m_params);
1016         }
1017     }
1018
1019     delete m_watcher;
1020     m_watcher = NULL;
1021 }
1022
1023 void SignonSessionCore::addToQueueOfRequestsByIdentity(quint32 id, SignonSessionCore *core) {
1024     TRACE();
1025
1026     if (!queuesOfRequestsByIdentity.contains(id)) {
1027         QQueue<SignonSessionCore *> queue;
1028         queuesOfRequestsByIdentity.insert(id, queue);
1029     }
1030
1031     queuesOfRequestsByIdentity[id].enqueue(core);
1032 }
1033
1034 void SignonSessionCore::removeFromQueueOfRequestsByIdentity(quint32 id, SignonSessionCore *core)
1035 {
1036     TRACE() << id;
1037     if (!queuesOfRequestsByIdentity.contains(id))
1038         return;
1039
1040     TRACE();
1041     QQueue<SignonSessionCore *> queue = queuesOfRequestsByIdentity[id];
1042     if (queue.isEmpty())
1043         return;
1044
1045     if (queue.head() != core)
1046         return;
1047
1048     queuesOfRequestsByIdentity[id].removeOne(core);
1049     wakeUpQueueOfRequestsByIdentity(id);
1050 }
1051
1052 void SignonSessionCore::wakeUpQueueOfRequestsByIdentity(quint32 id) {
1053     TRACE() << id;
1054
1055     if (!queuesOfRequestsByIdentity.contains(id))
1056         return;
1057
1058     QQueue<SignonSessionCore *> queue = queuesOfRequestsByIdentity[id];
1059
1060     if (queue.isEmpty())
1061         return;
1062
1063     TRACE() << queue.size();
1064
1065     SignonSessionCore *core = queue.head();
1066     if (!core)
1067         return;
1068
1069     QMetaObject::invokeMethod(core, "startNewRequest", Qt::QueuedConnection);
1070 }
1071
1072 void SignonSessionCore::startNewRequest()
1073 {
1074     TRACE();
1075
1076     keepInUse();
1077     bool processShouldBeStarted = true;
1078
1079     if (!m_listOfRequests.length()) {
1080         TRACE() << "the data queue is EMPTY!!!";
1081         processShouldBeStarted = false;
1082     } else if (m_plugin && m_plugin->isProcessing()) {
1083         TRACE() << " the plugin is in challenge processing";
1084         processShouldBeStarted = false;
1085     } else if (m_watcher && !m_watcher->isFinished()) {
1086         TRACE() << "watcher is in running mode";
1087         processShouldBeStarted = false;
1088     }
1089
1090     TRACE() << "Start the authentication process: " << processShouldBeStarted;
1091
1092     if (processShouldBeStarted)
1093         startProcess();
1094 }
1095
1096 void SignonSessionCore::destroy()
1097 {
1098     if (m_plugin->isProcessing() ||
1099         m_watcher != NULL) {
1100         keepInUse();
1101         return;
1102     }
1103
1104     if (m_id)
1105         sessionsOfStoredCredentials.remove(sessionName(m_id, m_method));
1106     else
1107         sessionsOfNonStoredCredentials.removeOne(this);
1108
1109     emit destroyed();
1110     deleteLater();
1111 }
1112
1113 void SignonSessionCore::addRef()
1114 {
1115     TRACE() << "refcount:" << m_refCount;
1116     m_refCount++;
1117     setAutoDestruct(false);
1118 }
1119
1120 void SignonSessionCore::removeRef()
1121 {
1122     TRACE() << "refcount:" << m_refCount;
1123     Q_ASSERT(m_refCount <= 0);
1124     m_refCount--;
1125     if (m_refCount == 0) {
1126         TRACE();
1127         setAutoDestruct(true);
1128     }
1129 }
1130
1131 void SignonSessionCore::credentialsSystemReady()
1132 {
1133     SignonSessionCore::wakeUpQueueOfRequestsByIdentity(m_id);
1134 }