Hello community, here is the log from the commit of package yast2-aduc for openSUSE:Factory checked in at 2019-09-26 20:41:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yast2-aduc (Old) and /work/SRC/openSUSE:Factory/.yast2-aduc.new.2352 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yast2-aduc" Thu Sep 26 20:41:24 2019 rev:7 rq:733244 version:1.8 Changes: -------- --- /work/SRC/openSUSE:Factory/yast2-aduc/yast2-aduc.changes 2019-09-25 08:27:17.158393649 +0200 +++ /work/SRC/openSUSE:Factory/.yast2-aduc.new.2352/yast2-aduc.changes 2019-09-26 20:41:27.562506366 +0200 @@ -1,0 +2,6 @@ +Wed Sep 25 20:29:49 UTC 2019 - dmul...@suse.com + +- Update to 1.8: + + Add ability to change/enable/unlock user's passwords; (bsc#1152052); + +------------------------------------------------------------------- Old: ---- yast2-aduc-1.7.tar.bz2 New: ---- yast2-aduc-1.8.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-aduc.spec ++++++ --- /var/tmp/diff_new_pack.dh13J9/_old 2019-09-26 20:41:28.246504539 +0200 +++ /var/tmp/diff_new_pack.dh13J9/_new 2019-09-26 20:41:28.250504528 +0200 @@ -17,7 +17,7 @@ Name: yast2-aduc -Version: 1.7 +Version: 1.8 Release: 0 Summary: Active Directory Users and Computers for YaST License: GPL-3.0-only ++++++ yast2-aduc-1.7.tar.bz2 -> yast2-aduc-1.8.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-aduc-1.7/package/yast2-aduc.changes new/yast2-aduc-1.8/package/yast2-aduc.changes --- old/yast2-aduc-1.7/package/yast2-aduc.changes 2019-09-23 20:03:30.000000000 +0200 +++ new/yast2-aduc-1.8/package/yast2-aduc.changes 2019-09-25 22:31:56.000000000 +0200 @@ -1,4 +1,10 @@ ------------------------------------------------------------------- +Wed Sep 25 20:29:49 UTC 2019 - dmul...@suse.com + +- Update to 1.8: + + Add ability to change/enable/unlock user's passwords; (bsc#1152052); + +------------------------------------------------------------------- Mon Sep 23 17:10:07 UTC 2019 - dmul...@suse.com - Update to 1.7: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-aduc-1.7/package/yast2-aduc.spec new/yast2-aduc-1.8/package/yast2-aduc.spec --- old/yast2-aduc-1.7/package/yast2-aduc.spec 2019-09-23 20:03:30.000000000 +0200 +++ new/yast2-aduc-1.8/package/yast2-aduc.spec 2019-09-25 22:31:56.000000000 +0200 @@ -17,7 +17,7 @@ Name: yast2-aduc -Version: 1.7 +Version: 1.8 Release: 0 Summary: Active Directory Users and Computers for YaST License: GPL-3.0-only diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-aduc-1.7/src/include/aduc/complex.py new/yast2-aduc-1.8/src/include/aduc/complex.py --- old/yast2-aduc-1.7/src/include/aduc/complex.py 2019-09-23 20:03:30.000000000 +0200 +++ new/yast2-aduc-1.8/src/include/aduc/complex.py 2019-09-25 22:31:56.000000000 +0200 @@ -7,7 +7,8 @@ import traceback from yast import ycpbuiltins from adcommon.strings import strcmp, strcasecmp -from adcommon.yldap import Ldap, LdapException, stringify_ldap, SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, addlist, modlist, y2error_dialog +from adcommon.yldap import Ldap, LdapException, stringify_ldap, SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, addlist, modlist, y2error_dialog, ldb +from samba import NTSTATUSError import six @@ -253,3 +254,38 @@ ycpbuiltins.y2error(traceback.format_exc()) ycpbuiltins.y2error('ldap.rename_s: %s\n' % str(e)) y2error_dialog(str(e)) + + def is_user(self, cn, container): + SAM_USER_OBJECT = 0x30000000 + res = self.search(container, SCOPE_ONELEVEL, '(cn=%s)' % cn, ['sAMAccountType']) + if len(res) == 1 and 'sAMAccountType' in res[0].keys(): + sAMAccountType = int(str(res[0]['sAMAccountType'])) + if sAMAccountType == SAM_USER_OBJECT: + return True + return False + + def is_user_enabled(self, cn, container): + DISABLED = 0x0002 + res = self.search(container, SCOPE_ONELEVEL, '(cn=%s)' % cn, ['userAccountControl']) + if len(res) == 1 and 'userAccountControl' in res[0].keys(): + userAccountControl = int(str(res[0]['userAccountControl'])) + if not bool(userAccountControl & DISABLED): + return True + return False + + def reset_password(self, dn, sAMAccountName, password, pwdLastSet, unlock): + try: + self.net.set_password(sAMAccountName, self.realm, password) + except NTSTATUSError as e: + y2error_dialog(e.args[-1]) + return False + ldif = 'dn: %s\nchangetype: modify\n' % dn + if unlock: + ldif += 'replace: lockoutTime\nlockoutTime: 0\n' + ldif += 'replace: pwdLastSet\npwdLastSet: %d\n' % pwdLastSet + try: + self.modify_ldif(ldif) + except ldb.LdbError as e: + y2error_dialog(e.args[-1]) + return False + return True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-aduc-1.7/src/include/aduc/dialogs.py new/yast2-aduc-1.8/src/include/aduc/dialogs.py --- old/yast2-aduc-1.7/src/include/aduc/dialogs.py 2019-09-23 20:03:30.000000000 +0200 +++ new/yast2-aduc-1.8/src/include/aduc/dialogs.py 2019-09-25 22:31:56.000000000 +0200 @@ -12,6 +12,7 @@ from adcommon.creds import YCreds, switch_domains from adcommon.ui import CreateMenu, DeleteButtonBox import traceback +from adcommon.yldap import ldb def escape_filter_chars(val): """ Escape special chars from RFC 4515 @@ -1156,7 +1157,7 @@ self.got_creds = ycred.Show(self.cred_valid) self.realm = self.lp.get('realm') - def __setup_menus(self, container=None, obj=False): + def __setup_menus(self, container=None, obj=None, user=False, enabled=True): menus = [{'title': '&File', 'id': 'file', 'type': 'Menu'}, {'title': 'Change domain...', 'id': 'change_domain', 'type': 'MenuEntry', 'parent': 'file'}, {'title': 'Exit', 'id': 'abort', 'type': 'MenuEntry', 'parent': 'file'}, @@ -1176,7 +1177,13 @@ menus.append({'title': 'User', 'id': 'context_add_user', 'type': 'MenuEntry', 'parent': 'new_but'}) menus.append({'title': 'Shared Folder', 'id': 'context_add_shared_folder', 'type': 'MenuEntry', 'parent': 'new_but'}) menus.append({'title': 'Refresh', 'id': 'refresh', 'type': 'MenuEntry', 'parent': 'action'}) - elif obj: + if user and enabled: + menus.append({'title': 'Disable Account', 'id': 'disable', 'type': 'MenuEntry', 'parent': 'action'}) + elif user and not enabled: + menus.append({'title': 'Enable Account', 'id': 'enable', 'type': 'MenuEntry', 'parent': 'action'}) + if user: + menus.append({'title': 'Reset Password...', 'id': 'reset', 'type': 'MenuEntry', 'parent': 'action'}) + if obj: menus.append({'title': 'Move...', 'id': 'context_move', 'type': 'MenuEntry', 'parent': 'action'}) menus.append({'title': 'Delete', 'id': 'delete', 'type': 'MenuEntry', 'parent': 'action'}) menus.append({'title': 'Properties', 'id': 'properties', 'type': 'MenuEntry', 'parent': 'action'}) @@ -1240,12 +1247,21 @@ #Item(Id('context_help'), 'Help'), ]) - def __obj_context_menu(self): - return Term('menu', [ - Item(Id('context_move'), 'Move...'), + def __obj_context_menu(self, user=False, enabled=True): + items = [ + Item(Id('context_move'), 'Move...') + ] + if user and not enabled: + items.append(Item(Id('enable'), 'Enable Account')) + if user and enabled: + items.append(Item(Id('disable'), 'Disable Account')) + if user: + items.append(Item(Id('reset'), 'Reset Password...')) + items.extend([ Item(Id('properties'), 'Properties'), Item(Id('delete'), 'Delete') ]) + return Term('menu', items) def __dom_context_menu(self): return Term('menu', [ @@ -1292,13 +1308,18 @@ elif str(ret) == 'next': return Symbol('abort') elif str(ret) == 'items': - self.__setup_menus(obj=True) + user = False + enabled = True + obj = UI.QueryWidget('items', 'CurrentItem') + if obj: + user = self.conn.is_user(obj, current_container) + enabled = self.conn.is_user_enabled(obj, current_container) + self.__setup_menus(obj=True, user=user, enabled=enabled) if event['EventReason'] == 'ContextMenuActivated': - check = UI.QueryWidget('items', 'CurrentItem') - if check is None: + if obj is None: UI.OpenContextMenu(self.__objs_context_menu(current_container)) else: - UI.OpenContextMenu(self.__obj_context_menu()) + UI.OpenContextMenu(self.__obj_context_menu(user=user, enabled=enabled)) elif event['EventReason'] == 'Activated': self.__show_properties(current_container) elif str(ret) == 'properties': @@ -1389,9 +1410,72 @@ if switch_domains(self.lp, self.creds, self.cred_valid): self.realm = self.lp.get('realm') Wizard.SetContents('Active Directory Users and Computers', self.__aduc_page(), '', False, False) + elif str(ret) == 'enable': + obj = UI.QueryWidget('items', 'CurrentItem') + searchList = self.conn.objects_list(current_container) + currentItem = self.__find_by_name(searchList, obj) + if currentItem: + try: + self.conn.enable_account('(sAMAccountName=%s)' % currentItem[-1]['sAMAccountName'][-1].decode()) + except ldb.LdbError as e: + MessageBox(e.args[-1]).Show() + else: + MessageBox('Object %s has been enabled.' % obj).Show() + elif str(ret) == 'disable': + obj = UI.QueryWidget('items', 'CurrentItem') + searchList = self.conn.objects_list(current_container) + currentItem = self.__find_by_name(searchList, obj) + if currentItem: + try: + self.conn.disable_account('(sAMAccountName=%s)' % currentItem[-1]['sAMAccountName'][-1].decode()) + except ldb.LdbError as e: + MessageBox(e.args[-1]).Show() + else: + MessageBox('Object %s has been disabled.' % obj).Show() + elif str(ret) == 'reset': + obj = UI.QueryWidget('items', 'CurrentItem') + searchList = self.conn.objects_list(current_container) + currentItem = self.__find_by_name(searchList, obj) + if currentItem: + password, pwdLastSet, unlock = self.__reset_password() + if password: + sam = currentItem[-1]['sAMAccountName'][-1].decode() + if self.conn.reset_password(currentItem[0], sam, password, pwdLastSet, unlock): + MessageBox('The password for %s has been changed.' % obj).Show() UI.SetApplicationTitle('Active Directory Users and Computers') return Symbol(ret) + def __reset_password(self): + UI.SetApplicationTitle('Reset Password') + UI.OpenDialog(HBox(HSpacing(1), VBox( + VSpacing(.3), + Left(Password(Id('userPassword'), Opt('hstretch'), 'New password:')), + Left(Password(Id('confirm_passwd'), Opt('hstretch'), 'Confirm password:')), + Left(CheckBox(Id('pwdLastSet'), Opt('hstretch'), UserDataModel['account']['pwdLastSet'], True)), + Left(CheckBox(Id('unlock'), Opt('hstretch'), 'Unlock the user\'s account', False)), + Right(HBox( + PushButton(Id('ok'), 'OK'), + PushButton(Id('cancel'), 'Cancel') + )), + VSpacing(.3), + ), HSpacing(1))) + while True: + ret = UI.UserInput() + if str(ret) == 'ok': + userPassword = UI.QueryWidget('userPassword', 'Value') + confirm_passwd = UI.QueryWidget('confirm_passwd', 'Value') + if userPassword != confirm_passwd: + self.__warn_message('Active Directory Domain Services', 'The New and Confirm passwords must match. Please re-type them.') + continue + pwdLastSet = 0 if UI.QueryWidget('pwdLastSet', 'Value') else -1 + unlock = UI.QueryWidget('unlock', 'Value') + UI.CloseDialog() + return (userPassword, pwdLastSet, unlock) + elif str(ret) == 'abort' or str(ret) == 'cancel': + break + UI.CloseDialog() + return (None, None, None) + def __warn_message(self, title, msg): if six.PY3 and type(msg) is bytes: msg = msg.decode('utf-8')