Hello community,

here is the log from the commit of package yast2-adsi for openSUSE:Factory 
checked in at 2019-05-10 09:20:02
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/yast2-adsi (Old)
 and      /work/SRC/openSUSE:Factory/.yast2-adsi.new.5148 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "yast2-adsi"

Fri May 10 09:20:02 2019 rev:2 rq:701833 version:1.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/yast2-adsi/yast2-adsi.changes    2019-04-17 
10:08:17.274774405 +0200
+++ /work/SRC/openSUSE:Factory/.yast2-adsi.new.5148/yast2-adsi.changes  
2019-05-10 09:20:02.772499256 +0200
@@ -1,0 +2,9 @@
+Thu May 09 17:06:30 UTC 2019 - [email protected]
+
+- Add the Connection Settings dialog, for connecting to different
+  naming contexts and switching domains/servers; (bsc#1134631).
+- Properties dialog opens wrong item when selected from tree view;
+  (bsc#1134630).
+- Version 1.1
+
+-------------------------------------------------------------------

Old:
----
  yast2-adsi-1.0.tar.bz2

New:
----
  yast2-adsi-1.1.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ yast2-adsi.spec ++++++
--- /var/tmp/diff_new_pack.9c5cbw/_old  2019-05-10 09:20:03.180500395 +0200
+++ /var/tmp/diff_new_pack.9c5cbw/_new  2019-05-10 09:20:03.184500406 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-adsi
-Version:        1.0
+Version:        1.1
 Release:        0
 Summary:        ADSI Edit for YaST
 License:        GPL-3.0-only
@@ -30,7 +30,7 @@
 Requires:       samba-client
 Requires:       samba-python3
 Requires:       yast2
-Requires:       yast2-adcommon-python
+Requires:       yast2-adcommon-python >= 0.3
 Requires:       yast2-python3-bindings >= 4.0.0
 BuildRequires:  autoconf
 BuildRequires:  automake

++++++ yast2-adsi-1.0.tar.bz2 -> yast2-adsi-1.1.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-adsi-1.0/package/yast2-adsi.changes 
new/yast2-adsi-1.1/package/yast2-adsi.changes
--- old/yast2-adsi-1.0/package/yast2-adsi.changes       2019-03-28 
20:03:39.000000000 +0100
+++ new/yast2-adsi-1.1/package/yast2-adsi.changes       2019-05-09 
19:46:53.000000000 +0200
@@ -1,4 +1,13 @@
 -------------------------------------------------------------------
+Thu May 09 17:06:30 UTC 2019 - [email protected]
+
+- Add the Connection Settings dialog, for connecting to different
+  naming contexts and switching domains/servers; (bsc#1134631).
+- Properties dialog opens wrong item when selected from tree view;
+  (bsc#1134630).
+- Version 1.1
+
+-------------------------------------------------------------------
 Thu Mar 28 19:03:09 UTC 2019 - [email protected]
 
 - Display the right attributes for editing (mostly)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-adsi-1.0/package/yast2-adsi.spec 
new/yast2-adsi-1.1/package/yast2-adsi.spec
--- old/yast2-adsi-1.0/package/yast2-adsi.spec  2019-03-28 20:03:39.000000000 
+0100
+++ new/yast2-adsi-1.1/package/yast2-adsi.spec  2019-05-09 19:46:53.000000000 
+0200
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-adsi
-Version:        1.0
+Version:        1.1
 Release:        0
 Summary:        ADSI Edit for YaST
 License:        GPL-3.0
@@ -31,7 +31,7 @@
 Requires:       yast2
 Requires:       yast2-python3-bindings >= 4.0.0
 Requires:       python3-ldap
-Requires:       yast2-adcommon-python
+Requires:       yast2-adcommon-python >= 0.3
 BuildRequires:  autoconf
 BuildRequires:  automake
 BuildRequires:  perl-XML-Writer
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-adsi-1.0/src/adsi.py 
new/yast2-adsi-1.1/src/adsi.py
--- old/yast2-adsi-1.0/src/adsi.py      2019-03-28 20:03:39.000000000 +0100
+++ new/yast2-adsi-1.1/src/adsi.py      2019-05-09 19:46:53.000000000 +0200
@@ -34,10 +34,13 @@
 
     # Set the loadparm context
     lp = LoadParm()
-    if os.getenv("SMB_CONF_PATH") is not None:
-        lp.load(os.getenv("SMB_CONF_PATH"))
-    else:
-        lp.load_default()
+    try:
+        if os.getenv("SMB_CONF_PATH") is not None:
+            lp.load(os.getenv("SMB_CONF_PATH"))
+        else:
+            lp.load_default()
+    except RuntimeError:
+        pass
 
     # Initialize the session
     creds = Credentials()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-adsi-1.0/src/complex.py 
new/yast2-adsi-1.1/src/complex.py
--- old/yast2-adsi-1.0/src/complex.py   2019-03-28 20:03:39.000000000 +0100
+++ new/yast2-adsi-1.1/src/complex.py   2019-05-09 19:46:53.000000000 +0200
@@ -11,15 +11,36 @@
 import six
 
 class Connection(Ldap):
-    def __init__(self, lp, creds):
-        super().__init__(lp, creds)
+    def __init__(self, lp, creds, ldap_url):
+        super().__init__(lp, creds, ldap_url=ldap_url)
         self.realm_dn = self.realm_to_dn(self.realm)
+        self.naming_contexts = self.__naming_contexts()
+        self.rootdse = False
+        if self.ldap_url.dn == 'Default naming context':
+            naming_context = 'defaultNamingContext'
+        elif self.ldap_url.dn == 'Configuration':
+            naming_context = 'configurationNamingContext'
+        elif self.ldap_url.dn == 'Schema':
+            naming_context = 'schemaNamingContext'
+        elif self.ldap_url.dn == 'RootDSE':
+            self.rootdse = True
+        else:
+            naming_context = self.ldap_url.dn
+        if not self.rootdse:
+            self.naming_context_name = self.ldap_url.dn
+            self.naming_context = 
self.naming_contexts[naming_context][-1].decode() if naming_context in 
self.naming_contexts else naming_context
         self.schema = {}
         self.__load_schema()
 
     def realm_to_dn(self, realm):
         return ','.join(['DC=%s' % part for part in realm.lower().split('.')])
 
+    def __naming_contexts(self):
+        attrs = ['configurationNamingContext', 'defaultNamingContext', 
'namingContexts', 'rootDomainNamingContext', 'schemaNamingContext']
+        res = self.ldap_search_s('', SCOPE_BASE, '(objectclass=*)', attrs)
+        if res and len(res) > 0 and len(res[0]) > 1:
+            return res[-1][-1]
+
     def __well_known_container(self, container):
         if strcmp(container, 'system'):
             wkguiduc = 'AB1D30F3768811D1ADED00C04FD8D5CD'
@@ -104,7 +125,7 @@
 
     def containers(self, container=None):
         if not container:
-            container = self.realm_dn
+            container = self.naming_context
         search = '(objectClass=*)'
         ret = self.ldap_search(container, SCOPE_ONELEVEL, search, ['name', 
'objectClass'])
         results = []
@@ -118,7 +139,7 @@
 
     def objs(self, container=None):
         if not container:
-            container = self.realm_dn
+            container = self.naming_context
         search = '(objectClass=*)'
         ret = self.ldap_search(container, SCOPE_ONELEVEL, search, ['name', 
'objectClass'])
         return [(e[1]['name'][-1], e[1]['objectClass'][-1], e[0]) for e in ret]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-adsi-1.0/src/dialogs.py 
new/yast2-adsi-1.1/src/dialogs.py
--- old/yast2-adsi-1.0/src/dialogs.py   2019-03-28 20:03:39.000000000 +0100
+++ new/yast2-adsi-1.1/src/dialogs.py   2019-05-09 19:46:53.000000000 +0200
@@ -15,6 +15,9 @@
 from adcommon.yldap import SCOPE_SUBTREE as SUBTREE
 from adcommon.creds import YCreds, MUST_USE_KERBEROS
 from adcommon.ui import CreateMenu, DeleteButtonBox
+from samba.net import Net
+from samba.dcerpc import nbt
+from samba.credentials import Credentials
 
 def have_x():
     from subprocess import Popen, PIPE
@@ -50,7 +53,10 @@
         self.conn = conn
         self.attribute = attr
         self.value = val
-        self.attr_type = 
self.conn.schema['attributeTypes'][self.attribute.encode()]
+        if self.attribute.encode() in self.conn.schema['attributeTypes']:
+            self.attr_type = 
self.conn.schema['attributeTypes'][self.attribute.encode()]
+        else:
+            self.attr_type = None
 
     def __dialog(self):
         opts = tuple()
@@ -74,7 +80,7 @@
 
     def Show(self):
         UI.SetApplicationTitle('String Attribute Editor')
-        if not self.attr_type['multi-valued'] or not 
self.attr_type['user-modifiable']:
+        if self.attr_type and (not self.attr_type['multi-valued'] or not 
self.attr_type['user-modifiable']):
             UI.OpenDialog(self.__dialog())
         else:
             return None
@@ -98,8 +104,9 @@
         self.conn = conn
         self.obj = obj
         attrs = []
-        for objectClass in self.obj['objectClass']:
-            attrs.extend(self.__extend_attrs(objectClass))
+        if 'objectClass' in self.obj: # RootDSE doesn't have an objectClass
+            for objectClass in self.obj['objectClass']:
+                attrs.extend(self.__extend_attrs(objectClass))
         attrs = list(set(attrs))
         for attr in attrs:
             if not attr.decode() in self.obj.keys():
@@ -134,7 +141,13 @@
         return val
 
     def __display_value(self, key, val):
-        attr_type = self.conn.schema['attributeTypes'][key.encode()]
+        if key.encode() in self.conn.schema['attributeTypes']:
+            attr_type = self.conn.schema['attributeTypes'][key.encode()]
+        else:
+            # RootDSE attributes don't show up in the schema, so we have to 
guess
+            if len(val) > 1: # multi-valued
+                return '; '.join([v.decode() for v in val])
+            return val[-1]
         if val == None:
             return '<not set>'
         else:
@@ -169,7 +182,11 @@
         ), HSpacing(3)))
 
     def Show(self):
-        UI.SetApplicationTitle(b'CN=%s Properties' % self.obj['cn'][-1])
+        if 'cn' in self.obj:
+            title = b'CN=%s Properties' % self.obj['cn'][-1]
+        else:
+            title = b''
+        UI.SetApplicationTitle(title)
         UI.OpenDialog(self.__new())
         while True:
             ret = UI.UserInput()
@@ -187,7 +204,7 @@
                 new_val = AttrEdit(self.conn, attr, val).Show()
                 if new_val is not None and self.obj[attr] != new_val:
                     self.obj[attr] = new_val
-                UI.SetApplicationTitle(b'CN=%s Properties' % 
self.obj['cn'][-1])
+                UI.SetApplicationTitle(title)
         UI.CloseDialog()
         return ret
 
@@ -304,21 +321,118 @@
         UI.CloseDialog()
         return ret
 
+class ConnectionSettings:
+    def __init__(self, creds, lp):
+        self.creds = creds
+        self.lp = lp
+        self.conn = None
+        self.server = None
+        realm = self.lp.get('realm')
+        if realm:
+            self.server = self.__fetch_server(realm)
+
+    def __fetch_server(self, realm):
+        net = Net(Credentials())
+        cldap_ret = net.finddc(domain=realm, flags=(nbt.NBT_SERVER_LDAP | 
nbt.NBT_SERVER_DS))
+        return cldap_ret.pdc_dns_name if cldap_ret else None
+
+    def __fetch_domain(self):
+        net = Net(Credentials())
+        cldap_ret = net.finddc(address=self.server, flags=(nbt.NBT_SERVER_LDAP 
| nbt.NBT_SERVER_DS))
+        return cldap_ret.dns_domain if cldap_ret else None
+
+    def __new(self):
+        self.contexts = ['Default naming context', 'Configuration', 'RootDSE', 
'Schema']
+        context = self.contexts[0]
+        if self.server:
+            path = 'ldap://%s/%s' % (self.server, context)
+        else:
+            path = ''
+        return MinSize(56, 22, HBox(HSpacing(3), VBox(
+                VSpacing(1),
+                HBox(
+                    HWeight(1, Left(Label('Name:'))),
+                    HWeight(6, InputField(Id('context'), Opt('hstretch'), '', 
context)),
+                ),
+                HBox(
+                    HWeight(1, Left(Label('Path:'))),
+                    HWeight(6, InputField(Id('path'), Opt('hstretch', 
'disabled'), '', path)),
+                ),
+                Frame('Connection Point', VBox(
+                    RadioButtonGroup(VBox(
+                        Left(RadioButton(Id('select_dn'), Opt('hstretch', 
'editable'), 'Select or type a Distinguished Name or Naming Context:', False)),
+                        Left(ComboBox(Id('context_type'), Opt('hstretch', 
'editable', 'notify', 'immediate'), '', [])),
+                        Left(RadioButton(Id('select_nc'), Opt('hstretch'), 
'Select a well known Naming Context:', True)),
+                        Left(ComboBox(Id('context_combo'), Opt('hstretch', 
'notify'), '', [Item(c) for c in self.contexts])),
+                    )),
+                )),
+                Frame('Computer', VBox(
+                    RadioButtonGroup(VBox(
+                        Left(RadioButton(Id('server_select'), Opt('hstretch'), 
'Select or type a domain or server: (Server | Domain [:port])', self.server is 
None)),
+                        Left(ComboBox(Id('server'), Opt('hstretch', 
'editable', 'notify', 'immediate'), '', [])),
+                        Left(RadioButton(Opt('hstretch', 'disabled' if 
self.server is None else ''), 'Default (Domain or server that you logged in 
to)', self.server is not None)),
+                    )),
+                    Left(CheckBox(Opt('hstretch', 'disabled'), 'Use SSL-based 
Encryption', True)),
+                )),
+                Bottom(Right(HBox(
+                    PushButton(Id('ok'), 'OK'),
+                    PushButton(Id('cancel'), 'Cancel'),
+                ))),
+                VSpacing(1),
+            ), HSpacing(3)))
+
+    def Show(self):
+        UI.SetApplicationTitle('Connection Settings')
+        UI.OpenDialog(self.__new())
+        while True:
+            ret = UI.UserInput()
+            if str(ret) == 'abort' or str(ret) == 'cancel':
+                break
+            elif ret == 'context_combo':
+                UI.ChangeWidget('select_nc', 'Value', True)
+                context = UI.QueryWidget('context_combo', 'Value')
+                UI.ChangeWidget('context', 'Value', context)
+                path = 'ldap://%s/%s' % (self.server, context) if self.server 
else None
+                if path:
+                    UI.ChangeWidget('path', 'Value', path)
+            elif ret == 'context_type':
+                UI.ChangeWidget('select_dn', 'Value', True)
+                context = UI.QueryWidget('context_type', 'Value')
+                UI.ChangeWidget('context', 'Value', context)
+                path = 'ldap://%s/%s' % (self.server, context) if self.server 
else None
+                if path:
+                    UI.ChangeWidget('path', 'Value', path)
+            elif ret == 'server':
+                UI.ChangeWidget('server_select', 'Value', True)
+                self.server = UI.QueryWidget('server', 'Value')
+                context = UI.QueryWidget('context_combo', 'Value')
+                UI.ChangeWidget('path', 'Value', 'ldap://%s/%s' % 
(self.server, context))
+            elif ret == 'ok':
+                realm = self.__fetch_domain().upper()
+                if realm:
+                    self.lp.set('realm', realm)
+                path = UI.QueryWidget('path', 'Value')
+                ycred = YCreds(self.creds)
+                def cred_valid():
+                    try:
+                        self.conn = Connection(self.lp, self.creds, path)
+                        return True
+                    except Exception as e:
+                        ycpbuiltins.y2error(str(e))
+                    return False
+                ycred.Show(cred_valid)
+                if self.conn:
+                    break
+        UI.CloseDialog()
+        return self.conn
+
 class ADSI:
     def __init__(self, lp, creds):
         self.realm = lp.get('realm')
         self.lp = lp
         self.creds = creds
         self.__setup_menus()
-        ycred = YCreds(creds)
-        self.got_creds = ycred.get_creds()
-        while self.got_creds:
-            try:
-                self.conn = Connection(lp, creds)
-                break
-            except Exception as e:
-                ycpbuiltins.y2error(str(e))
-                self.got_creds = ycred.get_creds()
+        self.conn = None
 
     def __setup_menus(self, obj=False):
         menus = [{'title': '&File', 'id': 'file', 'type': 'Menu'},
@@ -330,6 +444,9 @@
             menus.append({'title': 'Delete', 'id': 'delete', 'type': 
'MenuEntry', 'parent': 'action'})
             menus.append({'title': 'Refresh', 'id': 'refresh', 'type': 
'MenuEntry', 'parent': 'action'})
             menus.append({'title': 'Properties', 'id': 'properties', 'type': 
'MenuEntry', 'parent': 'action'})
+        else:
+            menus.append({'title': 'Action', 'id': 'action', 'type': 'Menu'})
+            menus.append({'title': 'Connect to...', 'id': 'connect', 'type': 
'MenuEntry', 'parent': 'action'})
         CreateMenu(menus)
 
     def __delete_selected_obj(self, current_object):
@@ -337,8 +454,6 @@
             self.conn.ldap_delete(current_object)
 
     def Show(self):
-        if not self.got_creds:
-            return Symbol('abort')
         UI.SetApplicationTitle('ADSI Edit')
         Wizard.SetContentsButtons('', self.__adsi_page(), '', 'Back', 'Close')
         DeleteButtonBox()
@@ -363,6 +478,11 @@
                     current_object = choice
                     self.__load_right_pane(current_container)
                     self.__setup_menus(obj=True)
+                elif choice == 'rootdse':
+                    current_container = ''
+                    current_object = None
+                    self.__load_right_pane(current_container)
+                    self.__setup_menus(obj=True)
                 else:
                     current_container = None
                     current_object = None
@@ -372,6 +492,9 @@
                     if current_container:
                         menu_open = True
                         UI.OpenContextMenu(self.__objs_context_menu())
+                    else:
+                        menu_open = True
+                        UI.OpenContextMenu(self.__connect_context_menu())
             elif ret == 'context_add_object':
                 obj = NewObjDialog(self.conn, current_container).Show()
                 if obj:
@@ -385,9 +508,9 @@
                     current_object = UI.QueryWidget('items', 'Value')
                     self.__setup_menus(obj=True)
                 else:
-                    self.__obj_properties(current_container)
+                    self.__obj_properties(current_container, current_object)
             elif ret == 'properties':
-                self.__obj_properties(current_container)
+                self.__obj_properties(current_container, current_object)
             elif ret == 'next':
                 break
             elif ret == 'refresh':
@@ -395,18 +518,23 @@
             elif str(ret) == 'delete':
                 self.__delete_selected_obj(current_object)
                 self.__refresh(current_container)
+            elif ret == 'connect':
+                self.conn = ConnectionSettings(self.creds, self.lp).Show()
+                if self.conn:
+                    UI.ReplaceWidget('ldap_tree', self.__ldap_tree())
             UI.SetApplicationTitle('ADSI Edit')
         return ret
 
-    def __obj_properties(self, current_container):
-        current_object = UI.QueryWidget('items', 'Value')
+    def __obj_properties(self, current_container, current_object):
+        if not current_object:
+            current_object = current_container
         obj = self.conn.obj(current_object)[-1]
         old_obj = copy.deepcopy(obj)
         obj = ObjAttrs(self.conn, obj).Show()
         if obj:
             obj = {key: obj[key] for key in obj.keys() if obj[key] != None}
             self.conn.mod_obj(current_object, old_obj, obj)
-            self.__refresh(current_container, current_object)
+            self.__refresh(current_container, current_object if current_object 
!= current_container else None)
 
     def __objs_context_menu(self):
         return Term('menu', [
@@ -418,6 +546,12 @@
             Item(Id('properties'), 'Properties'),
             ])
 
+    def __connect_context_menu(self):
+        return Term('menu', [
+            Item(Id('connect'), 'Connect to...'),
+            Item(Id('refresh'), 'Refresh'),
+        ])
+
     def __warn_delete(self, name):
         if six.PY3 and type(name) is bytes:
             name = name.decode('utf-8')
@@ -463,15 +597,20 @@
         return [Item(Id(e[0]), e[0].split(',')[0], e[0].lower() in 
expand.lower(), self.__fetch_children(e[0], expand)) for e in 
self.conn.containers(parent)]
 
     def __ldap_tree(self, expand=''):
-        top = self.conn.realm_to_dn(self.conn.realm)
-        items = self.__fetch_children(top, expand)
+        if self.conn and not self.conn.rootdse:
+            top = self.conn.naming_context
+            context = '%s [%s]' % (self.conn.naming_context_name, 
self.conn.dc_hostname)
+            items = self.__fetch_children(top, expand)
+            tree = [Item(context, True, [Item(Id(top), top, True, items)])]
+        elif self.conn and self.conn.rootdse:
+            context = 'RootDSE [%s]' % self.conn.dc_hostname
+            tree = [Item(context, True, [Item(Id('rootdse'), 'RootDSE', False, 
[])])]
+        else:
+            tree = []
 
-        return Tree(Id('adsi_tree'), Opt('notify', 'immediate', 
'notifyContextMenu'), 'ADSI Edit', [
-                Item('Default naming context', True, [
-                    Item(Id(top), top, True, items)
-                ])
-            ]
-        )
+        return Tree(Id('adsi_tree'), Opt('notify', 'immediate', 
'notifyContextMenu'), '', [
+            Item(Id('adsi_edit'), 'ADSI Edit', True, tree)
+        ])
 
     def __adsi_page(self):
         return HBox(


Reply via email to