From aa0a06f13c44e0eca0b3f2f0c34f0f7995b87159 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Sun, 23 Dec 2007 19:19:41 -0600 Subject: r26570: - Trim size of the swig-generated Python bindings by removing a bunch of {}'s. - Start working on Python equivalents for various EJS tests. - Fix regression in argument order for reg_diff_apply() in EJS bindings. (This used to be commit c550c03372cb260b78f6a6c132e70571bc4cb852) --- source4/scripting/python/misc_wrap.c | 73 +++----- source4/scripting/python/samba/__init__.py | 20 +- source4/scripting/python/samba/provision.py | 4 +- source4/scripting/python/samba/samba3.py | 224 +++++++++++++++++++++++ source4/scripting/python/samba/tests/__init__.py | 1 + source4/scripting/python/samba/tests/samba3.py | 57 ++++++ source4/scripting/python/samba/upgrade.py | 16 +- 7 files changed, 335 insertions(+), 60 deletions(-) create mode 100644 source4/scripting/python/samba/samba3.py create mode 100644 source4/scripting/python/samba/tests/samba3.py (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/misc_wrap.c b/source4/scripting/python/misc_wrap.c index dc1203e2f0..0f36647487 100644 --- a/source4/scripting/python/misc_wrap.c +++ b/source4/scripting/python/misc_wrap.c @@ -2808,9 +2808,7 @@ SWIGINTERN PyObject *_wrap_random_password(PyObject *SWIGUNUSEDPARM(self), PyObj (char *) "len", NULL }; - { - arg1 = NULL; - } + arg1 = NULL; if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"O:random_password",kwnames,&obj0)) SWIG_fail; ecode2 = SWIG_AsVal_size_t(obj0, &val2); if (!SWIG_IsOK(ecode2)) { @@ -2855,11 +2853,9 @@ SWIGINTERN PyObject *_wrap_ldb_set_credentials(PyObject *SWIGUNUSEDPARM(self), P } arg2 = (struct cli_credentials *)(argp2); } - { - if (arg1 == NULL) - SWIG_exception(SWIG_ValueError, - "ldb context must be non-NULL"); - } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); ldb_set_credentials(arg1,arg2); resultobj = SWIG_Py_Void(); return resultobj; @@ -2893,11 +2889,9 @@ SWIGINTERN PyObject *_wrap_ldb_set_session_info(PyObject *SWIGUNUSEDPARM(self), SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "ldb_set_session_info" "', argument " "2"" of type '" "struct auth_session_info *""'"); } arg2 = (struct auth_session_info *)(argp2); - { - if (arg1 == NULL) - SWIG_exception(SWIG_ValueError, - "ldb context must be non-NULL"); - } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); ldb_set_session_info(arg1,arg2); resultobj = SWIG_Py_Void(); return resultobj; @@ -2936,11 +2930,9 @@ SWIGINTERN PyObject *_wrap_ldb_set_loadparm(PyObject *SWIGUNUSEDPARM(self), PyOb } arg2 = (struct loadparm_context *)(argp2); } - { - if (arg1 == NULL) - SWIG_exception(SWIG_ValueError, - "ldb context must be non-NULL"); - } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); ldb_set_loadparm(arg1,arg2); resultobj = SWIG_Py_Void(); return resultobj; @@ -2975,11 +2967,9 @@ SWIGINTERN PyObject *_wrap_samdb_set_domain_sid(PyObject *SWIGUNUSEDPARM(self), SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "samdb_set_domain_sid" "', argument " "2"" of type '" "struct dom_sid const *""'"); } arg2 = (struct dom_sid *)(argp2); - { - if (arg1 == NULL) - SWIG_exception(SWIG_ValueError, - "ldb context must be non-NULL"); - } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); result = (bool)samdb_set_domain_sid(arg1,(struct dom_sid const *)arg2); resultobj = SWIG_From_bool((bool)(result)); return resultobj; @@ -3025,19 +3015,16 @@ SWIGINTERN PyObject *_wrap_dsdb_attach_schema_from_ldif_file(PyObject *SWIGUNUSE SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "dsdb_attach_schema_from_ldif_file" "', argument " "3"" of type '" "char const *""'"); } arg3 = (char *)(buf3); - { - if (arg1 == NULL) - SWIG_exception(SWIG_ValueError, - "ldb context must be non-NULL"); - } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); result = dsdb_attach_schema_from_ldif_file(arg1,(char const *)arg2,(char const *)arg3); - { - if (!W_ERROR_IS_OK(result)) { - PyObject *obj = Py_BuildValue("(i,s)", (&result)->v, win_errstr(result)); - PyErr_SetObject(PyExc_RuntimeError, obj); - } else if (resultobj == NULL) { - resultobj = Py_None; - } + if (!W_ERROR_IS_OK(result)) { + PyObject *obj = Py_BuildValue("(i,s)", (&result)->v, win_errstr(result)); + PyErr_SetObject(PyExc_RuntimeError, obj); + SWIG_fail; + } else if (resultobj == NULL) { + resultobj = Py_None; } if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); if (alloc3 == SWIG_NEWOBJ) free((char*)buf3); @@ -3079,11 +3066,9 @@ SWIGINTERN PyObject *_wrap_dsdb_set_global_schema(PyObject *SWIGUNUSEDPARM(self) SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "dsdb_set_global_schema" "', argument " "1"" of type '" "struct ldb_context *""'"); } arg1 = (struct ldb_context *)(argp1); - { - if (arg1 == NULL) - SWIG_exception(SWIG_ValueError, - "ldb context must be non-NULL"); - } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); result = (int)dsdb_set_global_schema(arg1); resultobj = SWIG_From_int((int)(result)); return resultobj; @@ -3109,11 +3094,9 @@ SWIGINTERN PyObject *_wrap_ldb_register_samba_handlers(PyObject *SWIGUNUSEDPARM( SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ldb_register_samba_handlers" "', argument " "1"" of type '" "struct ldb_context *""'"); } arg1 = (struct ldb_context *)(argp1); - { - if (arg1 == NULL) - SWIG_exception(SWIG_ValueError, - "ldb context must be non-NULL"); - } + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); result = (int)ldb_register_samba_handlers(arg1); resultobj = SWIG_From_int((int)(result)); return resultobj; diff --git a/source4/scripting/python/samba/__init__.py b/source4/scripting/python/samba/__init__.py index c735361353..5b34534133 100644 --- a/source4/scripting/python/samba/__init__.py +++ b/source4/scripting/python/samba/__init__.py @@ -91,7 +91,14 @@ class Ldb(ldb.Ldb): def searchone(self, basedn, attribute, expression=None, scope=ldb.SCOPE_BASE): - """Search for one attribute as a string.""" + """Search for one attribute as a string. + + :param basedn: BaseDN for the search. + :param attribute: Name of the attribute + :param expression: Optional search expression. + :param scope: Search scope (defaults to base). + :return: Value of attribute as a string or None if it wasn't found. + """ res = self.search(basedn, scope, expression, [attribute]) if len(res) != 1 or res[0][attribute] is None: return None @@ -100,7 +107,7 @@ class Ldb(ldb.Ldb): return values.pop() def erase(self): - """Erase an ldb, removing all records.""" + """Erase this ldb, removing all records.""" # delete the specials for attr in ["@INDEXLIST", "@ATTRIBUTES", "@SUBCLASSES", "@MODULES", "@OPTIONS", "@PARTITION", "@KLUDGEACL"]: @@ -149,13 +156,18 @@ class Ldb(ldb.Ldb): :param ldif_path: Path to LDIF file. """ - self.load_ldif_add(open(ldif_path, 'r').read()) + self.add_ldif(open(ldif_path, 'r').read()) - def load_ldif_add(self, ldif): + def add_ldif(self, ldif): for changetype, msg in self.parse_ldif(ldif): assert changetype == ldb.CHANGETYPE_NONE self.add(msg) + def modify_ldif(self, ldif): + for (changetype, msg) in ldb.parse_ldif(data): + assert changetype == CHANGETYPE_MODIFY + ldb.modify(msg) + def substitute_var(text, values): """substitute strings of the form ${NAME} in str, replacing diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index dcf567954a..a4e6c6a214 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -111,9 +111,7 @@ def setup_modify_ldif(ldb, ldif_path, substvars=None): assert "${" not in data - for (changetype, msg) in ldb.parse_ldif(data): - assert changetype == CHANGETYPE_MODIFY - ldb.modify(msg) + ldb.modify_ldif(data) def setup_ldb(ldb, ldif_path, subst_vars): diff --git a/source4/scripting/python/samba/samba3.py b/source4/scripting/python/samba/samba3.py new file mode 100644 index 0000000000..d8289ae756 --- /dev/null +++ b/source4/scripting/python/samba/samba3.py @@ -0,0 +1,224 @@ +#!/usr/bin/python + +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij 2007 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +"""Support for reading Samba 3 data files.""" + +REGISTRY_VALUE_PREFIX = "SAMBA_REGVAL" +REGISTRY_DB_VERSION = 1 + +import tdb + +class Registry: + """Simple read-only support for reading the Samba3 registry.""" + def __init__(self, file): + self.tdb = tdb.Tdb(file) + + def __len__(self): + """Return the number of keys.""" + return len(self.keys()) + + def keys(self): + """Return list with all the keys.""" + return [k.rstrip("\x00") for k in self.tdb.keys() if not k.startswith(REGISTRY_VALUE_PREFIX)] + + def subkeys(self, key): + data = self.tdb.get(key) + if data is None: + return [] + # FIXME: Parse data + return [] + + def values(self, key): + """Return a dictionary with the values set for a specific key.""" + data = self.tdb.get("%s/%s" % (REGISTRY_VALUE_PREFIX, key)) + if data is None: + return {} + # FIXME: Parse data + return {} + + +class PolicyDatabase: + def __init__(self, file): + self.tdb = tdb.Tdb(file) + self.min_password_length = tdb.fetch_uint32("min password length") + self.user_must_logon_to_change_password = tdb.fetch_uint32("password history") + self.user_must_logon_to_change_password = tdb.fetch_uint32("user must logon to change pasword") + self.maximum_password_age = tdb.fetch_uint32("maximum password age") + self.minimum_password_age = tdb.fetch_uint32("minimum password age") + self.lockout_duration = tdb.fetch_uint32("lockout duration") + self.reset_count_minutes = tdb.fetch_uint32("reset count minutes") + self.bad_lockout_minutes = tdb.fetch_uint32("bad lockout minutes") + self.disconnect_time = tdb.fetch_uint32("disconnect time") + self.refuse_machine_password_change = tdb.fetch_uint32("refuse machine password change") + + # FIXME: Read privileges as well + + +GROUPDB_DATABASE_VERSION_V1 = 1 # native byte format. +GROUPDB_DATABASE_VERSION_V2 = 2 # le format. + +GROUP_PREFIX = "UNIXGROUP/" + +# Alias memberships are stored reverse, as memberships. The performance +# critical operation is to determine the aliases a SID is member of, not +# listing alias members. So we store a list of alias SIDs a SID is member of +# hanging of the member as key. +MEMBEROF_PREFIX = "MEMBEROF/" + +class GroupMappingDatabase: + def __init__(self, file): + self.tdb = tdb.Tdb(file) + + +# High water mark keys +HWM_GROUP = "GROUP HWM" +HWM_USER = "USER HWM" + +# idmap version determines auto-conversion +IDMAP_VERSION = 2 + +class IdmapDatabase: + def __init__(self, file): + self.tdb = tdb.Tdb(file) + assert self.tdb.fetch_int32("IDMAP_VERSION") == IDMAP_VERSION + + +class SecretsDatabase: + def __init__(self, file): + self.tdb = tdb.Tdb(file) + self.domains = {} + for k, v in self.tdb.items(): + if k == "SECRETS/AUTH_PASSWORD": + self.auth_password = v + elif k == "SECRETS/AUTH_DOMAIN": + self.auth_domain = v + elif k == "SECRETS/AUTH_USER": + self.auth_user = v + elif k.startswith("SECRETS/SID/"): + pass # FIXME + elif k.startswith("SECRETS/DOMGUID/"): + pass # FIXME + elif k.startswith("SECRETS/LDAP_BIND_PW/"): + pass # FIXME + elif k.startswith("SECRETS/AFS_KEYFILE/"): + pass # FIXME + elif k.startswith("SECRETS/MACHINE_SEC_CHANNEL_TYPE/"): + pass # FIXME + elif k.startswith("SECRETS/MACHINE_LAST_CHANGE_TIME/"): + pass # FIXME + elif k.startswith("SECRETS/MACHINE_PASSWORD/"): + pass # FIXME + elif k.startswith("SECRETS/$MACHINE.ACC/"): + pass # FIXME + elif k.startswith("SECRETS/$DOMTRUST.ACC/"): + pass # FIXME + elif k == "INFO/random_seed": + self.random_seed = v + else: + raise "Unknown key %s in secrets database" % k + +SHARE_DATABASE_VERSION_V1 = 1 +SHARE_DATABASE_VERSION_V2 = 2 + +class ShareInfoDatabase: + def __init__(self, file): + self.tdb = tdb.Tdb(file) + assert self.tdb.fetch_int32("INFO/version") in (SHARE_DATABASE_VERSION_V1, SHARE_DATABASE_VERSION_V2) + + def get_secdesc(self, name): + secdesc = self.tdb.get("SECDESC/%s" % name) + # FIXME: Run ndr_pull_security_descriptor + + +ACB_DISABLED = 0x00000001 +ACB_HOMDIRREQ = 0x00000002 +ACB_PWNOTREQ = 0x00000004 +ACB_TEMPDUP = 0x00000008 +ACB_NORMAL = 0x00000010 +ACB_MNS = 0x00000020 +ACB_DOMTRUST = 0x00000040 +ACB_WSTRUST = 0x00000080 +ACB_SVRTRUST = 0x00000100 +ACB_PWNOEXP = 0x00000200 +ACB_AUTOLOCK = 0x00000400 +ACB_ENC_TXT_PWD_ALLOWED = 0x00000800 +ACB_SMARTCARD_REQUIRED = 0x00001000 +ACB_TRUSTED_FOR_DELEGATION = 0x00002000 +ACB_NOT_DELEGATED = 0x00004000 +ACB_USE_DES_KEY_ONLY = 0x00008000 +ACB_DONT_REQUIRE_PREAUTH = 0x00010000 +ACB_PW_EXPIRED = 0x00020000 +ACB_NO_AUTH_DATA_REQD = 0x00080000 + +acb_info_mapping = { + 'N': ACB_PWNOTREQ, # 'N'o password. + 'D': ACB_DISABLED, # 'D'isabled. + 'H': ACB_HOMDIRREQ, # 'H'omedir required. + 'T': ACB_TEMPDUP, # 'T'emp account. + 'U': ACB_NORMAL, # 'U'ser account (normal). + 'M': ACB_MNS, # 'M'NS logon user account. What is this ? + 'W': ACB_WSTRUST, # 'W'orkstation account. + 'S': ACB_SVRTRUST, # 'S'erver account. + 'L': ACB_AUTOLOCK, # 'L'ocked account. + 'X': ACB_PWNOEXP, # No 'X'piry on password + 'I': ACB_DOMTRUST, # 'I'nterdomain trust account. + ' ': 0 + } + + +class Smbpasswd: + def __init__(self, file): + pass + + +class TdbSam: + def __init__(self, file): + self.tdb = tdb.Tdb(file) + + +class WinsDatabase: + def __init__(self, file): + pass + + +class Samba3: + def __init__(self, smbconfpath, libdir): + self.smbconfpath = smbconfpath + self.libdir = libdir + + def get_policy_db(self): + return PolicyDatabase(os.path.join(libdir, "account_policy.tdb")) + + def get_registry(self): + return Registry(os.path.join(libdir, "registry.tdb")) + + def get_secrets_db(self): + return SecretsDatabase(os.path.join(libdir, "secrets.tdb")) + + def get_shares_db(self): + return ShareInfoDatabase(os.path.join(libdir, "share_info.tdb")) + + def get_idmap_db(self): + return IdmapDatabase(os.path.join(libdir, "winbindd_idmap.tdb")) + + def get_wins_db(self): + return WinsDatabase(os.path.join(libdir, "wins.dat")) + + def get_groupmapping_db(self): + return GroupMappingDatabase(os.path.join(libdir, "group_mapping.tdb")) diff --git a/source4/scripting/python/samba/tests/__init__.py b/source4/scripting/python/samba/tests/__init__.py index e213c1cc1f..b01807c02f 100644 --- a/source4/scripting/python/samba/tests/__init__.py +++ b/source4/scripting/python/samba/tests/__init__.py @@ -43,6 +43,7 @@ class TestCaseInTempDir(unittest.TestCase): def tearDown(self): super(TestCaseInTempDir, self).tearDown() + # FIXME: Remove all files in self.tempdir class SubstituteVarTestCase(unittest.TestCase): diff --git a/source4/scripting/python/samba/tests/samba3.py b/source4/scripting/python/samba/tests/samba3.py new file mode 100644 index 0000000000..c6b6281c60 --- /dev/null +++ b/source4/scripting/python/samba/tests/samba3.py @@ -0,0 +1,57 @@ +#!/usr/bin/python + +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij 2007 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import unittest +from samba.samba3 import GroupMappingDatabase, Registry, PolicyDatabase +import os + +DATADIR=os.path.join(os.path.dirname(__file__), "../../../../../testdata/samba3") + +class RegistryTestCase(unittest.TestCase): + def setUp(self): + self.registry = Registry(os.path.join(DATADIR, "registry.tdb")) + + def test_length(self): + self.assertEquals(28, len(self.registry)) + + def test_keys(self): + self.assertEquals([], self.registry.keys()) + + +class PolicyTestCase(unittest.TestCase): + def setUp(self): + self.policy = PolicyDatabase(os.path.join(DATADIR, "account_policy.tdb")) + + def test_policy(self): + self.assertEquals(self.policy.min_password_length, 5) + self.assertEquals(self.policy.minimum_password_age, 0) + self.assertEquals(self.policy.maximum_password_age, 999999999) + self.assertEquals(self.policy.refuse_machine_password_change, 0) + self.assertEquals(self.policy.reset_count_minutes, 0) + self.assertEquals(self.policy.disconnect_time, -1) + self.assertEquals(self.policy.user_must_logon_to_change_password, 0) + self.assertEquals(self.policy.password_history, 0) + self.assertEquals(self.policy.lockout_duration, 0) + self.assertEquals(self.policy.bad_lockout_minutes, 0) + + +class GroupsTestCase(unittest.TestCase): + def setUp(self): + self.groupdb = GroupMappingDatabase(os.path.join(DATADIR, "group_mapping.tdb")) + diff --git a/source4/scripting/python/samba/upgrade.py b/source4/scripting/python/samba/upgrade.py index 1c27f8ec25..3168fedf2d 100644 --- a/source4/scripting/python/samba/upgrade.py +++ b/source4/scripting/python/samba/upgrade.py @@ -7,7 +7,7 @@ """Support code for upgrading from Samba 3 to Samba 4.""" -from provision import findnss +from provision import findnss, provision import provision import grp import pwd @@ -69,7 +69,7 @@ data:: %s""" % (keydn, rv.name, rv.name, rv.type, ldb.encode(rv.data)) return ldif -def upgrade_sam_policy(samba3,dn): +def upgrade_sam_policy(policy,dn): ldif = """ dn: %s changetype: modify @@ -84,11 +84,11 @@ samba3UserMustLogonToChangePassword: %d samba3BadLockoutMinutes: %d samba3DisconnectTime: %d -""" % (dn, samba3.policy.min_password_length, - samba3.policy.password_history, samba3.policy.minimum_password_age, - samba3.policy.maximum_password_age, samba3.policy.lockout_duration, - samba3.policy.reset_count_minutes, samba3.policy.user_must_logon_to_change_password, - samba3.policy.bad_lockout_minutes, samba3.policy.disconnect_time) +""" % (dn, policy.min_password_length, + policy.password_history, policy.minimum_password_age, + policy.maximum_password_age, policy.lockout_duration, + policy.reset_count_minutes, policy.user_must_logon_to_change_password, + policy.bad_lockout_minutes, policy.disconnect_time) return ldif @@ -465,7 +465,7 @@ replace: type type: 4 replace: data data: %d -""" % samba3.policy.refuse_machine_password_change) +""" % policy.refuse_machine_password_change) message("Importing users") for account in samba3.samaccounts: -- cgit