1#!/usr/local/bin/python3.8 2from __future__ import (absolute_import, division, print_function) 3# Copyright 2019-2020 Fortinet, Inc. 4# 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. 9# 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. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <https://www.gnu.org/licenses/>. 17 18__metaclass__ = type 19 20ANSIBLE_METADATA = {'status': ['preview'], 21 'supported_by': 'community', 22 'metadata_version': '1.1'} 23 24DOCUMENTATION = ''' 25--- 26module: fortios_spamfilter_fortishield 27short_description: Configure FortiGuard - AntiSpam in Fortinet's FortiOS and FortiGate. 28description: 29 - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the 30 user to set and modify spamfilter feature and fortishield category. 31 Examples include all parameters and values need to be adjusted to datasources before usage. 32 Tested with FOS v6.0.0 33version_added: "2.10" 34author: 35 - Link Zheng (@chillancezen) 36 - Jie Xue (@JieX19) 37 - Hongbin Lu (@fgtdev-hblu) 38 - Frank Shen (@frankshen01) 39 - Miguel Angel Munoz (@mamunozgonzalez) 40 - Nicolas Thomas (@thomnico) 41notes: 42 - Legacy fortiosapi has been deprecated, httpapi is the preferred way to run playbooks 43 44requirements: 45 - ansible>=2.9.0 46options: 47 access_token: 48 description: 49 - Token-based authentication. 50 Generated from GUI of Fortigate. 51 type: str 52 required: false 53 enable_log: 54 description: 55 - Enable/Disable logging for task. 56 type: bool 57 required: false 58 default: false 59 vdom: 60 description: 61 - Virtual domain, among those defined previously. A vdom is a 62 virtual instance of the FortiGate that can be configured and 63 used as a different unit. 64 type: str 65 default: root 66 67 spamfilter_fortishield: 68 description: 69 - Configure FortiGuard - AntiSpam. 70 default: null 71 type: dict 72 suboptions: 73 spam_submit_force: 74 description: 75 - Enable/disable force insertion of a new mime entity for the submission text. 76 type: str 77 choices: 78 - enable 79 - disable 80 spam_submit_srv: 81 description: 82 - Hostname of the spam submission server. 83 type: str 84 spam_submit_txt2htm: 85 description: 86 - Enable/disable conversion of text email to HTML email. 87 type: str 88 choices: 89 - enable 90 - disable 91''' 92 93EXAMPLES = ''' 94- hosts: fortigates 95 collections: 96 - fortinet.fortios 97 connection: httpapi 98 vars: 99 vdom: "root" 100 ansible_httpapi_use_ssl: yes 101 ansible_httpapi_validate_certs: no 102 ansible_httpapi_port: 443 103 tasks: 104 - name: Configure FortiGuard - AntiSpam. 105 fortios_spamfilter_fortishield: 106 vdom: "{{ vdom }}" 107 spamfilter_fortishield: 108 spam_submit_force: "enable" 109 spam_submit_srv: "<your_own_value>" 110 spam_submit_txt2htm: "enable" 111 112''' 113 114RETURN = ''' 115build: 116 description: Build number of the fortigate image 117 returned: always 118 type: str 119 sample: '1547' 120http_method: 121 description: Last method used to provision the content into FortiGate 122 returned: always 123 type: str 124 sample: 'PUT' 125http_status: 126 description: Last result given by FortiGate on last operation applied 127 returned: always 128 type: str 129 sample: "200" 130mkey: 131 description: Master key (id) used in the last call to FortiGate 132 returned: success 133 type: str 134 sample: "id" 135name: 136 description: Name of the table used to fulfill the request 137 returned: always 138 type: str 139 sample: "urlfilter" 140path: 141 description: Path of the table used to fulfill the request 142 returned: always 143 type: str 144 sample: "webfilter" 145revision: 146 description: Internal revision number 147 returned: always 148 type: str 149 sample: "17.0.2.10658" 150serial: 151 description: Serial number of the unit 152 returned: always 153 type: str 154 sample: "FGVMEVYYQT3AB5352" 155status: 156 description: Indication of the operation's result 157 returned: always 158 type: str 159 sample: "success" 160vdom: 161 description: Virtual domain used 162 returned: always 163 type: str 164 sample: "root" 165version: 166 description: Version of the FortiGate 167 returned: always 168 type: str 169 sample: "v5.6.3" 170 171''' 172from ansible.module_utils.basic import AnsibleModule 173from ansible.module_utils.connection import Connection 174from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler 175from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi 176from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec 177from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning 178from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG 179from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison 180from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize 181 182 183def filter_spamfilter_fortishield_data(json): 184 option_list = ['spam_submit_force', 'spam_submit_srv', 'spam_submit_txt2htm'] 185 dictionary = {} 186 187 for attribute in option_list: 188 if attribute in json and json[attribute] is not None: 189 dictionary[attribute] = json[attribute] 190 191 return dictionary 192 193 194def underscore_to_hyphen(data): 195 if isinstance(data, list): 196 for i, elem in enumerate(data): 197 data[i] = underscore_to_hyphen(elem) 198 elif isinstance(data, dict): 199 new_data = {} 200 for k, v in data.items(): 201 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 202 data = new_data 203 204 return data 205 206 207def spamfilter_fortishield(data, fos): 208 vdom = data['vdom'] 209 spamfilter_fortishield_data = data['spamfilter_fortishield'] 210 filtered_data = underscore_to_hyphen(filter_spamfilter_fortishield_data(spamfilter_fortishield_data)) 211 212 return fos.set('spamfilter', 213 'fortishield', 214 data=filtered_data, 215 vdom=vdom) 216 217 218def is_successful_status(status): 219 return status['status'] == "success" or \ 220 status['http_method'] == "DELETE" and status['http_status'] == 404 221 222 223def fortios_spamfilter(data, fos): 224 225 if data['spamfilter_fortishield']: 226 resp = spamfilter_fortishield(data, fos) 227 else: 228 fos._module.fail_json(msg='missing task body: %s' % ('spamfilter_fortishield')) 229 230 return not is_successful_status(resp), \ 231 resp['status'] == "success" and \ 232 (resp['revision_changed'] if 'revision_changed' in resp else True), \ 233 resp 234 235 236versioned_schema = { 237 "type": "dict", 238 "children": { 239 "spam_submit_srv": { 240 "type": "string", 241 "revisions": { 242 "v6.0.11": True, 243 "v6.0.0": True, 244 "v6.0.5": True 245 } 246 }, 247 "spam_submit_txt2htm": { 248 "type": "string", 249 "options": [ 250 { 251 "value": "enable", 252 "revisions": { 253 "v6.0.11": True, 254 "v6.0.0": True, 255 "v6.0.5": True 256 } 257 }, 258 { 259 "value": "disable", 260 "revisions": { 261 "v6.0.11": True, 262 "v6.0.0": True, 263 "v6.0.5": True 264 } 265 } 266 ], 267 "revisions": { 268 "v6.0.11": True, 269 "v6.0.0": True, 270 "v6.0.5": True 271 } 272 }, 273 "spam_submit_force": { 274 "type": "string", 275 "options": [ 276 { 277 "value": "enable", 278 "revisions": { 279 "v6.0.11": True, 280 "v6.0.0": True, 281 "v6.0.5": True 282 } 283 }, 284 { 285 "value": "disable", 286 "revisions": { 287 "v6.0.11": True, 288 "v6.0.0": True, 289 "v6.0.5": True 290 } 291 } 292 ], 293 "revisions": { 294 "v6.0.11": True, 295 "v6.0.0": True, 296 "v6.0.5": True 297 } 298 } 299 }, 300 "revisions": { 301 "v6.0.11": True, 302 "v6.0.0": True, 303 "v6.0.5": True 304 } 305} 306 307 308def main(): 309 module_spec = schema_to_module_spec(versioned_schema) 310 mkeyname = None 311 fields = { 312 "access_token": {"required": False, "type": "str", "no_log": True}, 313 "enable_log": {"required": False, "type": bool}, 314 "vdom": {"required": False, "type": "str", "default": "root"}, 315 "spamfilter_fortishield": { 316 "required": False, "type": "dict", "default": None, 317 "options": { 318 } 319 } 320 } 321 for attribute_name in module_spec['options']: 322 fields["spamfilter_fortishield"]['options'][attribute_name] = module_spec['options'][attribute_name] 323 if mkeyname and mkeyname == attribute_name: 324 fields["spamfilter_fortishield"]['options'][attribute_name]['required'] = True 325 326 check_legacy_fortiosapi() 327 module = AnsibleModule(argument_spec=fields, 328 supports_check_mode=False) 329 330 versions_check_result = None 331 if module._socket_path: 332 connection = Connection(module._socket_path) 333 if 'access_token' in module.params: 334 connection.set_option('access_token', module.params['access_token']) 335 336 if 'enable_log' in module.params: 337 connection.set_option('enable_log', module.params['enable_log']) 338 else: 339 connection.set_option('enable_log', False) 340 fos = FortiOSHandler(connection, module, mkeyname) 341 versions_check_result = check_schema_versioning(fos, versioned_schema, "spamfilter_fortishield") 342 343 is_error, has_changed, result = fortios_spamfilter(module.params, fos) 344 345 else: 346 module.fail_json(**FAIL_SOCKET_MSG) 347 348 if versions_check_result and versions_check_result['matched'] is False: 349 module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv") 350 351 if not is_error: 352 if versions_check_result and versions_check_result['matched'] is False: 353 module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result) 354 else: 355 module.exit_json(changed=has_changed, meta=result) 356 else: 357 if versions_check_result and versions_check_result['matched'] is False: 358 module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result) 359 else: 360 module.fail_json(msg="Error in repo", meta=result) 361 362 363if __name__ == '__main__': 364 main() 365