1
0
mirror of https://github.com/samba-team/samba.git synced 2025-07-21 12:59:09 +03:00

r26628: python: Add more documentation, simplify code in Samba3 module.

(This used to be commit 3c329ee73d)
This commit is contained in:
Jelmer Vernooij
2007-12-29 18:14:15 -06:00
committed by Stefan Metzmacher
parent ac65321a46
commit 6817c5d885
7 changed files with 175 additions and 87 deletions

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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)