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_spamfilter_profile
27short_description: Configure AntiSpam profiles 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 profile 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    spamfilter_profile:
88        description:
89            - Configure AntiSpam profiles.
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                    - Comment.
107                type: str
108            external:
109                description:
110                    - Enable/disable external Email inspection.
111                type: str
112                choices:
113                    - enable
114                    - disable
115            flow_based:
116                description:
117                    - Enable/disable flow-based spam filtering.
118                type: str
119                choices:
120                    - enable
121                    - disable
122            gmail:
123                description:
124                    - Gmail.
125                type: dict
126                suboptions:
127                    log:
128                        description:
129                            - Enable/disable logging.
130                        type: str
131                        choices:
132                            - enable
133                            - disable
134            imap:
135                description:
136                    - IMAP.
137                type: dict
138                suboptions:
139                    action:
140                        description:
141                            - Action for spam email.
142                        type: str
143                        choices:
144                            - pass
145                            - tag
146                    log:
147                        description:
148                            - Enable/disable logging.
149                        type: str
150                        choices:
151                            - enable
152                            - disable
153                    tag_msg:
154                        description:
155                            - Subject text or header added to spam email.
156                        type: str
157                    tag_type:
158                        description:
159                            - Tag subject or header for spam email.
160                        type: list
161                        choices:
162                            - subject
163                            - header
164                            - spaminfo
165            mapi:
166                description:
167                    - MAPI.
168                type: dict
169                suboptions:
170                    action:
171                        description:
172                            - Action for spam email.
173                        type: str
174                        choices:
175                            - pass
176                            - discard
177                    log:
178                        description:
179                            - Enable/disable logging.
180                        type: str
181                        choices:
182                            - enable
183                            - disable
184            msn_hotmail:
185                description:
186                    - MSN Hotmail.
187                type: dict
188                suboptions:
189                    log:
190                        description:
191                            - Enable/disable logging.
192                        type: str
193                        choices:
194                            - enable
195                            - disable
196            name:
197                description:
198                    - Profile name.
199                required: true
200                type: str
201            options:
202                description:
203                    - Options.
204                type: list
205                choices:
206                    - bannedword
207                    - spambwl
208                    - spamfsip
209                    - spamfssubmit
210                    - spamfschksum
211                    - spamfsurl
212                    - spamhelodns
213                    - spamraddrdns
214                    - spamrbl
215                    - spamhdrcheck
216                    - spamfsphish
217            pop3:
218                description:
219                    - POP3.
220                type: dict
221                suboptions:
222                    action:
223                        description:
224                            - Action for spam email.
225                        type: str
226                        choices:
227                            - pass
228                            - tag
229                    log:
230                        description:
231                            - Enable/disable logging.
232                        type: str
233                        choices:
234                            - enable
235                            - disable
236                    tag_msg:
237                        description:
238                            - Subject text or header added to spam email.
239                        type: str
240                    tag_type:
241                        description:
242                            - Tag subject or header for spam email.
243                        type: list
244                        choices:
245                            - subject
246                            - header
247                            - spaminfo
248            replacemsg_group:
249                description:
250                    - Replacement message group. Source system.replacemsg-group.name.
251                type: str
252            smtp:
253                description:
254                    - SMTP.
255                type: dict
256                suboptions:
257                    action:
258                        description:
259                            - Action for spam email.
260                        type: str
261                        choices:
262                            - pass
263                            - tag
264                            - discard
265                    hdrip:
266                        description:
267                            - Enable/disable SMTP email header IP checks for spamfsip, spamrbl and spambwl filters.
268                        type: str
269                        choices:
270                            - disable
271                            - enable
272                    local_override:
273                        description:
274                            - Enable/disable local filter to override SMTP remote check result.
275                        type: str
276                        choices:
277                            - disable
278                            - enable
279                    log:
280                        description:
281                            - Enable/disable logging.
282                        type: str
283                        choices:
284                            - enable
285                            - disable
286                    tag_msg:
287                        description:
288                            - Subject text or header added to spam email.
289                        type: str
290                    tag_type:
291                        description:
292                            - Tag subject or header for spam email.
293                        type: list
294                        choices:
295                            - subject
296                            - header
297                            - spaminfo
298            spam_bwl_table:
299                description:
300                    - Anti-spam black/white list table ID. Source spamfilter.bwl.id.
301                type: int
302            spam_bword_table:
303                description:
304                    - Anti-spam banned word table ID. Source spamfilter.bword.id.
305                type: int
306            spam_bword_threshold:
307                description:
308                    - Spam banned word threshold.
309                type: int
310            spam_filtering:
311                description:
312                    - Enable/disable spam filtering.
313                type: str
314                choices:
315                    - enable
316                    - disable
317            spam_iptrust_table:
318                description:
319                    - Anti-spam IP trust table ID. Source spamfilter.iptrust.id.
320                type: int
321            spam_log:
322                description:
323                    - Enable/disable spam logging for email filtering.
324                type: str
325                choices:
326                    - disable
327                    - enable
328            spam_log_fortiguard_response:
329                description:
330                    - Enable/disable logging FortiGuard spam response.
331                type: str
332                choices:
333                    - disable
334                    - enable
335            spam_mheader_table:
336                description:
337                    - Anti-spam MIME header table ID. Source spamfilter.mheader.id.
338                type: int
339            spam_rbl_table:
340                description:
341                    - Anti-spam DNSBL table ID. Source spamfilter.dnsbl.id.
342                type: int
343            yahoo_mail:
344                description:
345                    - Yahoo! Mail.
346                type: dict
347                suboptions:
348                    log:
349                        description:
350                            - Enable/disable logging.
351                        type: str
352                        choices:
353                            - enable
354                            - disable
355'''
356
357EXAMPLES = '''
358- hosts: localhost
359  vars:
360   host: "192.168.122.40"
361   username: "admin"
362   password: ""
363   vdom: "root"
364   ssl_verify: "False"
365  tasks:
366  - name: Configure AntiSpam profiles.
367    fortios_spamfilter_profile:
368      host:  "{{ host }}"
369      username: "{{ username }}"
370      password: "{{ password }}"
371      vdom:  "{{ vdom }}"
372      https: "False"
373      state: "present"
374      spamfilter_profile:
375        comment: "Comment."
376        external: "enable"
377        flow_based: "enable"
378        gmail:
379            log: "enable"
380        imap:
381            action: "pass"
382            log: "enable"
383            tag_msg: "<your_own_value>"
384            tag_type: "subject"
385        mapi:
386            action: "pass"
387            log: "enable"
388        msn_hotmail:
389            log: "enable"
390        name: "default_name_18"
391        options: "bannedword"
392        pop3:
393            action: "pass"
394            log: "enable"
395            tag_msg: "<your_own_value>"
396            tag_type: "subject"
397        replacemsg_group: "<your_own_value> (source system.replacemsg-group.name)"
398        smtp:
399            action: "pass"
400            hdrip: "disable"
401            local_override: "disable"
402            log: "enable"
403            tag_msg: "<your_own_value>"
404            tag_type: "subject"
405        spam_bwl_table: "33 (source spamfilter.bwl.id)"
406        spam_bword_table: "34 (source spamfilter.bword.id)"
407        spam_bword_threshold: "35"
408        spam_filtering: "enable"
409        spam_iptrust_table: "37 (source spamfilter.iptrust.id)"
410        spam_log: "disable"
411        spam_log_fortiguard_response: "disable"
412        spam_mheader_table: "40 (source spamfilter.mheader.id)"
413        spam_rbl_table: "41 (source spamfilter.dnsbl.id)"
414        yahoo_mail:
415            log: "enable"
416'''
417
418RETURN = '''
419build:
420  description: Build number of the fortigate image
421  returned: always
422  type: str
423  sample: '1547'
424http_method:
425  description: Last method used to provision the content into FortiGate
426  returned: always
427  type: str
428  sample: 'PUT'
429http_status:
430  description: Last result given by FortiGate on last operation applied
431  returned: always
432  type: str
433  sample: "200"
434mkey:
435  description: Master key (id) used in the last call to FortiGate
436  returned: success
437  type: str
438  sample: "id"
439name:
440  description: Name of the table used to fulfill the request
441  returned: always
442  type: str
443  sample: "urlfilter"
444path:
445  description: Path of the table used to fulfill the request
446  returned: always
447  type: str
448  sample: "webfilter"
449revision:
450  description: Internal revision number
451  returned: always
452  type: str
453  sample: "17.0.2.10658"
454serial:
455  description: Serial number of the unit
456  returned: always
457  type: str
458  sample: "FGVMEVYYQT3AB5352"
459status:
460  description: Indication of the operation's result
461  returned: always
462  type: str
463  sample: "success"
464vdom:
465  description: Virtual domain used
466  returned: always
467  type: str
468  sample: "root"
469version:
470  description: Version of the FortiGate
471  returned: always
472  type: str
473  sample: "v5.6.3"
474
475'''
476
477from ansible.module_utils.basic import AnsibleModule
478from ansible.module_utils.connection import Connection
479from ansible.module_utils.network.fortios.fortios import FortiOSHandler
480from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG
481
482
483def login(data, fos):
484    host = data['host']
485    username = data['username']
486    password = data['password']
487    ssl_verify = data['ssl_verify']
488
489    fos.debug('on')
490    if 'https' in data and not data['https']:
491        fos.https('off')
492    else:
493        fos.https('on')
494
495    fos.login(host, username, password, verify=ssl_verify)
496
497
498def filter_spamfilter_profile_data(json):
499    option_list = ['comment', 'external', 'flow_based',
500                   'gmail', 'imap', 'mapi',
501                   'msn_hotmail', 'name', 'options',
502                   'pop3', 'replacemsg_group', 'smtp',
503                   'spam_bwl_table', 'spam_bword_table', 'spam_bword_threshold',
504                   'spam_filtering', 'spam_iptrust_table', 'spam_log',
505                   'spam_log_fortiguard_response', 'spam_mheader_table', 'spam_rbl_table',
506                   'yahoo_mail']
507    dictionary = {}
508
509    for attribute in option_list:
510        if attribute in json and json[attribute] is not None:
511            dictionary[attribute] = json[attribute]
512
513    return dictionary
514
515
516def flatten_multilists_attributes(data):
517    multilist_attrs = [[u'options'], [u'imap', u'tag_type'], [u'pop3', u'tag_type'], [u'smtp', u'tag_type']]
518
519    for attr in multilist_attrs:
520        try:
521            path = "data['" + "']['".join(elem for elem in attr) + "']"
522            current_val = eval(path)
523            flattened_val = ' '.join(elem for elem in current_val)
524            exec(path + '= flattened_val')
525        except BaseException:
526            pass
527
528    return data
529
530
531def underscore_to_hyphen(data):
532    if isinstance(data, list):
533        for elem in data:
534            elem = underscore_to_hyphen(elem)
535    elif isinstance(data, dict):
536        new_data = {}
537        for k, v in data.items():
538            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
539        data = new_data
540
541    return data
542
543
544def spamfilter_profile(data, fos):
545    vdom = data['vdom']
546    if 'state' in data and data['state']:
547        state = data['state']
548    elif 'state' in data['spamfilter_profile'] and data['spamfilter_profile']:
549        state = data['spamfilter_profile']['state']
550    else:
551        state = True
552    spamfilter_profile_data = data['spamfilter_profile']
553    spamfilter_profile_data = flatten_multilists_attributes(spamfilter_profile_data)
554    filtered_data = underscore_to_hyphen(filter_spamfilter_profile_data(spamfilter_profile_data))
555
556    if state == "present":
557        return fos.set('spamfilter',
558                       'profile',
559                       data=filtered_data,
560                       vdom=vdom)
561
562    elif state == "absent":
563        return fos.delete('spamfilter',
564                          'profile',
565                          mkey=filtered_data['name'],
566                          vdom=vdom)
567
568
569def is_successful_status(status):
570    return status['status'] == "success" or \
571        status['http_method'] == "DELETE" and status['http_status'] == 404
572
573
574def fortios_spamfilter(data, fos):
575
576    if data['spamfilter_profile']:
577        resp = spamfilter_profile(data, fos)
578
579    return not is_successful_status(resp), \
580        resp['status'] == "success", \
581        resp
582
583
584def main():
585    fields = {
586        "host": {"required": False, "type": "str"},
587        "username": {"required": False, "type": "str"},
588        "password": {"required": False, "type": "str", "default": "", "no_log": True},
589        "vdom": {"required": False, "type": "str", "default": "root"},
590        "https": {"required": False, "type": "bool", "default": True},
591        "ssl_verify": {"required": False, "type": "bool", "default": True},
592        "state": {"required": False, "type": "str",
593                  "choices": ["present", "absent"]},
594        "spamfilter_profile": {
595            "required": False, "type": "dict", "default": None,
596            "options": {
597                "state": {"required": False, "type": "str",
598                          "choices": ["present", "absent"]},
599                "comment": {"required": False, "type": "str"},
600                "external": {"required": False, "type": "str",
601                             "choices": ["enable", "disable"]},
602                "flow_based": {"required": False, "type": "str",
603                               "choices": ["enable", "disable"]},
604                "gmail": {"required": False, "type": "dict",
605                          "options": {
606                              "log": {"required": False, "type": "str",
607                                      "choices": ["enable", "disable"]}
608                          }},
609                "imap": {"required": False, "type": "dict",
610                         "options": {
611                             "action": {"required": False, "type": "str",
612                                        "choices": ["pass", "tag"]},
613                             "log": {"required": False, "type": "str",
614                                     "choices": ["enable", "disable"]},
615                             "tag_msg": {"required": False, "type": "str"},
616                             "tag_type": {"required": False, "type": "list",
617                                          "choices": ["subject", "header", "spaminfo"]}
618                         }},
619                "mapi": {"required": False, "type": "dict",
620                         "options": {
621                             "action": {"required": False, "type": "str",
622                                        "choices": ["pass", "discard"]},
623                             "log": {"required": False, "type": "str",
624                                     "choices": ["enable", "disable"]}
625                         }},
626                "msn_hotmail": {"required": False, "type": "dict",
627                                "options": {
628                                    "log": {"required": False, "type": "str",
629                                            "choices": ["enable", "disable"]}
630                                }},
631                "name": {"required": True, "type": "str"},
632                "options": {"required": False, "type": "list",
633                            "choices": ["bannedword", "spambwl", "spamfsip",
634                                        "spamfssubmit", "spamfschksum", "spamfsurl",
635                                        "spamhelodns", "spamraddrdns", "spamrbl",
636                                        "spamhdrcheck", "spamfsphish"]},
637                "pop3": {"required": False, "type": "dict",
638                         "options": {
639                             "action": {"required": False, "type": "str",
640                                        "choices": ["pass", "tag"]},
641                             "log": {"required": False, "type": "str",
642                                     "choices": ["enable", "disable"]},
643                             "tag_msg": {"required": False, "type": "str"},
644                             "tag_type": {"required": False, "type": "list",
645                                          "choices": ["subject", "header", "spaminfo"]}
646                         }},
647                "replacemsg_group": {"required": False, "type": "str"},
648                "smtp": {"required": False, "type": "dict",
649                         "options": {
650                             "action": {"required": False, "type": "str",
651                                        "choices": ["pass", "tag", "discard"]},
652                             "hdrip": {"required": False, "type": "str",
653                                       "choices": ["disable", "enable"]},
654                             "local_override": {"required": False, "type": "str",
655                                                "choices": ["disable", "enable"]},
656                             "log": {"required": False, "type": "str",
657                                     "choices": ["enable", "disable"]},
658                             "tag_msg": {"required": False, "type": "str"},
659                             "tag_type": {"required": False, "type": "list",
660                                          "choices": ["subject", "header", "spaminfo"]}
661                         }},
662                "spam_bwl_table": {"required": False, "type": "int"},
663                "spam_bword_table": {"required": False, "type": "int"},
664                "spam_bword_threshold": {"required": False, "type": "int"},
665                "spam_filtering": {"required": False, "type": "str",
666                                   "choices": ["enable", "disable"]},
667                "spam_iptrust_table": {"required": False, "type": "int"},
668                "spam_log": {"required": False, "type": "str",
669                             "choices": ["disable", "enable"]},
670                "spam_log_fortiguard_response": {"required": False, "type": "str",
671                                                 "choices": ["disable", "enable"]},
672                "spam_mheader_table": {"required": False, "type": "int"},
673                "spam_rbl_table": {"required": False, "type": "int"},
674                "yahoo_mail": {"required": False, "type": "dict",
675                               "options": {
676                                   "log": {"required": False, "type": "str",
677                                           "choices": ["enable", "disable"]}
678                               }}
679
680            }
681        }
682    }
683
684    module = AnsibleModule(argument_spec=fields,
685                           supports_check_mode=False)
686
687    # legacy_mode refers to using fortiosapi instead of HTTPAPI
688    legacy_mode = 'host' in module.params and module.params['host'] is not None and \
689                  'username' in module.params and module.params['username'] is not None and \
690                  'password' in module.params and module.params['password'] is not None
691
692    if not legacy_mode:
693        if module._socket_path:
694            connection = Connection(module._socket_path)
695            fos = FortiOSHandler(connection)
696
697            is_error, has_changed, result = fortios_spamfilter(module.params, fos)
698        else:
699            module.fail_json(**FAIL_SOCKET_MSG)
700    else:
701        try:
702            from fortiosapi import FortiOSAPI
703        except ImportError:
704            module.fail_json(msg="fortiosapi module is required")
705
706        fos = FortiOSAPI()
707
708        login(module.params, fos)
709        is_error, has_changed, result = fortios_spamfilter(module.params, fos)
710        fos.logout()
711
712    if not is_error:
713        module.exit_json(changed=has_changed, meta=result)
714    else:
715        module.fail_json(msg="Error in repo", meta=result)
716
717
718if __name__ == '__main__':
719    main()
720