#50293 Issue 50292 - Fix Plugin CLI and UI issues
Closed 3 years ago by spichugi. Opened 5 years ago by spichugi.
spichugi/389-ds-base plugin_cli_fix  into  master

@@ -296,6 +296,8 @@ 

                                      }

                                      this.toggleLoading();

                                  });

+                     } else {

+                         this.pluginList();

                      }

                  });

      }

@@ -317,7 +317,7 @@ 

          if 'nsds5beginreplicarefresh' not in status_attrs_dict:

              status_attrs_dict['nsds5beginreplicarefresh'] = [""]

          if 'nsds5replicalastinitstatus' not in status_attrs_dict:

-             status_attrs_dict['nsds5replicalastinitstatus'] = ["unavilable"]

+             status_attrs_dict['nsds5replicalastinitstatus'] = ["unavailable"]

          if 'nsds5replicachangessentsincestartup' not in status_attrs_dict:

              status_attrs_dict['nsds5replicachangessentsincestartup'] = ['0']

          if ensure_str(status_attrs_dict['nsds5replicachangessentsincestartup'][0]) == '':

@@ -30,18 +30,21 @@ 

      """

  

      log = log.getChild('generic_object_add')

+     # If Base DN was initially provided then 'props' should contain the RDN

+     # if 'props' doesn't have the RDN - it will fail with the right error during the validation in the 'create'

+     rdn = None

      # Gather the attributes

      attrs = _args_to_attrs(args, arg_to_attr)

      props.update({attr: value for (attr, value) in attrs.items() if value != ""})

  

      # Get RDN attribute and Base DN from the DN if Base DN is not specified

-     if dn is not None and basedn is None:

-         dn_parts = ldap.dn.explode_dn(dn)

- 

-         rdn = dn_parts[0]

-         basedn = ",".join(dn_parts[1:])

-     else:

-         raise ValueError('If Base DN is not specified - DN parameter should be')

+     if basedn is None:

Why not just change the error message instead of the logic?L

+         if dn is not None:

+             dn_parts = ldap.dn.explode_dn(dn)

+             rdn = dn_parts[0]

+             basedn = ",".join(dn_parts[1:])

+         else:

+             raise ValueError('If Base DN is not specified - DN parameter should be specified instead')

  

      new_object = dsldap_objects_class(inst, dn=dn)

      new_object.create(rdn=rdn, basedn=basedn, properties=props)

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

  from lib389.cli_conf.plugins import passthroughauth as cli_passthroughauth

  from lib389.cli_conf.plugins import retrochangelog as cli_retrochangelog

  from lib389.cli_conf.plugins import automember as cli_automember

+ from lib389.cli_conf.plugins import posix_winsync as cli_posix_winsync

Wait, I thought we didn't support winsync anymore ...?

  

  SINGULAR = Plugin

  MANY = Plugins
@@ -111,6 +112,7 @@ 

      cli_managedentries.create_parser(subcommands)

      cli_passthroughauth.create_parser(subcommands)

      cli_retrochangelog.create_parser(subcommands)

+     cli_posix_winsync.create_parser(subcommands)

  

      list_parser = subcommands.add_parser('list', help="List current configured (enabled and disabled) plugins")

      list_parser.set_defaults(func=plugin_list)

@@ -55,9 +55,10 @@ 

          raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % targetdn)

      if args and args.json:

          o_str = config.get_all_attrs_json()

-         print(o_str)

+ 

+         log.info(o_str)

      else:

-         print(config.display())

+         log.info(config.display())

  

  

  def accountpolicy_del_config(inst, basedn, log, args):
@@ -69,7 +70,7 @@ 

  

  

  def _add_parser_args(parser):

-     parser.add_argument('--always-record-login', choices=['yes', 'no'],

+     parser.add_argument('--always-record-login', choices=['yes', 'no'], type=str.lower,

                          help='Sets that every entry records its last login time (alwaysRecordLogin)')

      parser.add_argument('--alt-state-attr',

                          help='Provides a backup attribute for the server to reference '

@@ -9,15 +9,16 @@ 

  import json

  import ldap

  from lib389.plugins import AttributeUniquenessPlugin, AttributeUniquenessPlugins

- from lib389.cli_conf import add_generic_plugin_parsers, generic_object_edit, generic_object_add

+ from lib389.cli_conf import (add_generic_plugin_parsers, generic_object_edit, generic_object_add,

+                              generic_enable, generic_disable, generic_status)

  from lib389._constants import DN_PLUGIN

  

  arg_to_attr = {

-     'attr-name': 'uniqueness-attribute-name',

+     'attr_name': 'uniqueness-attribute-name',

      'subtree': 'uniqueness-subtrees',

-     'across-all-subtrees': 'uniqueness-across-all-subtrees',

-     'top-entry-oc': 'uniqueness-top-entry-oc',

-     'subtree-entries-oc': 'uniqueness-subtree-entries-oc'

+     'across_all_subtrees': 'uniqueness-across-all-subtrees',

+     'top_entry_oc': 'uniqueness-top-entry-oc',

+     'subtree_entries_oc': 'uniqueness-subtree-entries-oc'

  }

  

  
@@ -32,13 +33,13 @@ 

          else:

              result.append(plugin.rdn)

      if args.json:

-         print(json.dumps({"type": "list", "items": result_json}))

+         log.info(json.dumps({"type": "list", "items": result_json}))

      else:

          if len(result) > 0:

              for i in result:

-                 print(i)

+                 log.info(i)

          else:

-             print("No Attribute Uniqueness plugin instances")

+             log.info("No Attribute Uniqueness plugin instances")

  

  

  def attruniq_add(inst, basedn, log, args):
@@ -63,9 +64,9 @@ 

          raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % args.name)

      if args and args.json:

          o_str = plugin.get_all_attrs_json()

-         print(o_str)

+         log.info(o_str)

      else:

-         print(plugin.display())

+         log.info(plugin.display())

  

  

  def attruniq_del(inst, basedn, log, args):
@@ -85,7 +86,7 @@ 

      parser.add_argument('--subtree', nargs='+',

                          help='Sets the DN under which the plug-in checks for uniqueness of '

                               'the attributes value. This attribute is multi-valued (uniqueness-subtrees)')

-     parser.add_argument('--across-all-subtrees', choices=['on', 'off'],

+     parser.add_argument('--across-all-subtrees', choices=['on', 'off'], type=str.lower,

                          help='If enabled (on), the plug-in checks that the attribute is unique across all subtrees '

                               'set. If you set the attribute to off, uniqueness is only enforced within the subtree '

                               'of the updated entry (uniqueness-across-all-subtrees)')
@@ -120,3 +121,15 @@ 

      delete = subcommands.add_parser('delete', help='Delete the config entry')

      delete.add_argument('NAME', help='Sets the name of the plug-in configuration record')

      delete.set_defaults(func=attruniq_del)

+ 

+     enable = subcommands.add_parser('enable', help='enable plugin')

+     enable.add_argument('NAME', help='Sets the name of the plug-in configuration record')

+     enable.set_defaults(func=generic_enable)

+ 

+     disable = subcommands.add_parser('disable', help='disable plugin')

+     disable.add_argument('NAME', help='Sets the name of the plug-in configuration record')

+     disable.set_defaults(func=generic_disable)

+ 

+     status = subcommands.add_parser('status', help='display plugin status')

+     status.add_argument('NAME', help='Sets the name of the plug-in configuration record')

+     status.set_defaults(func=generic_status)

@@ -14,16 +14,16 @@ 

  

  

  arg_to_attr_definition = {

-     'default-group': 'autoMemberDefaultGroup',

+     'default_group': 'autoMemberDefaultGroup',

      'filter': 'autoMemberFilter',

-     'grouping-attr': 'autoMemberGroupingAttr',

+     'grouping_attr': 'autoMemberGroupingAttr',

      'scope': 'autoMemberScope'

  }

  

  arg_to_attr_regex = {

      'exclusive': 'autoMemberExclusiveRegex',

      'inclusive': 'autoMemberInclusiveRegex',

-     'target-group': 'autoMemberTargetGroup'

+     'target_group': 'autoMemberTargetGroup'

  }

  

  
@@ -42,7 +42,7 @@ 

          log.info("No automember definitions were found")

  

      if args.json:

-         print(json.dumps(result))

+         log.info(json.dumps(result))

  

  

  def definition_add(inst, basedn, log, args):
@@ -68,9 +68,9 @@ 

          raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % args.name)

      if args and args.json:

          o_str = definition.get_all_attrs_json()

-         print(o_str)

+         log.info(o_str)

      else:

-         print(definition.display())

+         log.info(definition.display())

  

  

  def definition_del(inst, basedn, log, args):
@@ -98,7 +98,7 @@ 

          log.info("No automember regexes were found")

  

      if args.json:

-         print(json.dumps(result))

+         log.info(json.dumps(result))

  

  

  def regex_add(inst, basedn, log, args):
@@ -129,9 +129,9 @@ 

          raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % args.name)

      if args and args.json:

          o_str = regex.get_all_attrs_json()

-         print(o_str)

+         log.info(o_str)

      else:

-         print(regex.display())

+         log.info(regex.display())

  

  

  def regex_del(inst, basedn, log, args):

@@ -54,7 +54,7 @@ 

          log.info("No DNA configurations were found")

  

      if args.json:

-         print(json.dumps(result))

+         log.info(json.dumps(result))

  

  

  def dna_add(inst, basedn, log, args):
@@ -80,9 +80,9 @@ 

          raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % args.NAME)

      if args and args.json:

          o_str = config.get_all_attrs_json()

-         print(o_str)

+         log.info(o_str)

      else:

-         print(config.display())

+         log.info(config.display())

  

  

  def dna_del(inst, basedn, log, args):
@@ -109,7 +109,7 @@ 

          log.info("No DNA shared configurations were found")

  

      if args.json:

-         print(json.dumps(result))

+         log.info(json.dumps(result))

  

  

  def dna_config_add(inst, basedn, log, args):
@@ -146,9 +146,9 @@ 

          raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % targetdn)

      if args and args.json:

          o_str = shared_config.get_all_attrs_json()

-         print(o_str)

+         log.info(o_str)

      else:

-         print(shared_config.display())

+         log.info(shared_config.display())

  

  

  def dna_config_del(inst, basedn, log, args):
@@ -160,7 +160,8 @@ 

  

  

  def _add_parser_args(parser):

-     parser.add_argument('--type', help='Sets which attributes have unique numbers being generated for them (dnaType)')

+     parser.add_argument('--type', nargs='+',

+                         help='Sets which attributes have unique numbers being generated for them (dnaType)')

      parser.add_argument('--prefix', help='Defines a prefix that can be prepended to the generated '

                                           'number values for the attribute (dnaPrefix)')

      parser.add_argument('--next-value', help='Gives the next available number which can be assigned (dnaNextValue)')

@@ -30,13 +30,13 @@ 

          else:

              result.append(config.rdn)

      if args.json:

-         print(json.dumps({"type": "list", "items": result_json}))

+         log.info(json.dumps({"type": "list", "items": result_json}))

      else:

          if len(result) > 0:

              for i in result:

-                 print(i)

+                 log.info(i)

          else:

-             print("No Linked Attributes plugin instances")

+             log.info("No Linked Attributes plugin config instances")

  

  

  def linkedattr_add(inst, basedn, log, args):
@@ -62,9 +62,9 @@ 

          raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % args.name)

      if args and args.json:

          o_str = config.get_all_attrs_json()

-         print(o_str)

+         log.info(o_str)

      else:

-         print(config.display())

+         log.info(config.display())

  

  

  def linkedattr_del(inst, basedn, log, args):
@@ -80,7 +80,7 @@ 

      log.info('Attempting to add task entry... This will fail if LinkedAttributes plug-in is not enabled.')

      if not plugin.status():

          log.error("'%s' is disabled. Fix up task can't be executed" % plugin.rdn)

-     fixup_task = plugin.fixup(args.basedn, args.filter)

+     fixup_task = plugin.fixup(args.linkdn)

      fixup_task.wait()

      exitcode = fixup_task.get_exit_code()

      if exitcode != 0:
@@ -104,8 +104,7 @@ 

      add_generic_plugin_parsers(subcommands, LinkedAttributesPlugin)

  

      fixup_parser = subcommands.add_parser('fixup', help='Run the fix-up task for linked attributes plugin')

-     fixup_parser.add_argument('basedn', help="basedn that contains entries to fix up")

-     fixup_parser.add_argument('-f', '--filter', help='Filter for entries to fix up linked attributes.')

+     fixup_parser.add_argument('-l', '--linkdn', help="Base DN that contains entries to fix up")

      fixup_parser.set_defaults(func=fixup)

  

      list = subcommands.add_parser('list', help='List available plugin configs')

@@ -48,13 +48,13 @@ 

          else:

              result.append(config.rdn)

      if args.json:

-         print(json.dumps({"type": "list", "items": result_json}))

+         log.info(json.dumps({"type": "list", "items": result_json}))

      else:

          if len(result) > 0:

              for i in result:

-                 print(i)

+                 log.info(i)

          else:

-             print("No Linked Attributes plugin instances")

+             log.info("No Linked Attributes plugin instances")

  

  

  def mep_config_add(inst, basedn, log, args):
@@ -87,9 +87,9 @@ 

          raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % args.name)

      if args and args.json:

          o_str = config.get_all_attrs_json()

-         print(o_str)

+         log.info(o_str)

      else:

-         print(config.display())

+         log.info(config.display())

  

  

  def mep_config_del(inst, basedn, log, args):
@@ -113,13 +113,13 @@ 

          else:

              result.append(template.rdn)

      if args.json:

-         print(json.dumps({"type": "list", "items": result_json}))

+         log.info(json.dumps({"type": "list", "items": result_json}))

      else:

          if len(result) > 0:

              for i in result:

-                 print(i)

+                 log.info(i)

          else:

-             print("No Linked Attributes plugin instances")

+             log.info("No Linked Attributes plugin instances")

  

  

  def mep_template_add(inst, basedn, log, args):
@@ -148,9 +148,9 @@ 

          raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % targetdn)

      if args and args.json:

          o_str = template.get_all_attrs_json()

-         print(o_str)

+         log.info(o_str)

      else:

-         print(template.display())

+         log.info(template.display())

  

  

  def mep_template_del(inst, basedn, log, args):

@@ -56,9 +56,9 @@ 

          raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % targetdn)

      if args and args.json:

          o_str = config.get_all_attrs_json()

-         print(o_str)

+         log.info(o_str)

      else:

-         print(config.display())

+         log.info(config.display())

  

  

  def memberof_del_config(inst, basedn, log, args):
@@ -69,11 +69,12 @@ 

      log.info("Successfully deleted the %s", targetdn)

  

  

- def fixup(inst, basedn, log, args):

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

      plugin = MemberOfPlugin(inst)

-     log.info('Attempting to add task entry... This will fail if MemberOf plug-in is not enabled.')

+     log.info('Attempting to add task entry...')

      if not plugin.status():

          log.error("'%s' is disabled. Fix up task can't be executed" % plugin.rdn)

+         return

      fixup_task = plugin.fixup(args.DN, args.filter)

      fixup_task.wait()

      exitcode = fixup_task.get_exit_code()
@@ -135,7 +136,7 @@ 

      del_config_.add_argument('DN', help='The config entry full DN')

  

      fixup = subcommands.add_parser('fixup', help='Run the fix-up task for memberOf plugin')

-     fixup.set_defaults(func=fixup)

+     fixup.set_defaults(func=do_fixup)

      fixup.add_argument('DN', help="Base DN that contains entries to fix up")

      fixup.add_argument('-f', '--filter',

                         help='Filter for entries to fix up.\n If omitted, all entries with objectclass '

@@ -8,8 +8,22 @@ 

  

  import json

  import ldap

- from lib389.plugins import PassThroughAuthenticationPlugin

- from lib389.cli_conf import add_generic_plugin_parsers

+ from lib389.plugins import (PassThroughAuthenticationPlugin, PAMPassThroughAuthPlugin,

+                             PAMPassThroughAuthConfigs, PAMPassThroughAuthConfig)

+ 

+ from lib389.cli_conf import add_generic_plugin_parsers, generic_object_edit, generic_object_add

+ 

+ arg_to_attr_pam = {

+     'exclude_suffix': 'pamExcludeSuffix',

+     'include_suffix': 'pamIncludeSuffix',

+     'missing_suffix': 'pamMissingSuffix',

+     'filter': 'pamFilter',

+     'id_attr': 'pamIDAttr',

+     'id_map_method': 'pamIDMapMethod',

+     'fallback': 'pamFallback',

+     'secure': 'pamSecure',

+     'service': 'pamService'

+ }

  

  

  def pta_list(inst, basedn, log, args):
@@ -18,13 +32,13 @@ 

      result = []

      urls = plugin.get_urls()

      if args.json:

-         print(json.dumps({"type": "list", "items": urls}))

+         log.info(json.dumps({"type": "list", "items": urls}))

      else:

          if len(urls) > 0:

              for i in result:

-                 print(i)

+                 log.info(i)

          else:

-             print("No Pass Through Auth attributes were found")

+             log.info("No Pass Through Auth URLs were found")

  

  

  def pta_add(inst, basedn, log, args):
@@ -62,27 +76,132 @@ 

      log.info("Successfully deleted %s", args.URL)

  

  

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

+     log = log.getChild('pam_pta_list')

+     configs = PAMPassThroughAuthConfigs(inst)

+     result = []

+     result_json = []

+     for config in configs.list():

+         if args.json:

+             result_json.append(config.get_all_attrs_json())

+         else:

+             result.append(config.rdn)

+     if args.json:

+         log.info(json.dumps({"type": "list", "items": result_json}))

+     else:

+         if len(result) > 0:

+             for i in result:

+                 log.info(i)

+         else:

+             log.info("No PAM Pass Through Auth plugin config instances")

+ 

+ 

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

+     log = log.getChild('pam_pta_add')

+     plugin = PAMPassThroughAuthPlugin(inst)

+     props = {'cn': args.NAME}

+     generic_object_add(PAMPassThroughAuthConfig, inst, log, args, arg_to_attr_pam, basedn=plugin.dn, props=props)

+ 

+ 

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

+     log = log.getChild('pam_pta_edit')

+     configs = PAMPassThroughAuthConfigs(inst)

+     config = configs.get(args.NAME)

+     generic_object_edit(config, log, args, arg_to_attr_pam)

+ 

+ 

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

+     log = log.getChild('pam_pta_show')

+     configs = PAMPassThroughAuthConfigs(inst)

+     config = configs.get(args.NAME)

+ 

+     if not config.exists():

+         raise ldap.NO_SUCH_OBJECT("Entry %s doesn't exists" % args.name)

+     if args and args.json:

+         o_str = config.get_all_attrs_json()

+         log.info(o_str)

+     else:

+         log.info(config.display())

+ 

+ 

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

+     log = log.getChild('pam_pta_del')

+     configs = PAMPassThroughAuthConfigs(inst)

+     config = configs.get(args.NAME)

+     config.delete()

+     log.info("Successfully deleted the %s", config.dn)

+ 

+ 

+ def _add_parser_args_pam(parser):

+     parser.add_argument('--exclude-suffix', nargs='+',

+                         help='Specifies a suffix to exclude from PAM authentication (pamExcludeSuffix)')

+     parser.add_argument('--include-suffix', nargs='+',

+                         help='Sets a suffix to include for PAM authentication (pamIncludeSuffix)')

+     parser.add_argument('--missing-suffix', choices=['ERROR', 'ALLOW', 'IGNORE'],

+                         help='Identifies how to handle missing include or exclude suffixes (pamMissingSuffix)')

+     parser.add_argument('--filter',

+                         help='Sets an LDAP filter to use to identify specific entries within '

+                              'the included suffixes for which to use PAM pass-through authentication (pamFilter)')

+     parser.add_argument('--id-attr', nargs='+',

+                         help='Contains the attribute name which is used to hold the PAM user ID (pamIDAttr)')

+     parser.add_argument('--id_map_method',

+                         help='Gives the method to use to map the LDAP bind DN to a PAM identity (pamIDMapMethod)')

+     parser.add_argument('--fallback', choices=['TRUE', 'FALSE'], type=str.upper,

+                         help='Sets whether to fallback to regular LDAP authentication '

+                              'if PAM authentication fails (pamFallback)')

+     parser.add_argument('--secure', choices=['TRUE', 'FALSE'], type=str.upper,

+                         help='Requires secure TLS connection for PAM authentication (pamSecure)')

+     parser.add_argument('--service',

+                         help='Contains the service name to pass to PAM (pamService)')

+ 

+ 

  def create_parser(subparsers):

-     passthroughauth_parser = subparsers.add_parser('pass-through-auth', help='Manage and configure Pass-Through Authentication plugin')

+     passthroughauth_parser = subparsers.add_parser('pass-through-auth',

+                                                    help='Manage and configure Pass-Through Authentication plugins '

+                                                         '(URLs and PAM)')

      subcommands = passthroughauth_parser.add_subparsers(help='action')

      add_generic_plugin_parsers(subcommands, PassThroughAuthenticationPlugin)

  

-     list = subcommands.add_parser('list', help='List available plugin configs')

-     list.set_defaults(func=pta_list)

- 

-     add = subcommands.add_parser('add', help='Add the config entry')

-     add.add_argument('URL', help='The full LDAP URL in format '

-                                   '"ldap|ldaps://authDS/subtree maxconns,maxops,timeout,ldver,connlifetime,startTLS". '

-                                   'If one optional parameter is specified the rest should be specified too')

-     add.set_defaults(func=pta_add)

- 

-     edit = subcommands.add_parser('modify', help='Edit the config entry')

-     edit.add_argument('OLD_URL', help='The full LDAP URL you get from the "list" command')

-     edit.add_argument('NEW_URL', help='The full LDAP URL in format '

-                                  '"ldap|ldaps://authDS/subtree maxconns,maxops,timeout,ldver,connlifetime,startTLS". '

-                                  'If one optional parameter is specified the rest should be specified too')

-     edit.set_defaults(func=pta_edit)

- 

-     delete = subcommands.add_parser('delete', help='Delete the config entry')

-     delete.add_argument('URL', help='The full LDAP URL you get from the "list" command')

-     delete.set_defaults(func=pta_del)

+     list = subcommands.add_parser('list', help='List pass-though plugin URLs or PAM configurations.')

+     subcommands_list = list.add_subparsers(help='action')

+     list_urls = subcommands_list.add_parser('urls', help='List URLs.')

+     list_urls.set_defaults(func=pta_list)

+     list_pam = subcommands_list.add_parser('pam-configs', help='List PAM configurations.')

+     list_pam.set_defaults(func=pam_pta_list)

+ 

+     url = subcommands.add_parser('url', help='Manage PTA URL configurations.')

+     subcommands_url = url.add_subparsers(help='action')

+ 

+     add_url = subcommands_url.add_parser('add', help='Add the config entry')

+     add_url.add_argument('URL',

+                          help='The full LDAP URL in format '

+                               '"ldap|ldaps://authDS/subtree maxconns,maxops,timeout,ldver,connlifetime,startTLS". '

+                               'If one optional parameter is specified the rest should be specified too')

+     add_url.set_defaults(func=pta_add)

+ 

+     edit_url = subcommands_url.add_parser('modify', help='Edit the config entry')

+     edit_url.add_argument('OLD_URL', help='The full LDAP URL you get from the "list" command')

+     edit_url.add_argument('NEW_URL',

+                           help='The full LDAP URL in format '

+                                '"ldap|ldaps://authDS/subtree maxconns,maxops,timeout,ldver,connlifetime,startTLS". '

+                                'If one optional parameter is specified the rest should be specified too')

+     edit_url.set_defaults(func=pta_edit)

+ 

+     delete_url = subcommands_url.add_parser('delete', help='Delete the config entry')

+     delete_url.add_argument('URL', help='The full LDAP URL you get from the "list" command')

+     delete_url.set_defaults(func=pta_del)

+ 

+     pam = subcommands.add_parser('pam-config', help='Manage PAM PTA configurations.')

+     pam.add_argument('NAME', help='The PAM PTA configuration name')

+     subcommands_pam = pam.add_subparsers(help='action')

+ 

+     add = subcommands_pam.add_parser('add', help='Add the config entry')

+     add.set_defaults(func=pam_pta_add)

+     _add_parser_args_pam(add)

+     edit = subcommands_pam.add_parser('set', help='Edit the config entry')

+     edit.set_defaults(func=pam_pta_edit)

+     _add_parser_args_pam(edit)

+     show = subcommands_pam.add_parser('show', help='Display the config entry')

+     show.set_defaults(func=pam_pta_show)

+     delete = subcommands_pam.add_parser('delete', help='Delete the config entry')

+     delete.set_defaults(func=pam_pta_del)

@@ -0,0 +1,53 @@ 

+ # --- BEGIN COPYRIGHT BLOCK ---

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

+ # All rights reserved.

+ #

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

+ # See LICENSE for details.

+ # --- END COPYRIGHT BLOCK ---

+ 

+ from lib389.plugins import POSIXWinsyncPlugin

+ from lib389.cli_conf import add_generic_plugin_parsers, generic_object_edit

+ 

+ arg_to_attr = {

+     'create_memberof_task': 'posixWinsyncCreateMemberOfTask',

+     'lower_case_uid': 'posixWinsyncLowerCaseUID',

+     'map_member_uid': 'posixWinsyncMapMemberUID',

+     'map_nested_grouping': 'posixWinsyncMapNestedGrouping',

+     'ms_sfu_schema': 'posixWinsyncMsSFUSchema'

+ }

+ 

+ 

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

+     log = log.getChild('winsync_edit')

+     plugin = POSIXWinsyncPlugin(inst)

+     generic_object_edit(plugin, log, args, arg_to_attr)

+ 

+ 

+ def _add_parser_args(parser):

+     parser.add_argument('--create-memberof-task', choices=['true', 'false'], type=str.lower,

+                         help=' sets whether to run the memberOf fix-up task immediately after a sync run in order '

+                              'to update group memberships for synced users (posixWinsyncCreateMemberOfTask)')

+     parser.add_argument('--lower-case-uid', choices=['true', 'false'], type=str.lower,

+                         help='Sets whether to store (and, if necessary, convert) the UID value in the memberUID '

+                              'attribute in lower case.(posixWinsyncLowerCaseUID)')

+     parser.add_argument('--map-member-uid', choices=['true', 'false'], type=str.lower,

+                         help='Sets whether to map the memberUID attribute in an Active Directory group to '

+                              'the uniqueMember attribute in a Directory Server group (posixWinsyncMapMemberUID)')

+     parser.add_argument('--map-nested-grouping', choices=['true', 'false'], type=str.lower,

+                         help='Manages if nested groups are updated when memberUID attributes in '

+                              'an Active Directory POSIX group change (posixWinsyncMapNestedGrouping)')

+     parser.add_argument('--ms-sfu-schema', choices=['true', 'false'], type=str.lower,

+                         help='Sets whether to the older Microsoft System Services for Unix 3.0 (msSFU30) '

+                              'schema when syncing Posix attributes from Active Directory (posixWinsyncMsSFUSchema)')

+ 

+ 

+ def create_parser(subparsers):

+     winsync = subparsers.add_parser('posix-winsync', help='Manage and configure The Posix Winsync API plugin')

+     subcommands = winsync.add_subparsers(help='action')

+     add_generic_plugin_parsers(subcommands, POSIXWinsyncPlugin)

+ 

+     edit = subcommands.add_parser('set', help='Edit the plugin')

+     edit.set_defaults(func=winsync_edit)

+     _add_parser_args(edit)

+ 

@@ -10,10 +10,11 @@ 

  from lib389.cli_conf import add_generic_plugin_parsers, generic_object_edit

  

  arg_to_attr = {

-     'is-replicated': 'isReplicated',

+     'is_replicated': 'isReplicated',

      'attribute': 'nsslapd-attribute',

      'directory': 'nsslapd-changelogdir',

-     'max-age': 'nsslapd-changelogmaxage',

+     'max_age': 'nsslapd-changelogmaxage',

+     'exclude_suffix': 'nsslapd-exclude-suffix'

  }

  

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

  

  

  def _add_parser_args(parser):

-     parser.add_argument('--is-replicated', choices=['true', 'false'],

+     parser.add_argument('--is-replicated', choices=['true', 'false'], type=str.lower,

                          help='Sets a flag to indicate on a change in the changelog whether the change is newly made '

                               'on that server or whether it was replicated over from another server (isReplicated)')

      parser.add_argument('--attribute',
@@ -36,6 +37,9 @@ 

      parser.add_argument('--max-age',

                          help='This attribute specifies the maximum age of any entry '

                               'in the changelog (nsslapd-changelogmaxage)')

+     parser.add_argument('--exclude-suffix',

+                         help='This attribute specifies the suffix which will be excluded '

+                              'from the scope of the plugin (nsslapd-exclude-suffix)')

  

  

  def create_parser(subparsers):

@@ -27,20 +27,20 @@ 

  

  

  def _add_parser_args(parser):

-     parser.add_argument('--allow-host',

+     parser.add_argument('--allow-host', nargs='+',

                          help='Sets what hosts, by fully-qualified domain name, the root user is allowed to use '

                               'to access the Directory Server. Any hosts not listed are implicitly denied '

                               '(rootdn-allow-host)')

-     parser.add_argument('--deny-host',

+     parser.add_argument('--deny-host', nargs='+',

                          help='Sets what hosts, by fully-qualified domain name, the root user is not allowed to use '

                               'to access the Directory Server Any hosts not listed are implicitly allowed '

                               '(rootdn-deny-host). If an host address is listed in both the rootdn-allow-host and '

                               'rootdn-deny-host attributes, it is denied access.')

-     parser.add_argument('--allow-ip',

+     parser.add_argument('--allow-ip', nargs='+',

                          help='Sets what IP addresses, either IPv4 or IPv6, for machines the root user is allowed '

                               'to use to access the Directory Server Any IP addresses not listed are implicitly '

                               'denied (rootdn-allow-ip)')

-     parser.add_argument('--deny-ip',

+     parser.add_argument('--deny-ip', nargs='+',

                          help='Sets what IP addresses, either IPv4 or IPv6, for machines the root user is not allowed '

                               'to use to access the Directory Server. Any IP addresses not listed are implicitly '

                               'allowed (rootdn-deny-ip) If an IP address is listed in both the rootdn-allow-ip and '

file modified
+102 -11
@@ -122,8 +122,22 @@ 

      :type dn: str

      """

  

