s4 net: Add spn module to list/add/remove spn on objects
[samba.git] / source4 / scripting / python / samba / netcmd / spn.py
1 #!/usr/bin/env python
2 #
3 # spn management
4 #
5 # Copyright Matthieu Patou mat@samba.org 2010
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20
21 import samba.getopt as options
22 import ldb
23 import re
24 from samba import provision
25 from samba.samdb import SamDB
26 from samba.auth import system_session
27 from samba.netcmd import (
28     Command,
29     CommandError,
30     SuperCommand,
31     Option
32     )
33
34 def _get_user_realm_domain(user):
35     """ get the realm or the domain and the base user
36         from user like:
37         * username
38         * DOMAIN\username
39         * username@REALM
40     """
41     baseuser = user
42     realm = ""
43     domain = ""
44     m = re.match(r"(\w+)\\(\w+$)", user)
45     if m:
46         domain = m.group(1)
47         baseuser = m.group(2)
48         return (baseuser.lower(), domain.upper(), realm)
49     m = re.match(r"(\w+)@(\w+)", user)
50     if m:
51         baseuser = m.group(1)
52         realm = m.group(2)
53     return (baseuser.lower(), domain, realm.upper())
54
55 class cmd_spn_list(Command):
56     """List spns of a given user."""
57     synopsis = "%prog spn list <user>"
58
59     takes_optiongroups = {
60         "sambaopts": options.SambaOptions,
61         "credopts": options.CredentialsOptions,
62         "versionopts": options.VersionOptions,
63         }
64
65     takes_args = ["user"]
66
67     def run(self, user, credopts=None, sambaopts=None, versionopts=None):
68         lp = sambaopts.get_loadparm()
69         creds = credopts.get_credentials(lp)
70         paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
71         sam = SamDB(paths.samdb, session_info=system_session(),
72                     credentials=creds, lp=lp)
73         # TODO once I understand how, use the domain info to naildown
74         # to the correct domain
75         (cleaneduser, realm, domain) = _get_user_realm_domain(user)
76         print cleaneduser
77         res = sam.search(expression="samaccountname=%s" % cleaneduser,
78                             scope=ldb.SCOPE_SUBTREE,
79                             attrs=["servicePrincipalName"])
80         if len(res) >0:
81             spns = res[0].get("servicePrincipalName")
82             found = False
83             flag = ldb.FLAG_MOD_ADD
84             if spns != None:
85                 print "User %s has the following servicePrincipalName: " %  str(res[0].dn)
86                 for e in spns:
87                     print "\t %s" % (str(e))
88
89             else:
90                 print "User %s has no servicePrincipalName" % str(res[0].dn)
91         else:
92             raise CommandError("User %s not found" % user)
93
94 class cmd_spn_add(Command):
95     """Create a new spn."""
96     synopsis = "%prog spn add [--force] <name> <user>"
97
98     takes_optiongroups = {
99         "sambaopts": options.SambaOptions,
100         "credopts": options.CredentialsOptions,
101         "versionopts": options.VersionOptions,
102         }
103     takes_options = [
104         Option("--force", help="Force the addition of the spn"\
105                                " even it exists already", action="store_true"),
106             ]
107     takes_args = ["name", "user"]
108
109     def run(self, name, user,  force=False, credopts=None, sambaopts=None, versionopts=None):
110         lp = sambaopts.get_loadparm()
111         creds = credopts.get_credentials(lp)
112         paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
113         sam = SamDB(paths.samdb, session_info=system_session(),
114                     credentials=creds, lp=lp)
115         res = sam.search(expression="servicePrincipalName=%s" % name,
116                             scope=ldb.SCOPE_SUBTREE,
117                             )
118         if len(res) != 0  and not force:
119             raise CommandError("Service principal %s already"
120                                    " affected to another user" % name)
121
122         (cleaneduser, realm, domain) = _get_user_realm_domain(user)
123         res = sam.search(expression="samaccountname=%s" % cleaneduser,
124                             scope=ldb.SCOPE_SUBTREE,
125                             attrs=["servicePrincipalName"])
126         if len(res) >0:
127             res[0].dn
128             msg = ldb.Message()
129             spns = res[0].get("servicePrincipalName")
130             tab = []
131             found = False
132             flag = ldb.FLAG_MOD_ADD
133             if spns != None:
134                 for e in spns:
135                     if str(e) == name:
136                         found = True
137                     tab.append(str(e))
138                 flag = ldb.FLAG_MOD_REPLACE
139             tab.append(name)
140             msg.dn = res[0].dn
141             msg["servicePrincipalName"] = ldb.MessageElement(tab, flag,
142                                                 "servicePrincipalName")
143             if not found:
144                 sam.modify(msg)
145             else:
146                 raise CommandError("Service principal %s already"
147                                        " affected to %s" % (name, user))
148         else:
149             raise CommandError("User %s not found" % user)
150
151
152 class cmd_spn_delete(Command):
153     """Delete a spn."""
154     synopsis = "%prog spn delete <name> [user]"
155
156     takes_optiongroups = {
157         "sambaopts": options.SambaOptions,
158         "credopts": options.CredentialsOptions,
159         "versionopts": options.VersionOptions,
160         }
161
162     takes_args = ["name", "user?"]
163
164     def run(self, name, user=None, credopts=None, sambaopts=None, versionopts=None):
165         lp = sambaopts.get_loadparm()
166         creds = credopts.get_credentials(lp)
167         paths = provision.provision_paths_from_lp(lp, lp.get("realm"))
168         sam = SamDB(paths.samdb, session_info=system_session(),
169                     credentials=creds, lp=lp)
170         res = sam.search(expression="servicePrincipalName=%s" % name,
171                             scope=ldb.SCOPE_SUBTREE,
172                             attrs=["servicePrincipalName", "samAccountName"])
173         if len(res) >0:
174             result = None
175             if user is not None:
176                 (cleaneduser, realm, domain) = _get_user_realm_domain(user)
177                 for elem in res:
178                     if str(elem["samAccountName"]).lower() == cleaneduser:
179                         result = elem
180                 if result is None:
181                     raise CommandError("Unable to find user %s with"
182                                            " spn %s" % (user, name))
183             else:
184                 if len(res) != 1:
185                     listUser = ""
186                     for r in res:
187                         listUser = "%s\n%s" % (listUser, str(r.dn))
188                     raise CommandError("More than one user has the spn %s "\
189                            "and no specific user was specified, list of users"\
190                            " with this spn:%s" % (name, listUser))
191                 else:
192                     result=res[0]
193
194
195             msg = ldb.Message()
196             spns = result.get("servicePrincipalName")
197             tab = []
198             if spns != None:
199                 for e in spns:
200                     if str(e) != name:
201                         tab.append(str(e))
202                 flag = ldb.FLAG_MOD_REPLACE
203             msg.dn = result.dn
204             msg["servicePrincipalName"] = ldb.MessageElement(tab, flag,
205                                             "servicePrincipalName")
206             sam.modify(msg)
207         else:
208             raise CommandError("Service principal %s not affected" % name)
209
210 class cmd_spn(SuperCommand):
211     """User management [server connection needed]"""
212
213     subcommands = {}
214     subcommands["add"] = cmd_spn_add()
215     subcommands["list"] = cmd_spn_list()
216     subcommands["delete"] = cmd_spn_delete()
217