summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/general/plugins/modules/consul_auth_method.py
blob: e28474c313dd83695a6e51c15be6660d227acbb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2024, Florian Apolloner (@apollo13)
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = """
module: consul_auth_method
short_description: Manipulate Consul auth methods
version_added: 8.3.0
description:
 - Allows the addition, modification and deletion of auth methods in a consul
   cluster via the agent. For more details on using and configuring ACLs,
   see U(https://www.consul.io/docs/guides/acl.html).
author:
  - Florian Apolloner (@apollo13)
extends_documentation_fragment:
  - community.general.consul
  - community.general.consul.actiongroup_consul
  - community.general.consul.token
  - community.general.attributes
attributes:
  check_mode:
    support: full
  diff_mode:
    support: partial
    details:
      - In check mode the diff will miss operational attributes.
options:
  state:
    description:
      - Whether the token should be present or absent.
    choices: ['present', 'absent']
    default: present
    type: str
  name:
    description:
      - Specifies a name for the ACL auth method.
      - The name can contain alphanumeric characters, dashes C(-), and underscores C(_).
    type: str
    required: true
  type:
    description:
      - The type of auth method being configured.
      - This field is immutable.
      - Required when the auth method is created.
    type: str
    choices: ['kubernetes', 'jwt', 'oidc', 'aws-iam']
  description:
    description:
      - Free form human readable description of the auth method.
    type: str
  display_name:
    description:
      - An optional name to use instead of O(name) when displaying information about this auth method.
    type: str
  max_token_ttl:
    description:
      - This specifies the maximum life of any token created by this auth method.
      - Can be specified in the form of V(60s) or V(5m) (that is, 60 seconds or 5 minutes, respectively).
    type: str
  token_locality:
    description:
      - Defines the kind of token that this auth method should produce.
    type: str
    choices: ['local', 'global']
  config:
    description:
      - The raw configuration to use for the chosen auth method.
      - Contents will vary depending upon the type chosen.
      - Required when the auth method is created.
    type: dict
"""

EXAMPLES = """
- name: Create an auth method
  community.general.consul_auth_method:
    name: test
    type: jwt
    config:
      jwt_validation_pubkeys:
        - |
          -----BEGIN PUBLIC KEY-----
          MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
          4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
          +qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
          kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
          0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
          cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
          mwIDAQAB
          -----END PUBLIC KEY-----
    token: "{{ consul_management_token }}"

- name: Delete auth method
  community.general.consul_auth_method:
    name: test
    state: absent
    token: "{{ consul_management_token }}"
"""

RETURN = """
auth_method:
  description: The auth method as returned by the consul HTTP API.
  returned: always
  type: dict
  sample:
    Config:
      JWTValidationPubkeys:
        - |-
          -----BEGIN PUBLIC KEY-----
          MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
          4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
          +qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
          kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
          0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
          cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
          mwIDAQAB
          -----END PUBLIC KEY-----
    CreateIndex: 416
    ModifyIndex: 487
    Name: test
    Type: jwt
operation:
    description: The operation performed.
    returned: changed
    type: str
    sample: update
"""

import re

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.consul import (
    AUTH_ARGUMENTS_SPEC,
    _ConsulModule,
    camel_case_key,
)


def normalize_ttl(ttl):
    matches = re.findall(r"(\d+)(:h|m|s)", ttl)
    ttl = 0
    for value, unit in matches:
        value = int(value)
        if unit == "m":
            value *= 60
        elif unit == "h":
            value *= 60 * 60
        ttl += value

    new_ttl = ""
    hours, remainder = divmod(ttl, 3600)
    if hours:
        new_ttl += "{0}h".format(hours)
    minutes, seconds = divmod(remainder, 60)
    if minutes:
        new_ttl += "{0}m".format(minutes)
    if seconds:
        new_ttl += "{0}s".format(seconds)
    return new_ttl


class ConsulAuthMethodModule(_ConsulModule):
    api_endpoint = "acl/auth-method"
    result_key = "auth_method"
    unique_identifiers = ["name"]

    def map_param(self, k, v, is_update):
        if k == "config" and v:
            v = {camel_case_key(k2): v2 for k2, v2 in v.items()}
        return super(ConsulAuthMethodModule, self).map_param(k, v, is_update)

    def needs_update(self, api_obj, module_obj):
        if "MaxTokenTTL" in module_obj:
            module_obj["MaxTokenTTL"] = normalize_ttl(module_obj["MaxTokenTTL"])
        return super(ConsulAuthMethodModule, self).needs_update(api_obj, module_obj)


_ARGUMENT_SPEC = {
    "name": dict(type="str", required=True),
    "type": dict(type="str", choices=["kubernetes", "jwt", "oidc", "aws-iam"]),
    "description": dict(type="str"),
    "display_name": dict(type="str"),
    "max_token_ttl": dict(type="str", no_log=False),
    "token_locality": dict(type="str", choices=["local", "global"]),
    "config": dict(type="dict"),
    "state": dict(default="present", choices=["present", "absent"]),
}
_ARGUMENT_SPEC.update(AUTH_ARGUMENTS_SPEC)


def main():
    module = AnsibleModule(
        _ARGUMENT_SPEC,
        supports_check_mode=True,
    )
    consul_module = ConsulAuthMethodModule(module)
    consul_module.execute()


if __name__ == "__main__":
    main()