#49957 Issue 49928 - WebUI schema functionality and improve CLI part
Closed 4 years ago by spichugi. Opened 6 years ago by spichugi.
spichugi/389-ds-base schema_webui  into  master

@@ -525,7 +525,7 @@ 

    color: #181818 !important;

    padding: 0px !important;

    line-height: 0 !important;

-   height: 40px;

+   height: 30px;

    max-height: 200px !important;

    width: 315px !important;

    text-align: left;

@@ -266,6 +266,7 @@ 

    get_and_set_config(); // cn=config stuff

    get_and_set_sasl();

    get_and_set_localpwp();

+   get_and_set_schema_tables();

  

    // Security page

    // Database page

@@ -22,11 +22,17 @@ 

      '</ul>' +

    '</div>';

  

+ var schema_oc_table;

+ var schema_at_table;

+ var schema_mr_table;

+ 

  function clear_oc_form() {

    // Clear input fields and reset dropboxes

    $("#add-edit-oc-header").html('Add Objectclass');

+   $(".ds-modal-error").hide();

    $("#oc-name").attr('disabled', false);

    $("#oc-name").val("");

+   $(".ds-input").css("border-color", "initial");

    $("#oc-oid").val("");

    $("#oc-parent").prop('selectedIndex',0);

    $("#schema-list").prop('selectedIndex',-1);
@@ -37,8 +43,10 @@ 

  function clear_attr_form() {

    // Clear input fields and reset dropboxes

    $("#add-edit-attr-header").html('Add Attribute');

+   $(".ds-modal-error").hide();

    $("#attr-name").attr('disabled', false);

    $("#attr-name").val("");

+   $(".ds-input").css("border-color", "initial");

    $("#attr-syntax").val("");

    $("#attr-desc").val("");

    $("#attr-oid").val("");
@@ -49,14 +57,79 @@ 

    $("#attr-sub-mr-select").prop('selectedIndex',0);

  };

  

+ function load_schema_objects_to_select(object, select_id) {

+   var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket', 'schema', object, 'list'];

+   cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

+     var obj = JSON.parse(data);

+     var data = []

+     for (var idx in obj['items']) {

+       item = obj['items'][idx];

+       if (item.name) {

+         data.push.apply(data, [item.name]);

+       } else {

+         data.push.apply(data, [item.oid]);

+       }

+     }

+     // Update html select

+     $.each(data, function (i, item) {

+         $("#" + select_id).append($('<option>', {

+             value: item,

+             text : item

+         }));

+     });

+   }).fail(function(data) {

+       console.log("failed: " + data.message);

+       check_inst_alive(1);

+   });

+ }

+ 

+ function get_and_set_schema_tables() {

+   load_schema_objects_to_select('matchingrules', 'attr-eq-mr-select')

+   load_schema_objects_to_select('matchingrules', 'attr-order-mr-select')

+   load_schema_objects_to_select('matchingrules', 'attr-sub-mr-select')

+   load_schema_objects_to_select('attributetypes', 'schema-list')

+   load_schema_objects_to_select('objectclasses', 'oc-parent')

+ 

+   // Load syntaxes

+   var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket', 'schema', "attributetypes", 'get_syntaxes'];

+   cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

+     var obj = JSON.parse(data);

+     var data = []

+     for (var idx in obj['items']) {

+       item = obj['items'][idx];

+       data.push.apply(data, [item]);

+     }

+     // Update html select

+     $.each(data, function (i, item) {

+         $("#attr-syntax").append($('<option>', {

+             value: item.id,

+             text : item.name + " (" + item.id + ")"

+         }));

+     });

+   }).fail(function(data) {

+       console.log("failed: " + data.message);

+       check_inst_alive(1);

+   });

  

- $(document).ready( function() {

-   $("#schema-content").load("schema.html", function (){

-     // TODO Get attributes, Objectclasses, syntaxes, and matching rules: populate tables and forms

- 

-     // Setup the tables: standard, custom, and Matching Rules

- 

-     var oc_table = $('#oc-table').DataTable ({

+   // Setup the tables: standard, custom, and Matching Rules

+   var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket', 'schema', 'objectclasses', 'list'];

+   cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

+     var obj = JSON.parse(data);

+     var data = [];

+     for (var idx in obj['items']) {

+       item = obj['items'][idx];

+       data.push.apply(data, [[

+         item.name,

+         item.oid,

+         item.sup,

+         item["must"].join(" "),

+         item["may"].join(" "),

+         oc_btn_html

+       ]]);

+     }

+     // Update html table

+     schema_oc_table = $('#oc-table').DataTable ({

+       "data": data,

        "paging": true,

        "bAutoWidth": false,

        "dom": '<"pull-left"f><"pull-right"l>tip',
@@ -70,7 +143,38 @@ 

          "orderable": false

        } ]

      });

-     var at_table = $('#attr-table').DataTable({

+   }).fail(function(data) {

+       console.log("failed: " + data.message);

+       check_inst_alive(1);

+   });

+ 

+   var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket', 'schema', 'attributetypes', 'list'];

+   cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

+     var obj = JSON.parse(data);

+     var data = [];

+     for (var idx in obj['items']) {

+       item = obj['items'][idx];

+       if (item.single_value) {

+           multivalued = 'no'

+       } else {

+           multivalued = 'yes'

+       }

+       data.push.apply(data, [[

+         item.name,

+         item.oid,

+         item.syntax,

+         multivalued,

+         item.equality,

+         item.ordering,

+         item.substr,

+         attr_btn_html,

+         item.desc,

+         item.aliases

+       ]]);

+     }

+     // Update html table

+     schema_at_table = $('#attr-table').DataTable({

+       "data": data,

        "paging": true,

        "bAutoWidth": false,

        "dom": '<"pull-left"f><"pull-right"l>tip', // Moves the search box to the left
@@ -82,11 +186,31 @@ 

        "columnDefs": [ {

          "targets": 7,

          "orderable": false

-       } ]

+       }, {

+         "targets": 8,

+         "visible": false

+        }]

      });

+   }).fail(function(data) {

+       console.log("failed: " + data.message);

+       check_inst_alive(1);

+   });

  

-     $('#schema-mr-table').DataTable({

+   var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket', 'schema', 'matchingrules', 'list'];

+   cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

+     var obj = JSON.parse(data);

+     var data = [];

+     for (var idx in obj['items']) {

+       item = obj['items'][idx];

+       data.push.apply(data, [[

+         item.name,

+         item.oid,

+         item.syntax,

+         item.desc]])

+     };

+     schema_mr_table = $('#schema-mr-table').DataTable({

        "paging": true,

+       "data": data,

        "bAutoWidth": false,

        "dom": '<"pull-left"f><"pull-right"l>tip',

        "language": {
@@ -96,8 +220,15 @@ 

        "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]],

      });

  

-     // Load everything from DS and update html

+   }).fail(function(data) {

+       console.log("failed: " + data.cmd);

+       check_inst_alive(1);

+   });

+ }

  

+ $(document).ready( function() {

+   // Set an interval event to wait for all the pages to load, then load the config

+   $("#schema-content").load("schema.html", function (){

      // Sort schema list awhile

      sort_list( $("#schema-list") );

  
@@ -128,44 +259,72 @@ 

       */

      $("#add-oc-button").on("click", function() {

        clear_oc_form();

+       document.getElementById("oc-parent").value = 'top'

      });

  

      $("#save-oc-button").on("click", function() {

-       var edit = false;

        var oc_name = $("#oc-name").val();

+       var oc_oid = $("#oc-oid").val();

+       var oc_parent = $("#oc-parent").val();

+       var oc_required_list = $('#oc-required-list option').map(function() { return $(this).val(); }).get();

+       var oc_allowed_list = $('#oc-allowed-list option').map(function() { return $(this).val(); }).get();

  

+       var action = 'add';

+       var edit = false;

        if ( $("#add-edit-oc-header").text().indexOf("Edit Objectclass") != -1){

          edit = true;

+         action = 'edit';

        }

-       // TODO - Do the actual save in DS

- 

-       // Update html

-       // If save successful close down form, otherwise keep form up and return

-       $("#add-edit-oc-form").modal('toggle');

- 

-       $("#oc-name").attr('disabled', false);

- 

- 

-       // Convert allowed/requires list to html format

-       var oc_required_list = $('#oc-required-list option').map(function() { return $(this).val(); }).get().join(', ');

-       var oc_allowed_list = $('#oc-allowed-list option').map(function() { return $(this).val(); }).get().join(', ');

- 

-       // Add or edit?

- 

-       // Update html table (if edit: delete old then add new)

-       if ( edit ) {

-         var selector = $('tr:contains(' + oc_name + ')');

-         oc_table.row(selector).remove().draw(false);

+       if (oc_name == '') {

+         report_err($("#oc-name"), 'You must provide an objectClass name');

+         return;

        }

+       var cmd = [DSCONF, server_inst, 'schema', 'objectclasses', action, oc_name];

+       // Process and validate parameters

+       cmd.push.apply(cmd, ["--oid", oc_oid]);

+       cmd.push.apply(cmd, ["--sup", oc_parent]);

+       cmd.push.apply(cmd, ["--must", oc_required_list]);

+       cmd.push.apply(cmd, ["--may", oc_allowed_list]);

+ 

+       $("#save-oc-spinner").show();

+       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).

+       done(function(data) {

+         $("#save-oc-spinner").hide();

+         popup_success("The objectClass was saved in DS");

+         $("#oc-name").attr('disabled', false);

+ 

+         // Update html table (if edit: delete old then add new)

+         if ( edit ) {

+           var selector = $('tr:contains(' + oc_name + ')');

+           schema_oc_table.row(selector).remove().draw(false);

+         }

+         if (oc_required_list) {

+           oc_required_list = oc_required_list.join(" ")

+         }

+         if (oc_allowed_list) {

+           oc_allowed_list = oc_allowed_list.join(" ")

+         }

  

-       oc_table.row.add( [

-         oc_name,

-         $("#oc-oid").val(),

-         $("#oc-parent").val(),

-         tableize(oc_required_list),

-         tableize(oc_allowed_list),

-         oc_btn_html

-       ] ).draw( false );

+         schema_oc_table.row.add( [

+           oc_name,

+           oc_oid,

+           oc_parent,

+           oc_required_list,

+           oc_allowed_list,

+           oc_btn_html

+         ] ).draw( false );

+         // Replace the option in 'Edit objectClass' window

+         if (!edit) {

+           var option = $('<option></option>').attr("value", oc_name).text(oc_name);

+           $("#oc-parent").append(option);

+         }

+         $("#add-edit-oc-form").modal('toggle');

+       }).

+       fail(function(data) {

+         $("#save-oc-spinner").hide();

+         popup_err("Error", "Failed to save the objectClass\n" + data.message);

+         $("#add-edit-oc-form").modal('toggle');

+       })

      });

  

      // Required Attributes
@@ -225,85 +384,155 @@ 

       */

      $("#create-attr-button").on("click", function() {

        clear_attr_form();

- 

      })

  

      $("#save-attr-button").on("click", function() {

        var attr_name = $("#attr-name").val();

-       var multiple = "no";

+       var attr_oid = $("#attr-oid").val();

+       var attr_syntax = $("#attr-syntax").val();

+       var attr_desc = $('#attr-desc').val();

+       var attr_aliases = $('#attr-alias').val().split(" ");

        var eq_mr= $('#attr-eq-mr-select').val();

        var order_mr = $('#attr-order-mr-select').val();

        var sub_mr  = $('#attr-sub-mr-select').val();

-       var multiple = "no";

+       var multiple = 'no';

        if ( $("#attr-multivalued").is(":checked") ) {

-         multiple = "yes";

+         multiple = 'yes';

        };

+       var action = 'add';

        var edit = false;

        if ( $("#add-edit-attr-header").text().indexOf("Edit Attribute") != -1){

          edit = true;

+         action = 'edit';

        }

  

-       // Do the actual save in DS

- 

-       // if save in DS successful close down form, otherwise keep form visible and return ^^^

-       $("#add-edit-attr-form").modal('toggle');

-       $("#attr-name").attr('disabled', false);

+       if (attr_name == '') {

+         report_err($("#attr-name"), 'You must provide an attribute name');

+         return;

+       }

+       if (attr_syntax == '') {

+         report_err($("#attr-syntax"), 'You must provide an attribute syntax');

+         return;

+       }

  

-       // Update html table (if edit: delete old then add new)

-       if ( edit ) {

-         var selector = $('tr:contains(' + attr_name + ')');

-         at_table.row(selector).remove().draw(false);

+       var cmd = [DSCONF, server_inst, 'schema', 'attributetypes', action, attr_name];

+       // Process and validate parameters

+       if (attr_aliases) {

+         cmd.push.apply(cmd, ["--aliases"]);

+         cmd.push.apply(cmd, attr_aliases);

        }

+       if (attr_syntax) {

+         cmd.push.apply(cmd, ["--syntax", attr_syntax]);

+       }

+       if (multiple == 'no') {

+         cmd.push.apply(cmd, ["--single-value"]);

+       } else {

+         cmd.push.apply(cmd, ["--multi-value"]);

+       }

+       cmd.push.apply(cmd, ["--oid", attr_oid]);

+       cmd.push.apply(cmd, ["--desc", attr_desc]);

+       cmd.push.apply(cmd, ["--equality", eq_mr]);

+       cmd.push.apply(cmd, ["--substr", order_mr]);

+       cmd.push.apply(cmd, ["--ordering", sub_mr]);

+       $("#save-attr-spinner").show();

+       cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).

+       done(function(data) {

+         $("#save-attr-spinner").hide();

+         popup_success("The attribute was saved in DS");

+         $("#attr-name").attr('disabled', false);

+         // Update html table (if edit: delete old then add new)

+         if ( edit ) {

+           var selector = $('tr:contains(' + attr_name + ')');

+           schema_at_table.row(selector).remove().draw(false);

+         }

  

-       // Create attribute row to dataTable

-       at_table.row.add( [

-             attr_name,

-             $("#attr-oid").val(),

-             $("#attr-syntax").val(),

-             multiple,

-             eq_mr,

-             order_mr,

-             sub_mr,

-             attr_btn_html

-         ] ).draw( false );

+         // Create attribute row to dataTable

+         schema_at_table.row.add( [

+               attr_name,

+               attr_oid,

+               attr_syntax,

+               multiple,

+               eq_mr,

+               order_mr,

+               sub_mr,

+               attr_btn_html,

+               attr_desc,

+               attr_aliases

+          ] ).draw( false );

+          if (!edit) {

+            var option = $('<option></option>').attr("value", attr_name).text(attr_name);

+            $("#schema-list").append(option);

+          }

+         $("#add-edit-attr-form").modal('toggle');

+       }).

+       fail(function(data) {

+         $("#save-attr-spinner").hide();

+         popup_err("Error", "Failed to save the attribute\n" + data.message);

+         $("#add-edit-attr-form").modal('toggle');

+      })

      });

  

      $(document).on('click', '.attr-edit-btn', function(e) {

          e.preventDefault();

          clear_attr_form();

-         var data = at_table.row( $(this).parents('tr') ).data();

+         var data = schema_at_table.row( $(this).parents('tr') ).data();

          var edit_attr_name = data[0];

-         var edit_attr_oid = "";

-         var edit_attr_desc = "";

-         var edit_attr_alias = "";

-         var edit_attr_syntax = "";

-         var edit_attr_multivalued = "";

-         var edit_attr_eq_mr = "";

-         var edit_attr_order_mr = "";

-         var edit_attr_sub_mr = "";

+         var edit_attr_oid = data[1];

+         var edit_attr_syntax = data[2];

+         var edit_attr_multivalued = data[3];

+         var edit_attr_eq_mr = data[4];

+         var edit_attr_order_mr = data[5];

+         var edit_attr_sub_mr = data[6];

+         var edit_attr_desc = data[8];

+         var edit_attr_aliases = data[9];

+         if (edit_attr_eq_mr) {

+           edit_attr_eq_mr = data[4]

+         }

+         if (edit_attr_order_mr) {

+           edit_attr_order_mr = data[5]

+         }

+         if (edit_attr_sub_mr) {

+           edit_attr_sub_mr = data[6]

+         }

  

          $("#add-edit-attr-header").html('Edit Attribute: ' + edit_attr_name);

          $("#attr-name").val(edit_attr_name);

          $("#attr-name").attr('disabled', true);

+         $("#attr-oid").val(edit_attr_oid);

+         $("#attr-desc").val(edit_attr_desc);

+         if (edit_attr_aliases) {

+           $("#attr-alias").val(edit_attr_aliases.join(" "));

+         }

+         $("#attr-syntax").val(edit_attr_syntax);

+         $("#attr-multivalued").val(edit_attr_syntax);

+         $("#attr-multivalued").prop('checked', false);

+         if (edit_attr_multivalued == "yes") {

+           $("#attr-multivalued").prop('checked', true);

+         }

+         $("#save-attr-spinner").show();

+         $("#attr-eq-mr-select")[0].value = edit_attr_eq_mr;

+         $("#attr-order-mr-select")[0].value = edit_attr_order_mr;

+         $("#attr-sub-mr-select")[0].value = edit_attr_sub_mr;

+         $("#save-attr-spinner").hide();

  

          $("#add-edit-attr-form").modal('toggle');

- 

-         // TODO Get fresh copy of attr to fill in edit form

- 

-         // Update modal html header and fields and show()

      } );

  

      $(document).on('click', '.attr-del-btn', function(e) {

        e.preventDefault();

-       var data = at_table.row( $(this).parents('tr') ).data();

+       var data = schema_at_table.row( $(this).parents('tr') ).data();

        var del_attr_name = data[0];

        var at_row = $(this);

        popup_confirm("Are you sure you want to delete attribute: <b>" + del_attr_name + "</b>", "Confirmation", function (yes) {

          if (yes) {

-           // TODO Delete attr from DS

- 

-           // Update html table

-           at_table.row( at_row.parents('tr') ).remove().draw( false );

+           var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket', 'schema', 'attributetypes', 'remove', del_attr_name];

+           cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

+             popup_success("Attribute was successfully removed!")

+             schema_at_table.row( at_row.parents('tr') ).remove().draw( false );

+             $("#schema-list option[value='" + del_attr_name + "']").remove();

+           }).fail(function(data) {

+             popup_err("Attribute removal error", del_attr_name + " removal has failed: " + data.message);

+           });

          }

        });

      });
@@ -311,19 +540,35 @@ 

      $(document).on('click', '.oc-edit-btn', function(e) {

        e.preventDefault();

        clear_oc_form();

-       var data = oc_table.row( $(this).parents('tr') ).data();

+       var data = schema_oc_table.row( $(this).parents('tr') ).data();

        var edit_oc_name = data[0];

-       var edit_oc_oid = "";

-       var edit_oc_desc = "";

-       var edit_oc_parent = "";

-       var edit_oc_required = "";

-       var edit_oc_allowed = "";

+       var edit_oc_oid = data[1];

+       var edit_oc_parent = data[2]

+       var edit_oc_required = data[3].split(" ");

+       var edit_oc_allowed = data[4].split(" ");

+         if (edit_oc_parent) {

+           edit_oc_parent = data[2]

+         }

  

+       $("#save-oc-spinner").show();

+       $("#add-edit-oc-header").html('Edit Objectclass: ' + edit_oc_name);

        $("#oc-name").attr('disabled', true);

        $("#oc-name").val(edit_oc_name);

-       $("#add-edit-oc-header").html('Edit Objectclass: ' + edit_oc_name);

- 

-       // TODO Get fresh copy of objectclass for edit form

+       $("#oc-oid").val(edit_oc_oid);

+       $("#oc-parent")[0].value = edit_oc_parent;

+       $.each(edit_oc_required, function (i, item) {

+         $("#oc-required-list").append($('<option>', {

+           value: item,

+           text : item

+         }));

+       });

+       $.each(edit_oc_allowed, function (i, item) {

+         $("#oc-allowed-list").append($('<option>', {

+           value: item,

+           text : item

+         }));

+       });

+       $("#save-oc-spinner").hide();

  

        // Update modal html header and fields and show()

        $("#add-edit-oc-form").modal('toggle');
@@ -332,16 +577,20 @@ 

  

      $(document).on('click', '.oc-del-btn', function(e) {

        e.preventDefault();

-       var data = oc_table.row( $(this).parents('tr') ).data();

+       var data = schema_oc_table.row( $(this).parents('tr') ).data();

        var del_oc_name = data[0];

        var oc_row = $(this);

  

        popup_confirm("Are you sure you want to delete objectclass: <b>" + del_oc_name + "</b>", "Confirmation", function (yes) {

          if (yes) {

-           // TODO Delete attr from DS

- 

-           // Update html table

-           oc_table.row( oc_row.parents('tr') ).remove().draw( false );

+           var cmd = [DSCONF, '-j', 'ldapi://%2fvar%2frun%2f' + server_id + '.socket', 'schema', 'objectclasses', 'remove', del_oc_name];

+           cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV]}).done(function(data) {

+             popup_success("ObjectClass was successfully removed!")

+             schema_oc_table.row( oc_row.parents('tr') ).remove().draw( false );

+             $("#oc-parent option[value='" + del_oc_name + "']").remove();

+           }).fail(function(data) {

+             popup_err("Error", del_oc_name + " removal has failed: " + data.message);

+           });

          }

        });

      });

@@ -1,6 +1,5 @@ 

  

  <div id="schema-content">

- 

    <div id="objectclass-page" class="all-pages" hidden>

      <h3 class="ds-config-header">Objectclasses</h3>

      <table id="oc-table" class="display ds-repl-table" cellspacing="0" width="100%">
@@ -15,69 +14,10 @@ 

          </tr>

        </thead>

        <tbody>

-         <tr>

-           <td>uiPerson</td>

-           <td>1.1.1.1.1.1.1.1.1.1</td>

-           <td>top</td>

-           <td>cn</td>

-           <td>sn uid givenname description userpassword longDescription certificateRevocationList more</td>

-           <td>

-             <div class="dropdown">

-               <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">

-                 Choose Action...

-                 <span class="caret"></span>

-               </button>

-               <ul class="dropdown-menu ds-agmt-dropdown" role="menu">

-                 <li role=""><a role="menuitem" tabindex="0" class="oc-edit-btn" href="#">Edit Objectclass</a></li>

-                 <li role=""><a role="menuitem" tabindex="1" class="oc-del-btn" href="#">Delete Objectclass</a></li>

-               </ul>

-             </div>

-           </td>

-         </tr>

- 

-         <tr>

-           <td>uiGroup</td>

-           <td>1.1.1.1.1.1.1.1.1.2</td>

-           <td>top</td>

-           <td>cn</td>

-           <td>sn member</td>

-           <td>

-             <div class="dropdown">

-               <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">

-                 Choose Action...

-                 <span class="caret"></span>

-               </button>

-               <ul class="dropdown-menu ds-agmt-dropdown" role="menu">

-                 <li role=""><a role="menuitem" tabindex="0" class="oc-edit-btn" href="#">Edit Objectclass</a></li>

-                 <li role=""><a role="menuitem" tabindex="1" class="oc-del-btn" href="#">Delete Objectclass</a></li>

-               </ul>

-             </div>

-           </td>

-         </tr>

-         <tr>

-           <td>uiContainer</td>

-           <td>1.1.1.1.1.1.1.1.1.3</td>

-           <td>top</td>

-           <td>cn</td>

-           <td>sn uid description</td>

-           <td>

-             <div class="dropdown">

-               <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">

-                 Choose Action...

-                 <span class="caret"></span>

-               </button>

-               <ul class="dropdown-menu ds-agmt-dropdown" role="menu" aria-labelledby="dropdownMenu2">

-                 <li role=""><a role="menuitem" tabindex="0" class="oc-edit-btn" href="#">Edit Objectclass</a></li>

-                 <li role=""><a role="menuitem" tabindex="1" class="oc-del-btn" href="#">Delete Objectclass</a></li>

-               </ul>

-             </div>

-           </td>

-         </tr>

        </tbody>

      </table>

      <button id="add-oc-button" name="create-oc" data-toggle="modal" data-target="#add-edit-oc-form" class="btn btn-primary ds-button">Create Objectclass</button>

    </div>

- 

    <div id="attribute-page" class="all-pages" hidden>

      <h3 class="ds-config-header">Attributes</h3>

      <table id="attr-table" class="display ds-repl-table" cellspacing="0" width="100%">
@@ -94,30 +34,10 @@ 

          </tr>

        </thead>

        <tbody>

-         <td>ssn</td>

-         <td>1.1.1.1.1.1.1.2</td>

-         <td>DirectoryString</td>

-         <td>no</td>

-         <td></td>

-         <td></td>

-         <td></td>

-         <td>

-           <div class="dropdown">

-             <button class="btn btn-default dropdown-toggle" type="button"data-toggle="dropdown">

-               Choose Action...

-               <span class="caret"></span>

-             </button>

-             <ul class="dropdown-menu ds-agmt-dropdown" role="menu">

-               <li role=""><a role="menuitem" tabindex="0" class="attr-edit-btn" href="#">Edit Attribute</a></li>

-               <li role=""><a role="menuitem" tabindex="1" class="attr-del-btn" href="#">Delete Attribute</a></li>

-             </ul>

-           </div>

-         </td>

        </tbody>

      </table>

      <button id="create-attr-button" data-toggle="modal" data-target="#add-edit-attr-form" class="btn btn-primary ds-button">Create Attribute</button>

    </div>

- 

    <div id="schema-mr" class="all-pages" hidden>

      <div class="">

        <h3 class="ds-config-header">Matching Rules</h3>
@@ -131,18 +51,6 @@ 

            </tr>

          </thead>

          <tbody id="standard-body">

-           <tr>

-             <td>equality</td>

-             <td>9.5.6.7.3.5.4.45.54.4</td>

-             <td>DirectoryString</td>

-             <td>My really long description for some reason"</td>

-           </tr>

-           <tr>

-             <td>equality-2</td>

-             <td>999.5.6.7.3.5.4.45.54.4</td>

-             <td>DirectoryString</td>

-             <td>My really long description for some reason again geez...</td>

-           </tr>

          </tbody>

        </table>

      </div>
@@ -153,7 +61,7 @@ 

  

    <!-- Add/edit Attribute modal -->

    <div class="modal fade" id="add-edit-attr-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="add-edit-attr-header" aria-hidden="true">

-     <div class="modal-dialog ds-modal">

+     <div class="modal-dialog">

        <div class="modal-content">

          <div class="modal-header">

            <button type="button" class="close" data-dismiss="modal" aria-hidden="true" aria-label="Close">
@@ -162,54 +70,66 @@ 

            <h4 class="modal-title" id="add-edit-attr-header">Add Attribute</h4>

          </div>

          <div class="modal-body">

-           <form class="form-horizontal">

-             <div class="ds-inline">

-               <div>

-                 <label for="attr-name" class="ds-config-label" title="The attribute name"><b

-                   >Attribute Name</b></label><input class="ds-input-lrg" type="text" id="attr-name" size="40" required />

-               </div>

-               <div>

-                 <label for="attr-syntax" class="ds-config-label" title="The attribute syntax"><b>Attribute Syntax</b></label><select

-                   class="btn btn-default dropdown ds-oc-dropdown" id="attr-syntax">

+           <div class="ds-inline">

+             <p class="ds-modal-error"></p>

+             <div>

+               <label for="attr-name" class="ds-config-label-lrg" title="The attribute name"><b

+                 >Attribute Name</b></label><input class="ds-input" type="text" id="attr-name" size="40"/>

+             </div>

+             <div>

+               <label for="attr-syntax" class="ds-config-label-lrg" title="The attribute syntax"><b>Attribute Syntax</b></label><select

+                 class="btn btn-default dropdown ds-oc-dropdown" id="attr-syntax">

+                 <option value="">Make a selection</option>

+               </select>

+             </div>

+             <div>

+               <input type="checkbox" class="ds-config-checkbox" id="attr-multivalued"><label

+                 for="attr-multivalued" class="ds-label"> Attribute Multi-Valued </label>

+             </div>

+             <div>

+               <label for="attr-desc" class="ds-config-label-lrg" title="The attribute description"><b

+                 >Attribute Description</b></label><input class="ds-input" type="text" id="attr-desc" size="40"/>

+             </div>

+             <div>

+               <label for="attr-oid" class="ds-config-label-lrg" title="The attribute name"><b

+                 >Attribute OID</b></label><input class="ds-input" type="text" id="attr-oid" size="40"/>

+             </div>

+             <div>

+               <label for="attr-alias" class="ds-config-label-lrg" title="The attribute alias list separated by space"><b

+                 >Attribute Aliases</b></label><input class="ds-input" type="text" id="attr-alias" size="40"/>

+             </div>

+               <div class="panel panel-default">

+               <div class="panel-heading"><strong>Matching rules</strong></div>

+               <div class="panel-body">

+                 <div>

+                   <label for="attr-eq-mr-select" class="ds-config-label-lrg"><b>Equality</b></label><select

+                         class="btn btn-default dropdown ds-oc-dropdown" id="attr-eq-mr-select">

+                   <option></option>

                  </select>

-               </div>

-               <div>

-                 <input type="checkbox" class="ds-config-checkbox" id="attr-multivalued"><label

-                   for="attr-multivalued" class="ds-label"> Attribute Multi-Valued </label>

-               </div>

-               <div>

-                 <label for="attr-desc" class="ds-config-label" title="The attribute description"><b

-                   >Attribute Description</b></label><input class="ds-input-lrg" type="text" id="attr-desc" size="40"/>

-               </div>

-               <div>

-                 <label for="attr-oid" class="ds-config-label" title="The attribute name"><b

-                   >Attribute OID</b></label><input class="ds-input-lrg" type="text" id="attr-oid" size="40"/>

-               </div>

-               <div>

-                 <label for="attr-alias" class="ds-config-label" title="The attribute alias list separated by commas"><b

-                   >Attribute Alias</b></label><input class="ds-input-lrg" type="text" id="attr-alias" size="40"/>

-               </div>

-               <div>

-                 <label for="attr-eq-mr-select" class="ds-config-label"><b>Equality Matching Rule</b></label><select

-                   class="btn btn-default dropdown ds-oc-dropdown" id="attr-eq-mr-select">

-                   </select>

-               </div>

-               <div>

-                 <label for="attr-order-mr-select" class="ds-config-label"><b>Ordering Matching Rule</b></label><select

-                   class="btn btn-default dropdown ds-oc-dropdown" id="attr-order-mr-select">

-                   </select>

-               </div>

-               <div>

-                 <label for="attr-sub-mr-select" class="ds-config-label"><b>Substring Matching Rule</b></label><select

-                   class="btn btn-default dropdown ds-oc-dropdown" id="attr-sub-mr-select">

-                   </select>

+                 </div>

+                 <div>

+                   <label for="attr-order-mr-select" class="ds-config-label-lrg"><b>Ordering</b></label><select

+                         class="btn btn-default dropdown ds-oc-dropdown" id="attr-order-mr-select">

+                   <option></option>

+                 </select>

+                 </div>

+                 <div>

+                   <label for="attr-sub-mr-select" class="ds-config-label-lrg"><b>Substring</b></label><select

+                         class="btn btn-default dropdown ds-oc-dropdown" id="attr-sub-mr-select">

+                   <option></option>

+                 </select>

+                 </div>

                </div>

              </div>

-           </form>

+           </div>

+           <div id="save-attr-spinner" class="ds-center" hidden>

+             <p></p>

+             <p><span class="spinner spinner-xs spinner-inline"></span> Processing...<p>

+           </div>

          </div>

          <div class="modal-footer ds-modal-footer">

            <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>

-           <button type="button" class="btn btn-primary" id="save-attr-button" data-dismiss="modal">Save</button>

+           <button type="button" class="btn btn-primary" id="save-attr-button">Save</button>

          </div>

        </div>

      </div>
@@ -218,7 +138,7 @@ 

  

    <!-- Add/Edit Objectclass -->

    <div class="modal fade" id="add-edit-oc-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="add-edit-oc-header" aria-hidden="true">

-     <div class="modal-dialog ds-modal">

+     <div class="modal-dialog">

        <div class="modal-content">

          <div class="modal-header">

            <button type="button" class="close" data-dismiss="modal" aria-hidden="true" aria-label="Close">
@@ -229,32 +149,18 @@ 

          <div class="modal-body">

            <form class="form-horizontal">

              <div class="ds-inline">

+               <p class="ds-modal-error"></p>

                <div>

                  <label for="oc-name" class="ds-label-sm" title="The objectclass name"><b

-                   >Objectclass Name</b></label><input class="ds-input-lrg" type="text" id="oc-name" size="40" required />

+                   >Objectclass Name</b></label><input class="ds-input" type="text" id="oc-name" size="40" required />

                </div>

                <div>

                  <label for="oc-oid" class="ds-label-sm" title="Objectclass OID (optional)"><b

-                   >OID (optional)</b></label><input class="ds-input-lrg" value="" type="text" id="oc-oid" size="40"/>

+                   >OID (optional)</b></label><input class="ds-input" value="" type="text" id="oc-oid" size="40"/>

                </div>

                <div>

                  <label for="oc-parent" class="ds-label-sm" title="The parent objectclass"><b>Parent Objectclass</b></label><select

                    class="btn btn-default dropdown ds-oc-dropdown" id="oc-parent">

-                     <option>Top</option>

-                     <option>Person</option>

-                     <option>Organizationalunit</option>

-                     <option>Dynamically loaded at startup...</option>

-                     <option>Top</option>

-                     <option>Person</option>

-                     <option>Organizationalunit</option>

-                     <option>Dynamically loaded at startup...</option>

-                     <option>Top</option>

-                     <option>Person</option>

-                     <option>Organizationalunit</option>

-                     <option>Dynamically loaded at startup...</option>

-                     <option>Top</option>

-                     <option>Person</option>

-                     <option>Organizationalunit</option>

                    </select>

                </div>

                <hr>
@@ -262,13 +168,6 @@ 

                  <div name="available-attrs">

                    <label class="ds-config-label" for="schema-list" title="The available attributes to choose from."><b>Available Attributes</b></label>

                    <select id="schema-list" class="ds-oc-form-list" name="availattrs" multiple>

-                     <option value="alias">alias</option>

-                     <option value="cn">cn</option>

-                     <option value="uid">uid</option>

-                     <option value="sn">sn</option>

-                     <option value="ou">ou</option>

-                     <option value="displayName">displayName</option>

-                     <option value="legalname">legalname</option>

                    </select>

                  </div>

  
@@ -288,24 +187,24 @@ 

                    <label class="ds-config-label" for="oc-required-list" title=

                      "Attributes required by the objectclass"><b>Required Attributes</b></label>

                    <select id="oc-required-list" class="ds-may-must-list" name="availattrs" multiple>

-                     <option value="uid">uid</option>

-                     <option value="cn">cn</option>

                    </select>

                    <p></p>

                    <label class="ds-config-label" for="oc-allowed-list" title=

                      "Attributes allowed by the objectclass"><b>Allowed Attributes</b></label>

                    <select id="oc-allowed-list" class="ds-may-must-list" name="availattrs" multiple>

-                     <option value="alias">alias</option>

-                     <option value="sn">sn</option>

                    </select>

                  </div>

                </div>

              </div>

            </form>

+           <div id="save-oc-spinner" class="ds-center" hidden>

+             <p></p>

+             <p><span class="spinner spinner-xs spinner-inline"></span> Processing...<p>

+           </div>

          </div>

          <div class="modal-footer ds-modal-footer">

            <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>

-           <button type="button" class="btn btn-primary" id="save-oc-button" data-dismiss="modal">Save</button>

+           <button type="button" class="btn btn-primary" id="save-oc-button">Save</button>

          </div>

        </div>

      </div>

@@ -269,6 +269,7 @@ 

          self.outputs = []

          self.log = logging.getLogger("LogCapture")

          self.log.addHandler(self)

+         self.log.setLevel(logging.INFO)

  

      def emit(self, record):

          self.outputs.append(record)

@@ -6,6 +6,7 @@ 

  # See LICENSE for details.

  # --- END COPYRIGHT BLOCK ---

  

+ from json import dumps as dump_json

  from lib389.cli_base import _get_arg

  from lib389.schema import Schema

  
@@ -24,7 +25,7 @@ 

      log = log.getChild('list_attributetypes')

      schema = Schema(inst)

      if args is not None and args.json:

-         print(schema.get_attributetypes(json=True))

+         print(dump_json(schema.get_attributetypes(json=True)))

      else:

          for attributetype in schema.get_attributetypes():

              log.info(attributetype)
@@ -34,7 +35,7 @@ 

      log = log.getChild('list_objectclasses')

      schema = Schema(inst)

      if args is not None and args.json:

-         print(schema.get_objectclasses(json=True))

+         print(dump_json(schema.get_objectclasses(json=True)))

      else:

          for oc in schema.get_objectclasses():

              log.info(oc)
@@ -44,7 +45,7 @@ 

      log = log.getChild('list_matchingrules')

      schema = Schema(inst)

      if args is not None and args.json:

-         print(schema.get_matchingrules(json=True))

+         print(dump_json(schema.get_matchingrules(json=True)))

      else:

          for mr in schema.get_matchingrules():

              log.info(mr)
@@ -54,9 +55,9 @@ 

      log = log.getChild('query_attributetype')

      schema = Schema(inst)

      # Need the query type

-     attr = _get_arg(args.attr, msg="Enter attribute to query")

+     attr = _get_arg(args.name, msg="Enter attribute to query")

      if args.json:

-         print(schema.query_attributetype(attr, json=args.json))

+         print(dump_json(schema.query_attributetype(attr, json=args.json)))

      else:

          attributetype, must, may = schema.query_attributetype(attr, json=args.json)

          log.info(attributetype)
@@ -74,10 +75,10 @@ 

      log = log.getChild('query_objectclass')

      schema = Schema(inst)

      # Need the query type

-     oc = _get_arg(args.attr, msg="Enter objectclass to query")

+     oc = _get_arg(args.name, msg="Enter objectclass to query")

      result = schema.query_objectclass(oc, json=args.json)

      if args.json:

-         print(result)

+         print(dump_json(result))

      else:

          log.info(result)

  
@@ -86,10 +87,10 @@ 

      log = log.getChild('query_matchingrule')

      schema = Schema(inst)

      # Need the query type

-     attr = _get_arg(args.attr, msg="Enter attribute to query")

+     attr = _get_arg(args.name, msg="Enter attribute to query")

      result = schema.query_matchingrule(attr, json=args.json)

      if args.json:

-         print(result)

+         print(dump_json(result))

      else:

          log.info(result)

  
@@ -98,6 +99,10 @@ 

      log = log.getChild('add_attributetype')

      schema = Schema(inst)

      parameters = _get_parameters(args, 'attributetypes')

+     aliases = parameters.pop("aliases", None)

+     if aliases is not None and aliases != [""]:

+         parameters["names"].extend(aliases)

+ 

      schema.add_attributetype(parameters)

      log.info("Successfully added the attributeType")

  
@@ -115,6 +120,10 @@ 

      log = log.getChild('edit_attributetype')

      schema = Schema(inst)

      parameters = _get_parameters(args, 'attributetypes')

+     aliases = parameters.pop("aliases", None)

+     if aliases is not None and aliases != [""]:

+         parameters["names"].extend(aliases)

+ 

      schema.edit_attributetype(args.name, parameters)

      log.info("Successfully changed the attributetype")

  
@@ -160,17 +169,29 @@ 

          log.info("To verify that the schema reload operation was successful, please check the error logs.")

  

  

+ def get_syntaxes(inst, basedn, log, args):

+     log = log.getChild('get_syntaxes')

+     schema = Schema(inst)

+     result = schema.get_attr_syntaxes(json=args.json)

+     if args.json:

+         print(dump_json(result))

+     else:

+         for name, id in result.items():

+             log.info("%s (%s)", name, id)

+ 

+ 

  def _get_parameters(args, type):

      if type not in ('attributetypes', 'objectclasses'):

          raise ValueError("Wrong parser type: %s" % type)

  

-     parameters = {'names': (args.name,),

+     parameters = {'names': [args.name,],

                    'oid': args.oid,

                    'desc': args.desc,

                    'obsolete': _validate_dual_args(args.obsolete, args.not_obsolete)}

  

      if type == 'attributetypes':

          parameters.update({'single_value': _validate_dual_args(args.single_value, args.multi_value),

+                            'aliases': args.aliases,

                             'syntax': args.syntax,

                             'syntax_len': None,  # We need it for

                             'x_ordered': None,   # the correct ldap.schema.models work
@@ -208,6 +229,7 @@ 

      if type == 'attributetypes':

          parser.add_argument('--syntax', required=True,

                              help='OID of the LDAP syntax assigned to the attribute')

+         parser.add_argument('--aliases', nargs='+', help='Additional NAMEs of the object.')

          parser.add_argument('--single-value', action='store_true',

                              help='True if the matching rule must have only one value'

                                   'Only one of the flags this or --multi-value should be specified')
@@ -259,11 +281,13 @@ 

  

      attributetypes_parser = schema_subcommands.add_parser('attributetypes', help='Work with attribute types on this system')

      attributetypes_subcommands = attributetypes_parser.add_subparsers(help='schema')

+     at_get_syntaxes_parser = attributetypes_subcommands.add_parser('get_syntaxes', help='List all available attribute type syntaxes')

+     at_get_syntaxes_parser.set_defaults(func=get_syntaxes)

      at_list_parser = attributetypes_subcommands.add_parser('list', help='List available attribute types on this system')

      at_list_parser.set_defaults(func=list_attributetypes)

      at_query_parser = attributetypes_subcommands.add_parser('query', help='Query an attribute to determine object classes that may or must take it')

      at_query_parser.set_defaults(func=query_attributetype)

-     at_query_parser.add_argument('attr', nargs='?', help='Attribute type to query')

+     at_query_parser.add_argument('name', nargs='?', help='Attribute type to query')

      at_add_parser = attributetypes_subcommands.add_parser('add', help='Add an attribute type to this system')

      at_add_parser.set_defaults(func=add_attributetype)

      _add_parser_args(at_add_parser, 'attributetypes')
@@ -280,7 +304,7 @@ 

      oc_list_parser.set_defaults(func=list_objectclasses)

      oc_query_parser = objectclasses_subcommands.add_parser('query', help='Query an objectClass')

      oc_query_parser.set_defaults(func=query_objectclass)

-     oc_query_parser.add_argument('attr', nargs='?', help='ObjectClass to query')

+     oc_query_parser.add_argument('name', nargs='?', help='ObjectClass to query')

      oc_add_parser = objectclasses_subcommands.add_parser('add', help='Add an objectClass to this system')

      oc_add_parser.set_defaults(func=add_objectclass)

      _add_parser_args(oc_add_parser, 'objectclasses')
@@ -297,7 +321,7 @@ 

      mr_list_parser.set_defaults(func=list_matchingrules)

      mr_query_parser = matchingrules_subcommands.add_parser('query', help='Query a matching rule')

      mr_query_parser.set_defaults(func=query_matchingrule)

-     mr_query_parser.add_argument('attr', nargs='?', help='Matching rule to query')

+     mr_query_parser.add_argument('name', nargs='?', help='Matching rule to query')

  

      reload_parser = schema_subcommands.add_parser('reload', help='Dynamically reload schema while server is running')

      reload_parser.set_defaults(func=reload_schema)

file modified
+92 -26
@@ -28,6 +28,32 @@ 

                                         'sup': (), 'equality': None, 'ordering': None, 'substr': None,

                                         'syntax': None, 'syntax_len': None, 'single_value': 0, 'collective': 0,

                                         'no_user_mod': 0, 'usage': 0, 'x_origin': None, 'x_ordered': None}}

+ ATTR_SYNTAXES = {"Binary": "1.3.6.1.4.1.1466.115.121.1.5",

+                  "Bit String": "1.3.6.1.4.1.1466.115.121.1.6",

+                  "Boolean": "1.3.6.1.4.1.1466.115.121.1.7",

+                  "Country String": "1.3.6.1.4.1.1466.115.121.1.11",

+                  "DN": "1.3.6.1.4.1.1466.115.121.1.12",

+                  "Delivery Method": "1.3.6.1.4.1.1466.115.121.1.14",

+                  "Directory String": "1.3.6.1.4.1.1466.115.121.1.15",

+                  "Enhanced Guide": "1.3.6.1.4.1.1466.115.121.1.21",

+                  "Facsimile": "1.3.6.1.4.1.1466.115.121.1.22",

+                  "Fax": "1.3.6.1.4.1.1466.115.121.1.23",

+                  "Generalized Time": "1.3.6.1.4.1.1466.115.121.1.24",

+                  "Guide": "1.3.6.1.4.1.1466.115.121.1.25",

+                  "IA5 String": "1.3.6.1.4.1.1466.115.121.1.26",

+                  "Integer": "1.3.6.1.4.1.1466.115.121.1.27",

+                  "JPEG": "1.3.6.1.4.1.1466.115.121.1.28",

+                  "Name and Optional UID": "1.3.6.1.4.1.1466.115.121.1.34",

+                  "Numeric String": "1.3.6.1.4.1.1466.115.121.1.36",

+                  "OctetString": "1.3.6.1.4.1.1466.115.121.1.40",

+                  "Object Class Description": "1.3.6.1.4.1.1466.115.121.1.37",

+                  "OID": "1.3.6.1.4.1.1466.115.121.1.38",

+                  "Postal Address": "1.3.6.1.4.1.1466.115.121.1.41",

+                  "Printable String": "1.3.6.1.4.1.1466.115.121.1.44",

+                  "Space-Insensitive String": "2.16.840.1.113730.3.7.1",

+                  "TelephoneNumber": "1.3.6.1.4.1.1466.115.121.1.50",

+                  "Teletex Terminal Identifier": "1.3.6.1.4.1.1466.115.121.1.51",

+                  "Telex Number": "1.3.6.1.4.1.1466.115.121.1.52"}

  

  

  class Schema(DSLdapObject):
@@ -50,6 +76,34 @@ 

          else:

              raise ValueError("Wrong object model was specified")

  

+     @staticmethod

+     def _validate_ldap_schema_value(value):

+         """Validate the values that we suppl to ldap.schema.models

+         because it expects some exact values.

+         It should tuple, not list.

+         It should be None or () if we don't want """

+ 

+         if type(value) == list:

+             value = tuple(value)

+         elif value == "":

+             value = None

+         if value == ("",):

+             value = ()

+         return value

+ 

+     @staticmethod

+     def get_attr_syntaxes(json=False):

+         """Get a list of available attribute syntaxes"""

+ 

+         if json:

+             attr_syntaxes_list = []

+             for name, id in ATTR_SYNTAXES.items():

+                 attr_syntaxes_list.append({'name': name, 'id': id})

+             result = {'type': 'list', 'items': attr_syntaxes_list}

+         else:

+             result = ATTR_SYNTAXES

+         return result

+ 

      def _get_schema_objects(self, object_model, json=False):

          attr_name = self._get_attr_name_by_model(object_model)

  
@@ -60,21 +114,29 @@ 

  

              for obj_i in object_insts:

                  # Add normalized name for sorting. Some matching rules don't have a name

-                 if len(obj_i["names"]) > 0:

-                     obj_i['name'] = obj_i['names'][0].lower()

+                 if len(obj_i["names"]) == 1:

+                     obj_i['name'] = obj_i['names'][0]

+                     obj_i['aliases'] = ""

+                 elif len(obj_i["names"]) > 1:

+                     obj_i['name'] = obj_i['names'][0]

+                     obj_i['aliases'] = obj_i['names'][1:]

                  else:

                      obj_i['name'] = ""

              object_insts = sorted(object_insts, key=itemgetter('name'))

              result = {'type': 'list', 'items': object_insts}

  

-             return dump_json(result)

+             return result

          else:

              return [object_model(obj_i) for obj_i in results]

  

      def _get_schema_object(self, name, object_model, json=False):

          objects = self._get_schema_objects(object_model, json=json)

-         schema_object = [obj_i for obj_i in objects if name.lower() in

-                          list(map(str.lower, obj_i.names))]

+         if json:

+             schema_object = [obj_i for obj_i in objects["items"] if name.lower() in

+                              list(map(str.lower, obj_i["names"]))]

+         else:

+             schema_object = [obj_i for obj_i in objects if name.lower() in

+                              list(map(str.lower, obj_i.names))]

  

          if len(schema_object) != 1:

              # This is an error.
@@ -106,9 +168,7 @@ 

              if oc_param.lower() not in OBJECT_MODEL_PARAMS[object_model].keys():

                  raise ValueError('Wrong parameter name was specified: %s' % oc_param)

              if value is not None:

-                 # ldap.schema.models requires tuple

-                 if type(value) == list:

-                     value = tuple(value)

+                 value = self._validate_ldap_schema_value(value)

                  setattr(schema_object, oc_param.lower(), value)

  

          # Set other not defined arguments so objectClass model work correctly
@@ -137,9 +197,7 @@ 

              if oc_param.lower() not in OBJECT_MODEL_PARAMS[object_model].keys():

                  raise ValueError('Wrong parameter name was specified: %s' % oc_param)

              if value is not None:

-                 # ldap.schema.models requires tuple

-                 if type(value) == list:

-                     value = tuple(value)

+                 value = self._validate_ldap_schema_value(value)

                  setattr(schema_object, oc_param.lower(), value)

  

          schema_object_str = str(schema_object)
@@ -310,10 +368,10 @@ 

          matching_rule = self._get_schema_object(mr_name, MatchingRule, json=json)

  

          if json:

-             result = {'type': 'schema', 'mr': vars(matching_rule)}

-             return dump_json(result)

+             result = {'type': 'schema', 'mr': matching_rule}

+             return result

          else:

-             return str(matching_rule)

+             return matching_rule

  

      def query_objectclass(self, objectclassname, json=False):

          """Returns a single ObjectClass instance that matches objectclassname.
@@ -333,10 +391,10 @@ 

          objectclass = self._get_schema_object(objectclassname, ObjectClass, json=json)

  

          if json:

-             result = {'type': 'schema', 'oc': vars(objectclass)}

-             return dump_json(result)

+             result = {'type': 'schema', 'oc': objectclass}

+             return result

          else:

-             return str(objectclass)

+             return objectclass

  

      def query_attributetype(self, attributetypename, json=False):

          """Returns a tuple of the AttributeType, and what objectclasses may or
@@ -363,13 +421,21 @@ 

          objectclasses = self.get_objectclasses()

  

          # Get the primary name of this attribute

-         attributetypename = attributetype.names[0]

+         if json:

+             attributetypenames = attributetype["names"]

+         else:

+             attributetypenames = attributetype.names

+ 

          # Build a set if they have may.

-         may = [oc for oc in objectclasses if attributetypename.lower() in

-                list(map(str.lower, oc.may))]

+         may = []

+         for attributetypename in attributetypenames:

+             may.extend([oc for oc in objectclasses if attributetypename.lower() in

+                         list(map(str.lower, oc.may))])

          # Build a set if they have must.

-         must = [oc for oc in objectclasses if attributetypename.lower() in

-                 list(map(str.lower, oc.must))]

+         must = []

+         for attributetypename in attributetypenames:

+             must.extend([oc for oc in objectclasses if attributetypename.lower() in

+                          list(map(str.lower, oc.must))])

  

          if json:

              # convert Objectclass class to dict, then sort each list
@@ -377,16 +443,16 @@ 

              must = [vars(oc) for oc in must]

              # Add normalized 'name' for sorting

              for oc in may:

-                 oc['name'] = oc['names'][0].lower()

+                 oc['name'] = oc['names'][0]

              for oc in must:

-                 oc['name'] = oc['names'][0].lower()

+                 oc['name'] = oc['names'][0]

              may = sorted(may, key=itemgetter('name'))

              must = sorted(must, key=itemgetter('name'))

              result = {'type': 'schema',

-                       'at': vars(attributetype),

+                       'at': attributetype,

                        'may': may,

                        'must': must}

-             return dump_json(result)

+             return result

          else:

              return str(attributetype), may, must

  

@@ -1,3 +1,11 @@ 

+ # --- BEGIN COPYRIGHT BLOCK ---

+ # Copyright (C) 2018 Red Hat, Inc.

+ # All rights reserved.

+ #

+ # License: GPL (version 3 or any later version).

+ # See LICENSE for details.

+ # --- END COPYRIGHT BLOCK ---

+ 

  import os

  import pytest

  import shutil

empty or binary file added

Description: Add schema functionality for add/edit/remove.
Fix small CLI schema issues and lib389 API part.
Set LogCapture level on the init.
Add copyright for cli/conf_backup_test.py.

https://pagure.io/389-ds-base/issue/49928

Reviewed by: ?

The adding tests requires this issue to be fixed - https://pagure.io/389-ds-base/issue/49911
It can be added without it but it will be full of unnecessary 'hacks'.

I want this PR to be merged this week before my PTO (if it will be possible, please, do not rush with the review).

I will add some basic tests in a meanwhile if everything will go okay. But the working WebUI and CLI come first.

This should be "objectclass" not "attribute"

Editing a stand objectclass (alias) fails:

Failed to save the objectClass
Error: {'desc': 'Server is unwilling to perform', 'info': 'object class alias: Cannot delete a standard object class'}

The old java console blocked editing of standard schema - we should see if its easy to do the same in the new UI. It could also wait for a second PR as its not needed, the current error is sufficient I guess, but we could handle it better.

[1] Also when editing "a6Record " attribute, all the matching rule dropdown lists have two blank items at the top. That should be cleaned up.

[2] Editing standard attribute also errors out:

Failed to save the attribute
Error: {'desc': 'Server is unwilling to perform', 'info': 'attribute type a6record: Cannot delete a standard attribute type'}

[3] Create attribute, we list the syntax OIDs, but we should also list the syntax name. Something like:

  • Directory String (1.2.1.1.23.2.1.32..2.212)

[4] Create attribute - syntax is required, but the UI does not enforce it. Then we get a ugly error from dsconf in the UI

[5] Edit Attribute (attr name "aaaaa") - doesn't work:

Failed to save the attribute
Error: {'desc': 'Invalid syntax', 'info': 'attribute type : The name is invalid. Names must begin with a letter'}

The rest looks good :-)

rebased onto efa39cb

6 years ago

Thanks! All the comments were incorporated (most of them). Please, review!

I will fix with the second PR the standard schema issue. I'll think more how to do it effectively.
Also, I'll fix the table attribute syntax representation in the next PR.

Pull-Request has been merged by spichugi

6 years ago

Hey there, looks good. Tests for the cli? I think that's about my only comment :)

Hey there, looks good. Tests for the cli? I think that's about my only comment :)

Check the first comment :)

389-ds-base is moving from Pagure to Github. This means that new issues and pull requests
will be accepted only in 389-ds-base's github repository.

This pull request has been cloned to Github as issue and is available here:
- https://github.com/389ds/389-ds-base/issues/3016

If you want to continue to work on the PR, please navigate to the github issue,
download the patch from the attachments and file a new pull request.

Thank you for understanding. We apologize for all inconvenience.

Pull-Request has been closed by spichugi

4 years ago