Update to upstream 1.0.1
[profile/ivi/gsignond.git] / src / daemon / db / gsignond-db-metadata-database.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of gsignond
5  *
6  * Copyright (C) 2012 Intel Corporation.
7  *
8  * Contact: Imran Zaman <imran.zaman@linux.intel.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
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 #if HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include <string.h>
29 #include <sys/stat.h>
30
31 #include "gsignond/gsignond-log.h"
32 #include "gsignond/gsignond-config.h"
33 #include "common/db/gsignond-db-error.h"
34 #include "common/gsignond-identity-info-internal.h"
35 #include "gsignond-db-metadata-database.h"
36
37 #define GSIGNOND_METADATA_DB_FILENAME   "metadata.db"
38
39 #define RETURN_IF_NOT_OPEN(obj, retval) \
40     if (gsignond_db_sql_database_is_open (obj) == FALSE) { \
41         GError* last_error = gsignond_db_create_error( \
42                             GSIGNOND_DB_ERROR_NOT_OPEN,\
43                             "DB Not Open"); \
44         DBG("MetadataDB is not available"); \
45         gsignond_db_sql_database_set_last_error(obj,\
46                                                 last_error); \
47         return retval; \
48     }
49
50 #define GSIGNOND_DB_METADATA_DATABASE_GET_PRIVATE(obj) \
51                                           (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
52                                            GSIGNOND_DB_TYPE_METADATA_DATABASE, \
53                                            GSignondDbMetadataDatabasePrivate))
54
55 G_DEFINE_TYPE (GSignondDbMetadataDatabase, gsignond_db_metadata_database,
56         GSIGNOND_DB_TYPE_SQL_DATABASE);
57
58 struct _GSignondDbMetadataDatabasePrivate
59 {
60 };
61
62 enum
63 {
64     PROP_0,
65     PROP_CONFIG,
66     N_PROPERTIES
67 };
68
69 static GParamSpec *properties[N_PROPERTIES] = { NULL, };
70
71 static void
72 _set_property (GObject *object, guint prop_id, const GValue *value,
73                GParamSpec *pspec)
74 {
75     GSignondDbMetadataDatabase *self = GSIGNOND_DB_METADATA_DATABASE (object);
76
77     switch (prop_id) {
78         case PROP_CONFIG:
79             g_assert (self->config == NULL);
80             self->config = g_value_dup_object (value);
81             break;
82         default:
83             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
84     }
85 }
86
87 static void
88 _get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
89 {
90     GSignondDbMetadataDatabase *self = GSIGNOND_DB_METADATA_DATABASE (object);
91
92     switch (prop_id) {
93         case PROP_CONFIG:
94             g_value_set_object (value, self->config);
95             break;
96         default:
97             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
98     }
99 }
100
101 static gboolean
102 _gsignond_db_metadata_database_open (
103         GSignondDbSqlDatabase *obj,
104         const gchar *filename,
105         int flags);
106
107 static gboolean
108 _gsignond_db_metadata_database_create (
109         GSignondDbSqlDatabase *obj);
110
111 static gboolean
112 _gsignond_db_metadata_database_clear (
113         GSignondDbSqlDatabase *obj);
114
115 /*!
116  * @enum GSignondIdentityFlags
117  * Flags for the identity to be stored into db
118  */
119 enum GSignondIdentityFlags {
120     GSignondIdentityFlag_Validated = 0x0001,
121     GSignondIdentityFlag_RememberSecret = 0x0002,
122     GSignondIdentityFlag_UserNameIsSecret = 0x0004,
123 };
124
125 static gint
126 _compare_strings (
127                 const gchar* a,
128                 const gchar* b,
129                 gpointer data)
130 {
131         (void)data;
132         return g_strcmp0 (a,b);
133 }
134
135 static GSequence *
136 _gsignond_db_metadata_database_list_to_sequence (GList *list)
137 {
138     GSequence *seq = NULL;
139     seq = g_sequence_new ((GDestroyNotify)g_free);
140     list = g_list_first (list);
141     for ( ; list != NULL; list = g_list_next (list)) {
142         g_sequence_insert_sorted (seq, (gchar *) list->data,
143                         (GCompareDataFunc)_compare_strings, NULL);
144     }
145     return seq;
146 }
147
148 static gboolean
149 _gsignond_db_metadata_database_read_identity (
150         sqlite3_stmt *stmt,
151         GSignondIdentityInfo *identity)
152 {
153     gint flags = 0;
154
155     gsignond_identity_info_set_caption (identity,
156             (const gchar *)sqlite3_column_text (stmt, 0));
157
158     flags = sqlite3_column_int (stmt, 2);
159
160     gsignond_identity_info_set_username_secret (identity,
161           flags & GSignondIdentityFlag_UserNameIsSecret);
162
163     if (flags & GSignondIdentityFlag_UserNameIsSecret) {
164         gsignond_identity_info_set_caption (identity, "");
165     } else {
166         gsignond_identity_info_set_username (identity,
167                     (const gchar *)sqlite3_column_text (stmt, 1));
168     }
169
170     gsignond_identity_info_set_store_secret (identity,
171             flags & GSignondIdentityFlag_RememberSecret);
172
173     gsignond_identity_info_set_validated (identity,
174             flags & GSignondIdentityFlag_Validated);
175
176     gsignond_identity_info_set_identity_type (identity,
177             sqlite3_column_int (stmt, 3));
178
179     return TRUE;
180 }
181
182 static gboolean
183 _gsignond_db_metadata_database_exec (
184         GSignondDbMetadataDatabase *self,
185         const gchar *query_format,
186         ...)
187 {
188     gboolean ret = FALSE;
189     gchar *query = NULL;
190     va_list args;
191
192     g_return_val_if_fail (query_format != NULL, FALSE);
193
194     va_start (args, query_format);
195     query = sqlite3_vmprintf (query_format, args);
196     va_end (args);
197
198     ret = gsignond_db_sql_database_exec (
199                     GSIGNOND_DB_SQL_DATABASE (self),
200                     query);
201     sqlite3_free (query);
202
203     return ret;
204 }
205
206
207 static GSequence *
208 _gsignond_db_metadata_database_get_sequence (
209         GSignondDbMetadataDatabase *self,
210         const gchar *query_format,
211         ...)
212 {
213     GSequence *seq = NULL;
214     GList *list = NULL;
215     gchar *query = NULL;
216     va_list args;
217
218     g_return_val_if_fail (query_format != NULL, NULL);
219
220     va_start (args, query_format);
221     query = sqlite3_vmprintf (query_format, args);
222     va_end (args);
223
224     list = gsignond_db_sql_database_query_exec_string_list (
225                     GSIGNOND_DB_SQL_DATABASE (self),
226                     query);
227     sqlite3_free (query);
228     seq = _gsignond_db_metadata_database_list_to_sequence (list);
229     g_list_free (list); /*list elements are owned by sequence*/
230
231     return seq;
232 }
233
234 guint32
235 _gsignond_db_metadata_database_update_credentials (
236         GSignondDbMetadataDatabase *self,
237         GSignondIdentityInfo *identity)
238 {
239     gchar *query = NULL;
240     gint flags = 0;
241     guint32 type;
242     gint64 id;
243     const gchar *caption= NULL, *username = NULL;
244     gboolean ret = FALSE;
245
246     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), 0);
247     g_return_val_if_fail (identity != NULL, 0);
248
249     if (gsignond_identity_info_get_validated (identity) )
250         flags |= GSignondIdentityFlag_Validated;
251     if (gsignond_identity_info_get_store_secret (identity) )
252         flags |= GSignondIdentityFlag_RememberSecret;
253     if (gsignond_identity_info_get_is_username_secret (identity) ) {
254         flags |= GSignondIdentityFlag_UserNameIsSecret;
255     } else {
256         username = gsignond_identity_info_get_username (identity);
257     }
258     caption = gsignond_identity_info_get_caption (identity);
259
260     id = gsignond_identity_info_get_id (identity);
261     type = gsignond_identity_info_get_identity_type (identity);
262     if (!gsignond_identity_info_get_is_identity_new (identity)) {
263         query = sqlite3_mprintf ("UPDATE IDENTITY SET caption = %Q, "
264                 "username = %Q, flags = %u, type = %u WHERE id = %u;",
265                 caption ?caption : "",username? username : "", flags, type, id);
266     } else {
267         query = sqlite3_mprintf ("INSERT INTO IDENTITY "
268                 "(caption, username, flags, type) "
269                 "VALUES(%Q, %Q, %u, %u);",
270                 caption ?caption : "",username? username : "", flags, type);
271     }
272     ret = gsignond_db_sql_database_exec (
273                 GSIGNOND_DB_SQL_DATABASE (self),
274                 query);
275     sqlite3_free (query);
276     if (!ret) {
277         return 0;
278     }
279
280     if (gsignond_identity_info_get_is_identity_new (identity)) {
281         id = gsignond_db_sql_database_get_last_insert_rowid (
282                     GSIGNOND_DB_SQL_DATABASE (self));
283     }
284     return (guint32)id;
285 }
286
287 gboolean
288 _gsignond_db_metadata_database_update_realms (
289         GSignondDbMetadataDatabase *self,
290         GSignondIdentityInfo *identity,
291         guint32 id,
292         GSequence *realms)
293 {
294     GSequenceIter *iter = NULL;
295
296     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
297     g_return_val_if_fail (identity != NULL, FALSE);
298
299     if (realms && g_sequence_get_length (realms) > 0) {
300
301         if (!gsignond_identity_info_get_is_identity_new (identity)) {
302             /* remove realms list */
303             DBG ("Remove old realms from DB as identity is not new");
304             _gsignond_db_metadata_database_exec (self,
305                     "DELETE FROM REALMS WHERE identity_id = %u;", id);
306         }
307
308         /* realms insert */
309         iter = g_sequence_get_begin_iter (realms);
310         while (!g_sequence_iter_is_end (iter)) {
311             if (!_gsignond_db_metadata_database_exec (self,
312                     "INSERT OR IGNORE INTO REALMS (identity_id, realm) "
313                     "VALUES (%u, %Q);",
314                     id, (const gchar *)g_sequence_get (iter))) {
315                 DBG ("Insert realms to DB failed");
316                 return FALSE;
317             }
318             iter = g_sequence_iter_next (iter);
319         }
320     }
321     return TRUE;
322 }
323
324 gboolean
325 _gsignond_db_metadata_database_insert_methods (
326         GSignondDbMetadataDatabase *self,
327         GSignondIdentityInfo *identity,
328         GHashTable *methods)
329 {
330     GSequenceIter *mech_iter = NULL;
331     GHashTableIter method_iter;
332     const gchar *method = NULL;
333     GSequence *mechanisms = NULL;
334
335     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
336     g_return_val_if_fail (identity != NULL, FALSE);
337
338     if (!methods || g_hash_table_size (methods) <=0) {
339         DBG ("no authentication methods found to store identity");
340         return FALSE;
341     }
342
343     g_hash_table_iter_init (&method_iter, methods);
344     while (g_hash_table_iter_next (&method_iter,
345                                    (gpointer)&method,
346                                    (gpointer)&mechanisms))
347     {
348         if (!_gsignond_db_metadata_database_exec ( self,
349                 "INSERT OR IGNORE INTO METHODS (method) "
350                 "VALUES( %Q );",
351                 method)) {
352             DBG ("Insert methods to DB failed");
353             return FALSE;
354         }
355         /* mechanisms insert */
356         mech_iter = g_sequence_get_begin_iter (mechanisms);
357         while (!g_sequence_iter_is_end (mech_iter)) {
358             if (!_gsignond_db_metadata_database_exec (self,
359                     "INSERT OR IGNORE INTO MECHANISMS (mechanism) "
360                     "VALUES(%Q);",
361                     g_sequence_get (mech_iter))) {
362                 DBG ("Insert mechanisms to DB failed");
363                 return FALSE;
364             }
365             mech_iter = g_sequence_iter_next (mech_iter);
366         }
367     }
368
369     return TRUE;
370 }
371
372 gboolean
373 _gsignond_db_metadata_database_update_acl (
374         GSignondDbMetadataDatabase *self,
375         GSignondIdentityInfo *identity,
376         GSignondSecurityContextList *acl)
377 {
378     GSignondSecurityContextList *list = NULL;
379     GSignondSecurityContext *ctx = NULL;
380
381     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
382     g_return_val_if_fail (identity != NULL, FALSE);
383
384     if (!acl || g_list_length (acl) <= 0) {
385         DBG ("NULL acl or no acl to be added to DB");
386         return FALSE;
387     }
388
389     for (list = acl;  list != NULL; list = g_list_next (list)) {
390         ctx = (GSignondSecurityContext *) list->data;
391         _gsignond_db_metadata_database_exec (self,
392                 "INSERT OR IGNORE INTO SECCTX (sysctx, appctx) "
393                 "VALUES (%Q, %Q);",
394                 ctx->sys_ctx, ctx->app_ctx);
395     }
396     return TRUE;
397 }
398
399 gboolean
400 _gsignond_db_metadata_database_update_owner (
401         GSignondDbMetadataDatabase *self,
402         GSignondIdentityInfo *identity,
403         GSignondSecurityContext *owner)
404 {
405     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
406     g_return_val_if_fail (identity != NULL, FALSE);
407
408     if (!owner) {
409         DBG ("no owner to be added to DB");
410         return FALSE;
411     }
412
413     if (owner->sys_ctx && strlen (owner->sys_ctx) > 0) {
414         _gsignond_db_metadata_database_exec (self,
415                     "INSERT OR IGNORE INTO "
416                     "SECCTX (sysctx, appctx) "
417                     "VALUES (%Q, %Q);",
418                     owner->sys_ctx, owner->app_ctx);
419     }
420
421     return TRUE;
422 }
423
424 static void
425 _gsignond_db_metadata_database_dispose (GObject *gobject)
426 {
427     g_return_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (gobject));
428     GSignondDbMetadataDatabase *self = GSIGNOND_DB_METADATA_DATABASE (gobject);
429
430     if (self->config) {
431         g_object_unref (self->config);
432         self->config = NULL;
433     }
434
435     /* Chain up to the parent class */
436     G_OBJECT_CLASS (gsignond_db_metadata_database_parent_class)->dispose (
437             gobject);
438 }
439
440 static void
441 gsignond_db_metadata_database_class_init (
442         GSignondDbMetadataDatabaseClass *klass)
443 {
444     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
445
446     gobject_class->set_property = _set_property;
447     gobject_class->get_property = _get_property;
448     gobject_class->dispose = _gsignond_db_metadata_database_dispose;
449
450     properties[PROP_CONFIG] = g_param_spec_object ("config",
451                                                    "config",
452                                                    "Configuration object",
453                                                    GSIGNOND_TYPE_CONFIG,
454                                                    G_PARAM_CONSTRUCT_ONLY |
455                                                    G_PARAM_READWRITE |
456                                                    G_PARAM_STATIC_STRINGS);
457     g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
458
459     GSignondDbSqlDatabaseClass *sql_class =
460             GSIGNOND_DB_SQL_DATABASE_CLASS (klass);
461
462     sql_class->create = _gsignond_db_metadata_database_create;
463     sql_class->clear = _gsignond_db_metadata_database_clear;
464
465 }
466
467 static void
468 gsignond_db_metadata_database_init (
469         GSignondDbMetadataDatabase *self)
470 {
471     self->config = NULL;
472 }
473
474 /**
475  * gsignond_db_metadata_database_new:
476  *
477  * @config: (transfer none) #GSignondConfig config data
478  *
479  * Creates new #GSignondDbMetadataDatabase object
480  *
481  * Returns : (transfer full) the #GSignondDbMetadataDatabase object
482  */
483 GSignondDbMetadataDatabase *
484 gsignond_db_metadata_database_new (GSignondConfig *config)
485 {
486     return GSIGNOND_DB_METADATA_DATABASE (
487             g_object_new (GSIGNOND_DB_TYPE_METADATA_DATABASE,
488                           "config", config, NULL));
489 }
490
491 static gboolean
492 _gsignond_db_metadata_database_open (
493         GSignondDbSqlDatabase *obj,
494         const gchar *filename,
495         int flags)
496 {
497     const gchar *dir = NULL;
498     gchar *db_filename = NULL;
499     gboolean ret = FALSE;
500     gint dir_created = 0;
501     GSignondDbMetadataDatabase *self = NULL;
502
503     self = GSIGNOND_DB_METADATA_DATABASE (obj);
504
505     g_return_val_if_fail (self, FALSE);
506
507     if (!filename || strlen (filename) <= 0) {
508         ERR ("Missing Metadata DB filename");
509         return FALSE;
510     }
511     dir = gsignond_config_get_string (self->config,
512             GSIGNOND_CONFIG_GENERAL_SECURE_DIR);
513     if (!dir) {
514         ERR ("Invalid Metadata DB directory");
515         return FALSE;
516     }
517     db_filename = g_build_filename (dir, filename, NULL);
518     if (!db_filename) {
519         ERR ("Invalid Metadata DB filename");
520         return FALSE;
521     }
522
523     dir_created = g_mkdir_with_parents (dir, S_IRWXU);
524     if (dir_created != 0) {
525         ERR ("Metadata DB directory does not exist");
526         goto _open_exit;
527     }
528
529     ret = gsignond_db_sql_database_open (obj, db_filename, flags);
530
531 _open_exit:
532     g_free (db_filename);
533     return ret;
534 }
535
536 static gboolean
537 _gsignond_db_metadata_database_create (
538         GSignondDbSqlDatabase *obj)
539 {
540     const gchar *queries = NULL;
541     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (obj), FALSE);
542     RETURN_IF_NOT_OPEN (obj, FALSE);
543     gint fk_enabled = 0;
544     gint version = 0;
545
546     queries = "PRAGMA foreign_keys = 1;";
547     if (!gsignond_db_sql_database_exec (obj, queries)) {
548         ERR ("Metadata DB enabling foreign keys failed");
549         return FALSE;
550     }
551
552     gsignond_db_sql_database_query_exec_int (obj, "PRAGMA foreign_keys;",
553             &fk_enabled);
554     if (!fk_enabled) {
555         ERR ("Metadata DB - foreign keys not enabled");
556         return FALSE;
557     }
558
559     version = gsignond_db_sql_database_get_db_version(obj,
560                 "PRAGMA user_version;");
561     if (version > 0) {
562         DBG ("Metadata DB is already created with with version (%d) and "
563                 "foreign keys enabled (%d)", version, fk_enabled);
564         return TRUE;
565     }
566
567     queries = "PRAGMA user_version = 1;";
568     if (!gsignond_db_sql_database_exec (obj, queries)) {
569         DBG ("Metadata DB setting version failed");
570         return FALSE;
571     }
572
573     version = gsignond_db_sql_database_get_db_version(obj,
574             "PRAGMA user_version;");
575     DBG ("Metadata DB is to be created with version (%d) and foreign keys "
576             "enabled(%d)", version, fk_enabled);
577
578     queries = ""
579             "CREATE TABLE IDENTITY"
580             "(id INTEGER PRIMARY KEY AUTOINCREMENT,"
581             "caption TEXT,"
582             "username TEXT,"
583             "flags INTEGER,"
584             "type INTEGER);"
585
586             "CREATE TABLE METHODS"
587             "(id INTEGER PRIMARY KEY AUTOINCREMENT,"
588             "method TEXT UNIQUE);"
589
590             "CREATE TABLE MECHANISMS"
591             "(id INTEGER PRIMARY KEY AUTOINCREMENT,"
592             "mechanism TEXT UNIQUE);"
593
594             "CREATE TABLE SECCTX"
595             "(id INTEGER PRIMARY KEY AUTOINCREMENT,"
596             "sysctx TEXT,"
597             "appctx TEXT,"
598             "CONSTRAINT tokc UNIQUE(sysctx, appctx) ON CONFLICT REPLACE);"
599
600             "CREATE INDEX sysidx ON SECCTX(sysctx);"
601             "CREATE INDEX appidx ON SECCTX(appctx);"
602
603             "CREATE TABLE REALMS"
604             "(identity_id INTEGER CONSTRAINT fk_identity_id "
605             "REFERENCES IDENTITY(id) ON DELETE CASCADE,"
606             "realm TEXT,"
607             "hostname TEXT,"
608             "PRIMARY KEY (identity_id, realm, hostname));"
609
610             "CREATE TABLE ACL"
611             "(rowid INTEGER PRIMARY KEY AUTOINCREMENT,"
612             "identity_id INTEGER CONSTRAINT fk_identity_id REFERENCES "
613             "IDENTITY(id) ON DELETE CASCADE,"
614             "method_id INTEGER CONSTRAINT fk_method_id REFERENCES "
615             "METHODS(id),"
616             "mechanism_id INTEGER CONSTRAINT fk_mechanism_id "
617             "REFERENCES MECHANISMS(id),"
618             "secctx_id INTEGER CONSTRAINT fk_secctx_id REFERENCES "
619             "SECCTX(id));"
620
621             "CREATE TABLE REFS"
622             "(identity_id INTEGER CONSTRAINT fk_identity_id "
623             "REFERENCES IDENTITY(id) ON DELETE CASCADE,"
624             "secctx_id INTEGER CONSTRAINT fk_secctx_id REFERENCES "
625             "SECCTX(id),"
626             "ref TEXT,"
627             "PRIMARY KEY (identity_id, secctx_id, ref));"
628
629             "CREATE TABLE OWNER"
630             "(rowid INTEGER PRIMARY KEY AUTOINCREMENT,"
631             "identity_id INTEGER CONSTRAINT fk_identity_id "
632             "REFERENCES IDENTITY(id) ON DELETE CASCADE,"
633             "secctx_id INTEGER CONSTRAINT fk_secctx_id REFERENCES SECCTX(id) "
634             ");"
635
636             // Triggers for deleting orphan SECCTX entries
637             "CREATE TRIGGER fkdstale_ACL_secctx_id_SECCTX_id"
638             "BEFORE DELETE ON [ACL]"
639             "FOR EACH ROW BEGIN"
640             "    DELETE FROM SECCTX WHERE SECCTX.id = OLD.secctx_id AND "
641             "    (SELECT COUNT(*) FROM REFS WHERE "
642             "    REFS.secctx_id = OLD.secctx_id) == 0 AND "
643             "    (SELECT COUNT(*) FROM OWNER WHERE "
644             "    OWNER.secctx_id = OLD.secctx_id) == 0;"
645             "END;"
646
647             "CREATE TRIGGER fkdstale_REFS_secctx_id_SECCTX_id"
648             "BEFORE DELETE ON [REFS]"
649             "FOR EACH ROW BEGIN"
650             "    DELETE FROM SECCTX WHERE SECCTX.id = OLD.secctx_id AND "
651             "    (SELECT COUNT(*) FROM ACL WHERE "
652             "    ACL.secctx_id = OLD.secctx_id) == 0 AND "
653             "    (SELECT COUNT(*) FROM OWNER WHERE "
654             "    OWNER.secctx_id = OLD.secctx_id) == 0;"
655             "END;"
656
657             "CREATE TRIGGER fkdstale_OWNER_secctx_id_SECCTX_id"
658             "BEFORE DELETE ON [OWNER]"
659             "FOR EACH ROW BEGIN"
660             "    DELETE FROM SECCTX WHERE SECCTX.id = OLD.secctx_id AND "
661             "    (SELECT COUNT(*) FROM ACL WHERE "
662             "    ACL.secctx_id = OLD.secctx_id) == 0 AND "
663             "    (SELECT COUNT(*) FROM REFS WHERE "
664             "    REFS.secctx_id = OLD.secctx_id) == 0;"
665             "END;"
666
667 #ifdef ENABLE_DB_ACL_TRIGGERS
668             // Trigger for deleting orphan METHODS entries
669             "CREATE TRIGGER fkdstale_ACL_method_id_METHODS_id"
670             "BEFORE DELETE ON [ACL]"
671             "FOR EACH ROW BEGIN"
672             "    DELETE FROM METHODS WHERE METHODS.id = OLD.method_id AND "
673             "    (SELECT COUNT(*) FROM ACL WHERE "
674             "    ACL.method_id = OLD.method_id) == 1;"
675             "END;"
676
677             // Trigger for deleting orphan MECHANISMS entries
678             "CREATE TRIGGER fkdstale_ACL_mechanism_id_MECHANISMS_id"
679             "BEFORE DELETE ON [ACL]"
680             "FOR EACH ROW BEGIN"
681             "    DELETE FROM MECHANISMS WHERE MECHANISMS.id = OLD.mechanism_id "
682             "    AND (SELECT COUNT(*) FROM ACL WHERE "
683             "    ACL.mechanism_id = OLD.mechanism_id) == 1;"
684             "END;"
685 #endif
686
687             /*
688              * triggers generated with
689              * http://www.rcs-comp.com/site/index.php/view/Utilities-
690              * SQLite_foreign_key_trigger_generator
691              */
692             /* insert triggers to force foreign keys support */
693             // Foreign Key Preventing insert
694             "CREATE TRIGGER fki_REALMS_identity_id_IDENTITY_id"
695             "BEFORE INSERT ON [REALMS]"
696             "FOR EACH ROW BEGIN"
697             "  SELECT RAISE(ROLLBACK, 'insert on table REALMS violates foreign "
698             "key constraint fki_REALMS_identity_id_IDENTITY_id')"
699             "  WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM IDENTITY "
700             "WHERE id = NEW.identity_id) IS NULL;"
701             "END;"
702
703             // Foreign key preventing update
704             "CREATE TRIGGER fku_REALMS_identity_id_IDENTITY_id"
705             "BEFORE UPDATE ON [REALMS]"
706             "FOR EACH ROW BEGIN"
707             "    SELECT RAISE(ROLLBACK, 'update on table REALMS violates "
708             "foreign key constraint fku_REALMS_identity_id_IDENTITY_id')"
709             "      WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM "
710             "IDENTITY WHERE id = NEW.identity_id) IS NULL;"
711             "END;"
712
713             // Foreign Key Preventing insert
714             "CREATE TRIGGER fki_ACL_identity_id_IDENTITY_id"
715             "BEFORE INSERT ON [ACL]"
716             "FOR EACH ROW BEGIN"
717             "  SELECT RAISE(ROLLBACK, 'insert on table ACL violates foreign "
718             "key constraint fki_ACL_identity_id_IDENTITY_id')"
719             "  WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM IDENTITY "
720             "WHERE id = NEW.identity_id) IS NULL;"
721             "END;"
722
723             // Foreign key preventing update
724             "CREATE TRIGGER fku_ACL_identity_id_IDENTITY_id"
725             "BEFORE UPDATE ON [ACL]"
726             "FOR EACH ROW BEGIN"
727             "    SELECT RAISE(ROLLBACK, 'update on table ACL violates foreign "
728             "key constraint fku_ACL_identity_id_IDENTITY_id')"
729             "      WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM "
730             "IDENTITY WHERE id = NEW.identity_id) IS NULL;"
731             "END;"
732
733             // Foreign Key Preventing insert
734             "CREATE TRIGGER fki_ACL_method_id_METHODS_id"
735             "BEFORE INSERT ON [ACL]"
736             "FOR EACH ROW BEGIN"
737             "  SELECT RAISE(ROLLBACK, 'insert on table ACL violates foreign "
738             "key constraint fki_ACL_method_id_METHODS_id')"
739             "  WHERE NEW.method_id IS NOT NULL AND (SELECT id FROM METHODS "
740             "WHERE id = NEW.method_id) IS NULL;"
741             "END;"
742
743             // Foreign key preventing update
744             "CREATE TRIGGER fku_ACL_method_id_METHODS_id"
745             "BEFORE UPDATE ON [ACL]"
746             "FOR EACH ROW BEGIN"
747             "    SELECT RAISE(ROLLBACK, 'update on table ACL violates foreign "
748             "key constraint fku_ACL_method_id_METHODS_id')"
749             "      WHERE NEW.method_id IS NOT NULL AND (SELECT id FROM METHODS "
750             "WHERE id = NEW.method_id) IS NULL;"
751             "END;"
752
753             // Foreign Key Preventing insert
754             "CREATE TRIGGER fki_ACL_mechanism_id_MECHANISMS_id"
755             "BEFORE INSERT ON [ACL]"
756             "FOR EACH ROW BEGIN"
757             "  SELECT RAISE(ROLLBACK, 'insert on table ACL violates foreign "
758             "key constraint fki_ACL_mechanism_id_MECHANISMS_id')"
759             "  WHERE NEW.mechanism_id IS NOT NULL AND (SELECT id FROM "
760             "MECHANISMS WHERE id = NEW.mechanism_id) IS NULL;"
761             "END;"
762
763             // Foreign key preventing update
764             "CREATE TRIGGER fku_ACL_mechanism_id_MECHANISMS_id"
765             "BEFORE UPDATE ON [ACL]"
766             "FOR EACH ROW BEGIN"
767             "    SELECT RAISE(ROLLBACK, 'update on table ACL violates foreign "
768             "key constraint fku_ACL_mechanism_id_MECHANISMS_id')"
769             "      WHERE NEW.mechanism_id IS NOT NULL AND (SELECT id FROM "
770             "MECHANISMS WHERE id = NEW.mechanism_id) IS NULL;"
771             "END;"
772
773             // Foreign Key Preventing insert
774             "CREATE TRIGGER fki_ACL_secctx_id_SECCTX_id"
775             "BEFORE INSERT ON [ACL]"
776             "FOR EACH ROW BEGIN"
777             "  SELECT RAISE(ROLLBACK, 'insert on table ACL violates foreign "
778             "key constraint fki_ACL_secctx_id_SECCTX_id')"
779             "  WHERE NEW.secctx_id IS NOT NULL AND (SELECT id FROM SECCTX "
780             "WHERE id = NEW.secctx_id) IS NULL;"
781             "END;"
782
783             // Foreign key preventing update
784             "CREATE TRIGGER fku_ACL_secctx_id_SECCTX_id"
785             "BEFORE UPDATE ON [ACL]"
786             "FOR EACH ROW BEGIN"
787             "    SELECT RAISE(ROLLBACK, 'update on table ACL violates foreign "
788             "key constraint fku_ACL_secctx_id_SECCTX_id')"
789             "      WHERE NEW.secctx_id IS NOT NULL AND (SELECT id FROM SECCTX "
790             "WHERE id = NEW.secctx_id) IS NULL;"
791             "END;"
792
793             // Foreign Key Preventing insert
794             "CREATE TRIGGER fki_REFS_identity_id_IDENTITY_id"
795             "BEFORE INSERT ON [REFS]"
796             "FOR EACH ROW BEGIN"
797             "  SELECT RAISE(ROLLBACK, 'insert on table REFS violates foreign "
798             "key constraint fki_REFS_identity_id_IDENTITY_id')"
799             "  WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM IDENTITY "
800             "WHERE id = NEW.identity_id) IS NULL;"
801             "END;"
802
803             // Foreign key preventing update
804             "CREATE TRIGGER fku_REFS_identity_id_IDENTITY_id"
805             "BEFORE UPDATE ON [REFS]"
806             "FOR EACH ROW BEGIN"
807             "    SELECT RAISE(ROLLBACK, 'update on table REFS violates foreign "
808             "key constraint fku_REFS_identity_id_IDENTITY_id')"
809             "      WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM "
810             "IDENTITY WHERE id = NEW.identity_id) IS NULL;"
811             "END;"
812
813             // Foreign Key Preventing insert
814             "CREATE TRIGGER fki_REFS_secctx_id_SECCTX_id"
815             "BEFORE INSERT ON [REFS]"
816             "FOR EACH ROW BEGIN"
817             "  SELECT RAISE(ROLLBACK, 'insert on table REFS violates foreign "
818             "key constraint fki_REFS_secctx_id_SECCTX_id')"
819             "  WHERE NEW.secctx_id IS NOT NULL AND (SELECT id FROM SECCTX "
820             "WHERE id = NEW.secctx_id) IS NULL;"
821             "END;"
822
823             // Foreign key preventing update
824             "CREATE TRIGGER fku_REFS_secctx_id_SECCTX_id"
825             "BEFORE UPDATE ON [REFS]"
826             "FOR EACH ROW BEGIN"
827             "    SELECT RAISE(ROLLBACK, 'update on table REFS violates foreign "
828             "key constraint fku_REFS_secctx_id_SECCTX_id')"
829             "      WHERE NEW.secctx_id IS NOT NULL AND (SELECT id FROM SECCTX "
830             "WHERE id = NEW.secctx_id) IS NULL;"
831             "END;"
832
833             // Foreign Key Preventing insert
834             "CREATE TRIGGER fki_OWNER_identity_id_IDENTITY_id"
835             "BEFORE INSERT ON [OWNER]"
836             "FOR EACH ROW BEGIN"
837             "    SELECT RAISE(ROLLBACK, 'insert on table OWNER violates "
838             "foreign key constraint fki_OWNER_identity_id_IDENTITY_id')"
839             "    WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM "
840             "IDENTITY WHERE id = NEW.identity_id) IS NULL;"
841             "END;"
842
843             // Foreign key preventing update
844             "CREATE TRIGGER fku_OWNER_identity_id_IDENTITY_id"
845             "BEFORE UPDATE ON [OWNER]"
846             "FOR EACH ROW BEGIN"
847             "    SELECT RAISE(ROLLBACK, 'update on table OWNER violates "
848             "foreign key constraint fku_OWNER_identity_id_IDENTITY_id')"
849             "    WHERE NEW.identity_id IS NOT NULL AND (SELECT id FROM "
850             "IDENTITY WHERE id = NEW.identity_id) IS NULL;"
851             "END;"
852
853             // Foreign Key Preventing insert
854             "CREATE TRIGGER fki_OWNER_secctx_id_SECCTX_id"
855             "BEFORE INSERT ON [OWNER]"
856             "FOR EACH ROW BEGIN"
857             "   SELECT RAISE(ROLLBACK, 'insert on table OWNER violates foreign "
858             "key constraint fki_OWNER_secctx_id_SECCTX_id')"
859             "   WHERE NEW.secctx_id IS NOT NULL AND (SELECT id FROM SECCTX "
860             "WHERE id = NEW.secctx_id) IS NULL;"
861             "END;"
862
863             // Foreign key preventing update
864             "CREATE TRIGGER fku_OWNER_secctx_id_SECCTX_id"
865             "BEFORE UPDATE ON [OWNER]"
866             "FOR EACH ROW BEGIN"
867             "    SELECT RAISE(ROLLBACK, 'update on table OWNER violates "
868             "foreign key constraint fku_OWNER_secctx_id_SECCTX_id')"
869             "    WHERE NEW.secctx_id IS NOT NULL AND (SELECT id FROM SECCTX "
870             "WHERE id = NEW.secctx_id) IS NULL;"
871             "END;"
872             ;
873
874     return gsignond_db_sql_database_transaction_exec (obj, queries);
875 }
876
877 static gboolean
878 _gsignond_db_metadata_database_clear (
879         GSignondDbSqlDatabase *obj)
880 {
881     const gchar *queries = NULL;
882
883     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (obj), FALSE);
884     RETURN_IF_NOT_OPEN (obj, FALSE);
885
886     queries = ""
887             "DELETE FROM IDENTITY;"
888             "DELETE FROM METHODS;"
889             "DELETE FROM MECHANISMS;"
890             "DELETE FROM ACL;"
891             "DELETE FROM REALMS;"
892             "DELETE FROM SECCTX;"
893             "DELETE FROM OWNER;";
894
895     return gsignond_db_sql_database_transaction_exec (obj, queries);
896 }
897
898 /**
899  * gsignond_db_metadata_database_open:
900  *
901  * @self: instance of #GSignondDbMetadataDatabase
902  *
903  * Opens a connection to DB.
904  *
905  * Returns: TRUE if successful, FALSE otherwise.
906  */
907 gboolean
908 gsignond_db_metadata_database_open (GSignondDbMetadataDatabase *self)
909 {
910     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
911
912     if (gsignond_db_sql_database_is_open (GSIGNOND_DB_SQL_DATABASE (self)))
913         return TRUE;
914
915     return _gsignond_db_metadata_database_open (
916             GSIGNOND_DB_SQL_DATABASE (self),
917             GSIGNOND_METADATA_DB_FILENAME,
918             SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
919 }
920
921 /**
922  * gsignond_db_metadata_database_insert_method:
923  *
924  * @self: instance of #GSignondDbMetadataDatabase
925  * @method: the method to be inserted
926  * @method_id: (transfer none) id of the method inserted
927  *
928  * Inserts the method into the db.
929  *
930  * Returns: TRUE if successful, FALSE otherwise
931  */
932 gboolean
933 gsignond_db_metadata_database_insert_method (
934         GSignondDbMetadataDatabase *self,
935         const gchar *method,
936         guint32 *method_id)
937 {
938     gchar *query = NULL;
939     gboolean ret = FALSE;
940     *method_id = 0;
941
942     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
943     g_return_val_if_fail (method != NULL, FALSE);
944     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), FALSE);
945
946     query = sqlite3_mprintf ("INSERT INTO METHODS (method) "
947                              "VALUES (%Q);",
948                              method);
949     ret = gsignond_db_sql_database_transaction_exec (
950             GSIGNOND_DB_SQL_DATABASE (self), query);
951     sqlite3_free (query);
952     if (ret) {
953         DBG ("Retrieve method id for the inserted method");
954         *method_id = gsignond_db_metadata_database_get_method_id (self, method);
955     }
956     return ret;
957 }
958
959 /**
960  * gsignond_db_metadata_database_get_method_id:
961  *
962  * @self: instance of #GSignondDbMetadataDatabase
963  * @method: the method to be fetched
964  *
965  * Fetches the id of the specified method.
966  *
967  * Returns: the method if successful, 0 otherwise
968  */
969 guint32
970 gsignond_db_metadata_database_get_method_id (
971         GSignondDbMetadataDatabase *self,
972         const gchar *method)
973 {
974     gchar *query = NULL;
975     gint method_id = 0;
976
977     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
978     g_return_val_if_fail (method != NULL, FALSE);
979     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), method_id);
980
981     query = sqlite3_mprintf ("SELECT id FROM METHODS "
982                              "WHERE method = %Q;",
983                              method);
984     gsignond_db_sql_database_query_exec_int (
985                 GSIGNOND_DB_SQL_DATABASE (self),
986                 query,
987                 &method_id);
988     sqlite3_free (query);
989
990     return (guint32) method_id;
991 }
992
993 /**
994  * gsignond_db_metadata_database_get_methods:
995  *
996  * @self: instance of #GSignondDbMetadataDatabase
997  * @identity_id: the id of the identity
998  * @sec_ctx: the security context
999  *
1000  * Fetches the list of the methods with the specified identity id.
1001  *
1002  * Returns: (transfer full) the list if successful, NULL otherwise.
1003  * When done list should be freed with g_list_free_full (list, g_free)
1004  */
1005 GList *
1006 gsignond_db_metadata_database_get_methods (
1007         GSignondDbMetadataDatabase *self,
1008         const guint32 identity_id,
1009         GSignondSecurityContext* sec_ctx)
1010 {
1011     gchar *query = NULL;
1012     GList *methods = NULL;
1013
1014     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), NULL);
1015     g_return_val_if_fail (sec_ctx != NULL, NULL);
1016     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), NULL);
1017
1018     if (sec_ctx->sys_ctx && strlen (sec_ctx->sys_ctx) <= 0) {
1019         query = sqlite3_mprintf ("SELECT DISTINCT METHODS.method FROM "
1020                     "( ACL JOIN METHODS ON ACL.method_id = METHODS.id ) "
1021                     "WHERE ACL.identity_id = %u;",
1022                     identity_id);
1023     } else {
1024         query = sqlite3_mprintf ("SELECT DISTINCT METHODS.method FROM "
1025                 "( ACL JOIN METHODS ON ACL.method_id = METHODS.id ) "
1026                 "WHERE ACL.identity_id = %u AND ACL.secctx_id = "
1027                 "(SELECT id FROM SECCTX "
1028                 "WHERE sysctx = %Q AND appctx = %Q);",
1029                 identity_id, sec_ctx->sys_ctx, sec_ctx->app_ctx);
1030     }
1031
1032     methods = gsignond_db_sql_database_query_exec_string_list (
1033                     GSIGNOND_DB_SQL_DATABASE (self),
1034                     query);
1035     sqlite3_free (query);
1036
1037     return methods;
1038 }
1039
1040 /**
1041  * gsignond_db_metadata_database_update_identity:
1042  *
1043  * @self: instance of #GSignondDbMetadataDatabase
1044  * @identity: the identity #GSignondIdentityInfo object
1045  *
1046  * Updates the database with the data in the identity.
1047  *
1048  * Returns: the id of the identity if successful, 0 otherwise
1049  */
1050 guint32
1051 gsignond_db_metadata_database_update_identity (
1052         GSignondDbMetadataDatabase *self,
1053         GSignondIdentityInfo *identity)
1054 {
1055     GSignondDbSqlDatabase *sql = NULL;
1056     guint32 id = 0;
1057     guint32 ret = 0;
1058     GHashTable *methods = NULL;
1059     GSequence *realms = NULL;
1060     GSignondSecurityContextList *acl = NULL, *list = NULL;
1061     GSignondSecurityContext *owner = NULL;
1062     GHashTableIter method_iter;
1063     const gchar *method = NULL;
1064     GSequence *mechanisms = NULL;
1065     GSignondIdentityInfoPropFlags edit_flags;
1066     gboolean was_new_identity;
1067
1068     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), 0);
1069     g_return_val_if_fail (identity != NULL, 0);
1070     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), id);
1071
1072     edit_flags = gsignond_identity_info_get_edit_flags (identity);
1073
1074     DBG ("Identity EDIT FLAGS : %x", edit_flags);
1075     if (edit_flags == IDENTITY_INFO_PROP_NONE) {
1076         DBG("No Changes found to update");
1077         return gsignond_identity_info_get_id (identity);
1078     }
1079
1080     was_new_identity = gsignond_identity_info_get_is_identity_new (identity);
1081
1082     sql = GSIGNOND_DB_SQL_DATABASE (self);
1083     if (!gsignond_db_sql_database_start_transaction (sql)) {
1084         return 0;
1085     }
1086
1087     /* credentials */
1088     id = _gsignond_db_metadata_database_update_credentials (self, identity);
1089     if (id == 0) {
1090         DBG ("Update credentials failed");
1091         gsignond_db_sql_database_rollback_transaction (sql);
1092         return 0;
1093     }
1094
1095     /* realms */
1096     if (edit_flags & IDENTITY_INFO_PROP_REALMS) {
1097         realms = gsignond_identity_info_get_realms (identity);
1098         if (!_gsignond_db_metadata_database_update_realms (self,
1099                                         identity, id, realms)) {
1100             DBG ("Update realms failed");
1101             gsignond_db_sql_database_rollback_transaction (sql);
1102             goto finished;
1103         }
1104     }
1105
1106     /* owner */
1107     owner = gsignond_identity_info_get_owner (identity);
1108     if (!owner) {
1109         WARN("Missing mandatory owner field");
1110         gsignond_db_sql_database_rollback_transaction (sql);
1111         goto finished;
1112     }
1113
1114     if (edit_flags & IDENTITY_INFO_PROP_OWNER) {
1115         if (!was_new_identity) {
1116             /* remove owner */
1117             _gsignond_db_metadata_database_exec (self,
1118                     "DELETE FROM OWNER WHERE identity_id = %u;", id);
1119         }
1120         if (!_gsignond_db_metadata_database_update_owner (self, identity, owner)){
1121             DBG ("Update owner failed");
1122             gsignond_db_sql_database_rollback_transaction (sql);
1123             goto finished;
1124         }
1125
1126         /* insert owner */
1127         _gsignond_db_metadata_database_exec (self,
1128                     "INSERT OR REPLACE INTO OWNER "
1129                     "(identity_id, secctx_id) "
1130                     "VALUES ( %u, "
1131                     "( SELECT id FROM SECCTX WHERE sysctx = %Q AND appctx = %Q ));",
1132                     id, owner->sys_ctx, owner->app_ctx);
1133     }
1134
1135     /* acl */
1136     acl = gsignond_identity_info_get_access_control_list (identity);
1137     if (!acl) {
1138         WARN("Missing mandatory ACL field");
1139         gsignond_db_sql_database_rollback_transaction (sql);
1140         goto finished;
1141     }
1142     if (edit_flags & IDENTITY_INFO_PROP_ACL) {
1143         if (!was_new_identity) {
1144             /* remove acl */
1145             _gsignond_db_metadata_database_exec (self,
1146                 "DELETE FROM ACL WHERE identity_id = %u;", id);
1147         }
1148         if (!_gsignond_db_metadata_database_update_acl (self, identity, acl)) {
1149             DBG ("Update acl failed");
1150             gsignond_db_sql_database_rollback_transaction (sql);
1151             goto finished;
1152         }
1153     }
1154
1155     /* methods */
1156     methods = gsignond_identity_info_get_methods (identity);
1157     if (edit_flags & IDENTITY_INFO_PROP_METHODS) {
1158         if (!_gsignond_db_metadata_database_insert_methods (self, identity,
1159                 methods)) {
1160             DBG ("Update methods failed");
1161         }
1162     }
1163
1164     if ((edit_flags & IDENTITY_INFO_PROP_ACL ||
1165          edit_flags & IDENTITY_INFO_PROP_METHODS) &&
1166         methods) {
1167     /* ACL insert, this will do basically identity level ACL */
1168     g_hash_table_iter_init (&method_iter, methods);
1169     while (g_hash_table_iter_next (&method_iter, (gpointer)&method,
1170             (gpointer)&mechanisms)) {
1171
1172         if (g_list_length (acl) > 0) {
1173             for (list = acl;  list != NULL; list = g_list_next (list)) {
1174                 GSequenceIter *mech_iter = NULL;
1175                 GSignondSecurityContext *ctx = NULL;
1176
1177                 ctx = (GSignondSecurityContext *) list->data;
1178                 mech_iter = g_sequence_get_begin_iter (mechanisms);
1179                 while (!g_sequence_iter_is_end (mech_iter)) {
1180                     _gsignond_db_metadata_database_exec (self,
1181                             "INSERT OR REPLACE INTO ACL "
1182                             "(identity_id, method_id, mechanism_id, secctx_id) "
1183                             "VALUES ( %u, "
1184                             "( SELECT id FROM METHODS WHERE method = %Q ),"
1185                             "( SELECT id FROM MECHANISMS WHERE mechanism= %Q ),"
1186                             " ( SELECT id FROM SECCTX WHERE sysctx = %Q "
1187                             "AND appctx = %Q));",
1188                             id, method, g_sequence_get (mech_iter),
1189                             ctx->sys_ctx, ctx->app_ctx);
1190                     mech_iter = g_sequence_iter_next (mech_iter);
1191                 }
1192                 if (g_sequence_get_length (mechanisms) <= 0) {
1193                     _gsignond_db_metadata_database_exec (self,
1194                             "INSERT OR REPLACE INTO ACL "
1195                             "(identity_id, method_id, secctx_id) "
1196                             "VALUES ( %u, "
1197                             "( SELECT id FROM METHODS WHERE method = %Q),"
1198                             "( SELECT id FROM SECCTX WHERE sysctx = %Q AND "
1199                             "appctx = %Q ));",
1200                             id, method, ctx->sys_ctx, ctx->app_ctx);
1201                 }
1202             }
1203
1204         } else {
1205             GSequenceIter *mech_iter = NULL;
1206             mech_iter = g_sequence_get_begin_iter (mechanisms);
1207             while (!g_sequence_iter_is_end (mech_iter)) {
1208                 _gsignond_db_metadata_database_exec (self,
1209                         "INSERT OR REPLACE INTO ACL "
1210                         "(identity_id, method_id, mechanism_id) "
1211                         "VALUES ( %u, "
1212                         "( SELECT id FROM METHODS WHERE method = %Q ),"
1213                         "( SELECT id FROM MECHANISMS WHERE mechanism= %Q ));",
1214                         id, method, g_sequence_get (mech_iter));
1215                 mech_iter = g_sequence_iter_next (mech_iter);
1216             }
1217             if (g_sequence_get_length (mechanisms) <= 0) {
1218                 _gsignond_db_metadata_database_exec (self,
1219                         "INSERT OR REPLACE INTO ACL (identity_id, method_id) "
1220                         "VALUES ( %u, "
1221                         "( SELECT id FROM METHODS WHERE method = %Q ));",
1222                         id, method );
1223             }
1224         }
1225     }
1226     /* insert acl in case where methods are missing */
1227     if (g_hash_table_size (methods) <= 0) {
1228         for (list = acl;  list != NULL; list = g_list_next (list)) {
1229             GSignondSecurityContext *ctx = NULL;
1230
1231             ctx = (GSignondSecurityContext *) list->data;
1232             _gsignond_db_metadata_database_exec (self,
1233                     "INSERT OR REPLACE INTO ACL "
1234                     "(identity_id, secctx_id) "
1235                     "VALUES ( %u, "
1236                     "( SELECT id FROM SECCTX WHERE sysctx = %Q AND "
1237                     "appctx = %Q));",
1238                     id, ctx->sys_ctx, ctx->app_ctx);
1239         }
1240     }
1241     }
1242
1243     if (gsignond_db_sql_database_commit_transaction (sql)) {
1244         DBG ("Identity updated");
1245         ret = id;
1246     }
1247
1248 finished:
1249     if (methods) g_hash_table_unref (methods);
1250     if (realms) g_sequence_free (realms);
1251     if (acl) gsignond_security_context_list_free (acl);
1252     if (owner) gsignond_security_context_free (owner);
1253
1254     return ret;
1255 }
1256
1257 /**
1258  * gsignond_db_metadata_database_update_identity:
1259  *
1260  * @self: instance of #GSignondDbMetadataDatabase
1261  * @identity_id: the id of the identity
1262  *
1263  * Reads the identity data from the database based on the given id.
1264  *
1265  * Returns: (transfer full) the #GSignondIdentityInfo identity if successful,
1266  * NULL otherwise.
1267  */
1268 GSignondIdentityInfo *
1269 gsignond_db_metadata_database_get_identity (
1270         GSignondDbMetadataDatabase *self,
1271         const guint32 identity_id)
1272 {
1273     GSignondIdentityInfo *identity = NULL;
1274     gchar *query = NULL;
1275     gint rows = 0;
1276     GSequence *realms = NULL, *mechanisms = NULL;
1277     GHashTable *methods = NULL, *tuples = NULL;
1278     GHashTableIter iter;
1279     gchar *method = NULL;
1280     gint method_id = 0;
1281     GSignondSecurityContextList *acl = NULL;
1282     GSignondSecurityContext *owner = NULL;
1283
1284     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), NULL);
1285     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), NULL);
1286
1287     identity = gsignond_identity_info_new ();
1288     query = sqlite3_mprintf ("SELECT caption, username, flags, type "
1289                              "FROM IDENTITY WHERE id = %u;",
1290                              identity_id);
1291     rows = gsignond_db_sql_database_query_exec (GSIGNOND_DB_SQL_DATABASE (self),
1292             query, (GSignondDbSqlDatabaseQueryCallback)
1293             _gsignond_db_metadata_database_read_identity,
1294             identity);
1295     sqlite3_free (query);
1296     if (G_UNLIKELY (rows <= 0)) {
1297         DBG ("Fetch IDENTITY '%d' failed", identity_id);
1298         gsignond_identity_info_unref (identity);
1299         return NULL;
1300     }
1301     gsignond_identity_info_set_id (identity, identity_id);
1302
1303     /*realms*/
1304     realms = _gsignond_db_metadata_database_get_sequence (self,
1305             "SELECT realm FROM REALMS "
1306             "WHERE identity_id = %u;",
1307             identity_id);
1308     if (realms) {
1309         gsignond_identity_info_set_realms (identity, realms);
1310         g_sequence_free (realms);
1311     }
1312
1313     /*acl*/
1314     acl = gsignond_db_metadata_database_get_accesscontrol_list (self,
1315             identity_id);
1316     if (acl) {
1317         gsignond_identity_info_set_access_control_list (identity, acl);
1318         gsignond_security_context_list_free (acl);
1319     }
1320
1321     /*owner*/
1322     owner = gsignond_db_metadata_database_get_owner (self,
1323             identity_id);
1324     if (owner) {
1325         gsignond_identity_info_set_owner (identity, owner);
1326         gsignond_security_context_free (owner);
1327     }
1328
1329     /*methods*/
1330     query = sqlite3_mprintf ("SELECT DISTINCT ACL.method_id, METHODS.method "
1331             "FROM ( ACL JOIN METHODS ON ACL.method_id = METHODS.id ) "
1332             "WHERE ACL.identity_id = %u;",
1333             identity_id);
1334     tuples = gsignond_db_sql_database_query_exec_int_string_tuple (
1335                     GSIGNOND_DB_SQL_DATABASE (self),
1336                     query);
1337     sqlite3_free (query);
1338
1339     if (tuples) {
1340         methods = g_hash_table_new_full ((GHashFunc)g_str_hash,
1341                 (GEqualFunc)g_str_equal,
1342                 (GDestroyNotify)g_free,
1343                 (GDestroyNotify)g_sequence_free);
1344         g_hash_table_iter_init(&iter, tuples);
1345         while (g_hash_table_iter_next (&iter, (gpointer *)&method_id,
1346                 (gpointer *)&method)) {
1347             /*mechanisms*/
1348             mechanisms = _gsignond_db_metadata_database_get_sequence (self,
1349                     "SELECT DISTINCT MECHANISMS.mechanism FROM "
1350                     "( MECHANISMS JOIN ACL ON ACL.mechanism_id = MECHANISMS.id ) "
1351                     "WHERE ACL.method_id = %u AND ACL.identity_id = %u;",
1352                     method_id, identity_id);
1353             g_hash_table_insert(methods, g_strdup(method), mechanisms);
1354         }
1355         g_hash_table_destroy (tuples);
1356         gsignond_identity_info_set_methods (identity, methods);
1357         g_hash_table_destroy (methods);
1358     }
1359
1360     return identity;
1361 }
1362
1363 /**
1364  * gsignond_db_metadata_database_get_identities:
1365  *
1366  * @self: instance of #GSignondDbMetadataDatabase
1367  * @filter: (transfer none) filter to apply (supported filters: Owner, Type & Caption)
1368  *
1369  * Reads all the identities that are matched by applying @filter,
1370  * from the database into a list.
1371  *
1372  * Returns: (transfer full) the list #GSignondIdentityInfoList if successful,
1373  * NULL otherwise. When done the list should be freed with
1374  * gsignond_identity_info_list_free
1375  */
1376 GSignondIdentityInfoList *
1377 gsignond_db_metadata_database_get_identities (
1378         GSignondDbMetadataDatabase *self,
1379         GSignondDictionary *filter)
1380 {
1381     GSignondIdentityInfoList *identities = NULL;
1382     gchar *owner_query = NULL;
1383     gchar *caption_query = NULL;
1384     gchar *type_query = NULL;
1385     gchar *query = NULL;
1386     GArray *ids = NULL;
1387     gint i;
1388
1389     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
1390     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), NULL);
1391
1392     if (filter) {
1393         GVariant *owner_var = NULL;
1394         const gchar *caption = NULL;
1395         gint type = 0;
1396         gboolean append_where = TRUE;
1397
1398         if ((owner_var = gsignond_dictionary_get (filter, "Owner"))) {
1399                 GSignondSecurityContext *owner_ctx =
1400                          gsignond_security_context_from_variant(owner_var);
1401                 owner_query = sqlite3_mprintf ("WHERE id IN "
1402                         "(SELECT identity_id FROM owner WHERE secctx_id = "
1403                         "(SELECT id FROM secctx WHERE sysctx=%Q AND appctx=%Q))",
1404                         gsignond_security_context_get_system_context(owner_ctx),
1405                         gsignond_security_context_get_application_context(owner_ctx));
1406                 gsignond_security_context_free (owner_ctx);
1407                 append_where = FALSE;
1408         }
1409
1410         if ((caption = gsignond_dictionary_get_string (filter, "Caption"))) {
1411                 caption_query = sqlite3_mprintf (" %s caption like '%s%%'",
1412                                              append_where ? "WHERE" : "AND", caption);
1413                 append_where = FALSE;
1414         }
1415
1416         if (gsignond_dictionary_get_int32 (filter, "Type", &type)) {
1417                 type_query = sqlite3_mprintf (" %s type = %d",
1418                                 append_where ? "WHERE" : "AND", type);
1419                 append_where = FALSE;
1420         }
1421     }
1422
1423     query = sqlite3_mprintf ("SELECT id FROM IDENTITY %s%s%s ORDER BY id",
1424                 owner_query ? owner_query : "",
1425                 caption_query ? caption_query : "",
1426                 type_query ? type_query : "");
1427     sqlite3_free(owner_query);
1428     sqlite3_free(caption_query);
1429     sqlite3_free(type_query);
1430
1431     ids = gsignond_db_sql_database_query_exec_int_array (
1432                 GSIGNOND_DB_SQL_DATABASE (self),
1433                 query);
1434     sqlite3_free (query);
1435     if (!ids) {
1436         DBG ("No identity found");
1437         return NULL;
1438     }
1439
1440     for (i=0; i < ids->len; i++) {
1441         GSignondIdentityInfo *identity = NULL;
1442         identity = gsignond_db_metadata_database_get_identity (self,
1443                 g_array_index (ids, gint, i));
1444         if (identity) {
1445             identities = g_list_append (identities, identity);
1446         }
1447     }
1448     g_array_free (ids, TRUE);
1449     return identities;
1450 }
1451
1452 /**
1453  * gsignond_db_metadata_database_remove_identity:
1454  *
1455  * @self: instance of #GSignondDbMetadataDatabase
1456  * @identity_id: the id of the identity
1457  *
1458  * Removes the identity data from the database based on the given id.
1459  *
1460  * Returns: TRUE if successful,FALSE otherwise.
1461  */
1462 gboolean
1463 gsignond_db_metadata_database_remove_identity (
1464         GSignondDbMetadataDatabase *self,
1465         const guint32 identity_id)
1466 {
1467     gchar *queries = NULL;
1468     gboolean ret = FALSE;
1469
1470     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
1471     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), FALSE);
1472
1473     /* Triggers should handle the cleanup of other tables */
1474     queries = sqlite3_mprintf ("DELETE FROM IDENTITY WHERE id = %u;",
1475                                identity_id);
1476     ret = gsignond_db_sql_database_transaction_exec (
1477             GSIGNOND_DB_SQL_DATABASE (self), queries);
1478     sqlite3_free (queries);
1479
1480     return ret;
1481 }
1482
1483 /**
1484  * gsignond_db_metadata_database_insert_reference:
1485  *
1486  * @self: instance of #GSignondDbMetadataDatabase
1487  * @identity_id: the id of the identity
1488  * @ref_owner: the owner security context
1489  * @reference: reference for the given identity
1490  *
1491  * Insert reference into the database for the given identity id.
1492  *
1493  * Returns: TRUE if successful,FALSE otherwise.
1494  */
1495 gboolean
1496 gsignond_db_metadata_database_insert_reference (
1497         GSignondDbMetadataDatabase *self,
1498         const guint32 identity_id,
1499         const GSignondSecurityContext *ref_owner,
1500         const gchar *reference)
1501 {
1502     GSignondDbSqlDatabase *sql = NULL;
1503
1504     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), 0);
1505     g_return_val_if_fail (ref_owner != NULL && reference != NULL, FALSE);
1506     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), FALSE);
1507
1508     sql = GSIGNOND_DB_SQL_DATABASE (self);
1509     if (!gsignond_db_sql_database_start_transaction (sql)) {
1510         DBG ("Start transaction failed");
1511         return FALSE;
1512     }
1513
1514     if (!_gsignond_db_metadata_database_exec (self,
1515             "INSERT OR IGNORE INTO SECCTX (sysctx, appctx) "
1516             "VALUES ( %Q, %Q );", ref_owner->sys_ctx, ref_owner->app_ctx)) {
1517         DBG ("Insertion SECCTX to DB failed");
1518         gsignond_db_sql_database_rollback_transaction (sql);
1519         return FALSE;
1520     }
1521     if (!_gsignond_db_metadata_database_exec (self,
1522             "INSERT OR REPLACE INTO REFS "
1523             "(identity_id, secctx_id, ref) "
1524             "VALUES ( %u, "
1525             "( SELECT id FROM SECCTX "
1526             "WHERE sysctx = %Q AND appctx = %Q), %Q );",
1527             identity_id, ref_owner->sys_ctx, ref_owner->app_ctx, reference)) {
1528         DBG ("Insertion to REFS failed");
1529         gsignond_db_sql_database_rollback_transaction (sql);
1530         return FALSE;
1531     }
1532
1533     return gsignond_db_sql_database_commit_transaction (sql);
1534 }
1535
1536 /**
1537  * gsignond_db_metadata_database_remove_reference:
1538  *
1539  * @self: instance of #GSignondDbMetadataDatabase
1540  * @identity_id: the id of the identity
1541  * @ref_owner: the owner security context
1542  * @reference: reference for the given identity
1543  *
1544  * Removes reference from the database for the given identity id.
1545  *
1546  * Returns: TRUE if successful,FALSE otherwise.
1547  */
1548 gboolean
1549 gsignond_db_metadata_database_remove_reference (
1550         GSignondDbMetadataDatabase *self,
1551         const guint32 identity_id,
1552         const GSignondSecurityContext *ref_owner,
1553         const gchar *reference)
1554 {
1555     GSignondDbSqlDatabase *sql = NULL;
1556     GList *refs = NULL;
1557     gboolean ret = TRUE;
1558     guint len = 0;
1559
1560     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), 0);
1561     g_return_val_if_fail (ref_owner != NULL, FALSE);
1562     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), FALSE);
1563
1564     sql = GSIGNOND_DB_SQL_DATABASE (self);
1565     if (!gsignond_db_sql_database_start_transaction (sql)) {
1566         DBG ("Start transaction failed");
1567         return FALSE;
1568     }
1569
1570     refs = gsignond_db_metadata_database_get_references (self,
1571             identity_id, ref_owner);
1572
1573     len = g_list_length (refs);
1574     if (reference && !g_list_find_custom (refs, reference,
1575                 (GCompareFunc)g_strcmp0))
1576         ret = FALSE;
1577     g_list_free_full (refs, (GDestroyNotify)g_free);
1578     if (len <= 0 || !ret) {
1579         DBG ("No ref found");
1580         gsignond_db_sql_database_rollback_transaction (sql);
1581         return FALSE;
1582     }
1583
1584     if (!reference || strlen (reference) <= 0) {
1585         ret = _gsignond_db_metadata_database_exec (self,
1586                 "DELETE FROM REFS "
1587                 "WHERE identity_id = %u AND "
1588                 "secctx_id = ( SELECT id FROM SECCTX "
1589                 "WHERE sysctx = %Q AND appctx = %Q );",
1590                 identity_id, ref_owner->sys_ctx, ref_owner->app_ctx);
1591     } else {
1592         ret = _gsignond_db_metadata_database_exec (self,
1593                 "DELETE FROM REFS "
1594                 "WHERE identity_id = %u AND "
1595                 "secctx_id = ( SELECT id FROM SECCTX "
1596                 "WHERE sysctx = %Q AND appctx = %Q ) "
1597                 "AND ref = :ref;",
1598                 identity_id, ref_owner->sys_ctx, ref_owner->app_ctx, reference);
1599     }
1600     if (!ret) {
1601         DBG ("Delete refs from DB failed");
1602         gsignond_db_sql_database_rollback_transaction (sql);
1603         return FALSE;
1604     }
1605
1606     return gsignond_db_sql_database_commit_transaction (sql);
1607 }
1608
1609 /**
1610  * gsignond_db_metadata_database_get_references:
1611  *
1612  * @self: instance of #GSignondDbMetadataDatabase
1613  * @identity_id: the id of the identity
1614  * @ref_owner: the owner security context
1615  *
1616  * Gets references from the database for the given identity id.
1617  *
1618  * Returns: (transfer full) the list #GList if successful,
1619  * NULL otherwise. When done the list should be freed with
1620  * g_list_free_full (list, g_free)
1621  */
1622 GList *
1623 gsignond_db_metadata_database_get_references (
1624         GSignondDbMetadataDatabase *self,
1625         const guint32 identity_id,
1626         const GSignondSecurityContext* ref_owner)
1627 {
1628     gchar *query = NULL;
1629     GList *list = NULL;
1630
1631     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), NULL);
1632     g_return_val_if_fail (ref_owner != NULL, NULL);
1633     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), NULL);
1634
1635     if (!ref_owner->sys_ctx || strlen (ref_owner->sys_ctx) <= 0) {
1636         query = sqlite3_mprintf ("SELECT ref FROM REFS "
1637                                  "WHERE identity_id = %u;",
1638                                  identity_id);
1639     } else {
1640         query = sqlite3_mprintf ("SELECT ref FROM REFS "
1641                 "WHERE identity_id = %u AND "
1642                 "secctx_id = (SELECT id FROM SECCTX "
1643                 "WHERE sysctx = %Q AND appctx = %Q );",
1644                 identity_id, ref_owner->sys_ctx, ref_owner->app_ctx );
1645     }
1646     list = gsignond_db_sql_database_query_exec_string_list (
1647             GSIGNOND_DB_SQL_DATABASE (self),
1648             query);
1649     sqlite3_free (query);
1650     return list;
1651 }
1652
1653 /**
1654  * gsignond_db_metadata_database_get_accesscontrol_list:
1655  *
1656  * @self: instance of #GSignondDbMetadataDatabase
1657  * @identity_id: the id of the identity whose access control list is needed
1658  *
1659  * Gets all the access control list from the database into a list.
1660  *
1661  * Returns: (transfer full) the list #GSignondSecurityContextList if successful,
1662  * NULL otherwise. When done the list should be freed with
1663  * gsignond_identity_info_list_free
1664  */
1665 GSignondSecurityContextList *
1666 gsignond_db_metadata_database_get_accesscontrol_list(
1667         GSignondDbMetadataDatabase *self,
1668         const guint32 identity_id)
1669 {
1670     GSignondSecurityContextList *list = NULL;
1671     GHashTable *tuples = NULL;
1672     gchar *query = NULL;
1673     GHashTableIter iter;
1674     const gchar *sysctx = NULL, *appctx = NULL;
1675     GSignondSecurityContext *ctx = NULL;
1676
1677     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
1678     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), NULL);
1679
1680     query = sqlite3_mprintf ("SELECT sysctx, appctx FROM SECCTX "
1681             "WHERE id IN "
1682             "(SELECT secctx_id FROM ACL WHERE identity_id = %u);",
1683             identity_id);
1684     tuples = gsignond_db_sql_database_query_exec_string_tuple (
1685                     GSIGNOND_DB_SQL_DATABASE (self),
1686                     query);
1687     sqlite3_free (query);
1688
1689     if (tuples) {
1690         g_hash_table_iter_init(&iter, tuples);
1691         while (g_hash_table_iter_next (&iter, (gpointer *)&sysctx,
1692                 (gpointer *)&appctx)) {
1693             ctx = gsignond_security_context_new_from_values (sysctx, appctx);
1694             list = g_list_append (list, ctx);
1695         }
1696         g_hash_table_unref (tuples);
1697     }
1698     return list;
1699 }
1700
1701 /**
1702  * gsignond_db_metadata_database_get_owner:
1703  *
1704  * @self: instance of #GSignondDbMetadataDatabase
1705  * @identity_id: the id of the identity whose owner list is needed
1706  *
1707  * Gets the onwer of identity referred by @identity_id from the database.
1708  *
1709  * Returns: (transfer full) the  #GSignondSecurityContext if successful,
1710  * NULL otherwise. When done the list should be freed with
1711  * gsignond_identity_info_unref
1712  */
1713 GSignondSecurityContext *
1714 gsignond_db_metadata_database_get_owner(
1715         GSignondDbMetadataDatabase *self,
1716         const guint32 identity_id)
1717 {
1718     GHashTable *tuples = NULL;
1719     gchar *query = NULL;
1720     GSignondSecurityContext *ctx = NULL;
1721
1722     g_return_val_if_fail (GSIGNOND_DB_IS_METADATA_DATABASE (self), FALSE);
1723     RETURN_IF_NOT_OPEN (GSIGNOND_DB_SQL_DATABASE (self), NULL);
1724
1725     query = sqlite3_mprintf ("SELECT sysctx, appctx FROM SECCTX "
1726             "WHERE id IN "
1727             "(SELECT secctx_id FROM OWNER WHERE identity_id = %u);",
1728             identity_id);
1729     tuples = gsignond_db_sql_database_query_exec_string_tuple (
1730                     GSIGNOND_DB_SQL_DATABASE (self),
1731                     query);
1732     sqlite3_free (query);
1733
1734     if (tuples) {
1735         GHashTableIter iter;
1736         const gchar *sysctx = NULL, *appctx = NULL;
1737         g_hash_table_iter_init(&iter, tuples);
1738         while (g_hash_table_iter_next (&iter, (gpointer *)&sysctx,
1739                 (gpointer *)&appctx)) {
1740             ctx = gsignond_security_context_new_from_values (sysctx, appctx);
1741             break;
1742         }
1743         g_hash_table_unref (tuples);
1744     }
1745     return ctx;
1746 }
1747
1748