diff options
Diffstat (limited to 'source4/scripting')
| -rw-r--r-- | source4/scripting/python/config.mk | 2 | ||||
| -rw-r--r-- | source4/scripting/python/samba/__init__.py | 8 | ||||
| -rw-r--r-- | source4/scripting/python/samba/getopt.py | 2 | ||||
| -rw-r--r-- | source4/scripting/python/samba/provision.py | 96 | ||||
| -rw-r--r-- | source4/scripting/python/samba/samba3.py | 100 | ||||
| -rw-r--r-- | source4/scripting/python/samba/samdb.py | 50 | ||||
| -rw-r--r-- | source4/scripting/python/samba/tests/__init__.py | 4 | 
7 files changed, 175 insertions, 87 deletions
| diff --git a/source4/scripting/python/config.mk b/source4/scripting/python/config.mk index 4a531f5062..cfd179aff5 100644 --- a/source4/scripting/python/config.mk +++ b/source4/scripting/python/config.mk @@ -30,7 +30,7 @@ realdistclean::  pythonmods: $(PYTHON_DSOS) -PYDOCTOR_MODULES=bin/python/ldb.py bin/python/auth.py bin/python/credentials.py bin/python/registry.py bin/python/tdb.py bin/python/security.py +PYDOCTOR_MODULES=bin/python/ldb.py bin/python/auth.py bin/python/credentials.py bin/python/registry.py bin/python/tdb.py bin/python/security.py bin/python/events.py bin/python/net.py  pydoctor:: pythonmods  	LD_LIBRARY_PATH=bin/shared PYTHONPATH=bin/python pydoctor --make-html --docformat=restructuredtext --add-package scripting/python/samba/ $(addprefix --add-module , $(PYDOCTOR_MODULES)) diff --git a/source4/scripting/python/samba/__init__.py b/source4/scripting/python/samba/__init__.py index 359457d815..01fdea6665 100644 --- a/source4/scripting/python/samba/__init__.py +++ b/source4/scripting/python/samba/__init__.py @@ -159,11 +159,19 @@ class Ldb(ldb.Ldb):          self.add_ldif(open(ldif_path, 'r').read())      def add_ldif(self, ldif): +        """Add data based on a LDIF string. + +        :param ldif: LDIF text. +        """          for changetype, msg in self.parse_ldif(ldif):              assert changetype == ldb.CHANGETYPE_NONE              self.add(msg)      def modify_ldif(self, ldif): +        """Modify database based on a LDIF string. + +        :param ldif: LDIF text. +        """          for (changetype, msg) in self.parse_ldif(ldif):              assert changetype == ldb.CHANGETYPE_MODIFY              self.modify(msg) diff --git a/source4/scripting/python/samba/getopt.py b/source4/scripting/python/samba/getopt.py index 87cf171ca2..c0e7053062 100644 --- a/source4/scripting/python/samba/getopt.py +++ b/source4/scripting/python/samba/getopt.py @@ -58,7 +58,7 @@ class CredentialsOptions(optparse.OptionGroup):          self.creds.set_password(arg)      def set_simple_bind_dn(self, option, opt_str, arg, parser): -        self.creds.set_simple_bind_dn(arg) +        self.creds.set_bind_dn(arg)      def get_credentials(self):          return self.creds diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index fa45f7a79d..bdfe035c41 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -74,6 +74,14 @@ def findnss(nssfn, *names):  def open_ldb(session_info, credentials, lp, dbname): +    """Open a LDB, thrashing it if it is corrupt. + +    :param session_info: auth session information +    :param credentials: credentials +    :param lp: Loadparm context +    :param dbname: Path of the database to open. +    :return: a Ldb object +    """      assert session_info is not None      try:          return Ldb(dbname, session_info=session_info, credentials=credentials,  @@ -86,7 +94,12 @@ def open_ldb(session_info, credentials, lp, dbname):  def setup_add_ldif(ldb, ldif_path, subst_vars=None): -    """Setup a ldb in the private dir.""" +    """Setup a ldb in the private dir. +     +    :param ldb: LDB file to import data into +    :param ldif_path: Path of the LDIF file to load +    :param subst_vars: Optional variables to subsitute in LDIF. +    """      assert isinstance(ldif_path, str)      data = open(ldif_path, 'r').read() @@ -126,7 +139,12 @@ def setup_ldb(ldb, ldif_path, subst_vars):  def setup_file(template, fname, substvars): -    """Setup a file in the private dir.""" +    """Setup a file in the private dir. + +    :param template: Path of the template file. +    :param fname: Path of the file to create. +    :param substvars: Substitution variables. +    """      f = fname      if os.path.exists(f): @@ -179,7 +197,17 @@ def provision_paths_from_lp(lp, dnsdomain):  def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,                           wheel, backup): -    """setup reasonable name mappings for sam names to unix names.""" +    """setup reasonable name mappings for sam names to unix names. +     +    :param ldb: SamDB object. +    :param sid: The domain sid. +    :param domaindn: The domain DN. +    :param root: Name of the UNIX root user. +    :param nobody: Name of the UNIX nobody user. +    :param nogroup: Name of the unix nobody group. +    :param users: Name of the unix users group. +    :param wheel: Name of the wheel group (users that can become root). +    :param backup: Name of the backup group."""      # add some foreign sids if they are not present already      ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")      ldb.add_foreign(domaindn, "S-1-1-0", "World") @@ -591,7 +619,8 @@ def provision(lp, setup_dir, message, blank, paths, session_info,      if nogroup is None:          nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[2]      if users is None: -        users = findnss(grp.getgrnam, "users", "guest", "other", "unknown", "usr")[2] +        users = findnss(grp.getgrnam, "users", "guest", "other", "unknown",  +                        "usr")[2]      if wheel is None:          wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2]      if backup is None: @@ -748,13 +777,32 @@ def provision(lp, setup_dir, message, blank, paths, session_info,      return domaindn  def create_phplpapdadmin_config(path, setup_path, s4_ldapi_path): +    """Create a PHP LDAP admin configuration file. + +    :param path: Path to write the configuration to. +    :param setup_path: Function to generate setup paths. +    :param s4_ldapi_path: Path to Samba 4 LDAPI socket. +    """      setup_file(setup_path("phpldapadmin-config.php"),                  path, {"S4_LDAPI_URI": "ldapi://%s" % s4_ldapi_path.replace("/", "%2F")})  def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,                     hostip, hostname, dnspass, realm, domainguid, hostguid): -    """Write out a DNS zone file, from the info in the current database.""" +    """Write out a DNS zone file, from the info in the current database. +     +    :param path: Path of the new file. +    :param setup_path": Setup path function. +    :param samdb: SamDB object +    :param dnsdomain: DNS Domain name +    :param domaindn: DN of the Domain +    :param hostip: Local IP +    :param hostname: Local hostname +    :param dnspass: Password for DNS +    :param realm: Realm name +    :param domainguid: GUID of the domain. +    :param hostguid: GUID of the host. +    """      setup_file(setup_path("provision.zone"), path, {              "DNSPASS_B64": b64encode(dnspass), @@ -795,7 +843,14 @@ def provision_ldapbase(setup_dir, message, paths):  def load_schema(setup_path, samdb, schemadn, netbiosname, configdn): -    """Load schema.""" +    """Load schema. +     +    :param samdb: Load a schema into a SamDB. +    :param setup_path: Setup path function. +    :param schemadn: DN of the schema +    :param netbiosname: NetBIOS name of the host. +    :param configdn: DN of the configuration +    """      schema_data = open(setup_path("schema.ldif"), 'r').read()      schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()      schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn}) @@ -807,32 +862,3 @@ def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):                      "DEFAULTSITE": DEFAULTSITE})      samdb.attach_schema_from_ldif(head_data, schema_data) - -def join_domain(domain, netbios_name, join_type, creds): -    ctx = NetContext(creds) -    joindom = object() -    joindom.domain = domain -    joindom.join_type = join_type -    joindom.netbios_name = netbios_name -    if not ctx.JoinDomain(joindom): -        raise Exception("Domain Join failed: " + joindom.error_string) - - -def vampire(domain, session_info, credentials, message): -    """Vampire a remote domain.   -     -    Session info and credentials are required for for -    access to our local database (might be remote ldap) -    """ -    ctx = NetContext(credentials) -    machine_creds = Credentials() -    machine_creds.set_domain(form.domain) -    if not machine_creds.set_machine_account(): -        raise Exception("Failed to access domain join information!") -    vampire_ctx.machine_creds = machine_creds -    vampire_ctx.session_info = session_info -    if not ctx.SamSyncLdb(vampire_ctx): -        raise Exception("Migration of remote domain to Samba failed: %s " % vampire_ctx.error_string) - - - diff --git a/source4/scripting/python/samba/samba3.py b/source4/scripting/python/samba/samba3.py index 27656900ad..113348e6f1 100644 --- a/source4/scripting/python/samba/samba3.py +++ b/source4/scripting/python/samba/samba3.py @@ -25,14 +25,33 @@ REGISTRY_DB_VERSION = 1  import os  import tdb -class Registry: -    """Simple read-only support for reading the Samba3 registry.""" + +class TdbDatabase: +    """Simple Samba 3 TDB database reader."""      def __init__(self, file): +        """Open a file. + +        :param file: Path of the file to open. +        """          self.tdb = tdb.Tdb(file, flags=os.O_RDONLY) +        self._check_version() + +    def _check_version(self): +        pass      def close(self): +        """Close resources associated with this object."""          self.tdb.close() + +class Registry(TdbDatabase): +    """Simple read-only support for reading the Samba3 registry. +     +    :note: This object uses the same syntax for registry key paths as  +        Samba 3. This particular format uses forward slashes for key path  +        separators and abbreviations for the predefined key names.  +        e.g.: HKLM/Software/Bar. +    """      def __len__(self):          """Return the number of keys."""          return len(self.keys()) @@ -42,6 +61,11 @@ class Registry:          return [k.rstrip("\x00") for k in self.tdb.keys() if not k.startswith(REGISTRY_VALUE_PREFIX)]      def subkeys(self, key): +        """Retrieve the subkeys for the specified key. + +        :param key: Key path. +        :return: list with key names +        """          data = self.tdb.get("%s\x00" % key)          if data is None:              return [] @@ -54,7 +78,11 @@ class Registry:          return keys      def values(self, key): -        """Return a dictionary with the values set for a specific key.""" +        """Return a dictionary with the values set for a specific key. +         +        :param key: Key to retrieve values for. +        :return: Dictionary with value names as key, tuple with type and  +            data as value."""          data = self.tdb.get("%s/%s\x00" % (REGISTRY_VALUE_PREFIX, key))          if data is None:              return {} @@ -77,9 +105,14 @@ class Registry:          return ret -class PolicyDatabase: +class PolicyDatabase(TdbDatabase): +    """Samba 3 Account Policy database reader."""      def __init__(self, file): -        self.tdb = tdb.Tdb(file, flags=os.O_RDONLY) +        """Open a policy database +         +        :param file: Path to the file to open. +        """ +        super(PolicyDatabase, self).__init__(file)          self.min_password_length = self.tdb.fetch_uint32("min password length\x00")          self.password_history = self.tdb.fetch_uint32("password history\x00")          self.user_must_logon_to_change_password = self.tdb.fetch_uint32("user must logon to change pasword\x00") @@ -93,9 +126,6 @@ class PolicyDatabase:          # FIXME: Read privileges as well -    def close(self): -        self.tdb.close() -  GROUPDB_DATABASE_VERSION_V1 = 1 # native byte format.  GROUPDB_DATABASE_VERSION_V2 = 2 # le format. @@ -108,17 +138,27 @@ GROUP_PREFIX = "UNIXGROUP/"  # hanging of the member as key.  MEMBEROF_PREFIX = "MEMBEROF/" -class GroupMappingDatabase: -    def __init__(self, file):  -        self.tdb = tdb.Tdb(file, flags=os.O_RDONLY) +class GroupMappingDatabase(TdbDatabase): +    """Samba 3 group mapping database reader.""" +    def _check_version(self):          assert self.tdb.fetch_int32("INFO/version\x00") in (GROUPDB_DATABASE_VERSION_V1, GROUPDB_DATABASE_VERSION_V2)      def groupsids(self): +        """Retrieve the SIDs for the groups in this database. + +        :return: List with sids as strings. +        """          for k in self.tdb.keys():              if k.startswith(GROUP_PREFIX):                  yield k[len(GROUP_PREFIX):].rstrip("\0")      def get_group(self, sid): +        """Retrieve the group mapping information for a particular group. + +        :param sid: SID of the group +        :return: None if the group can not be found, otherwise  +            a tuple with gid, sid_name_use, the NT name and comment. +        """          data = self.tdb.get("%s%s\0" % (GROUP_PREFIX, sid))          if data is None:              return data @@ -128,13 +168,11 @@ class GroupMappingDatabase:          return (gid, sid_name_use, nt_name, comment)      def aliases(self): +        """Retrieve the aliases in this database."""          for k in self.tdb.keys():              if k.startswith(MEMBEROF_PREFIX):                  yield k[len(MEMBEROF_PREFIX):].rstrip("\0") -    def close(self): -        self.tdb.close() -  # High water mark keys  IDMAP_HWM_GROUP = "GROUP HWM\0" @@ -146,22 +184,29 @@ IDMAP_USER_PREFIX = "UID "  # idmap version determines auto-conversion  IDMAP_VERSION_V2 = 2 -class IdmapDatabase: -    def __init__(self, file): -        self.tdb = tdb.Tdb(file, flags=os.O_RDONLY) +class IdmapDatabase(TdbDatabase): +    """Samba 3 ID map database reader.""" +    def _check_version(self):          assert self.tdb.fetch_int32("IDMAP_VERSION\0") == IDMAP_VERSION_V2      def uids(self): +        """Retrieve a list of all uids in this database."""          for k in self.tdb.keys():              if k.startswith(IDMAP_USER_PREFIX):                  yield int(k[len(IDMAP_USER_PREFIX):].rstrip("\0"))      def gids(self): +        """Retrieve a list of all gids in this database."""          for k in self.tdb.keys():              if k.startswith(IDMAP_GROUP_PREFIX):                  yield int(k[len(IDMAP_GROUP_PREFIX):].rstrip("\0"))      def get_user_sid(self, uid): +        """Retrieve the SID associated with a particular uid. + +        :param uid: UID to retrieve SID for. +        :return: A SID or None if no mapping was found. +        """          data = self.tdb.get("%s%d\0" % (IDMAP_USER_PREFIX, uid))          if data is None:              return data @@ -174,19 +219,15 @@ class IdmapDatabase:          return data.rstrip("\0")      def get_user_hwm(self): +        """Obtain the user high-water mark."""          return self.tdb.fetch_uint32(IDMAP_HWM_USER)      def get_group_hwm(self): +        """Obtain the group high-water mark."""          return self.tdb.fetch_uint32(IDMAP_HWM_GROUP) -    def close(self): -        self.tdb.close() - - -class SecretsDatabase: -    def __init__(self, file): -        self.tdb = tdb.Tdb(file, flags=os.O_RDONLY) +class SecretsDatabase(TdbDatabase):      def get_auth_password(self):          return self.tdb.get("SECRETS/AUTH_PASSWORD") @@ -241,16 +282,12 @@ class SecretsDatabase:      def get_sid(self, host):          return self.tdb.get("SECRETS/SID/%s" % host.upper()) -    def close(self): -        self.tdb.close() -  SHARE_DATABASE_VERSION_V1 = 1  SHARE_DATABASE_VERSION_V2 = 2 -class ShareInfoDatabase: -    def __init__(self, file): -        self.tdb = tdb.Tdb(file, flags=os.O_RDONLY) +class ShareInfoDatabase(TdbDatabase): +    def _check_version(self):          assert self.tdb.fetch_int32("INFO/version\0") in (SHARE_DATABASE_VERSION_V1, SHARE_DATABASE_VERSION_V2)      def get_secdesc(self, name): @@ -258,9 +295,6 @@ class ShareInfoDatabase:          # FIXME: Run ndr_pull_security_descriptor          return secdesc -    def close(self): -        self.tdb.close() -  class Shares:      def __init__(self, lp, shareinfo): diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py index 46707f060f..2af56d8d8e 100644 --- a/source4/scripting/python/samba/samdb.py +++ b/source4/scripting/python/samba/samdb.py @@ -20,13 +20,20 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  # +"""Convenience functions for using the SAM.""" +  import samba  import misc  import ldb  class SamDB(samba.Ldb): +    """The SAM database."""      def __init__(self, url=None, session_info=None, credentials=None,                    modules_dir=None, lp=None): +        """Open the Sam Database. + +        :param url: URL of the database. +        """          super(SamDB, self).__init__(session_info=session_info, credentials=credentials,                                      modules_dir=modules_dir, lp=lp)          assert misc.dsdb_set_global_schema(self) == 0 @@ -47,7 +54,12 @@ description: %s              self.add(msg[1])      def setup_name_mapping(self, domaindn, sid, unixname): -        """Setup a mapping between a sam name and a unix name.""" +        """Setup a mapping between a sam name and a unix name. +         +        :param domaindn: DN of the domain. +        :param sid: SID of the NT-side of the mapping. +        :param unixname: Unix name to map to. +        """          res = self.search(ldb.Dn(self, domaindn), ldb.SCOPE_SUBTREE,                            "objectSid=%s" % sid, ["dn"])          assert len(res) == 1, "Failed to find record for objectSid %s" % sid @@ -61,7 +73,7 @@ unixName: %s          self.modify(self.parse_ldif(mod).next()[1])      def enable_account(self, user_dn): -        """enable the account. +        """Enable an account.          :param user_dn: Dn of the account to enable.          """ @@ -75,10 +87,15 @@ changetype: modify  replace: userAccountControl  userAccountControl: %u  """ % (user_dn, userAccountControl) -        self.modify(mod) +        self.modify_ldif(mod) -    def newuser(self, username, unixname, password, message): -        """add a new user record""" +    def newuser(self, username, unixname, password): +        """add a new user record. +         +        :param username: Name of the new user. +        :param unixname: Name of the unix user to map to. +        :param password: Password for the new user +        """          # connect to the sam           self.transaction_start() @@ -97,13 +114,13 @@ userAccountControl: %u          #  the new user record. note the reliance on the samdb module to fill          #  in a sid, guid etc          # -        ldif = """ -dn: %s -sAMAccountName: %s -unixName: %s -sambaPassword: %s -objectClass: user -    """ % (user_dn, username, unixname, password) +        #  now the real work +        self.add({"dn": user_dn,  +            "sAMAccountName": username, +            "unixName": unixname, +            "sambaPassword": password, +            "objectClass": "user"}) +          #  add the user to the users group as well          modgroup = """  dn: %s @@ -113,11 +130,6 @@ member: %s  """ % (dom_users, user_dn) -        #  now the real work -        message("Adding user %s" % user_dn) -        self.add(ldif) - -        message("Modifying group %s" % dom_users)          self.modify(modgroup)          #  modify the userAccountControl to remove the disabled bit @@ -125,6 +137,10 @@ member: %s          self.transaction_commit()      def set_domain_sid(self, sid): +        """Change the domain SID used by this SamDB. + +        :param sid: The new domain sid to use. +        """          misc.samdb_set_domain_sid(self, sid)      def attach_schema_from_ldif(self, pf, df): diff --git a/source4/scripting/python/samba/tests/__init__.py b/source4/scripting/python/samba/tests/__init__.py index 5e1ff87c2b..ad8a2524b5 100644 --- a/source4/scripting/python/samba/tests/__init__.py +++ b/source4/scripting/python/samba/tests/__init__.py @@ -17,6 +17,8 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  # +"""Samba Python tests.""" +  import os  import ldb  import samba @@ -24,11 +26,13 @@ import tempfile  import unittest  class LdbTestCase(unittest.TestCase): +    """Trivial test case for running tests against a LDB."""      def setUp(self):          self.filename = os.tempnam()          self.ldb = samba.Ldb(self.filename)      def set_modules(self, modules=[]): +        """Change the modules for this Ldb."""          m = ldb.Message()          m.dn = ldb.Dn(self.ldb, "@MODULES")          m["@LIST"] = ",".join(modules) | 
