summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorRowland Penny <rowland@devstation.samdom.example.com>2019-07-02 13:41:34 +0100
committerAndreas Schneider <asn@cryptomilk.org>2019-10-17 12:21:55 +0000
commit68155811aba10a498cc07bf8a1c6f3a092e6147c (patch)
tree31212604a7e3bfad15b47e0e9a748a2dcf0bea06 /python
parentaacbd383b9e843b5d36b91ef75f6f2d2724a438b (diff)
downloadsamba-68155811aba10a498cc07bf8a1c6f3a092e6147c.tar.gz
samba-tool: Add facility to add rfc2307 attributes to an already created user or group
Signed-off-by: Rowland Penny <rpenny@samba.org> Reviewed-by: David Mulder <dmulder@suse.com> Reviewed-by: Andrew Bartlet <abartlet@samba.org> Reviewed-by: Andreas Schneider <asn@samba.org> Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org> Autobuild-Date(master): Thu Oct 17 12:21:55 UTC 2019 on sn-devel-184
Diffstat (limited to 'python')
-rw-r--r--python/samba/netcmd/group.py96
-rw-r--r--python/samba/netcmd/user.py224
-rw-r--r--python/samba/tests/samba_tool/group.py102
-rw-r--r--python/samba/tests/samba_tool/user.py58
4 files changed, 473 insertions, 7 deletions
diff --git a/python/samba/netcmd/group.py b/python/samba/netcmd/group.py
index 536c1cba613..4798b773ddf 100644
--- a/python/samba/netcmd/group.py
+++ b/python/samba/netcmd/group.py
@@ -804,6 +804,101 @@ class cmd_group_edit(Command):
self.outf.write("Modified group '%s' successfully\n" % groupname)
+class cmd_group_add_unix_attrs(Command):
+ """Add RFC2307 attributes to a group.
+
+This command adds Unix attributes to a group account in the Active
+Directory domain.
+The groupname specified on the command is the sAMaccountName.
+
+Unix (RFC2307) attributes will be added to the group account.
+
+Add 'idmap_ldb:use rfc2307 = Yes' to smb.conf to use these attributes for
+UID/GID mapping.
+
+The command may be run from the root userid or another authorized userid.
+The -H or --URL= option can be used to execute the command against a
+remote server.
+
+Example1:
+samba-tool group addunixattrs Group1 10000
+
+Example1 shows how to add RFC2307 attributes to a domain enabled group
+account.
+
+The groups Unix ID will be set to '10000', provided this ID isn't already
+in use.
+
+"""
+ synopsis = "%prog <groupname> <gidnumber> [options]"
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server",
+ type=str, metavar="URL", dest="H"),
+ ]
+
+ takes_args = ["groupname", "gidnumber"]
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ def run(self, groupname, gidnumber, credopts=None, sambaopts=None,
+ versionopts=None, H=None):
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp)
+
+ samdb = SamDB(url=H, session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ domaindn = samdb.domain_dn()
+
+ # Check group exists and doesn't have a gidNumber
+ filter = "(samaccountname={})".format(ldb.binary_encode(groupname))
+ res = samdb.search(domaindn,
+ scope=ldb.SCOPE_SUBTREE,
+ expression=filter)
+ if (len(res) == 0):
+ raise CommandError("Unable to find group '{}'".format(groupname))
+
+ group_dn = res[0].dn
+
+ if "gidNumber" in res[0]:
+ raise CommandError("Group {} is a Unix group.".format(groupname))
+
+ # Check if supplied gidnumber isn't already being used
+ filter = "(&(objectClass=group)(gidNumber={}))".format(gidnumber)
+ res = samdb.search(domaindn,
+ scope=ldb.SCOPE_SUBTREE,
+ expression=filter)
+ if (len(res) != 0):
+ raise CommandError('gidNumber {} already used.'.format(gidnumber))
+
+ if not lp.get("idmap_ldb:use rfc2307"):
+ self.outf.write("You are setting a Unix/RFC2307 GID. "
+ "You may want to set 'idmap_ldb:use rfc2307 = Yes'"
+ " in smb.conf to use the attributes for "
+ "XID/SID-mapping.\n")
+
+ group_mod = """
+dn: {0}
+changetype: modify
+add: gidNumber
+gidNumber: {1}
+""".format(group_dn, gidnumber)
+
+ try:
+ samdb.modify_ldif(group_mod)
+ except ldb.LdbError as e:
+ raise CommandError("Failed to modify group '{0}': {1}"
+ .format(groupname, e))
+
+ self.outf.write("Modified Group '{}' successfully\n".format(groupname))
+
+
class cmd_group(SuperCommand):
"""Group management."""
@@ -818,3 +913,4 @@ class cmd_group(SuperCommand):
subcommands["move"] = cmd_group_move()
subcommands["show"] = cmd_group_show()
subcommands["stats"] = cmd_group_stats()
+ subcommands["addunixattrs"] = cmd_group_add_unix_attrs()
diff --git a/python/samba/netcmd/user.py b/python/samba/netcmd/user.py
index 6730ab718de..affbbf067c1 100644
--- a/python/samba/netcmd/user.py
+++ b/python/samba/netcmd/user.py
@@ -2603,6 +2603,229 @@ class cmd_user_move(Command):
(username, full_new_parent_dn))
+class cmd_user_add_unix_attrs(Command):
+ """Add RFC2307 attributes to a user.
+
+This command adds Unix attributes to a user account in the Active
+Directory domain.
+
+The username specified on the command is the sAMaccountName.
+
+You must supply a unique uidNumber.
+
+Unix (RFC2307) attributes will be added to the user account.
+
+If you supply a gidNumber with '--gid-number', this will be used for the
+users Unix 'gidNumber' attribute.
+
+If '--gid-number' is not supplied, the users Unix gidNumber will be set to the
+one found in 'Domain Users', this means Domain Users must have a gidNumber
+attribute.
+
+if '--unix-home' is not supplied, the users Unix home directory will be
+set to /home/DOMAIN/username
+
+if '--login-shell' is not supplied, the users Unix login shell will be
+set to '/bin/sh'
+
+if ---gecos' is not supplied, the users Unix gecos field will be set to the
+users 'CN'
+
+Add 'idmap_ldb:use rfc2307 = Yes' to the smb.conf on DCs, to use these
+attributes for UID/GID mapping.
+
+The command may be run from the root userid or another authorised userid.
+The -H or --URL= option can be used to execute the command against a
+remote server.
+
+Example1:
+samba-tool user addunixattrs User1 10001
+
+Example1 shows how to add RFC2307 attributes to a domain enabled user
+account, Domain Users will be set as the users gidNumber.
+
+The users Unix ID will be set to '10001', provided this ID isn't already
+in use.
+
+Example2:
+samba-tool user addunixattrs User2 10002 --gid-number=10001 \
+--unix-home=/home/User2
+
+Example2 shows how to add RFC2307 attributes to a domain enabled user
+account.
+
+The users Unix ID will be set to '10002', provided this ID isn't already
+in use.
+
+The users gidNumber attribute will be set to '10001'
+
+The users Unix home directory will be set to '/home/user2'
+
+Example3:
+samba-tool user addunixattrs User3 10003 --gid-number=10001 \
+--login-shell=/bin/false --gecos='User3 test'
+
+Example3 shows how to add RFC2307 attributes to a domain enabled user
+account.
+
+The users Unix ID will be set to '10003', provided this ID isn't already
+in use.
+
+The users gidNumber attribute will be set to '10001'
+
+The users Unix login shell will be set to '/bin/false'
+
+The users gecos field will be set to 'User3 test'
+
+Example4:
+samba-tool user addunixattrs User4 10004 --gid-number=10001 \
+--unix-home=/home/User4 --login-shell=/bin/bash --gecos='User4 test'
+
+Example4 shows how to add RFC2307 attributes to a domain enabled user
+account.
+
+The users Unix ID will be set to '10004', provided this ID isn't already
+in use.
+
+The users gidNumber attribute will be set to '10001'
+
+The users Unix home directory will be set to '/home/User4'
+
+The users Unix login shell will be set to '/bin/bash'
+
+The users gecos field will be set to 'User4 test'
+
+"""
+
+ synopsis = "%prog <username> <uid-number> [options]"
+
+ takes_options = [
+ Option("-H", "--URL", help="LDB URL for database or target server",
+ type=str, metavar="URL", dest="H"),
+ Option("--gid-number", help="User's Unix/RFC2307 GID", type=str),
+ Option("--unix-home", help="User's Unix/RFC2307 home directory",
+ type=str),
+ Option("--login-shell", help="User's Unix/RFC2307 login shell",
+ type=str),
+ Option("--gecos", help="User's Unix/RFC2307 GECOS field", type=str),
+ Option("--uid", help="User's Unix/RFC2307 username", type=str),
+ ]
+
+ takes_args = ["username", "uid-number"]
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ def run(self, username, uid_number, credopts=None, sambaopts=None,
+ versionopts=None, H=None, gid_number=None, unix_home=None,
+ login_shell=None, gecos=None, uid=None):
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp)
+
+ samdb = SamDB(url=H, session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ domaindn = samdb.domain_dn()
+
+ # Check that uidNumber supplied isn't already in use
+ filter = ("(&(objectClass=person)(uidNumber={}))"
+ .format(uid_number))
+ res = samdb.search(domaindn,
+ scope=ldb.SCOPE_SUBTREE,
+ expression=filter)
+ if (len(res) != 0):
+ raise CommandError("uidNumber {} is already being used."
+ .format(uid_number))
+
+ # Check user exists and doesn't have a uidNumber
+ filter = "(samaccountname={})".format(ldb.binary_encode(username))
+ res = samdb.search(domaindn,
+ scope=ldb.SCOPE_SUBTREE,
+ expression=filter)
+ if (len(res) == 0):
+ raise CommandError("Unable to find user '{}'".format(username))
+
+ user_dn = res[0].dn
+
+ if "uidNumber" in res[0]:
+ raise CommandError("User {} is already a Unix user."
+ .format(username))
+
+ if gecos is None:
+ gecos = res[0]["cn"][0]
+
+ if uid is None:
+ uid = res[0]["cn"][0]
+
+ if gid_number is None:
+ search_filter = ("(samaccountname={})"
+ .format(ldb.binary_encode('Domain Users')))
+ try:
+ res = samdb.search(domaindn,
+ scope=ldb.SCOPE_SUBTREE,
+ expression=search_filter)
+ for msg in res:
+ gid_number=msg.get('gidNumber')
+ except IndexError:
+ raise CommandError('Domain Users does not have a'
+ ' gidNumber attribute')
+
+ if login_shell is None:
+ login_shell = "/bin/sh"
+
+ if unix_home is None:
+ # obtain nETBIOS Domain Name
+ filter = "(&(objectClass=crossRef)(nETBIOSName=*))"
+ searchdn = ("CN=Partitions,CN=Configuration," + domaindn)
+ try:
+ res = samdb.search(searchdn,
+ scope=ldb.SCOPE_SUBTREE,
+ expression=filter)
+ unix_domain = res[0]["nETBIOSName"][0]
+ except IndexError:
+ raise CommandError('Unable to find Unix domain')
+
+ unix_home = "/home/{0}/{1}".format(unix_domain, username)
+
+ if not lp.get("idmap_ldb:use rfc2307"):
+ self.outf.write("You are setting a Unix/RFC2307 UID & GID. "
+ "You may want to set 'idmap_ldb:use rfc2307 = Yes'"
+ " in smb.conf to use the attributes for "
+ "XID/SID-mapping.\n")
+
+ user_mod = """
+dn: {0}
+changetype: modify
+add: uidNumber
+uidNumber: {1}
+add: gidnumber
+gidNumber: {2}
+add: gecos
+gecos: {3}
+add: uid
+uid: {4}
+add: loginshell
+loginShell: {5}
+add: unixHomeDirectory
+unixHomeDirectory: {6}
+""".format(user_dn, uid_number, gid_number, gecos, uid, login_shell, unix_home)
+
+ samdb.transaction_start()
+ try:
+ samdb.modify_ldif(user_mod)
+ except ldb.LdbError as e:
+ raise CommandError("Failed to modify user '{0}': {1}"
+ .format(username, e))
+ else:
+ samdb.transaction_commit()
+ self.outf.write("Modified User '{}' successfully\n"
+ .format(username))
+
+
class cmd_user(SuperCommand):
"""User management."""
@@ -2621,3 +2844,4 @@ class cmd_user(SuperCommand):
subcommands["edit"] = cmd_user_edit()
subcommands["show"] = cmd_user_show()
subcommands["move"] = cmd_user_move()
+ subcommands["addunixattrs"] = cmd_user_add_unix_attrs()
diff --git a/python/samba/tests/samba_tool/group.py b/python/samba/tests/samba_tool/group.py
index e521c720b77..215219c3918 100644
--- a/python/samba/tests/samba_tool/group.py
+++ b/python/samba/tests/samba_tool/group.py
@@ -39,14 +39,29 @@ class GroupCmdTestCase(SambaToolCmdTest):
self.groups.append(self._randomGroup({"name": "testgroup2"}))
self.groups.append(self._randomGroup({"name": "testgroup3"}))
self.groups.append(self._randomGroup({"name": "testgroup4"}))
-
- # setup the 4 groups and ensure they are correct
+ self.groups.append(self._randomPosixGroup({"name": "posixgroup1"}))
+ self.groups.append(self._randomPosixGroup({"name": "posixgroup2"}))
+ self.groups.append(self._randomPosixGroup({"name": "posixgroup3"}))
+ self.groups.append(self._randomPosixGroup({"name": "posixgroup4"}))
+ self.groups.append(self._randomUnixGroup({"name": "unixgroup1"}))
+ self.groups.append(self._randomUnixGroup({"name": "unixgroup2"}))
+ self.groups.append(self._randomUnixGroup({"name": "unixgroup3"}))
+ self.groups.append(self._randomUnixGroup({"name": "unixgroup4"}))
+
+ # setup the 12 groups and ensure they are correct
for group in self.groups:
- (result, out, err) = self._create_group(group)
+ (result, out, err) = group["createGroupFn"](group)
self.assertCmdSuccess(result, out, err)
self.assertEquals(err, "", "There shouldn't be any error message")
- self.assertIn("Added group %s" % group["name"], out)
+
+ if 'unix' in group["name"]:
+ self.assertIn("Modified Group '%s' successfully"
+ % group["name"], out)
+ else:
+ self.assertIn("Added group %s" % group["name"], out)
+
+ group["checkGroupFn"](group)
found = self._find_group(group["name"])
@@ -221,14 +236,72 @@ class GroupCmdTestCase(SambaToolCmdTest):
self.assertIn("dn: CN=Domain Users,CN=Users,DC=samba,DC=example,DC=com", out)
def _randomGroup(self, base={}):
- """create a group with random attribute values, you can specify base attributes"""
+ """create a group with random attribute values, you can specify base
+ attributes"""
group = {
"name": self.randomName(),
"description": self.randomName(count=100),
+ "createGroupFn": self._create_group,
+ "checkGroupFn": self._check_group,
+ }
+ group.update(base)
+ return group
+
+ def _randomPosixGroup(self, base={}):
+ """create a group with random attribute values and additional RFC2307
+ attributes, you can specify base attributes"""
+ group = self._randomGroup({})
+ group.update(base)
+ posixAttributes = {
+ "unixdomain": self.randomName(),
+ "gidNumber": self.randomXid(),
+ "createGroupFn": self._create_posix_group,
+ "checkGroupFn": self._check_posix_group,
+ }
+ group.update(posixAttributes)
+ group.update(base)
+ return group
+
+ def _randomUnixGroup(self, base={}):
+ """create a group with random attribute values and additional RFC2307
+ attributes, you can specify base attributes"""
+ group = self._randomGroup({})
+ group.update(base)
+ posixAttributes = {
+ "gidNumber": self.randomXid(),
+ "createGroupFn": self._create_unix_group,
+ "checkGroupFn": self._check_unix_group,
}
+ group.update(posixAttributes)
group.update(base)
return group
+ def _check_group(self, group):
+ """ check if a group from SamDB has the same attributes as
+ its template """
+ found = self._find_group(group["name"])
+
+ self.assertEquals("%s" % found.get("name"), group["name"])
+ self.assertEquals("%s" % found.get("description"), group["description"])
+
+ def _check_posix_group(self, group):
+ """ check if a posix_group from SamDB has the same attributes as
+ its template """
+ found = self._find_group(group["name"])
+
+ self.assertEquals("%s" % found.get("gidNumber"), "%s" %
+ group["gidNumber"])
+ self._check_group(group)
+
+ def _check_unix_group(self, group):
+ """ check if a unix_group from SamDB has the same attributes as its
+template """
+ found = self._find_group(group["name"])
+
+ self.assertEquals("%s" % found.get("gidNumber"), "%s" %
+ group["gidNumber"])
+ self._check_group(group)
+
def _create_group(self, group):
return self.runsubcmd("group", "add", group["name"],
"--description=%s" % group["description"],
@@ -236,6 +309,25 @@ class GroupCmdTestCase(SambaToolCmdTest):
"-U%s%%%s" % (os.environ["DC_USERNAME"],
os.environ["DC_PASSWORD"]))
+ def _create_posix_group(self, group):
+ """ create a new group with RFC2307 attributes """
+ return self.runsubcmd("group", "add", group["name"],
+ "--description=%s" % group["description"],
+ "--nis-domain=%s" % group["unixdomain"],
+ "--gid-number=%s" % group["gidNumber"],
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"],
+ os.environ["DC_PASSWORD"]))
+
+ def _create_unix_group(self, group):
+ """ Add RFC2307 attributes to a group"""
+ self._create_group(group)
+ return self.runsubcmd("group", "addunixattrs", group["name"],
+ "%s" % group["gidNumber"],
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"],
+ os.environ["DC_PASSWORD"]))
+
def _find_group(self, name):
search_filter = ("(&(sAMAccountName=%s)(objectCategory=%s,%s))" %
(ldb.binary_encode(name),
diff --git a/python/samba/tests/samba_tool/user.py b/python/samba/tests/samba_tool/user.py
index 7ae80876b2a..4ebc9eb3bab 100644
--- a/python/samba/tests/samba_tool/user.py
+++ b/python/samba/tests/samba_tool/user.py
@@ -50,14 +50,23 @@ class UserCmdTestCase(SambaToolCmdTest):
self.users.append(self._randomPosixUser({"name": "posixuser2"}))
self.users.append(self._randomPosixUser({"name": "posixuser3"}))
self.users.append(self._randomPosixUser({"name": "posixuser4"}))
+ self.users.append(self._randomUnixUser({"name": "unixuser1"}))
+ self.users.append(self._randomUnixUser({"name": "unixuser2"}))
+ self.users.append(self._randomUnixUser({"name": "unixuser3"}))
+ self.users.append(self._randomUnixUser({"name": "unixuser4"}))
- # setup the 8 users and ensure they are correct
+ # setup the 12 users and ensure they are correct
for user in self.users:
(result, out, err) = user["createUserFn"](user)
self.assertCmdSuccess(result, out, err)
self.assertEquals(err, "", "Shouldn't be any error messages")
- self.assertIn("User '%s' created successfully" % user["name"], out)
+ if 'unix' in user["name"]:
+ self.assertIn("Modified User '%s' successfully" % user["name"],
+ out)
+ else:
+ self.assertIn("User '%s' created successfully" % user["name"],
+ out)
user["checkUserFn"](user)
@@ -539,6 +548,24 @@ sAMAccountName: %s
user.update(base)
return user
+ def _randomUnixUser(self, base={}):
+ """create a user with random attribute values and additional RFC2307
+ attributes, you can specify base attributes"""
+ user = self._randomUser({})
+ user.update(base)
+ posixAttributes = {
+ "uidNumber": self.randomXid(),
+ "gidNumber": self.randomXid(),
+ "uid": self.randomName(),
+ "loginShell": self.randomName(),
+ "gecos": self.randomName(),
+ "createUserFn": self._create_unix_user,
+ "checkUserFn": self._check_unix_user,
+ }
+ user.update(posixAttributes)
+ user.update(base)
+ return user
+
def _check_user(self, user):
""" check if a user from SamDB has the same attributes as its template """
found = self._find_user(user["name"])
@@ -560,6 +587,20 @@ sAMAccountName: %s
self.assertEquals("%s" % found.get("uid"), user["uid"])
self._check_user(user)
+ def _check_unix_user(self, user):
+ """ check if a unix_user from SamDB has the same attributes as its
+template """
+ found = self._find_user(user["name"])
+
+ self.assertEquals("%s" % found.get("loginShell"), user["loginShell"])
+ self.assertEquals("%s" % found.get("gecos"), user["gecos"])
+ self.assertEquals("%s" % found.get("uidNumber"), "%s" %
+ user["uidNumber"])
+ self.assertEquals("%s" % found.get("gidNumber"), "%s" %
+ user["gidNumber"])
+ self.assertEquals("%s" % found.get("uid"), user["uid"])
+ self._check_user(user)
+
def _create_user(self, user):
return self.runsubcmd("user", "create", user["name"], user["password"],
"--surname=%s" % user["surname"],
@@ -588,6 +629,19 @@ sAMAccountName: %s
"-H", "ldap://%s" % os.environ["DC_SERVER"],
"-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+ def _create_unix_user(self, user):
+ """ Add RFC2307 attributes to a user"""
+ self._create_user(user)
+ return self.runsubcmd("user", "addunixattrs", user["name"],
+ "%s" % user["uidNumber"],
+ "--gid-number=%s" % user["gidNumber"],
+ "--gecos=%s" % user["gecos"],
+ "--login-shell=%s" % user["loginShell"],
+ "--uid=%s" % user["uid"],
+ "-H", "ldap://%s" % os.environ["DC_SERVER"],
+ "-U%s%%%s" % (os.environ["DC_USERNAME"],
+ os.environ["DC_PASSWORD"]))
+
def _find_user(self, name):
search_filter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (ldb.binary_encode(name), "CN=Person,CN=Schema,CN=Configuration", self.samdb.domain_dn())
userlist = self.samdb.search(base=self.samdb.domain_dn(),