Restructured signon database and added unit tests
[accounts-sso:signon.git] / src / signond / signonidentity.cpp
1 /*
2  * This file is part of signon
3  *
4  * Copyright (C) 2009-2010 Nokia Corporation.
5  *
6  * Contact: Aurel Popirtac <ext-aurel.popirtac@nokia.com>
7  * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * version 2.1 as published by the Free Software Foundation.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  */
23
24 #include <iostream>
25
26 #include "signond-common.h"
27 #include "signonidentity.h"
28 #include "signonui_interface.h"
29
30 #include "accesscontrolmanager.h"
31 #include "signonidentityadaptor.h"
32
33
34 #define SIGNON_RETURN_IF_CAM_UNAVAILABLE(_ret_arg_) do {                          \
35         if (!(CredentialsAccessManager::instance()->credentialsSystemOpened())) { \
36             QDBusMessage errReply = message().createErrorReply(                   \
37                     internalServerErrName,                                        \
38                     internalServerErrStr + QLatin1String("Could not access Signon Database.")); \
39             SIGNOND_BUS.send(errReply); \
40             return _ret_arg_;           \
41         }                               \
42     } while(0)
43
44 namespace SignonDaemonNS {
45
46     const QString internalServerErrName = SIGNOND_INTERNAL_SERVER_ERR_NAME;
47     const QString internalServerErrStr = SIGNOND_INTERNAL_SERVER_ERR_STR;
48
49     SignonIdentity::SignonIdentity(quint32 id, int timeout,
50                                    SignonDaemon *parent)
51             : SignonDisposable(timeout, parent),
52               m_pInfo(NULL),
53               m_pSignonDaemon(parent),
54               m_registered(false)
55     {
56         m_id = id;
57
58         /*
59          * creation of unique name for the given identity
60          * */
61         static quint32 incr = 0;
62         QString objectName = SIGNOND_DAEMON_OBJECTPATH + QLatin1String("/Identity_")
63                              + QString::number(incr++, 16);
64         setObjectName(objectName);
65
66         m_signonui = new SignonUiAdaptor(
67                                         SIGNON_UI_SERVICE,
68                                         SIGNON_UI_DAEMON_OBJECTPATH,
69                                         SIGNOND_BUS,
70                                         this);
71     }
72
73     SignonIdentity::~SignonIdentity()
74     {
75         if (m_registered)
76         {
77             emit unregistered();
78             QDBusConnection connection = SIGNOND_BUS;
79             connection.unregisterObject(objectName());
80         }
81
82         if (credentialsStored())
83             m_pSignonDaemon->m_storedIdentities.remove(m_id);
84         else
85             m_pSignonDaemon->m_unstoredIdentities.remove(objectName());
86
87         delete m_signonui;
88     }
89
90     bool SignonIdentity::init()
91     {
92         QDBusConnection connection = SIGNOND_BUS;
93
94         if (!connection.isConnected()) {
95             QDBusError err = connection.lastError();
96             TRACE() << "Connection cannot be established:" << err.errorString(err.type()) ;
97             return false;
98         }
99
100         QDBusConnection::RegisterOptions registerOptions = QDBusConnection::ExportAllContents;
101
102 #ifndef SIGNON_DISABLE_ACCESS_CONTROL
103         (void)new SignonIdentityAdaptor(this);
104         registerOptions = QDBusConnection::ExportAdaptors;
105 #endif
106
107         if (!connection.registerObject(objectName(), this, registerOptions)) {
108             TRACE() << "Object cannot be registered: " << objectName();
109             return false;
110         }
111
112         return (m_registered = true);
113     }
114
115     SignonIdentity *SignonIdentity::createIdentity(quint32 id, SignonDaemon *parent)
116     {
117         SignonIdentity *identity =
118             new SignonIdentity(id, parent->identityTimeout(), parent);
119
120         if (!identity->init()) {
121             TRACE() << "The created identity is invalid and will be deleted.\n";
122             delete identity;
123             return NULL;
124         }
125
126         return identity;
127     }
128
129     void SignonIdentity::destroy()
130     {
131         if (m_registered)
132         {
133             emit unregistered();
134             QDBusConnection connection = SIGNOND_BUS;
135             connection.unregisterObject(objectName());
136             m_registered = false;
137         }
138
139         deleteLater();
140     }
141
142     SignonIdentityInfo SignonIdentity::queryInfo(bool &ok, bool queryPassword)
143     {
144         ok = true;
145
146         if (m_pInfo) {
147             return *m_pInfo;
148         } else {
149             CredentialsDB *db = CredentialsAccessManager::instance()->credentialsDB();
150             m_pInfo = new SignonIdentityInfo(db->credentials(m_id, queryPassword));
151
152             if (db->error().type() != QSqlError::NoError) {
153                 ok = false;
154                 delete m_pInfo;
155                 m_pInfo = NULL;
156                 return SignonIdentityInfo();
157             }
158         }
159         return *m_pInfo;
160     }
161
162     quint32 SignonIdentity::requestCredentialsUpdate(const QString &displayMessage)
163     {
164         RequestCounter::instance()->addIdentityResquest();
165         Q_UNUSED(displayMessage);
166
167         QDBusMessage errReply = message().createErrorReply(
168                                                 SIGNOND_UNKNOWN_ERR_NAME,
169                                                 QLatin1String("Not implemented."));
170         SIGNOND_BUS.send(errReply);
171         keepInUse();
172         return 0;
173     }
174
175     QList<QVariant> SignonIdentity::queryInfo()
176     {
177         RequestCounter::instance()->addIdentityResquest();
178         TRACE() << "QUERYING INFO";
179
180         SIGNON_RETURN_IF_CAM_UNAVAILABLE(QList<QVariant>());
181
182         bool ok;
183         SignonIdentityInfo info = queryInfo(ok, false);
184
185         TRACE() << info.serialize();
186         if (!ok) {
187             TRACE();
188             QDBusMessage errReply = message().createErrorReply(
189                                         SIGNOND_CREDENTIALS_NOT_AVAILABLE_ERR_NAME,
190                                         QString(SIGNOND_CREDENTIALS_NOT_AVAILABLE_ERR_STR
191                                                 + QLatin1String("Database querying error occurred.")));
192             SIGNOND_BUS.send(errReply);
193             return QList<QVariant>();
194         }
195
196         if (info.m_id == 0) {
197             TRACE();
198             QDBusMessage errReply = message().createErrorReply(SIGNOND_IDENTITY_NOT_FOUND_ERR_NAME,
199                                                                SIGNOND_IDENTITY_NOT_FOUND_ERR_STR);
200             SIGNOND_BUS.send(errReply);
201             return QList<QVariant>();
202         }
203
204         TRACE() << "INFO as variant list:" << info.toVariantList();
205         keepInUse();
206         return info.toVariantList();
207     }
208
209     bool SignonIdentity::verifyUser(const QString &displayMessage)
210     {
211         RequestCounter::instance()->addIdentityResquest();
212         Q_UNUSED(displayMessage)
213
214         SIGNON_RETURN_IF_CAM_UNAVAILABLE(false);
215
216         QDBusMessage errReply = message().createErrorReply(SIGNOND_UNKNOWN_ERR_NAME,
217                                                            QLatin1String("Not implemented."));
218         SIGNOND_BUS.send(errReply);
219         keepInUse();
220         return false;
221     }
222
223     bool SignonIdentity::verifySecret(const QString &secret)
224     {
225         RequestCounter::instance()->addIdentityResquest();
226
227         SIGNON_RETURN_IF_CAM_UNAVAILABLE(false);
228
229         bool ok;
230         queryInfo(ok);
231         if (!ok) {
232             TRACE();
233             QDBusMessage errReply = message().createErrorReply(
234                                         SIGNOND_CREDENTIALS_NOT_AVAILABLE_ERR_NAME,
235                                         QString(SIGNOND_CREDENTIALS_NOT_AVAILABLE_ERR_STR
236                                                 + QLatin1String("Database querying error occurred.")));
237             SIGNOND_BUS.send(errReply);
238             return false;
239         }
240
241         CredentialsDB *db = CredentialsAccessManager::instance()->credentialsDB();
242         bool ret = db->checkPassword(m_pInfo->m_id, m_pInfo->m_userName, secret);
243
244         keepInUse();
245         return ret;
246     }
247
248     void SignonIdentity::remove()
249     {
250         RequestCounter::instance()->addIdentityResquest();
251
252         SIGNON_RETURN_IF_CAM_UNAVAILABLE();
253
254         CredentialsDB *db = CredentialsAccessManager::instance()->credentialsDB();
255         if (!db->removeCredentials(m_id)) {
256             TRACE() << "Error occurred while inserting/updating credemtials.";
257             QDBusMessage errReply = message().createErrorReply(
258                                                         SIGNOND_REMOVE_FAILED_ERR_NAME,
259                                                         QString(SIGNOND_REMOVE_FAILED_ERR_STR
260                                                                 + QLatin1String("Database error occurred.")));
261             SIGNOND_BUS.send(errReply);
262         }
263         emit infoUpdated((int)SignOn::IdentityRemoved);
264         keepInUse();
265     }
266
267     bool SignonIdentity::signOut()
268     {
269         RequestCounter::instance()->addIdentityResquest();
270         TRACE() << "Signout request. Identity ID: " << id();
271         /*
272            - If the identity is stored (thus registered here)
273            signal 'sign out' to all identities subsribed to this object,
274            otherwise the only identity subscribed to this is the newly
275            created client side identity, which called this method.
276            - This is just a safety check, as the client identity - if it is a new one -
277            should not inform server side to sign out.
278         */
279         if (id() != SIGNOND_NEW_IDENTITY)
280             emit infoUpdated((int)SignOn::IdentitySignedOut);
281
282         keepInUse();
283         return true;
284     }
285
286     quint32 SignonIdentity::storeCredentials(const quint32 id,
287                                              const QString &userName,
288                                              const QString &secret,
289                                              const bool storeSecret,
290                                              const QMap<QString, QVariant> &methods,
291                                              const QString &caption,
292                                              const QStringList &realms,
293                                              const QStringList &accessControlList,
294                                              const int type)
295     {
296         RequestCounter::instance()->addIdentityResquest();
297
298         SIGNON_RETURN_IF_CAM_UNAVAILABLE(SIGNOND_NEW_IDENTITY);
299
300         QString aegisIdToken = AccessControlManager::idTokenOfPeer(static_cast<QDBusContext>(*this));
301
302         QStringList accessControlListLocal = accessControlList;
303         if (!aegisIdToken.isNull())
304             accessControlListLocal.prepend(aegisIdToken);
305
306         SignonIdentityInfo info(id, userName, secret, methods, caption,
307                                 realms, accessControlListLocal, type);
308
309         TRACE() << info.serialize();
310         storeCredentials(info, storeSecret);
311         if (m_id == SIGNOND_NEW_IDENTITY) {
312             QDBusMessage errReply = message().createErrorReply(SIGNOND_STORE_FAILED_ERR_NAME,
313                                                                SIGNOND_STORE_FAILED_ERR_STR);
314             SIGNOND_BUS.send(errReply);
315         }
316
317         keepInUse();
318         return m_id;
319     }
320
321     quint32 SignonIdentity::storeCredentials(const SignonIdentityInfo &info, bool storeSecret)
322     {
323         CredentialsDB *db = CredentialsAccessManager::instance()->credentialsDB();
324         if (db == NULL) {
325             BLAME() << "NULL database handler object.";
326             return SIGNOND_NEW_IDENTITY;
327         }
328
329         bool newIdentity = (info.m_id == SIGNOND_NEW_IDENTITY);
330
331         if (newIdentity)
332             m_id = db->insertCredentials(info, storeSecret);
333         else
334             db->updateCredentials(info, storeSecret);
335
336         if (db->errorOccurred()) {
337             if (newIdentity)
338                 m_id = SIGNOND_NEW_IDENTITY;
339
340             TRACE() << "Error occurred while inserting/updating credentials.";
341         } else {
342             if (m_pInfo) {
343                 delete m_pInfo;
344                 m_pInfo = NULL;
345             }
346             m_pSignonDaemon->identityStored(this);
347             TRACE() << "FRESH, JUST STORED CREDENTIALS ID:" << m_id;
348             emit infoUpdated((int)SignOn::IdentityDataUpdated);
349         }
350         return m_id;
351     }
352
353 } //namespace SignonDaemonNS