1 # implement samba-tool ou commands
3 # Copyright Bjoern Baumbach 2018-2019 <bb@samba.org>
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 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
)
71 full_old_ou_dn
= samdb
.normalize_dn_in_domain(old_ou_dn
)
72 except Exception as e
:
73 raise CommandError('Invalid old_ou_dn "%s": %s' %
76 full_new_ou_dn
= samdb
.normalize_dn_in_domain(new_ou_dn
)
77 except Exception as e
:
78 raise CommandError('Invalid new_ou_dn "%s": %s' %
82 res
= samdb
.search(base
=full_old_ou_dn
,
83 expression
="(objectclass=organizationalUnit)",
84 scope
=ldb
.SCOPE_BASE
, attrs
=[])
86 self
.outf
.write('Unable to find ou "%s"\n' % old_ou_dn
)
89 samdb
.rename(full_old_ou_dn
, full_new_ou_dn
)
90 except Exception as e
:
91 raise CommandError('Failed to rename ou "%s"' % full_old_ou_dn
, e
)
92 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
)
135 full_old_ou_dn
= samdb
.normalize_dn_in_domain(old_ou_dn
)
136 except Exception as e
:
137 raise CommandError('Invalid old_ou_dn "%s": %s' %
140 full_new_parent_dn
= samdb
.normalize_dn_in_domain(new_parent_dn
)
141 except Exception as e
:
142 raise CommandError('Invalid new_parent_dn "%s": %s' %
145 full_new_ou_dn
= ldb
.Dn(samdb
, str(full_old_ou_dn
))
146 full_new_ou_dn
.remove_base_components(len(full_old_ou_dn
) - 1)
147 full_new_ou_dn
.add_base(full_new_parent_dn
)
150 res
= samdb
.search(base
=full_old_ou_dn
,
151 expression
="(objectclass=organizationalUnit)",
152 scope
=ldb
.SCOPE_BASE
, attrs
=[])
154 self
.outf
.write('Unable to find ou "%s"\n' % full_old_ou_dn
)
156 samdb
.rename(full_old_ou_dn
, full_new_ou_dn
)
157 except Exception as e
:
158 raise CommandError('Failed to move ou "%s"' % full_old_ou_dn
, e
)
159 self
.outf
.write('Moved ou "%s" into "%s"\n' %
160 (full_old_ou_dn
, full_new_parent_dn
))
163 class cmd_add(Command
):
164 """Add a new organizational unit.
166 The name of the new ou can be specified as a full DN or without the
170 samba-tool ou add 'OU=OrgUnit'
171 samba-tool ou add 'OU=SubOU,OU=OrgUnit,DC=samdom,DC=example,DC=com'
173 The examples show how an administrator would add 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
))
206 samdb
.create_ou(full_ou_dn
, description
=description
)
207 except Exception as e
:
208 raise CommandError('Failed to add ou "%s"' % full_ou_dn
, e
)
210 self
.outf
.write('Added ou "%s"\n' % full_ou_dn
)
213 class cmd_listobjects(Command
):
214 """List all objects in an organizational unit.
216 The name of the organizational unit can be specified as a full DN
217 or without the domainDN component.
220 samba-tool ou listobjects 'OU=OrgUnit,DC=samdom,DC=example,DC=com'
221 samba-tool ou listobjects 'OU=OrgUnit'
223 The examples show how an administrator would list all child objects
226 synopsis
= "%prog <ou_dn> [options]"
229 Option("-H", "--URL", help="LDB URL for database or target server",
230 type=str, metavar
="URL", dest
="H"),
231 Option("--full-dn", dest
="full_dn", default
=False, action
='store_true',
232 help="Display DNs including the base DN."),
233 Option("-r", "--recursive", dest
="recursive", default
=False,
234 action
='store_true', help="List objects recursively."),
237 takes_args
= ["ou_dn"]
238 takes_optiongroups
= {
239 "sambaopts": options
.SambaOptions
,
240 "credopts": options
.CredentialsOptions
,
241 "versionopts": options
.VersionOptions
,
244 def run(self
, ou_dn
, credopts
=None, sambaopts
=None, versionopts
=None,
245 H
=None, full_dn
=False, recursive
=False):
246 lp
= sambaopts
.get_loadparm()
247 creds
= credopts
.get_credentials(lp
, fallback_machine
=True)
248 samdb
= SamDB(url
=H
, session_info
=system_session(),
249 credentials
=creds
, lp
=lp
)
250 domain_dn
= ldb
.Dn(samdb
, samdb
.domain_dn())
253 full_ou_dn
= samdb
.normalize_dn_in_domain(ou_dn
)
254 except Exception as e
:
255 raise CommandError('Invalid ou_dn "%s": %s' % (ou_dn
, e
))
258 scope
= ldb
.SCOPE_ONELEVEL
261 scope
= ldb
.SCOPE_SUBTREE
264 children
= samdb
.search(base
=full_ou_dn
,
265 expression
="(objectclass=*)",
266 scope
=scope
, attrs
=[])
267 if len(children
) <= minchildren
:
268 self
.outf
.write('ou "%s" is empty\n' % ou_dn
)
271 for child
in sorted(children
, key
=attrgetter('dn')):
272 if child
.dn
== full_ou_dn
:
275 child
.dn
.remove_base_components(len(domain_dn
))
276 self
.outf
.write("%s\n" % child
.dn
)
278 except Exception as e
:
279 raise CommandError('Failed to list contents of ou "%s"' %
283 class cmd_list(Command
):
284 """List all organizational units.
287 samba-tool ou listobjects
289 The example shows how an administrator would list all organizational
293 synopsis
= "%prog [options]"
296 Option("-H", "--URL", help="LDB URL for database or target server",
297 type=str, metavar
="URL", dest
="H"),
298 Option("-b", "--base-dn",
299 help="Specify base DN to use.",
301 Option("--full-dn", dest
="full_dn", default
=False, action
='store_true',
302 help="Display DNs including the base DN."),
305 takes_optiongroups
= {
306 "sambaopts": options
.SambaOptions
,
307 "credopts": options
.CredentialsOptions
,
308 "versionopts": options
.VersionOptions
,
318 lp
= sambaopts
.get_loadparm()
319 creds
= credopts
.get_credentials(lp
, fallback_machine
=True)
320 samdb
= SamDB(url
=H
, session_info
=system_session(),
321 credentials
=creds
, lp
=lp
)
323 search_dn
= ldb
.Dn(samdb
, samdb
.domain_dn())
325 search_dn
= samdb
.normalize_dn_in_domain(base_dn
)
327 res
= samdb
.search(search_dn
,
328 scope
=ldb
.SCOPE_SUBTREE
,
329 expression
="(objectClass=organizationalUnit)",
334 for msg
in sorted(res
, key
=attrgetter('dn')):
336 domain_dn
= ldb
.Dn(samdb
, samdb
.domain_dn())
337 msg
.dn
.remove_base_components(len(domain_dn
))
338 self
.outf
.write("%s\n" % str(msg
.dn
))
341 class cmd_delete(Command
):
342 """Delete an organizational unit.
344 The name of the organizational unit can be specified as a full DN
345 or without the domainDN component.
348 samba-tool ou delete 'OU=OrgUnit,DC=samdom,DC=example,DC=com'
349 samba-tool ou delete 'OU=OrgUnit'
351 The examples show how an administrator would delete the ou 'OrgUnit'.
354 synopsis
= "%prog <ou_dn> [options]"
357 Option("-H", "--URL", help="LDB URL for database or target server",
358 type=str, metavar
="URL", dest
="H"),
359 Option("--force-subtree-delete", dest
="force_subtree_delete",
360 default
=False, action
='store_true',
361 help="Delete organizational unit and all children recursively"),
364 takes_args
= ["ou_dn"]
365 takes_optiongroups
= {
366 "sambaopts": options
.SambaOptions
,
367 "credopts": options
.CredentialsOptions
,
368 "versionopts": options
.VersionOptions
,
371 def run(self
, ou_dn
, credopts
=None, sambaopts
=None, versionopts
=None,
372 H
=None, force_subtree_delete
=False):
373 lp
= sambaopts
.get_loadparm()
374 creds
= credopts
.get_credentials(lp
, fallback_machine
=True)
375 samdb
= SamDB(url
=H
, session_info
=system_session(),
376 credentials
=creds
, lp
=lp
)
379 full_ou_dn
= samdb
.normalize_dn_in_domain(ou_dn
)
380 except Exception as e
:
381 raise CommandError('Invalid ou_dn "%s": %s' % (ou_dn
, e
))
384 if force_subtree_delete
:
385 controls
= ["tree_delete:1"]
388 res
= samdb
.search(base
=full_ou_dn
,
389 expression
="(objectclass=organizationalUnit)",
390 scope
=ldb
.SCOPE_BASE
, attrs
=[])
392 self
.outf
.write('Unable to find ou "%s"\n' % ou_dn
)
394 samdb
.delete(full_ou_dn
, controls
)
395 except Exception as e
:
396 raise CommandError('Failed to delete ou "%s"' % full_ou_dn
, e
)
398 self
.outf
.write('Deleted ou "%s"\n' % full_ou_dn
)
401 class cmd_ou(SuperCommand
):
402 """Organizational Units (OU) management."""
405 subcommands
["add"] = cmd_add()
406 subcommands
["create"] = cmd_add()
407 subcommands
["delete"] = cmd_delete()
408 subcommands
["move"] = cmd_move()
409 subcommands
["rename"] = cmd_rename()
410 subcommands
["list"] = cmd_list()
411 subcommands
["listobjects"] = cmd_listobjects()