1#!/usr/bin/python 2from __future__ import (absolute_import, division, print_function) 3# Copyright 2019 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_webfilter_content 27short_description: Configure Web filter banned word table 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 webfilter feature and content category. 31 Examples include all parameters and values need to be adjusted to datasources before usage. 32 Tested with FOS v6.0.5 33version_added: "2.8" 34author: 35 - Miguel Angel Munoz (@mamunozgonzalez) 36 - Nicolas Thomas (@thomnico) 37notes: 38 - Requires fortiosapi library developed by Fortinet 39 - Run as a local_action in your playbook 40requirements: 41 - fortiosapi>=0.9.8 42options: 43 host: 44 description: 45 - FortiOS or FortiGate IP address. 46 type: str 47 required: false 48 username: 49 description: 50 - FortiOS or FortiGate username. 51 type: str 52 required: false 53 password: 54 description: 55 - FortiOS or FortiGate password. 56 type: str 57 default: "" 58 vdom: 59 description: 60 - Virtual domain, among those defined previously. A vdom is a 61 virtual instance of the FortiGate that can be configured and 62 used as a different unit. 63 type: str 64 default: root 65 https: 66 description: 67 - Indicates if the requests towards FortiGate must use HTTPS protocol. 68 type: bool 69 default: true 70 ssl_verify: 71 description: 72 - Ensures FortiGate certificate must be verified by a proper CA. 73 type: bool 74 default: true 75 version_added: 2.9 76 state: 77 description: 78 - Indicates whether to create or remove the object. 79 This attribute was present already in previous version in a deeper level. 80 It has been moved out to this outer level. 81 type: str 82 required: false 83 choices: 84 - present 85 - absent 86 version_added: 2.9 87 webfilter_content: 88 description: 89 - Configure Web filter banned word table. 90 default: null 91 type: dict 92 suboptions: 93 state: 94 description: 95 - B(Deprecated) 96 - Starting with Ansible 2.9 we recommend using the top-level 'state' parameter. 97 - HORIZONTALLINE 98 - Indicates whether to create or remove the object. 99 type: str 100 required: false 101 choices: 102 - present 103 - absent 104 comment: 105 description: 106 - Optional comments. 107 type: str 108 entries: 109 description: 110 - Configure banned word entries. 111 type: list 112 suboptions: 113 action: 114 description: 115 - Block or exempt word when a match is found. 116 type: str 117 choices: 118 - block 119 - exempt 120 lang: 121 description: 122 - Language of banned word. 123 type: str 124 choices: 125 - western 126 - simch 127 - trach 128 - japanese 129 - korean 130 - french 131 - thai 132 - spanish 133 - cyrillic 134 name: 135 description: 136 - Banned word. 137 required: true 138 type: str 139 pattern_type: 140 description: 141 - "Banned word pattern type: wildcard pattern or Perl regular expression." 142 type: str 143 choices: 144 - wildcard 145 - regexp 146 score: 147 description: 148 - Score, to be applied every time the word appears on a web page (0 - 4294967295). 149 type: int 150 status: 151 description: 152 - Enable/disable banned word. 153 type: str 154 choices: 155 - enable 156 - disable 157 id: 158 description: 159 - ID. 160 required: true 161 type: int 162 name: 163 description: 164 - Name of table. 165 type: str 166''' 167 168EXAMPLES = ''' 169- hosts: localhost 170 vars: 171 host: "192.168.122.40" 172 username: "admin" 173 password: "" 174 vdom: "root" 175 ssl_verify: "False" 176 tasks: 177 - name: Configure Web filter banned word table. 178 fortios_webfilter_content: 179 host: "{{ host }}" 180 username: "{{ username }}" 181 password: "{{ password }}" 182 vdom: "{{ vdom }}" 183 https: "False" 184 state: "present" 185 webfilter_content: 186 comment: "Optional comments." 187 entries: 188 - 189 action: "block" 190 lang: "western" 191 name: "default_name_7" 192 pattern_type: "wildcard" 193 score: "9" 194 status: "enable" 195 id: "11" 196 name: "default_name_12" 197''' 198 199RETURN = ''' 200build: 201 description: Build number of the fortigate image 202 returned: always 203 type: str 204 sample: '1547' 205http_method: 206 description: Last method used to provision the content into FortiGate 207 returned: always 208 type: str 209 sample: 'PUT' 210http_status: 211 description: Last result given by FortiGate on last operation applied 212 returned: always 213 type: str 214 sample: "200" 215mkey: 216 description: Master key (id) used in the last call to FortiGate 217 returned: success 218 type: str 219 sample: "id" 220name: 221 description: Name of the table used to fulfill the request 222 returned: always 223 type: str 224 sample: "urlfilter" 225path: 226 description: Path of the table used to fulfill the request 227 returned: always 228 type: str 229 sample: "webfilter" 230revision: 231 description: Internal revision number 232 returned: always 233 type: str 234 sample: "17.0.2.10658" 235serial: 236 description: Serial number of the unit 237 returned: always 238 type: str 239 sample: "FGVMEVYYQT3AB5352" 240status: 241 description: Indication of the operation's result 242 returned: always 243 type: str 244 sample: "success" 245vdom: 246 description: Virtual domain used 247 returned: always 248 type: str 249 sample: "root" 250version: 251 description: Version of the FortiGate 252 returned: always 253 type: str 254 sample: "v5.6.3" 255 256''' 257 258from ansible.module_utils.basic import AnsibleModule 259from ansible.module_utils.connection import Connection 260from ansible.module_utils.network.fortios.fortios import FortiOSHandler 261from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 262 263 264def login(data, fos): 265 host = data['host'] 266 username = data['username'] 267 password = data['password'] 268 ssl_verify = data['ssl_verify'] 269 270 fos.debug('on') 271 if 'https' in data and not data['https']: 272 fos.https('off') 273 else: 274 fos.https('on') 275 276 fos.login(host, username, password, verify=ssl_verify) 277 278 279def filter_webfilter_content_data(json): 280 option_list = ['comment', 'entries', 'id', 281 'name'] 282 dictionary = {} 283 284 for attribute in option_list: 285 if attribute in json and json[attribute] is not None: 286 dictionary[attribute] = json[attribute] 287 288 return dictionary 289 290 291def underscore_to_hyphen(data): 292 if isinstance(data, list): 293 for elem in data: 294 elem = underscore_to_hyphen(elem) 295 elif isinstance(data, dict): 296 new_data = {} 297 for k, v in data.items(): 298 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 299 data = new_data 300 301 return data 302 303 304def webfilter_content(data, fos): 305 vdom = data['vdom'] 306 if 'state' in data and data['state']: 307 state = data['state'] 308 elif 'state' in data['webfilter_content'] and data['webfilter_content']: 309 state = data['webfilter_content']['state'] 310 else: 311 state = True 312 webfilter_content_data = data['webfilter_content'] 313 filtered_data = underscore_to_hyphen(filter_webfilter_content_data(webfilter_content_data)) 314 315 if state == "present": 316 return fos.set('webfilter', 317 'content', 318 data=filtered_data, 319 vdom=vdom) 320 321 elif state == "absent": 322 return fos.delete('webfilter', 323 'content', 324 mkey=filtered_data['id'], 325 vdom=vdom) 326 327 328def is_successful_status(status): 329 return status['status'] == "success" or \ 330 status['http_method'] == "DELETE" and status['http_status'] == 404 331 332 333def fortios_webfilter(data, fos): 334 335 if data['webfilter_content']: 336 resp = webfilter_content(data, fos) 337 338 return not is_successful_status(resp), \ 339 resp['status'] == "success", \ 340 resp 341 342 343def main(): 344 fields = { 345 "host": {"required": False, "type": "str"}, 346 "username": {"required": False, "type": "str"}, 347 "password": {"required": False, "type": "str", "default": "", "no_log": True}, 348 "vdom": {"required": False, "type": "str", "default": "root"}, 349 "https": {"required": False, "type": "bool", "default": True}, 350 "ssl_verify": {"required": False, "type": "bool", "default": True}, 351 "state": {"required": False, "type": "str", 352 "choices": ["present", "absent"]}, 353 "webfilter_content": { 354 "required": False, "type": "dict", "default": None, 355 "options": { 356 "state": {"required": False, "type": "str", 357 "choices": ["present", "absent"]}, 358 "comment": {"required": False, "type": "str"}, 359 "entries": {"required": False, "type": "list", 360 "options": { 361 "action": {"required": False, "type": "str", 362 "choices": ["block", "exempt"]}, 363 "lang": {"required": False, "type": "str", 364 "choices": ["western", "simch", "trach", 365 "japanese", "korean", "french", 366 "thai", "spanish", "cyrillic"]}, 367 "name": {"required": True, "type": "str"}, 368 "pattern_type": {"required": False, "type": "str", 369 "choices": ["wildcard", "regexp"]}, 370 "score": {"required": False, "type": "int"}, 371 "status": {"required": False, "type": "str", 372 "choices": ["enable", "disable"]} 373 }}, 374 "id": {"required": True, "type": "int"}, 375 "name": {"required": False, "type": "str"} 376 377 } 378 } 379 } 380 381 module = AnsibleModule(argument_spec=fields, 382 supports_check_mode=False) 383 384 # legacy_mode refers to using fortiosapi instead of HTTPAPI 385 legacy_mode = 'host' in module.params and module.params['host'] is not None and \ 386 'username' in module.params and module.params['username'] is not None and \ 387 'password' in module.params and module.params['password'] is not None 388 389 if not legacy_mode: 390 if module._socket_path: 391 connection = Connection(module._socket_path) 392 fos = FortiOSHandler(connection) 393 394 is_error, has_changed, result = fortios_webfilter(module.params, fos) 395 else: 396 module.fail_json(**FAIL_SOCKET_MSG) 397 else: 398 try: 399 from fortiosapi import FortiOSAPI 400 except ImportError: 401 module.fail_json(msg="fortiosapi module is required") 402 403 fos = FortiOSAPI() 404 405 login(module.params, fos) 406 is_error, has_changed, result = fortios_webfilter(module.params, fos) 407 fos.logout() 408 409 if not is_error: 410 module.exit_json(changed=has_changed, meta=result) 411 else: 412 module.fail_json(msg="Error in repo", meta=result) 413 414 415if __name__ == '__main__': 416 main() 417