1 # implement samba_tool ou commands
3 # Copyright Bjoern Baumbach <bb@sernet.de> 2018
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 import samba.getopt as options
22 from samba.auth import system_session
23 from samba.netcmd import (
29 from samba.samdb import SamDB
30 from samba import dsdb
31 from operator import attrgetter
33 class cmd_rename(Command):
34 """Rename an organizational unit.
36 The name of the organizational units can be specified as a full DN
37 or without the domainDN component.
40 samba-tool ou rename 'OU=OrgUnit,DC=samdom,DC=example,DC=com' \
41 'OU=NewNameOfOrgUnit,DC=samdom,DC=example,DC=com'
42 samba-tool ou rename 'OU=OrgUnit' 'OU=NewNameOfOrgUnit'
44 The examples show how an administrator would rename an ou 'OrgUnit'
45 to 'NewNameOfOrgUnit'. The new DN would be
46 'OU=NewNameOfOrgUnit,DC=samdom,DC=example,DC=com'
49 synopsis = "%prog <old_ou_dn> <new_ou_dn> [options]"
52 Option("-H", "--URL", help="LDB URL for database or target server",
53 type=str, metavar="URL", dest="H"),
56 takes_args = ["old_ou_dn", "new_ou_dn"]
57 takes_optiongroups = {
58 "sambaopts": options.SambaOptions,
59 "credopts": options.CredentialsOptions,
60 "versionopts": options.VersionOptions,
63 def run(self, old_ou_dn, new_ou_dn, credopts=None, sambaopts=None,
64 versionopts=None, H=None):
65 lp = sambaopts.get_loadparm()
66 creds = credopts.get_credentials(lp, fallback_machine=True)
67 samdb = SamDB(url=H, session_info=system_session(),
68 credentials=creds, lp=lp)
69 domain_dn = ldb.Dn(samdb, samdb.domain_dn())
72 full_old_ou_dn = samdb.normalize_dn_in_domain(old_ou_dn)
73 except Exception as e:
74 raise CommandError('Invalid old_ou_dn "%s": %s' %
75 (old_ou_dn, e.message))
77 full_new_ou_dn = samdb.normalize_dn_in_domain(new_ou_dn)
78 except Exception as e:
79 raise CommandError('Invalid new_ou_dn "%s": %s' %
80 (new_ou_dn, e.message))
83 res = samdb.search(base=full_old_ou_dn,
84 expression="(objectclass=organizationalUnit)",
85 scope=ldb.SCOPE_BASE, attrs=[])
87 self.outf.write('Unable to find ou "%s"\n' % old_ou_dn)
90 samdb.rename(full_old_ou_dn, full_new_ou_dn)
91 except Exception as e:
92 raise CommandError('Failed to rename ou "%s"' % full_old_ou_dn, e)
93 self.outf.write('Renamed ou "%s" to "%s"\n' % (full_old_ou_dn,
96 class cmd_move(Command):
97 """Move an organizational unit.
99 The name of the organizational units can be specified as a full DN
100 or without the domainDN component.
103 samba-tool ou move 'OU=OrgUnit,DC=samdom,DC=example,DC=com' \
104 'OU=NewParentOfOrgUnit,DC=samdom,DC=example,DC=com'
105 samba-tool ou rename 'OU=OrgUnit' 'OU=NewParentOfOrgUnit'
107 The examples show how an administrator would move an ou 'OrgUnit'
108 into the ou 'NewParentOfOrgUnit'. The ou 'OrgUnit' would become
109 a child of the 'NewParentOfOrgUnit' ou. The new DN would be
110 'OU=OrgUnit,OU=NewParentOfOrgUnit,DC=samdom,DC=example,DC=com'
113 synopsis = "%prog <old_ou_dn> <new_parent_dn> [options]"
116 Option("-H", "--URL", help="LDB URL for database or target server",
117 type=str, metavar="URL", dest="H"),
120 takes_args = ["old_ou_dn", "new_parent_dn"]
121 takes_optiongroups = {
122 "sambaopts": options.SambaOptions,
123 "credopts": options.CredentialsOptions,
124 "versionopts": options.VersionOptions,
127 def run(self, old_ou_dn, new_parent_dn, credopts=None, sambaopts=None,
128 versionopts=None, H=None):
129 lp = sambaopts.get_loadparm()
130 creds = credopts.get_credentials(lp, fallback_machine=True)
131 samdb = SamDB(url=H, session_info=system_session(),
132 credentials=creds, lp=lp)
134 domain_dn = ldb.Dn(samdb, samdb.domain_dn())
136 full_old_ou_dn = samdb.normalize_dn_in_domain(old_ou_dn)
137 except Exception as e:
138 raise CommandError('Invalid old_ou_dn "%s": %s' %
139 (old_ou_dn, e.message))
141 full_new_parent_dn = samdb.normalize_dn_in_domain(new_parent_dn)
142 except Exception as e:
143 raise CommandError('Invalid new_parent_dn "%s": %s' %
144 (new_parent_dn, e.message))
146 full_new_ou_dn = ldb.Dn(samdb, str(full_old_ou_dn))
147 full_new_ou_dn.remove_base_components(len(full_old_ou_dn) - 1)
148 full_new_ou_dn.add_base(full_new_parent_dn)
151 res = samdb.search(base=full_old_ou_dn,
152 expression="(objectclass=organizationalUnit)",
153 scope=ldb.SCOPE_BASE, attrs=[])
155 self.outf.write('Unable to find ou "%s"\n' % full_old_ou_dn)
157 samdb.rename(full_old_ou_dn, full_new_ou_dn)
158 except Exception as e:
159 raise CommandError('Failed to move ou "%s"' % full_old_ou_dn, e)
160 self.outf.write('Moved ou "%s" into "%s"\n' %
161 (full_old_ou_dn, full_new_parent_dn))
163 class cmd_create(Command):
164 """Create an organizational unit.
166 The name of the new ou can be specified as a full DN or without the
170 samba-tool ou create 'OU=OrgUnit'
171 samba-tool ou create 'OU=SubOU,OU=OrgUnit,DC=samdom,DC=example,DC=com'
173 The examples show how an administrator would create a new ou 'OrgUnit'
174 and a new ou 'SubOU' as a child of the ou 'OrgUnit'.
177 synopsis = "%prog <ou_dn> [options]"
180 Option("-H", "--URL", help="LDB URL for database or target server",
181 type=str, metavar="URL", dest="H"),
182 Option("--description", help="OU's description",
183 type=str, dest="description"),
186 takes_args = ["ou_dn"]
187 takes_optiongroups = {
188 "sambaopts": options.SambaOptions,
189 "credopts": options.CredentialsOptions,
190 "versionopts": options.VersionOptions,
193 def run(self, ou_dn, credopts=None, sambaopts=None, versionopts=None,
194 H=None, description=None):
195 lp = sambaopts.get_loadparm()
196 creds = credopts.get_credentials(lp, fallback_machine=True)
197 samdb = SamDB(url=H, session_info=system_session(),
198 credentials=creds, lp=lp)
201 full_ou_dn = samdb.normalize_dn_in_domain(ou_dn)
202 except Exception as e:
203 raise CommandError('Invalid ou_dn "%s": %s' % (ou_dn, e.message))
206 samdb.create_ou(full_ou_dn, description=description)
207 except Exception as e:
208 raise CommandError('Failed to create ou "%s"' % full_ou_dn, e)
210 self.outf.write('Created ou "%s"\n' % full_ou_dn)
212 class cmd_listobjects(Command):
213 """List all objects in an organizational unit.
215 The name of the organizational unit can be specified as a full DN
216 or without the domainDN component.
219 samba-tool ou listobjects 'OU=OrgUnit,DC=samdom,DC=example,DC=com'
220 samba-tool ou listobjects 'OU=OrgUnit'
222 The examples show how an administrator would list all child objects
225 synopsis = "%prog <ou_dn> [options]"
228 Option("-H", "--URL", help="LDB URL for database or target server",
229 type=str, metavar="URL", dest="H"),
230 Option("--full-dn", dest="full_dn", default=False, action='store_true',
231 help="Display DNs including the base DN."),
232 Option("-r", "--recursive", dest="recursive", default=False,
233 action='store_true', help="List objects recursively."),
236 takes_args = ["ou_dn"]
237 takes_optiongroups = {
238 "sambaopts": options.SambaOptions,
239 "credopts": options.CredentialsOptions,
240 "versionopts": options.VersionOptions,
243 def run(self, ou_dn, credopts=None, sambaopts=None, versionopts=None,
244 H=None, full_dn=False, recursive=False):
245 lp = sambaopts.get_loadparm()
246 creds = credopts.get_credentials(lp, fallback_machine=True)
247 samdb = SamDB(url=H, session_info=system_session(),
248 credentials=creds, lp=lp)
249 domain_dn = ldb.Dn(samdb, samdb.domain_dn())
252 full_ou_dn = samdb.normalize_dn_in_domain(ou_dn)
253 except Exception as e:
254 raise CommandError('Invalid ou_dn "%s": %s' % (ou_dn, e.message))
257 scope = ldb.SCOPE_ONELEVEL
260 scope = ldb.SCOPE_SUBTREE
263 childs = samdb.search(base=full_ou_dn,
264 expression="(objectclass=*)",
265 scope=scope, attrs=[])
266 if len(childs) <= minchilds:
267 self.outf.write('ou "%s" is empty\n' % ou_dn)
270 for child in sorted(childs, key=attrgetter('dn')):
271 if child.dn == full_ou_dn:
274 child.dn.remove_base_components(len(domain_dn))
275 self.outf.write("%s\n" % child.dn)
277 except Exception as e:
278 raise CommandError('Failed to list contents of ou "%s"' %
281 class cmd_list(Command):
282 """List all organizational units.
285 samba-tool ou listobjects
287 The example shows how an administrator would list all organizational
291 synopsis = "%prog [options]"
294 Option("-H", "--URL", help="LDB URL for database or target server",
295 type=str, metavar="URL", dest="H"),
296 Option("--full-dn", dest="full_dn", default=False, action='store_true',
297 help="Display DNs including the base DN."),
300 takes_optiongroups = {
301 "sambaopts": options.SambaOptions,
302 "credopts": options.CredentialsOptions,
303 "versionopts": options.VersionOptions,
306 def run(self, sambaopts=None, credopts=None, versionopts=None, H=None,
308 lp = sambaopts.get_loadparm()
309 creds = credopts.get_credentials(lp, fallback_machine=True)
310 samdb = SamDB(url=H, session_info=system_session(),
311 credentials=creds, lp=lp)
313 domain_dn = ldb.Dn(samdb, samdb.domain_dn())
314 res = samdb.search(domain_dn, scope=ldb.SCOPE_SUBTREE,
315 expression="(objectClass=organizationalUnit)",
320 for msg in sorted(res, key=attrgetter('dn')):
322 msg.dn.remove_base_components(len(domain_dn))
323 self.outf.write("%s\n" % str(msg.dn))
325 class cmd_delete(Command):
326 """Delete an organizational unit.
328 The name of the organizational unit can be specified as a full DN
329 or without the domainDN component.
332 samba-tool ou delete 'OU=OrgUnit,DC=samdom,DC=example,DC=com'
333 samba-tool ou delete 'OU=OrgUnit'
335 The examples show how an administrator would delete the ou 'OrgUnit'.
338 synopsis = "%prog <ou_dn> [options]"
341 Option("-H", "--URL", help="LDB URL for database or target server",
342 type=str, metavar="URL", dest="H"),
343 Option("--force-subtree-delete", dest="force_subtree_delete",
344 default=False, action='store_true',
345 help="Delete organizational unit and all children reclusively"),
348 takes_args = ["ou_dn"]
349 takes_optiongroups = {
350 "sambaopts": options.SambaOptions,
351 "credopts": options.CredentialsOptions,
352 "versionopts": options.VersionOptions,
355 def run(self, ou_dn, credopts=None, sambaopts=None, versionopts=None,
356 H=None, force_subtree_delete=False):
357 lp = sambaopts.get_loadparm()
358 creds = credopts.get_credentials(lp, fallback_machine=True)
359 samdb = SamDB(url=H, session_info=system_session(),
360 credentials=creds, lp=lp)
361 domain_dn = ldb.Dn(samdb, samdb.domain_dn())
364 full_ou_dn = samdb.normalize_dn_in_domain(ou_dn)
365 except Exception as e:
366 raise CommandError('Invalid ou_dn "%s": %s' % (ou_dn, e.message))
369 if force_subtree_delete:
370 controls = ["tree_delete:1"]
373 res = samdb.search(base=full_ou_dn,
374 expression="(objectclass=organizationalUnit)",
375 scope=ldb.SCOPE_BASE, attrs=[])
377 self.outf.write('Unable to find ou "%s"\n' % ou_dn)
379 samdb.delete(full_ou_dn, controls)
380 except Exception as e:
381 raise CommandError('Failed to delete ou "%s"' % full_ou_dn, e)
383 self.outf.write('Deleted ou "%s"\n' % full_ou_dn)
386 class cmd_ou(SuperCommand):
387 """Organizational Units (OU) management"""
390 subcommands["create"] = cmd_create()
391 subcommands["delete"] = cmd_delete()
392 subcommands["move"] = cmd_move()
393 subcommands["rename"] = cmd_rename()
394 subcommands["list"] = cmd_list()
395 subcommands["listobjects"] = cmd_listobjects()