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