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_system_fsso_polling
27short_description: Configure Fortinet Single Sign On (FSSO) server 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 system feature and fsso_polling 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    system_fsso_polling:
68        description:
69            - Configure Fortinet Single Sign On (FSSO) server.
70        default: null
71        type: dict
72        suboptions:
73            auth_password:
74                description:
75                    - Password to connect to FSSO Agent.
76                type: str
77            authentication:
78                description:
79                    - Enable/disable FSSO Agent Authentication.
80                type: str
81                choices:
82                    - enable
83                    - disable
84            listening_port:
85                description:
86                    - Listening port to accept clients (1 - 65535).
87                type: int
88            status:
89                description:
90                    - Enable/disable FSSO Polling Mode.
91                type: str
92                choices:
93                    - enable
94                    - disable
95'''
96
97EXAMPLES = '''
98- hosts: fortigates
99  collections:
100    - fortinet.fortios
101  connection: httpapi
102  vars:
103   vdom: "root"
104   ansible_httpapi_use_ssl: yes
105   ansible_httpapi_validate_certs: no
106   ansible_httpapi_port: 443
107  tasks:
108  - name: Configure Fortinet Single Sign On (FSSO) server.
109    fortios_system_fsso_polling:
110      vdom:  "{{ vdom }}"
111      system_fsso_polling:
112        auth_password: "<your_own_value>"
113        authentication: "enable"
114        listening_port: "5"
115        status: "enable"
116
117'''
118
119RETURN = '''
120build:
121  description: Build number of the fortigate image
122  returned: always
123  type: str
124  sample: '1547'
125http_method:
126  description: Last method used to provision the content into FortiGate
127  returned: always
128  type: str
129  sample: 'PUT'
130http_status:
131  description: Last result given by FortiGate on last operation applied
132  returned: always
133  type: str
134  sample: "200"
135mkey:
136  description: Master key (id) used in the last call to FortiGate
137  returned: success
138  type: str
139  sample: "id"
140name:
141  description: Name of the table used to fulfill the request
142  returned: always
143  type: str
144  sample: "urlfilter"
145path:
146  description: Path of the table used to fulfill the request
147  returned: always
148  type: str
149  sample: "webfilter"
150revision:
151  description: Internal revision number
152  returned: always
153  type: str
154  sample: "17.0.2.10658"
155serial:
156  description: Serial number of the unit
157  returned: always
158  type: str
159  sample: "FGVMEVYYQT3AB5352"
160status:
161  description: Indication of the operation's result
162  returned: always
163  type: str
164  sample: "success"
165vdom:
166  description: Virtual domain used
167  returned: always
168  type: str
169  sample: "root"
170version:
171  description: Version of the FortiGate
172  returned: always
173  type: str
174  sample: "v5.6.3"
175
176'''
177from ansible.module_utils.basic import AnsibleModule
178from ansible.module_utils.connection import Connection
179from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler
180from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi
181from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec
182from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning
183from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG
184from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison
185from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize
186
187
188def filter_system_fsso_polling_data(json):
189    option_list = ['auth_password', 'authentication', 'listening_port',
190                   'status']
191    dictionary = {}
192
193    for attribute in option_list:
194        if attribute in json and json[attribute] is not None:
195            dictionary[attribute] = json[attribute]
196
197    return dictionary
198
199
200def underscore_to_hyphen(data):
201    if isinstance(data, list):
202        for i, elem in enumerate(data):
203            data[i] = underscore_to_hyphen(elem)
204    elif isinstance(data, dict):
205        new_data = {}
206        for k, v in data.items():
207            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
208        data = new_data
209
210    return data
211
212
213def system_fsso_polling(data, fos):
214    vdom = data['vdom']
215    system_fsso_polling_data = data['system_fsso_polling']
216    filtered_data = underscore_to_hyphen(filter_system_fsso_polling_data(system_fsso_polling_data))
217
218    return fos.set('system',
219                   'fsso-polling',
220                   data=filtered_data,
221                   vdom=vdom)
222
223
224def is_successful_status(status):
225    return status['status'] == "success" or \
226        status['http_method'] == "DELETE" and status['http_status'] == 404
227
228
229def fortios_system(data, fos):
230
231    if data['system_fsso_polling']:
232        resp = system_fsso_polling(data, fos)
233    else:
234        fos._module.fail_json(msg='missing task body: %s' % ('system_fsso_polling'))
235
236    return not is_successful_status(resp), \
237        resp['status'] == "success" and \
238        (resp['revision_changed'] if 'revision_changed' in resp else True), \
239        resp
240
241
242versioned_schema = {
243    "type": "dict",
244    "children": {
245        "status": {
246            "type": "string",
247            "options": [
248                {
249                    "value": "enable",
250                    "revisions": {
251                        "v6.0.0": True,
252                        "v7.0.0": True,
253                        "v6.0.5": True,
254                        "v6.4.4": True,
255                        "v6.4.0": True,
256                        "v6.4.1": True,
257                        "v6.2.0": True,
258                        "v6.2.3": True,
259                        "v6.2.5": True,
260                        "v6.2.7": True,
261                        "v6.0.11": True
262                    }
263                },
264                {
265                    "value": "disable",
266                    "revisions": {
267                        "v6.0.0": True,
268                        "v7.0.0": True,
269                        "v6.0.5": True,
270                        "v6.4.4": True,
271                        "v6.4.0": True,
272                        "v6.4.1": True,
273                        "v6.2.0": True,
274                        "v6.2.3": True,
275                        "v6.2.5": True,
276                        "v6.2.7": True,
277                        "v6.0.11": True
278                    }
279                }
280            ],
281            "revisions": {
282                "v6.0.0": True,
283                "v7.0.0": True,
284                "v6.0.5": True,
285                "v6.4.4": True,
286                "v6.4.0": True,
287                "v6.4.1": True,
288                "v6.2.0": True,
289                "v6.2.3": True,
290                "v6.2.5": True,
291                "v6.2.7": True,
292                "v6.0.11": True
293            }
294        },
295        "authentication": {
296            "type": "string",
297            "options": [
298                {
299                    "value": "enable",
300                    "revisions": {
301                        "v6.0.0": True,
302                        "v7.0.0": True,
303                        "v6.0.5": True,
304                        "v6.4.4": True,
305                        "v6.4.0": True,
306                        "v6.4.1": True,
307                        "v6.2.0": True,
308                        "v6.2.3": True,
309                        "v6.2.5": True,
310                        "v6.2.7": True,
311                        "v6.0.11": True
312                    }
313                },
314                {
315                    "value": "disable",
316                    "revisions": {
317                        "v6.0.0": True,
318                        "v7.0.0": True,
319                        "v6.0.5": True,
320                        "v6.4.4": True,
321                        "v6.4.0": True,
322                        "v6.4.1": True,
323                        "v6.2.0": True,
324                        "v6.2.3": True,
325                        "v6.2.5": True,
326                        "v6.2.7": True,
327                        "v6.0.11": True
328                    }
329                }
330            ],
331            "revisions": {
332                "v6.0.0": True,
333                "v7.0.0": True,
334                "v6.0.5": True,
335                "v6.4.4": True,
336                "v6.4.0": True,
337                "v6.4.1": True,
338                "v6.2.0": True,
339                "v6.2.3": True,
340                "v6.2.5": True,
341                "v6.2.7": True,
342                "v6.0.11": True
343            }
344        },
345        "auth_password": {
346            "type": "string",
347            "revisions": {
348                "v6.0.0": True,
349                "v7.0.0": True,
350                "v6.0.5": True,
351                "v6.4.4": True,
352                "v6.4.0": True,
353                "v6.4.1": True,
354                "v6.2.0": True,
355                "v6.2.3": True,
356                "v6.2.5": True,
357                "v6.2.7": True,
358                "v6.0.11": True
359            }
360        },
361        "listening_port": {
362            "type": "integer",
363            "revisions": {
364                "v6.0.0": True,
365                "v7.0.0": True,
366                "v6.0.5": True,
367                "v6.4.4": True,
368                "v6.4.0": True,
369                "v6.4.1": True,
370                "v6.2.0": True,
371                "v6.2.3": True,
372                "v6.2.5": True,
373                "v6.2.7": True,
374                "v6.0.11": True
375            }
376        }
377    },
378    "revisions": {
379        "v6.0.0": True,
380        "v7.0.0": True,
381        "v6.0.5": True,
382        "v6.4.4": True,
383        "v6.4.0": True,
384        "v6.4.1": True,
385        "v6.2.0": True,
386        "v6.2.3": True,
387        "v6.2.5": True,
388        "v6.2.7": True,
389        "v6.0.11": True
390    }
391}
392
393
394def main():
395    module_spec = schema_to_module_spec(versioned_schema)
396    mkeyname = None
397    fields = {
398        "access_token": {"required": False, "type": "str", "no_log": True},
399        "enable_log": {"required": False, "type": bool},
400        "vdom": {"required": False, "type": "str", "default": "root"},
401        "system_fsso_polling": {
402            "required": False, "type": "dict", "default": None,
403            "options": {
404            }
405        }
406    }
407    for attribute_name in module_spec['options']:
408        fields["system_fsso_polling"]['options'][attribute_name] = module_spec['options'][attribute_name]
409        if mkeyname and mkeyname == attribute_name:
410            fields["system_fsso_polling"]['options'][attribute_name]['required'] = True
411
412    check_legacy_fortiosapi()
413    module = AnsibleModule(argument_spec=fields,
414                           supports_check_mode=False)
415
416    versions_check_result = None
417    if module._socket_path:
418        connection = Connection(module._socket_path)
419        if 'access_token' in module.params:
420            connection.set_option('access_token', module.params['access_token'])
421
422        if 'enable_log' in module.params:
423            connection.set_option('enable_log', module.params['enable_log'])
424        else:
425            connection.set_option('enable_log', False)
426        fos = FortiOSHandler(connection, module, mkeyname)
427        versions_check_result = check_schema_versioning(fos, versioned_schema, "system_fsso_polling")
428
429        is_error, has_changed, result = fortios_system(module.params, fos)
430
431    else:
432        module.fail_json(**FAIL_SOCKET_MSG)
433
434    if versions_check_result and versions_check_result['matched'] is False:
435        module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv")
436
437    if not is_error:
438        if versions_check_result and versions_check_result['matched'] is False:
439            module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result)
440        else:
441            module.exit_json(changed=has_changed, meta=result)
442    else:
443        if versions_check_result and versions_check_result['matched'] is False:
444            module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result)
445        else:
446            module.fail_json(msg="Error in repo", meta=result)
447
448
449if __name__ == '__main__':
450    main()
451