anastasis-gtk

Demonstrator GUI for Anastasis
Log | Files | Refs | README | LICENSE

anastasis-gtk_pe-edit-policy.c (14308B)


      1 /*
      2      This file is part of anastasis-gtk.
      3      Copyright (C) 2021 Anastasis SARL
      4 
      5      Anastasis is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      Anastasis is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with Anastasis; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19 */
     20 
     21 /**
     22  * @file src/anastasis/anastasis-gtk_pe-edit-policy.c
     23  * @brief Handle request to interactively edit policy
     24  * @author Christian Grothoff
     25  */
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include <strings.h>
     28 #include "anastasis_gtk_util.h"
     29 #include "anastasis-gtk_action.h"
     30 #include "anastasis-gtk_helper.h"
     31 #include "anastasis-gtk_pe.h"
     32 #include <jansson.h>
     33 
     34 
     35 /**
     36  * Context for the edit dialog.
     37  */
     38 struct EditDialogContext;
     39 
     40 /**
     41  * Information we track per line in the grid.
     42  */
     43 struct LineContext
     44 {
     45   /**
     46    * Kept in a DLL.
     47    */
     48   struct LineContext *next;
     49 
     50   /**
     51    * Kept in a DLL.
     52    */
     53   struct LineContext *prev;
     54 
     55   /**
     56    * Context this line context belongs with.
     57    */
     58   struct EditDialogContext *edc;
     59 
     60   /**
     61    * Our drop down.
     62    */
     63   GtkDropDown *cb;
     64 
     65   /**
     66    * The provider URLs our drop down offers.
     67    */
     68   GtkStringList *model;
     69 
     70   /**
     71    * Challenge index for this line.
     72    */
     73   unsigned int cindex;
     74 
     75   /**
     76    * Is this challenge used?
     77    */
     78   bool on;
     79 };
     80 
     81 
     82 /**
     83  * Context for the edit dialog.
     84  */
     85 struct EditDialogContext
     86 {
     87   /**
     88    * Builder of the dialog.
     89    */
     90   GtkBuilder *builder;
     91 
     92   /**
     93    * Head of line contexts for this dialog
     94    */
     95   struct LineContext *lc_head;
     96 
     97   /**
     98    * Tail of line contexts for this dialog
     99    */
    100   struct LineContext *lc_tail;
    101 
    102   /**
    103    * Policy index. UINT_MAX for a new policy.
    104    */
    105   unsigned int pindex;
    106 
    107 };
    108 
    109 
    110 /**
    111  * Key under which the dialog's `struct EditDialogContext` is attached to its
    112  * builder.  GtkBuilder hands the builder itself to the signal handlers, so
    113  * that is where we keep our own state.
    114  */
    115 #define EDC_KEY "anastasis-edit-dialog-context"
    116 
    117 
    118 /**
    119  * Handle the response from the edit dialog.
    120  *
    121  * @param dialog the dialog
    122  * @param response_id what the user's action was
    123  * @param user_data the builder of the dialog
    124  */
    125 void
    126 anastasis_gtk_policy_edit_dialog_response_cb (
    127   GtkDialog *dialog,
    128   gint response_id,
    129   gpointer user_data)
    130 {
    131   struct EditDialogContext *edc = g_object_get_data (G_OBJECT (user_data),
    132                                                      EDC_KEY);
    133 
    134   if (GTK_RESPONSE_OK == response_id)
    135   {
    136     json_t *policy;
    137 
    138     policy = json_array ();
    139     GNUNET_assert (NULL != policy);
    140     for (struct LineContext *lctx = edc->lc_head;
    141          NULL != lctx;
    142          lctx = lctx->next)
    143     {
    144       guint selected;
    145 
    146       if (! lctx->on)
    147         continue;
    148       selected = gtk_drop_down_get_selected (lctx->cb);
    149       if (GTK_INVALID_LIST_POSITION == selected)
    150       {
    151         GNUNET_break (0);
    152         continue;
    153       }
    154       GNUNET_assert (0 ==
    155                      json_array_append_new (
    156                        policy,
    157                        GNUNET_JSON_PACK (
    158                          GNUNET_JSON_pack_uint64 ("authentication_method",
    159                                                   lctx->cindex),
    160                          GNUNET_JSON_pack_string ("provider",
    161                                                   gtk_string_list_get_string (
    162                                                     lctx->model,
    163                                                     selected)))));
    164     }
    165     if (UINT_MAX == edc->pindex)
    166     {
    167       json_t *args;
    168 
    169       args = GNUNET_JSON_PACK (
    170         GNUNET_JSON_pack_array_steal ("policy",
    171                                       policy));
    172       AG_ra = ANASTASIS_redux_action (AG_redux_state,
    173                                       "add_policy",
    174                                       args,
    175                                       &AG_action_cb,
    176                                       NULL);
    177       json_decref (args);
    178     }
    179     else
    180     {
    181       json_t *args;
    182 
    183       args = GNUNET_JSON_PACK (
    184         GNUNET_JSON_pack_uint64 ("policy_index",
    185                                  edc->pindex),
    186         GNUNET_JSON_pack_array_steal ("policy",
    187                                       policy));
    188       AG_ra = ANASTASIS_redux_action (AG_redux_state,
    189                                       "update_policy",
    190                                       args,
    191                                       &AG_action_cb,
    192                                       NULL);
    193       json_decref (args);
    194     }
    195   }
    196   /* clean up */
    197   {
    198     struct LineContext *lctx;
    199 
    200     while (NULL != (lctx = edc->lc_head))
    201     {
    202       GNUNET_CONTAINER_DLL_remove (edc->lc_head,
    203                                    edc->lc_tail,
    204                                    lctx);
    205       GNUNET_free (lctx);
    206     }
    207   }
    208   gtk_window_destroy (GTK_WINDOW (dialog));
    209   g_object_unref (G_OBJECT (edc->builder));
    210   GNUNET_free (edc);
    211 }
    212 
    213 
    214 /**
    215  * The user changed an entry in the drop down of an edit
    216  * dialog. Update the ability to confirm the selection:
    217  * if at least one authentication method is selected, the
    218  * OK button should be sensitive.
    219  *
    220  * @param widget the drop down that was changed
    221  * @param pspec unused
    222  * @param user_data the `struct EditDialogContext`
    223  */
    224 static void
    225 drop_down_changed_cb (
    226   GtkDropDown *widget,
    227   GParamSpec *pspec,
    228   gpointer user_data)
    229 {
    230   struct LineContext *lc = user_data;
    231   struct EditDialogContext *edc = lc->edc;
    232 
    233   (void) widget;
    234   (void) pspec;
    235   /* Update our line context's on/off flag */
    236   {
    237     guint selected = gtk_drop_down_get_selected (lc->cb);
    238 
    239     if (GTK_INVALID_LIST_POSITION == selected)
    240     {
    241       GNUNET_break (0);
    242     }
    243     else
    244     {
    245       lc->on = (0 !=
    246                 strcmp (_ ("<off>"),
    247                         gtk_string_list_get_string (lc->model,
    248                                                     selected)));
    249     }
    250   }
    251   /* finally, update "OK" button sensitivity */
    252   {
    253     GtkWidget *ok_button;
    254     bool legal = false;
    255 
    256     for (struct LineContext *lctx = edc->lc_head;
    257          NULL != lctx;
    258          lctx = lctx->next)
    259       legal |= lctx->on;
    260     ok_button = GTK_WIDGET (gtk_builder_get_object (edc->builder,
    261                                                     "ok_button"));
    262     gtk_widget_set_sensitive (ok_button,
    263                               legal);
    264   }
    265 }
    266 
    267 
    268 /**
    269  * Check if the authentication provider @a ap offers
    270  * authentications of type @a type. If so, return true.
    271  *
    272  * @param type method to check for
    273  * @param ap provider to check against
    274  * @return true if @a ap offers @a type
    275  */
    276 static bool
    277 ap_matches (const char *type,
    278             json_t *ap)
    279 {
    280   json_t *methods;
    281   size_t index;
    282   json_t *method;
    283   const char *status;
    284 
    285   status = json_string_value (json_object_get (ap,
    286                                                "status"));
    287   if (0 != strcasecmp (status,
    288                        "ok"))
    289   {
    290     /* provider is down, cannot proceed */
    291     return false;
    292   }
    293   methods = json_object_get (ap,
    294                              "methods");
    295   GNUNET_break (NULL != methods);
    296   json_array_foreach (methods, index, method)
    297   {
    298     const char *offer;
    299 
    300     offer = json_string_value (json_object_get (method,
    301                                                 "type"));
    302     if (NULL == offer)
    303     {
    304       GNUNET_break (0);
    305       continue;
    306     }
    307     if (0 == strcasecmp (type,
    308                          offer))
    309       return true;
    310   }
    311   return false;
    312 }
    313 
    314 
    315 /**
    316  * Create a list with all of the URLs of Anastasis providers offering
    317  * @a type as an authentication method.
    318  *
    319  * @return the model
    320  */
    321 static GtkStringList *
    322 make_model (const char *type)
    323 {
    324   GtkStringList *sl;
    325   json_t *aps;
    326   const char *url;
    327   json_t *ap;
    328 
    329   sl = gtk_string_list_new (NULL);
    330   gtk_string_list_append (sl,
    331                           _ ("<off>"));
    332   aps = json_object_get (AG_redux_state,
    333                          "authentication_providers");
    334   GNUNET_break (NULL != aps);
    335   json_object_foreach (aps, url, ap) {
    336     if (ap_matches (type,
    337                     ap))
    338     {
    339       gtk_string_list_append (sl,
    340                               url);
    341     }
    342   }
    343 
    344   return sl;
    345 }
    346 
    347 
    348 /**
    349  * Select entry in @a cb based on the @a url.
    350  *
    351  * @param url provider to select
    352  * @param lctx line to update
    353  */
    354 static void
    355 select_by_url (const char *url,
    356                struct LineContext *lctx)
    357 {
    358   guint n = g_list_model_get_n_items (G_LIST_MODEL (lctx->model));
    359 
    360   for (guint i = 0; i < n; i++)
    361   {
    362     if (0 == strcmp (gtk_string_list_get_string (lctx->model,
    363                                                  i),
    364                      url))
    365     {
    366       gtk_drop_down_set_selected (lctx->cb,
    367                                   i);
    368       lctx->on = true;
    369       return;
    370     }
    371   }
    372   GNUNET_break (0); /* not found */
    373 }
    374 
    375 
    376 /**
    377  * Select entry in @a cb based on the @a methods for
    378  * challenge @a cindex.
    379  *
    380  * @param methods methods of policy to base selection on
    381  * @param lctx line to update
    382  */
    383 static void
    384 select_by_policy (const json_t *methods,
    385                   struct LineContext *lctx)
    386 {
    387   size_t index;
    388   json_t *method;
    389 
    390   /* default to the "<off>" entry we put first */
    391   gtk_drop_down_set_selected (lctx->cb,
    392                               0);
    393   json_array_foreach (methods, index, method) {
    394     json_int_t am = json_integer_value (json_object_get (method, \
    395                                                          "authentication_method"));
    396     const char *url;
    397 
    398     if (am != lctx->cindex)
    399       continue;
    400     url = json_string_value (json_object_get (method,
    401                                               "provider"));
    402     select_by_url (url,
    403                    lctx);
    404     break;
    405   }
    406 }
    407 
    408 
    409 void
    410 AG_edit_policy (guint pindex)
    411 {
    412   struct EditDialogContext *edc;
    413   GtkGrid *grid;
    414   json_t *methods = NULL;
    415 
    416   edc = GNUNET_new (struct EditDialogContext);
    417   edc->builder = ANASTASIS_GTK_get_new_builder (
    418     ANASTASIS_GTK_project_data (),
    419     "anastasis_gtk_edit_policy.ui",
    420     NULL);
    421   edc->pindex = pindex;
    422   if (NULL == edc->builder)
    423   {
    424     GNUNET_break (0);
    425     GNUNET_free (edc);
    426     return;
    427   }
    428   g_object_set_data (G_OBJECT (edc->builder),
    429                      EDC_KEY,
    430                      edc);
    431   if (UINT_MAX != pindex)
    432   {
    433     json_t *policies;
    434     json_t *policy;
    435 
    436     policies = json_object_get (AG_redux_state,
    437                                 "policies");
    438     policy = json_array_get (policies,
    439                              pindex);
    440     methods = json_object_get (policy,
    441                                "methods");
    442     GNUNET_break (NULL != methods);
    443   }
    444   grid = GTK_GRID (gtk_builder_get_object (edc->builder,
    445                                            "policy_grid"));
    446   {
    447     json_t *ams = json_object_get (AG_redux_state,
    448                                    "authentication_methods");
    449     json_t *am;
    450     size_t index;
    451     gint row = 1;
    452 
    453     json_array_foreach (ams, index, am) {
    454       const char *type;
    455       const char *instructions;
    456       struct GNUNET_JSON_Specification spec[] = {
    457         GNUNET_JSON_spec_string ("type",
    458                                  &type),
    459         GNUNET_JSON_spec_string ("instructions",
    460                                  &instructions),
    461         GNUNET_JSON_spec_end ()
    462       };
    463       char *labels;
    464       GtkWidget *label;
    465       GtkWidget *cb;
    466       struct LineContext *lctx;
    467 
    468       if (GNUNET_OK !=
    469           GNUNET_JSON_parse (am,
    470                              spec,
    471                              NULL, NULL))
    472       {
    473         GNUNET_break (0);
    474         continue;
    475       }
    476       lctx = GNUNET_new (struct LineContext);
    477       lctx->cindex = index;
    478       GNUNET_asprintf (&labels,
    479                        "<b>%s</b>: %s",
    480                        type,
    481                        instructions);
    482       label = gtk_label_new (NULL);
    483       gtk_label_set_markup (GTK_LABEL (label),
    484                             labels);
    485       GNUNET_free (labels);
    486       lctx->model = make_model (type);
    487       /* the drop down takes over our reference to the model */
    488       cb = gtk_drop_down_new (G_LIST_MODEL (lctx->model),
    489                               NULL);
    490       lctx->cb = GTK_DROP_DOWN (cb);
    491       lctx->edc = edc;
    492       GNUNET_CONTAINER_DLL_insert (edc->lc_head,
    493                                    edc->lc_tail,
    494                                    lctx);
    495       g_object_connect (cb,
    496                         "signal::notify::selected",
    497                         &drop_down_changed_cb, lctx,
    498                         NULL);
    499       if (NULL != methods)
    500         select_by_policy (methods,
    501                           lctx);
    502       gtk_grid_insert_row (grid,
    503                            row);
    504       gtk_grid_attach (grid,
    505                        label,
    506                        0,
    507                        row,
    508                        1,
    509                        1);
    510       gtk_widget_set_hexpand (cb,
    511                               TRUE);
    512       gtk_widget_set_vexpand (cb,
    513                               TRUE);
    514       gtk_grid_attach (grid,
    515                        cb,
    516                        1,
    517                        row,
    518                        1,
    519                        1);
    520       row++;
    521     }
    522   }
    523   {
    524     GtkRoot *toplevel;
    525     GtkWidget *ad;
    526     GtkWidget *anchor;
    527     GtkRequisition req;
    528 
    529     anchor = GTK_WIDGET (GCG_get_main_window_object (
    530                            "anastasis_gtk_main_window_quit_button"));
    531     toplevel = gtk_widget_get_root (anchor);
    532     ad = GTK_WIDGET (gtk_builder_get_object (edc->builder,
    533                                              "anastasis_gtk_policy_edit_dialog")
    534                      );
    535     gtk_widget_get_preferred_size (ad,
    536                                    NULL,
    537                                    &req);
    538     gtk_window_set_default_size (GTK_WINDOW (ad),
    539                                  req.width,
    540                                  req.height);
    541     gtk_window_set_transient_for (GTK_WINDOW (ad),
    542                                   GTK_WINDOW (toplevel));
    543     gtk_window_present (GTK_WINDOW (ad));
    544   }
    545 }
    546 
    547 
    548 /* end of anastasis-gtk_pe-edit-policy.c */