+     _plugin_properties = {

+         'nsslapd-pluginEnabled': 'off',

+         'nsslapd-pluginPath': 'libattr-unique-plugin',

+         'nsslapd-pluginInitfunc': 'NSUniqueAttr_Init',

+         'nsslapd-pluginType': 'betxnpreoperation',

+         'nsslapd-plugin-depends-on-type': 'database',

+         'nsslapd-pluginId': 'NSUniqueAttr',

+         'nsslapd-pluginVendor': '389 Project',

+         'nsslapd-pluginVersion': 'none',

+         'nsslapd-pluginDescription': 'Enforce unique attribute values',

+     }

+ 

      def __init__(self, instance, dn="cn=attribute uniqueness,cn=plugins,cn=config"):

          super(AttributeUniquenessPlugin, self).__init__(instance, dn)

+         self._protected = False

+         self._create_objectclasses = ['top', 'nsslapdplugin', 'extensibleObject']

  

      ## These are some wrappers to the important attributes

      # This plugin will be "tricky" in that it can have "many" instance
@@ -170,14 +184,18 @@ 

      """

  

      def __init__(self, instance, basedn="cn=plugins,cn=config"):

-         super(Plugins, self).__init__(instance=instance)

-         self._objectclasses = ['top', 'nsslapdplugin']

+         super(DSLdapObjects, self).__init__(instance)

+         self._instance = instance

+         self._objectclasses = ['top', 'nsslapdplugin', 'extensibleObject']

          self._filterattrs = ['cn', 'nsslapd-pluginPath']

          self._childobject = AttributeUniquenessPlugin

          self._basedn = basedn

          # This is used to allow entry to instance to work

          self._list_attrlist = ['dn', 'nsslapd-pluginPath']

          self._search_filter = "(nsslapd-pluginId=NSUniqueAttr)"

+         self._scope = ldap.SCOPE_SUBTREE

+         self._server_controls = None

+         self._client_controls = None

  

      def list(self):

          """Get a list of all plugin instances where nsslapd-pluginId: NSUniqueAttr
