Skip to content

Commit 6294b7a

Browse files
committed
feat: add sudo module
1 parent b1660c0 commit 6294b7a

File tree

1 file changed

+243
-0
lines changed

1 file changed

+243
-0
lines changed

plugins/modules/pfsense_sudo.py

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright: (c) 2018, Frederic Bor <frederic.bor@wanadoo.fr>
5+
# Copyright: (c) 2021, Jan Wenzel <jan.wenzel@gonicus.de>
6+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
7+
8+
from __future__ import absolute_import, division, print_function
9+
__metaclass__ = type
10+
11+
from pprint import pformat
12+
13+
ANSIBLE_METADATA = {'metadata_version': '1.1',
14+
'status': ['preview'],
15+
'supported_by': 'community'}
16+
17+
DOCUMENTATION = """
18+
---
19+
module: pfsense_sudo
20+
version_added: "0.4.2"
21+
author: Jan Wenzel (@coffeelover)
22+
short_description: Manage sudo settings
23+
description:
24+
- Manage pfSense sudo settings
25+
notes:
26+
options:
27+
config:
28+
description: Setup each sudo rule
29+
required: true
30+
type: list
31+
elements: dict
32+
suboptions:
33+
username:
34+
description: User or group name (prefix user with user: and group with group:)
35+
required: True
36+
type: str
37+
runas:
38+
description: Run As
39+
required: False
40+
type: str
41+
default: user:root
42+
nopasswd:
43+
description: Require password
44+
required: False
45+
type: bool
46+
default: False
47+
cmdlist:
48+
description: List of allowed commands (full paths required)
49+
required: False
50+
type: list
51+
default: ['ALL']
52+
elements: str
53+
add_includedir:
54+
description: Include additional custom configuration files from /usr/local/etc/sudoers.d
55+
required: False
56+
type: str
57+
choices: none, include_start, include_end
58+
"""
59+
60+
EXAMPLES = """
61+
"""
62+
63+
RETURN = """
64+
"""
65+
66+
from copy import deepcopy
67+
from ansible.module_utils.basic import AnsibleModule
68+
from ansible_collections.pfsensible.core.plugins.module_utils.module_base import PFSenseModuleBase
69+
70+
SUDO_CONFIG_ARGUMENT_SPEC = dict(
71+
username=dict(required=True, type='str'),
72+
runas=dict(required=False, type='str', default='user:root'),
73+
nopasswd=dict(required=False, type='bool', default=False),
74+
cmdlist=dict(required=False, type='list', elements='str', default=['ALL']),
75+
)
76+
77+
SUDO_ARGUMENT_SPEC = dict(
78+
row=dict(required=False, type='list', elements='dict', options=SUDO_CONFIG_ARGUMENT_SPEC),
79+
add_includedir=dict(required=False, type='str', choices=[
80+
'none',
81+
'include_start',
82+
'include_end',
83+
], default='none'),
84+
)
85+
86+
class PFSenseSudoModule(PFSenseModuleBase):
87+
""" module managing pfsense sudo settings """
88+
89+
@staticmethod
90+
def get_argument_spec():
91+
""" return argument spec """
92+
return SUDO_ARGUMENT_SPEC
93+
94+
##############################
95+
# init
96+
#
97+
def __init__(self, module, pfsense=None):
98+
super(PFSenseSudoModule, self).__init__(module, pfsense)
99+
self.name = "sudo"
100+
pkgs_elt = self.pfsense.get_element('installedpackages')
101+
sudo_elt = self.pfsense.get_element('sudo', pkgs_elt, create_node=True)
102+
self.root_elt = self.pfsense.get_element('config', sudo_elt, create_node=True)
103+
self.target_elt = self.root_elt
104+
self.params = dict()
105+
self.obj = dict()
106+
self.before = None
107+
self.before_elt = None
108+
self.params_to_delete = list()
109+
110+
##############################
111+
# params processing
112+
#
113+
def _params_to_obj(self):
114+
""" return a dict from module params """
115+
params = self.params
116+
117+
obj = self.pfsense.element_to_dict(self.root_elt)
118+
self.before = deepcopy(obj)
119+
self.before_elt = deepcopy(self.root_elt)
120+
121+
def _set_param_list(target, param):
122+
if params.get(param) is not None:
123+
if param == 'row':
124+
rows = []
125+
for entry in params.get(param):
126+
row = dict()
127+
for subparam in SUDO_CONFIG_ARGUMENT_SPEC:
128+
if entry.get(subparam) is not None:
129+
value = entry.get(subparam)
130+
if SUDO_CONFIG_ARGUMENT_SPEC[subparam]['type'] == 'bool':
131+
if value is True:
132+
row[subparam] = 'ON'
133+
elif SUDO_CONFIG_ARGUMENT_SPEC[subparam]['type'] == 'list':
134+
if subparam == 'cmdlist':
135+
row[subparam] = ','.join(value)
136+
else:
137+
if isinstance(value, str):
138+
row[subparam] = value
139+
else:
140+
row[subparam] = str(value)
141+
rows.append(row)
142+
143+
target[param] = rows
144+
145+
def _set_param(target, param):
146+
if params.get(param) is not None:
147+
if isinstance(params[param], str):
148+
target[param] = params[param]
149+
else:
150+
target[param] = str(params[param])
151+
152+
def _set_param_bool(target, param):
153+
if params.get(param) is not None:
154+
value = params.get(param)
155+
if value is True and param not in target:
156+
target[param] = ''
157+
elif value is False and param in target:
158+
del target[param]
159+
160+
for param in SUDO_ARGUMENT_SPEC:
161+
if SUDO_ARGUMENT_SPEC[param]['type'] == 'list':
162+
_set_param_list(obj, param)
163+
elif SUDO_ARGUMENT_SPEC[param]['type'] == 'bool':
164+
_set_param_bool(obj, param)
165+
else:
166+
_set_param(obj, param)
167+
168+
# self.module.fail_json(isinstance(obj['row'], list))
169+
170+
return obj
171+
172+
def _validate_params(self):
173+
""" do some extra checks on input parameters """
174+
pass
175+
176+
##############################
177+
# XML processing
178+
#
179+
def _remove_deleted_params(self):
180+
""" Remove from target_elt a few deleted params """
181+
changed = False
182+
for param in SUDO_ARGUMENT_SPEC:
183+
if SUDO_ARGUMENT_SPEC[param]['type'] == 'bool':
184+
if self.pfsense.remove_deleted_param_from_elt(self.target_elt, param, self.obj):
185+
changed = True
186+
187+
return changed
188+
189+
##############################
190+
# run
191+
#
192+
def run(self, params):
193+
""" process input params to add/update/delete """
194+
self.params = params
195+
self.target_elt = self.root_elt
196+
self._validate_params()
197+
self.obj = self._params_to_obj()
198+
self._add()
199+
200+
def _update(self):
201+
""" make the target pfsense reload """
202+
cmd = '''
203+
require_once("sudo.inc");
204+
$retval = 0;
205+
$retval |= sudo_write_config();
206+
'''
207+
return self.pfsense.phpshell(cmd)
208+
209+
##############################
210+
# Logging
211+
#
212+
@staticmethod
213+
def _get_obj_name():
214+
""" return obj's name """
215+
return "sudo"
216+
217+
def _log_fields(self, before=None):
218+
""" generate pseudo-CLI command fields parameters to create an obj """
219+
values = ''
220+
221+
for param in SUDO_ARGUMENT_SPEC:
222+
if SUDO_ARGUMENT_SPEC[param]['type'] == 'bool':
223+
values += self.format_updated_cli_field(self.obj, self.before, param, fvalue=self.fvalue_bool, add_comma=(values), log_none=False)
224+
elif SUDO_ARGUMENT_SPEC[param]['type'] == 'list':
225+
pass
226+
else:
227+
values += self.format_updated_cli_field(self.obj, self.before, param, add_comma=(values), log_none=False)
228+
229+
return values
230+
231+
232+
def main():
233+
module = AnsibleModule(
234+
argument_spec=SUDO_ARGUMENT_SPEC,
235+
supports_check_mode=True)
236+
237+
pfmodule = PFSenseSudoModule(module)
238+
pfmodule.run(module.params)
239+
pfmodule.commit_changes()
240+
241+
242+
if __name__ == '__main__':
243+
main()

0 commit comments

Comments
 (0)