Skip to content

Commit 0f20155

Browse files
shulutkovphlogistonjohn
authored andcommitted
sambacc: add organizational unit (OU) support for ad dc configuration
It is possible to specify a group and a user who belongs to a specific organizational group. See file examples/addc_ou.json. Signed-off-by: myback <[email protected]>
1 parent 6ab6b6e commit 0f20155

File tree

7 files changed

+305
-19
lines changed

7 files changed

+305
-19
lines changed

examples/addc_ou.json

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
{
2+
"samba-container-config": "v0",
3+
"configs": {
4+
"demo": {
5+
"instance_features": ["addc"],
6+
"domain_settings": "sink",
7+
"instance_name": "dc1"
8+
}
9+
},
10+
"domain_settings": {
11+
"sink": {
12+
"realm": "DOMAIN1.SINK.TEST",
13+
"short_domain": "DOMAIN1",
14+
"admin_password": "Passw0rd"
15+
}
16+
},
17+
"organizational_units": {
18+
"sink": [
19+
{"name": "employees"}
20+
]
21+
},
22+
"domain_groups": {
23+
"sink": [
24+
{"name": "supervisors"},
25+
{
26+
"name": "employees",
27+
"ou": "employees"
28+
},
29+
{"name": "characters"},
30+
{"name": "bulk"}
31+
]
32+
},
33+
"domain_users": {
34+
"sink": [
35+
{
36+
"name": "bwayne",
37+
"password": "1115Rose.",
38+
"given_name": "Bruce",
39+
"surname": "Wayne",
40+
"member_of": ["supervisors", "characters", "employees"],
41+
"ou": "employees"
42+
},
43+
{
44+
"name": "ckent",
45+
"password": "1115Rose.",
46+
"given_name": "Clark",
47+
"surname": "Kent",
48+
"member_of": ["characters", "employees"],
49+
"ou": "employees"
50+
},
51+
{
52+
"name": "bbanner",
53+
"password": "1115Rose.",
54+
"given_name": "Bruce",
55+
"surname": "Banner",
56+
"member_of": ["characters", "employees"],
57+
"ou": "employees"
58+
},
59+
{
60+
"name": "pparker",
61+
"password": "1115Rose.",
62+
"given_name": "Peter",
63+
"surname": "Parker",
64+
"member_of": ["characters", "employees"],
65+
"ou": "employees"
66+
},
67+
{
68+
"name": "user0",
69+
"password": "1115Rose.",
70+
"given_name": "George0",
71+
"surname": "Hue-Sir",
72+
"member_of": ["bulk"]
73+
},
74+
{
75+
"name": "user1",
76+
"password": "1115Rose.",
77+
"given_name": "George1",
78+
"surname": "Hue-Sir",
79+
"member_of": ["bulk"]
80+
},
81+
{
82+
"name": "user2",
83+
"password": "1115Rose.",
84+
"given_name": "George2",
85+
"surname": "Hue-Sir",
86+
"member_of": ["bulk"]
87+
},
88+
{
89+
"name": "user3",
90+
"password": "1115Rose.",
91+
"given_name": "George3",
92+
"surname": "Hue-Sir",
93+
"member_of": ["bulk"]
94+
},
95+
{
96+
"name": "user4",
97+
"password": "1115Rose.",
98+
"given_name": "George4",
99+
"surname": "Hue-Sir",
100+
"member_of": ["bulk"]
101+
},
102+
{
103+
"name": "user5",
104+
"password": "1115Rose.",
105+
"given_name": "George5",
106+
"surname": "Hue-Sir",
107+
"member_of": ["bulk"]
108+
},
109+
{
110+
"name": "user6",
111+
"password": "1115Rose.",
112+
"given_name": "George6",
113+
"surname": "Hue-Sir",
114+
"member_of": ["bulk"]
115+
},
116+
{
117+
"name": "user7",
118+
"password": "1115Rose.",
119+
"given_name": "George7",
120+
"surname": "Hue-Sir",
121+
"member_of": ["bulk"]
122+
},
123+
{
124+
"name": "user8",
125+
"password": "1115Rose.",
126+
"given_name": "George8",
127+
"surname": "Hue-Sir",
128+
"member_of": ["bulk"]
129+
},
130+
{
131+
"name": "user9",
132+
"password": "1115Rose.",
133+
"given_name": "George9",
134+
"surname": "Hue-Sir",
135+
"member_of": ["bulk"]
136+
}
137+
]
138+
}
139+
}

sambacc/addc.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,25 @@ def create_user(
7575
password: str,
7676
surname: typing.Optional[str],
7777
given_name: typing.Optional[str],
78+
ou: typing.Optional[str] = None,
7879
) -> None:
79-
cmd = _user_create_cmd(name, password, surname, given_name)
80+
cmd = _user_create_cmd(name, password, surname, given_name, ou)
8081
_logger.info("Creating user: %r", name)
8182
subprocess.check_call(cmd)
8283

8384

84-
def create_group(name: str) -> None:
85-
cmd = _group_add_cmd(name)
85+
def create_group(name: str, ou: typing.Optional[str] = None) -> None:
86+
cmd = _group_add_cmd(name, ou)
8687
_logger.info("Creating group: %r", name)
8788
subprocess.check_call(cmd)
8889

8990

91+
def create_ou(name: str) -> None:
92+
cmd = _ou_add_cmd(name)
93+
_logger.info("Creating organizational unit: %r", name)
94+
subprocess.check_call(cmd)
95+
96+
9097
def add_group_members(group_name: str, members: list[str]) -> None:
9198
cmd = _group_add_members_cmd(group_name, members)
9299
_logger.info("Adding group members: %r", cmd)
@@ -163,6 +170,7 @@ def _user_create_cmd(
163170
password: str,
164171
surname: typing.Optional[str],
165172
given_name: typing.Optional[str],
173+
ou: typing.Optional[str],
166174
) -> list[str]:
167175
cmd = samba_cmds.sambatool[
168176
"user",
@@ -174,15 +182,28 @@ def _user_create_cmd(
174182
cmd.append(f"--surname={surname}")
175183
if given_name:
176184
cmd.append(f"--given-name={given_name}")
185+
if ou:
186+
cmd.append(f"--userou=OU={ou}")
177187
return cmd
178188

179189

180-
def _group_add_cmd(name: str) -> list[str]:
190+
def _group_add_cmd(name: str, ou: typing.Optional[str]) -> list[str]:
181191
cmd = samba_cmds.sambatool[
182192
"group",
183193
"add",
184194
name,
185195
].argv()
196+
if ou:
197+
cmd.append(f"--groupou=OU={ou}")
198+
return cmd
199+
200+
201+
def _ou_add_cmd(name: str) -> list[str]:
202+
cmd = samba_cmds.sambatool[
203+
"ou",
204+
"add",
205+
f"OU={name}",
206+
].argv()
186207
return cmd
187208

188209

sambacc/commands/addc.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,19 @@ def _prep_populate(ctx: Context) -> None:
155155
return
156156
_logger.info("Populating domain with default entries")
157157

158+
for ou in ctx.instance_config.organizational_units():
159+
addc.create_ou(ou.ou_name)
160+
158161
for dgroup in ctx.instance_config.domain_groups():
159-
addc.create_group(dgroup.groupname)
162+
addc.create_group(dgroup.groupname, dgroup.ou)
160163

161164
for duser in ctx.instance_config.domain_users():
162165
addc.create_user(
163166
name=duser.username,
164167
password=duser.plaintext_passwd,
165168
surname=duser.surname,
166169
given_name=duser.given_name,
170+
ou=duser.ou,
167171
)
168172
# TODO: probably should improve this to avoid extra calls / loops
169173
for gname in duser.member_of:

sambacc/config.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,16 @@ def domain_groups(self) -> typing.Iterable[DomainGroupEntry]:
337337
for n, entry in enumerate(dgroups):
338338
yield DomainGroupEntry(self, entry, n)
339339

340+
def organizational_units(self) -> typing.Iterable[OrganizationalUnitEntry]:
341+
if not self.with_addc:
342+
raise ValueError("ad dc not supported by configuration")
343+
ds_name: str = self.iconfig["domain_settings"]
344+
o_units = self.gconfig.data.get("organizational_units", {}).get(
345+
ds_name, []
346+
)
347+
for n, entry in enumerate(o_units):
348+
yield OrganizationalUnitEntry(self, entry, n)
349+
340350
def __eq__(self, other: typing.Any) -> bool:
341351
if isinstance(other, InstanceConfig) and self.iconfig == other.iconfig:
342352
self_shares = _shares_data(self.gconfig, self.iconfig)
@@ -476,6 +486,7 @@ def __init__(self, iconf: InstanceConfig, grec: dict, num: int):
476486
self.groupname = grec["name"]
477487
self.entry_num = num
478488
self._gid = grec.get("gid")
489+
self.ou = grec.get("ou")
479490
if self._gid is not None:
480491
if not isinstance(self._gid, int):
481492
raise ValueError("invalid gid value")
@@ -505,6 +516,7 @@ def __init__(self, iconf: InstanceConfig, urec: dict, num: int):
505516
self.surname = urec.get("surname")
506517
self.given_name = urec.get("given_name")
507518
self.member_of = urec.get("member_of", [])
519+
self.ou = urec.get("ou")
508520
if not isinstance(self.member_of, list):
509521
raise ValueError("member_of should contain a list of group names")
510522

@@ -513,6 +525,13 @@ class DomainGroupEntry(GroupEntry):
513525
pass
514526

515527

528+
class OrganizationalUnitEntry:
529+
def __init__(self, iconf: InstanceConfig, urec: dict, num: int):
530+
self.iconfig = iconf
531+
self.ou_name = urec["name"]
532+
self.entry_num = num
533+
534+
516535
class PermissionsConfig:
517536
_method_key: str = "method"
518537
_status_xattr_key: str = "status_xattr"

sambacc/schema/conf-v0.schema.json

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
}
4848
},
4949
"user_entry": {
50-
"description": "A user that will be instantiated in the local contianer environment to\nin order to provide access to smb shares.\n",
50+
"description": "A user that will be instantiated in the local container environment to\nin order to provide access to smb shares.\n",
5151
"type": "object",
5252
"properties": {
5353
"name": {
@@ -77,7 +77,7 @@
7777
"additionalProperties": false
7878
},
7979
"group_entry": {
80-
"description": "A group that will be instantiated in the local contianer environment to\nin order to provide access to smb shares.\n",
80+
"description": "A group that will be instantiated in the local container environment to\nin order to provide access to smb shares.\n",
8181
"type": "object",
8282
"properties": {
8383
"name": {
@@ -123,6 +123,10 @@
123123
"description": "A plain-text password",
124124
"type": "string"
125125
},
126+
"ou": {
127+
"description": "A organizational unit that the user should belong to",
128+
"type": "string"
129+
},
126130
"member_of": {
127131
"description": "A list of group names that the user should belong to",
128132
"type": "array",
@@ -146,6 +150,24 @@
146150
},
147151
"gid": {
148152
"type": "integer"
153+
},
154+
"ou": {
155+
"description": "A organizational unit that the user should belong to",
156+
"type": "string"
157+
}
158+
},
159+
"required": [
160+
"name"
161+
],
162+
"additionalProperties": false
163+
},
164+
"organizational_unit_entry": {
165+
"description": "A organizational unit that will be created in the specified AD domain. These\ngroups are populated in the directory after the domain is provisioned.\n",
166+
"type": "object",
167+
"properties": {
168+
"name": {
169+
"description": "The organizational unit name",
170+
"type": "string"
149171
}
150172
},
151173
"required": [
@@ -157,12 +179,12 @@
157179
"properties": {
158180
"samba-container-config": {
159181
"type": "string",
160-
"title": "Cofiguration Format Version",
182+
"title": "Configuration Format Version",
161183
"description": "A short version string that assists in allowing the configuration\nformat to (some day) support incompatible version changes.\n(It is unique to the configuration and is not the version of sambacc)\n"
162184
},
163185
"configs": {
164186
"title": "Container Configurations",
165-
"description": "A mapping of named configurations (instances) to top-level configuration\nblocks. A useable configuration file must have at least one configuration,\nbut more than one is supported.\n",
187+
"description": "A mapping of named configurations (instances) to top-level configuration\nblocks. A usable configuration file must have at least one configuration,\nbut more than one is supported.\n",
166188
"type": "object",
167189
"additionalProperties": {
168190
"type": "object",
@@ -208,7 +230,7 @@
208230
}
209231
},
210232
"globals": {
211-
"description": "A mapping of samba global configuation blocks. The global section names\nare not passed to Samba. All sections selected by a configuration are\nmerged together before passing to Samba.\n",
233+
"description": "A mapping of samba global configuration blocks. The global section names\nare not passed to Samba. All sections selected by a configuration are\nmerged together before passing to Samba.\n",
212234
"type": "object",
213235
"additionalProperties": {
214236
"type": "object",
@@ -243,7 +265,7 @@
243265
}
244266
},
245267
"users": {
246-
"description": "Users to add to the container environment in order to provide\nShare access-control wihout becoming a domain member server.\n",
268+
"description": "Users to add to the container environment in order to provide\nShare access-control without becoming a domain member server.\n",
247269
"type": "object",
248270
"properties": {
249271
"all_entries": {
@@ -255,7 +277,7 @@
255277
}
256278
},
257279
"groups": {
258-
"description": "Groups to add to the container environment in order to provide\nShare access-control wihout becoming a domain member server.\n",
280+
"description": "Groups to add to the container environment in order to provide\nShare access-control without becoming a domain member server.\n",
259281
"type": "object",
260282
"properties": {
261283
"all_entries": {
@@ -286,6 +308,16 @@
286308
}
287309
}
288310
},
311+
"organizational_units": {
312+
"description": "The organizational_unit section defines initial organizational unit that will be\nautomatically added to a newly provisioned domain. This section is\na mapping of the domain settings name to a list of domain group entries.\n",
313+
"type": "object",
314+
"additionalProperties": {
315+
"type": "array",
316+
"items": {
317+
"$ref": "#/$defs/organizational_unit_entry"
318+
}
319+
}
320+
},
289321
"ctdb": {
290322
"type": "object",
291323
"additionalProperties": {

0 commit comments

Comments
 (0)