@@ -215,7 +233,7 @@ 

          # Filter based on the objectclasses and the basedn

          # Based on the selector, we should filter on that too.

          # This will yield and & filter for objectClass with as many terms as needed.

-         filterstr = "&(cn=%s)%s" % (selector, self._search_filter)

+         filterstr = "(&(cn=%s)%s)" % (selector, self._search_filter)

          self._log.debug('_gen_selector filter = %s' % filterstr)

          return self._instance.search_ext_s(

              base=self._basedn,
@@ -1185,21 +1203,19 @@ 

      def __init__(self, instance, dn="cn=Linked Attributes,cn=plugins,cn=config"):

          super(LinkedAttributesPlugin, self).__init__(instance, dn)

  

-     def fixup(self, basedn, _filter=None):

+     def fixup(self, linkdn):

          """Create a fixup linked attributes task

  

-         :param basedn: Basedn to fix up

-         :type basedn: str

-         :param _filter: a filter for entries to fix up

-         :type _filter: str

+         :param linkdn: Link DN to fix up

+         :type linkdn: str

  

          :returns: an instance of Task(DSLdapObject)

          """

  

          task = tasks.FixupLinkedAttributesTask(self._instance)

-         task_properties = {'basedn': basedn}

-         if _filter is not None:

-             task_properties['filter'] = _filter

+         task_properties = {}

+         if linkdn is not None:

+             task_properties['linkdn'] = linkdn

          task.create(properties=task_properties)

  

          return task
@@ -1235,6 +1251,7 @@ 

          super(LinkedAttributesConfigs, self).__init__(instance)

          self._objectclasses = ['top', 'extensibleObject']

          self._filterattrs = ['cn']

+         self._scope = ldap.SCOPE_ONELEVEL

          self._childobject = LinkedAttributesConfig

          self._basedn = basedn

  
@@ -1265,6 +1282,80 @@ 

          return result

  

  

+ class POSIXWinsyncPlugin(Plugin):

+     """A single instance of Posix Winsync API plugin entry

+ 

+     :param instance: An instance

+     :type instance: lib389.DirSrv

+     :param dn: Entry DN

+     :type dn: str

+     """

+ 

+     def __init__(self, instance, dn="cn=Posix Winsync API,cn=plugins,cn=config"):

+         super(POSIXWinsyncPlugin, self).__init__(instance, dn)

+ 

+ 

+ class PAMPassThroughAuthPlugin(Plugin):

+     """A single instance of PAM Pass Through Auth plugin entry

+ 

+     :param instance: An instance

+     :type instance: lib389.DirSrv

+     :param dn: Entry DN

+     :type dn: str

+     """

+ 

+     def __init__(self, instance, dn="cn=PAM Pass Through Auth,cn=plugins,cn=config"):

+         super(PAMPassThroughAuthPlugin, self).__init__(instance, dn)

+ 

+ 

+ class PAMPassThroughAuthConfig(Plugin):

+     """A single instance of PAM Pass Through Auth config entry

+ 

+     :param instance: An instance

+     :type instance: lib389.DirSrv

+     :param dn: Entry DN

+     :type dn: str

+     """

+ 

+     _plugin_properties = {

+         'cn' : 'USN',

+         'nsslapd-pluginEnabled': 'off',

+         'nsslapd-pluginPath': 'libpam-passthru-plugin',

+         'nsslapd-pluginInitfunc': 'pam_passthruauth_init',

+         'nsslapd-pluginType': 'betxnpreoperation',

+         'nsslapd-plugin-depends-on-type': 'database',

+         'nsslapd-pluginId': 'PAM',

+         'nsslapd-pluginVendor': '389 Project',

+         'nsslapd-pluginVersion': '1.3.7.0',

+         'nsslapd-pluginDescription': 'PAM Pass Through Auth plugin'

+     }

+ 

+     def __init__(self, instance, dn=None):

+         super(PAMPassThroughAuthConfig, self).__init__(instance, dn)

+         self._rdn_attribute = 'cn'

+         self._must_attributes = ['cn']

+         self._create_objectclasses = ['top', 'extensibleObject', 'nsslapdplugin', 'pamConfig']

+         self._protected = False

+ 

+ 

+ class PAMPassThroughAuthConfigs(DSLdapObjects):

+     """A DSLdapObjects entity which represents PAM Pass Through Auth config entry

+ 

+     :param instance: An instance

+     :type instance: lib389.DirSrv

+     :param basedn: Base DN for all account entries below

+     :type basedn: str

+     """

+ 

+     def __init__(self, instance, basedn="cn=PAM Pass Through Auth,cn=plugins,cn=config"):

+         super(PAMPassThroughAuthConfigs, self).__init__(instance)

+         self._objectclasses = ['top', 'extensibleObject', 'nsslapdplugin', 'pamConfig']

+         self._filterattrs = ['cn']

+         self._scope = ldap.SCOPE_ONELEVEL

+         self._childobject = PAMPassThroughAuthConfig

+         self._basedn = basedn

+ 

+ 

  class USNPlugin(Plugin):

      """A single instance of USN (Update Sequence Number) plugin entry

  

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

          dn = "cn=" + self.cn + "," + DN_FIXUP_LINKED_ATTIBUTES

  

          super(FixupLinkedAttributesTask, self).__init__(instance, dn)

-         self._must_attributes.extend(['basedn'])

  

  

  class MemberOfFixupTask(Task):

Description: Fix 'All plugins' tab rendering issue.
Fix nsds5replicalastinitstatus typo.
Fix generic_object_add logic for cases when RDN is in props and BaseDN is supplied.
Add Posix Winsync API plugin
Add PAM PTA plugin
Fix underscore issues in plugin arguments.
Fix Linked Attribute plugin Fixup task arguments and name.

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

Reviewed by: ?

Looks good. One thing, the logging issue has been fixed so we can stop using "print" and start using "log.info" were applicable.

Ack!

Why not just change the error message instead of the logic?L

Wait, I thought we didn't support winsync anymore ...?

Is linkdn required? If so, it shouldn't be optional.

rebased onto 882e091710a316755e52cb13003a37cf8c116ecc

5 years ago

Looks good. One thing, the logging issue has been fixed so we can stop using "print" and start using "log.info" were applicable.

Done

Why not just change the error message instead of the logic?L

There was an error with the old code (ValueError when only basedn and props were specified). And the previous logic doesn't make much sense... The new one is more clear and the error is fixed with it.

Wait, I thought we didn't support winsync anymore ...?

@mreynolds said that we need the plugin CLI/UI for this one for now.

Is linkdn required? If so, it shouldn't be optional.

It is optional, actually. If this attribute is not set on the task entry, then all configured linked attributes are updated.

Wait, I thought we didn't support winsync anymore ...?

We absolutely still support winsync/passSync

Wait, I thought we didn't support winsync anymore ...?

We absolutely still support winsync/passSync

Today I learnt :)

rebased onto 09965c4

5 years ago

Pull-Request has been merged by spichugi

5 years ago

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/3352

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

3 years ago