diff -rupN a/source4/scripting/python/samba/netcmd/domain.py b/source4/scripting/python/samba/netcmd/domain.py --- a/source4/scripting/python/samba/netcmd/domain.py 2012-11-13 09:03:39.000000000 +0100 +++ b/source4/scripting/python/samba/netcmd/domain.py 2012-11-14 13:49:48.166617281 +0100 @@ -1218,6 +1218,8 @@ class cmd_domain_classicupgrade(Command) help="Define if we should use the native fs capabilities or a tdb file for storing attributes likes ntacl, auto tries to make an inteligent guess based on the user rights and system capabilities", default="auto"), Option("--use-ntvfs", help="Use NTVFS for the fileserver (default = no)", action="store_true"), + Option("--add-upn", + help="Define if we should add attribute userPrincipalName in the form username@REALM (default = no)", action="store_true"), Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND", choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"], help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), " @@ -1231,7 +1233,7 @@ class cmd_domain_classicupgrade(Command) def run(self, smbconf=None, targetdir=None, dbdir=None, testparm=None, quiet=False, verbose=False, use_xattrs=None, sambaopts=None, versionopts=None, - dns_backend=None, use_ntvfs=False): + dns_backend=None, use_ntvfs=False, add_upn=False): if not os.path.exists(smbconf): raise CommandError("File %s does not exist" % smbconf) @@ -1315,7 +1317,7 @@ class cmd_domain_classicupgrade(Command) logger.info("Provisioning") upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session(), - useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs) + useeadb=eadb, dns_backend=dns_backend, use_ntvfs=use_ntvfs, add_upn=add_upn) class cmd_domain_samba3upgrade(cmd_domain_classicupgrade): diff -rupN a/source4/scripting/python/samba/netcmd/user.py b/source4/scripting/python/samba/netcmd/user.py --- a/source4/scripting/python/samba/netcmd/user.py 2012-10-30 10:01:47.000000000 +0100 +++ b/source4/scripting/python/samba/netcmd/user.py 2012-11-14 13:47:14.692618202 +0100 @@ -83,6 +83,7 @@ Example3 shows how to create a new user type=str), Option("--surname", help="User's surname", type=str), Option("--given-name", help="User's given name", type=str), + Option("--upn", help="User Principal Name", type=str), Option("--initials", help="User's initials", type=str), Option("--profile-path", help="User's profile path", type=str), Option("--script-path", help="User's logon script path", type=str), @@ -109,7 +110,7 @@ Example3 shows how to create a new user def run(self, username, password=None, credopts=None, sambaopts=None, versionopts=None, H=None, must_change_at_next_login=False, random_password=False, use_username_as_cn=False, userou=None, - surname=None, given_name=None, initials=None, profile_path=None, + surname=None, given_name=None, upn=None, initials=None, profile_path=None, script_path=None, home_drive=None, home_directory=None, job_title=None, department=None, company=None, description=None, mail_address=None, internet_address=None, telephone_number=None, @@ -134,7 +135,7 @@ Example3 shows how to create a new user samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp) samdb.newuser(username, password, force_password_change_at_next_login_req=must_change_at_next_login, - useusernameascn=use_username_as_cn, userou=userou, surname=surname, givenname=given_name, initials=initials, + useusernameascn=use_username_as_cn, userou=userou, surname=surname, givenname=given_name, upn=upn, initials=initials, profilepath=profile_path, homedrive=home_drive, scriptpath=script_path, homedirectory=home_directory, jobtitle=job_title, department=department, company=company, description=description, mailaddress=mail_address, internetaddress=internet_address, @@ -422,6 +423,73 @@ Example4 shows how to set the account ex self.outf.write("Expiry for user '%s' disabled.\n" % ( username or filter)) +class cmd_user_setupn(Command): + """Set the userPrincipalName of a user account. + +The user can either be specified by their sAMAccountName or using the --filter option. + +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 on a remote server. + +Example1: +samba-tool user setupn User1 --upn=LOGIN@DOMAINFQDN --URL=ldap://samba.samdom.example.com --username=administrator --password=passw1rd + +Example1 shows how to set the userPrincipalname of an account in a remote LDAP server. The --URL parameter is used to specify the remote target server. The --username= and --password= options are used to pass the username and password of a user that exists on the remote server and is authorized to update that server. + +Example2: +samba-tool user setupn User1 --upn=LOGIN@DOMAINFQDN + +Example2 shows how to set the userPrincipalname of an account. The username or sAMAccountName is specified using the --filter= paramter and the username in this example is User1. + +Example3: +samba-tool user setupn User1 + +Example3 shows how to clear the userPrincipalname of an account. The username or sAMAccountName is specified using the --filter= paramter and the username in this example is User1. + +""" + synopsis = "%prog (|--filter ) [options]" + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "versionopts": options.VersionOptions, + "credopts": options.CredentialsOptions, + } + + takes_options = [ + Option("-H", "--URL", help="LDB URL for database or target server", type=str, + metavar="URL", dest="H"), + Option("--filter", help="LDAP Filter to set upn on", type=str), + Option("--upn", help="userPrincipalName", type=str, default=""), + ] + + takes_args = ["username?"] + + def run(self, username=None, sambaopts=None, credopts=None, + versionopts=None, H=None, filter=None, upn=None): + if username is None and filter is None: + raise CommandError("Either the username or '--filter' must be specified!") + + if filter is None: + filter = "(&(objectClass=user)(sAMAccountName=%s))" % (ldb.binary_encode(username)) + + lp = sambaopts.get_loadparm() + creds = credopts.get_credentials(lp) + + samdb = SamDB(url=H, session_info=system_session(), + credentials=creds, lp=lp) + + try: + samdb.setupn(filter, upn) + except Exception, msg: + # FIXME: Catch more specific exception + raise CommandError("Failed to set userPrincipalName for user '%s': %s" % ( + username or filter, msg)) + if upn: + self.outf.write("UserPrincipalName for user '%s' set to UPN: %u .\n" % ( + username or filter, upn)) + else: + self.outf.write("UserPrincipalName for user '%s' cleared.\n" % ( + username or filter)) + class cmd_user_password(Command): """Change password for a user account (the one provided in authentication). @@ -566,5 +634,6 @@ class cmd_user(SuperCommand): subcommands["enable"] = cmd_user_enable() subcommands["list"] = cmd_user_list() subcommands["setexpiry"] = cmd_user_setexpiry() + subcommands["setupn"] = cmd_user_setupn() subcommands["password"] = cmd_user_password() subcommands["setpassword"] = cmd_user_setpassword() diff -rupN a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py --- a/source4/scripting/python/samba/samdb.py 2012-10-16 09:33:06.000000000 +0200 +++ b/source4/scripting/python/samba/samdb.py 2012-11-14 13:39:37.648365251 +0100 @@ -285,7 +285,7 @@ member: %s def newuser(self, username, password, force_password_change_at_next_login_req=False, - useusernameascn=False, userou=None, surname=None, givenname=None, + useusernameascn=False, userou=None, surname=None, givenname=None, upn=None, initials=None, profilepath=None, scriptpath=None, homedrive=None, homedirectory=None, jobtitle=None, department=None, company=None, description=None, mailaddress=None, internetaddress=None, @@ -301,6 +301,7 @@ member: %s :param userou: Object container (without domainDN postfix) for new user :param surname: Surname of the new user :param givenname: First name of the new user + :param upn: userPrincipalName of the new user :param initials: Initials of the new user :param profilepath: Profile path of the new user :param scriptpath: Logon script path of the new user @@ -340,7 +341,6 @@ member: %s # fills in the default informations ldbmessage = {"dn": user_dn, "sAMAccountName": username, - "userPrincipalName": user_principal_name, "objectClass": "user"} if surname is not None: @@ -348,6 +348,11 @@ member: %s if givenname is not None: ldbmessage["givenName"] = givenname + + if upn is not None: + ldbmessage["userPrincipalName"] = upn + else: + ldbmessage["userPrincipalName"] = user_principal_name if displayname is not "": ldbmessage["displayName"] = displayname @@ -513,6 +518,46 @@ accountExpires: %u else: self.transaction_commit() + def setupn(self, search_filter, userPrincipalName): + """Sets the userPrincipalName for a user + + :param search_filter: LDAP filter to find the user (eg + samaccountname=name) + :param upn: userPrincipalName in form LOGIN@DOMAINFQDN + """ + self.transaction_start() + try: + res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE, + expression=search_filter, + attrs=[]) + if len(res) == 0: + raise Exception('Unable to find user "%s"' % search_filter) + assert(len(res) == 1) + user_dn = res[0].dn + + setexp="" + + if len(userPrincipalName) == 0: + setexp = """ +dn: %s +changetype: modify +delete: userPrincipalName +""" % (user_dn) + else: + setexp = """ +dn: %s +changetype: modify +replace: userPrincipalName +userPrincipalName: %u +""" % (user_dn, userPrincipalName) + + self.modify_ldif(setexp) + except: + self.transaction_cancel() + raise + else: + self.transaction_commit() + def set_domain_sid(self, sid): """Change the domain SID used by this LDB. diff -rupN a/source4/scripting/python/samba/upgrade.py b/source4/scripting/python/samba/upgrade.py --- a/source4/scripting/python/samba/upgrade.py 2012-11-13 09:03:39.000000000 +0100 +++ b/source4/scripting/python/samba/upgrade.py 2012-11-14 13:19:22.499373699 +0100 @@ -127,6 +127,30 @@ def add_posix_attrs(logger, samdb, sid, 'Could not add posix attrs for AD entry for sid=%s, (%s)', str(sid), str(e)) +def add_userPrincipalName(logger, samdb, sid, name, realm, xid_type): + """Add userPrincipalName for the user + + :param samdb: Samba4 sam.ldb database + :param sid: user/group sid + :param name: user/group name + :param realm: fqdn domain name + :param xid_type: type of id (ID_TYPE_UID/ID_TYPE_GID) + """ + user_principal_name = "%s@%s" % (name, realm) + + try: + m = ldb.Message() + m.dn = ldb.Dn(samdb, "" % str(sid)) + if xid_type == "ID_TYPE_UID": + m['userPrincipalName'] = ldb.MessageElement( + str(user_principal_name), ldb.FLAG_MOD_REPLACE, 'userPrincipalName') + + samdb.modify(m) + except ldb.LdbError, e: + logger.warn( + 'Could not add userPrincipalName attr for AD entry for sid=%s, (%s)', + str(sid), str(e)) + def add_ad_posix_idmap_entry(samdb, sid, xid, xid_type, logger): """Create idmap entry @@ -157,7 +181,6 @@ def add_ad_posix_idmap_entry(samdb, sid, 'Could not modify AD idmap entry for sid=%s, id=%s, type=%s (%s)', str(sid), str(xid), xid_type, str(e)) - def add_idmap_entry(idmapdb, sid, xid, xid_type, logger): """Create idmap entry @@ -549,7 +572,7 @@ def get_posix_attr_from_ldap_backend(log def upgrade_from_samba3(samba3, logger, targetdir, session_info=None, - useeadb=False, dns_backend=None, use_ntvfs=False): + useeadb=False, dns_backend=None, use_ntvfs=False, add_upn=False): """Upgrade from samba3 database to samba4 AD database :param samba3: samba3 object @@ -887,6 +910,8 @@ Please fix this account before attemptin (username in shells) and (shells[username] is not None) and \ (username in pgids) and (pgids[username] is not None): add_posix_attrs(samdb=result.samdb, sid=userdata[username].user_sid, name=username, nisdomain=domainname.lower(), xid_type="ID_TYPE_UID", home=homes[username], shell=shells[username], pgid=pgids[username], logger=logger) + if add_upn: + add_userPrincipalName(samdb=result.samdb, sid=userdata[username].user_sid, name=username, realm=realm.upper(), xid_type="ID_TYPE_UID", logger=logger) logger.info("Adding users to groups") for g in grouplist: