Skip to content

Commit 35bf42e

Browse files
authored
Add External User Mappings modules (cloudera-labs#248)
Signed-off-by: rsuplina <[email protected]>
1 parent dd8a679 commit 35bf42e

File tree

4 files changed

+749
-0
lines changed

4 files changed

+749
-0
lines changed
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
# Copyright 2024 Cloudera, Inc. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from ansible_collections.cloudera.cluster.plugins.module_utils.cm_utils import (
16+
ClouderaManagerModule,
17+
)
18+
19+
from cm_client import (
20+
ExternalUserMappingsResourceApi,
21+
ApiExternalUserMapping,
22+
ApiAuthRoleRef,
23+
)
24+
25+
ANSIBLE_METADATA = {
26+
"metadata_version": "1.1",
27+
"status": ["preview"],
28+
"supported_by": "community",
29+
}
30+
31+
DOCUMENTATION = r"""
32+
---
33+
module: external_user_mappings
34+
short_description: Create, update, or delete external user mappings
35+
description:
36+
- Configure details of a specific external user mapping.
37+
- Create a new external user mapping.
38+
- Update an existing external user mapping.
39+
- Delete a external user mapping.
40+
- The module supports C(check_mode).
41+
author:
42+
- "Ronald Suplina (@rsuplina)"
43+
requirements:
44+
- cm_client
45+
options:
46+
name:
47+
description:
48+
- The name of the external mapping.
49+
type: str
50+
required: no
51+
uuid:
52+
description:
53+
- The uuid of the external mapping.
54+
type: str
55+
required: no
56+
type:
57+
description:
58+
- The type of the external mapping.
59+
type: str
60+
required: no
61+
auth_roles:
62+
description:
63+
- A list of authorization roles that the external user mapping will include.
64+
type: list
65+
required: no
66+
state:
67+
description:
68+
- Defines the desired state of the external user mapping.
69+
- If I(state=present), the external user mapping will be created if it doesn't exist or updated if it does.
70+
- If I(state=absent), the external user mapping will be modified by removing the specified authorization roles or entirely deleted if no specific roles are provided.
71+
type: str
72+
required: no
73+
default: present
74+
choices:
75+
- present
76+
- absent
77+
purge:
78+
description:
79+
- Flag for whether the declared authorization roles should append or overwrite any existing authorization roles.
80+
- If I(purge=True), all existing authorization roles will be removed, and only the provided authorization roles will be set.
81+
- If I(purge=False), the provided authorization roles will be added to the existing ones, and any duplicates will be ignored.
82+
type: bool
83+
default: False
84+
attributes:
85+
check_mode:
86+
support: full
87+
diff_mode:
88+
support: full
89+
"""
90+
91+
EXAMPLES = r"""
92+
---
93+
- name: Create external user mapping with admin permissions
94+
cloudera.cluster.external_user_mappings:
95+
host: example.cloudera.com
96+
username: "jane_smith"
97+
password: "S&peR4Ec*re"
98+
name: "admin_user"
99+
state: "present"
100+
type: "LDAP"
101+
auth_roles: ["ROLE_CLUSTER_ADMIN"]
102+
103+
- name: Add additional permissions to external user mapping
104+
cloudera.cluster.external_user_mappings:
105+
host: example.cloudera.com
106+
username: "jane_smith"
107+
password: "S&peR4Ec*re"
108+
name: "basic_user"
109+
state: "present"
110+
type: "LDAP"
111+
auth_roles: ["ROLE_DASHBOARD_USER","ROLE_USER","ROLE_CLUSTER_CREATOR"]
112+
113+
- name: Replace current permissions in external user mapping
114+
cloudera.cluster.external_user_mappings:
115+
host: example.cloudera.com
116+
username: "jane_smith"
117+
password: "S&peR4Ec*re"
118+
name: "basic_user"
119+
state: "present"
120+
purge: "True"
121+
type: "LDAP"
122+
auth_roles: ["ROLE_DASHBOARD_USER","ROLE_USER"]
123+
124+
- name: Remove specified authorization roles from external user mapping
125+
cloudera.cluster.external_user_mappings:
126+
host: example.cloudera.com
127+
username: "jane_smith"
128+
password: "S&peR4Ec*re"
129+
name: "default_user"
130+
state: "absent"
131+
type: "LDAP"
132+
auth_roles: ["ROLE_DASHBOARD_USER","ROLE_USER"]
133+
134+
- name: Remove external user mapping
135+
cloudera.cluster.external_user_mappings:
136+
host: example.cloudera.com
137+
username: "jane_smith"
138+
password: "S&peR4Ec*re"
139+
name: "default_user"
140+
state: "absent"
141+
type: "LDAP"
142+
143+
- name: Remove all authorizing roles from external user mapping
144+
cloudera.cluster.external_user_mappings:
145+
host: example.cloudera.com
146+
username: "jane_smith"
147+
password: "S&peR4Ec*re"
148+
name: "basic_user"
149+
purge: True
150+
auth_roles: []
151+
"""
152+
153+
RETURN = r"""
154+
---
155+
external_user_mappings:
156+
description:
157+
- A dictionary containing details of external user mapping.
158+
type: dict
159+
elements: complex
160+
returned: always
161+
contains:
162+
name:
163+
description:
164+
- The name of the external mapping.
165+
type: str
166+
returned: always
167+
type:
168+
description:
169+
- The type of the external mapping.
170+
type: str
171+
returned: always
172+
uuid:
173+
description:
174+
- The UUID of the external mapping.
175+
type: str
176+
returned: always
177+
auth_roles:
178+
description:
179+
- The list of auth roles associated with external user mapping.
180+
type: list
181+
returned: always
182+
"""
183+
184+
185+
class ClouderaExternalUserMappingsInfo(ClouderaManagerModule):
186+
def __init__(self, module):
187+
super(ClouderaExternalUserMappingsInfo, self).__init__(module)
188+
189+
# Set the parameters
190+
self.name = self.get_param("name")
191+
self.uuid = self.get_param("uuid")
192+
self.state = self.get_param("state")
193+
self.type = self.get_param("type")
194+
self.purge = self.get_param("purge")
195+
self.auth_roles = self.get_param("auth_roles")
196+
197+
# Initialize the return value
198+
self.external_user_mappings_output = []
199+
self.changed = False
200+
self.diff = {}
201+
202+
# Execute the logic
203+
self.process()
204+
205+
@ClouderaManagerModule.handle_process
206+
def process(self):
207+
api_instance = ExternalUserMappingsResourceApi(self.api_client)
208+
existing = []
209+
210+
if self.name:
211+
all_external_user_mappings = api_instance.read_external_user_mappings()
212+
for mapping in all_external_user_mappings.items:
213+
if self.name == mapping.name:
214+
existing = api_instance.read_external_user_mapping(
215+
uuid=mapping.uuid
216+
).to_dict()
217+
break
218+
if self.uuid:
219+
existing = api_instance.read_external_user_mapping(uuid=self.uuid).to_dict()
220+
if self.state == "present":
221+
if existing:
222+
existing_auth_roles = [
223+
auth_role["name"] for auth_role in existing["auth_roles"]
224+
]
225+
incoming_auth_roles = set(self.auth_roles)
226+
if existing_auth_roles != incoming_auth_roles:
227+
if self.module._diff:
228+
self.diff.update(
229+
before=list(existing_auth_roles - incoming_auth_roles),
230+
after=list(incoming_auth_roles - existing_auth_roles),
231+
)
232+
if self.purge:
233+
target_auth_roles = incoming_auth_roles
234+
else:
235+
existing_auth_roles = set(existing_auth_roles)
236+
new_auth_roles = incoming_auth_roles - existing_auth_roles
237+
target_auth_roles = existing_auth_roles.union(new_auth_roles)
238+
239+
auth_roles = [
240+
ApiAuthRoleRef(name=role) for role in target_auth_roles
241+
]
242+
update_existing_auth_roles = ApiExternalUserMapping(
243+
name=self.name,
244+
uuid=mapping.uuid,
245+
type=self.type,
246+
auth_roles=auth_roles,
247+
)
248+
if not self.module.check_mode:
249+
self.external_user_mappings_output = (
250+
api_instance.update_external_user_mapping(
251+
uuid=mapping.uuid, body=update_existing_auth_roles
252+
)
253+
).to_dict()
254+
self.changed = True
255+
else:
256+
auth_roles = [ApiAuthRoleRef(name=role) for role in self.auth_roles]
257+
external_user_mappings_body = ApiExternalUserMapping(
258+
name=self.name,
259+
uuid=mapping.uuid,
260+
type=self.type,
261+
auth_roles=auth_roles,
262+
)
263+
264+
if not self.module.check_mode:
265+
self.external_user_mappings_output = (
266+
api_instance.create_external_user_mappings(
267+
body={"items": [external_user_mappings_body]}
268+
)
269+
).to_dict()["items"]
270+
self.changed = True
271+
272+
if self.state == "absent":
273+
if existing:
274+
if self.auth_roles:
275+
existing_auth_roles = set(
276+
auth_role["name"] for auth_role in existing["auth_roles"]
277+
)
278+
incoming_auth_roles = set(self.auth_roles)
279+
280+
roles_to_delete = existing_auth_roles.intersection(
281+
incoming_auth_roles
282+
)
283+
if self.module._diff:
284+
self.diff.update(
285+
before=list(roles_to_delete),
286+
after=[],
287+
)
288+
if roles_to_delete:
289+
remaining_roles = existing_auth_roles - roles_to_delete
290+
auth_roles = [
291+
ApiAuthRoleRef(name=role) for role in remaining_roles
292+
]
293+
update_existing_auth_roles = ApiExternalUserMapping(
294+
name=self.name,
295+
uuid=mapping.uuid,
296+
type=self.type,
297+
auth_roles=auth_roles,
298+
)
299+
if not self.module.check_mode:
300+
self.external_user_mappings_output = (
301+
api_instance.update_external_user_mapping(
302+
uuid=mapping.uuid, body=update_existing_auth_roles
303+
)
304+
).to_dict()
305+
self.changed = True
306+
else:
307+
if not self.module.check_mode:
308+
self.external_user_mappings_output = (
309+
api_instance.delete_external_user_mapping(uuid=mapping.uuid)
310+
).to_dict()
311+
self.changed = True
312+
313+
314+
def main():
315+
module = ClouderaManagerModule.ansible_module(
316+
argument_spec=dict(
317+
name=dict(required=False, type="str"),
318+
uuid=dict(required=False, type="str"),
319+
type=dict(required=False, type="str"),
320+
purge=dict(required=False, type="bool", default=False),
321+
auth_roles=dict(required=False, type="list"),
322+
state=dict(
323+
type="str",
324+
default="present",
325+
choices=["present", "absent"],
326+
),
327+
),
328+
supports_check_mode=True,
329+
required_one_of=[
330+
("name", "uuid"),
331+
],
332+
mutually_exclusive=[
333+
("name", "uuid"),
334+
],
335+
)
336+
337+
result = ClouderaExternalUserMappingsInfo(module)
338+
339+
output = dict(
340+
changed=result.changed,
341+
external_user_mappings_output=result.external_user_mappings_output,
342+
)
343+
if module._diff:
344+
output.update(diff=result.diff)
345+
346+
if result.debug:
347+
log = result.log_capture.getvalue()
348+
output.update(debug=log, debug_lines=log.split("\n"))
349+
350+
module.exit_json(**output)
351+
352+
353+
if __name__ == "__main__":
354+
main()

0 commit comments

Comments
 (0)