Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ACL policies #480

Merged
merged 7 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions ansible/modules/hashivault/hashivault_acl_policy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ansible.module_utils.hashivault import hashivault_argspec
from ansible.module_utils.hashivault import hashivault_auth_client
from ansible.module_utils.hashivault import hashivault_init
from ansible.module_utils.hashivault import hashiwrapper

ANSIBLE_METADATA = ANSIBLE_METADATA = {'status': ['stableinterface'], 'supported_by': 'community', 'version': '1.1'}
DOCUMENTATION = '''
---
module: hashivault_acl_policy
version_added: "2.1.0"
short_description: Hashicorp Vault acl policy set module
description:
- Module to set an ACL policy in Hashicorp Vault.
options:
name:
description:
- policy name.
state:
type: str
choices: ["present", "absent"]
default: present
description:
- present or absent
rules:
description:
- policy rules.
rules_file:
description:
- name of local file to read for policy rules.
extends_documentation_fragment: hashivault
'''
EXAMPLES = '''
---
- hosts: localhost
tasks:
- hashivault_acl_policy:
name: my_policy
rules: '{{rules}}'
'''


def main():
argspec = hashivault_argspec()
argspec['name'] = dict(required=True, type='str')
argspec['rules'] = dict(required=False, type='str')
argspec['rules_file'] = dict(required=False, type='str')
argspec['state'] = dict(required=False, choices=['present', 'absent'], default='present')
mutually_exclusive = [['rules', 'rules_file']]
module = hashivault_init(argspec, mutually_exclusive=mutually_exclusive, supports_check_mode=True)
result = hashivault_acl_policy(module)
if result.get('failed'):
module.fail_json(**result)
else:
module.exit_json(**result)


@hashiwrapper
def hashivault_acl_policy(module):
params = module.params
client = hashivault_auth_client(params)
state = params.get('state')
name = params.get('name')
exists = False
changed = False
current_state = {}
desired_state = {}

# get current policies
current_policies = client.sys.list_acl_policies()
if isinstance(current_policies, dict):
current_policies = current_policies.get('data', current_policies).get('keys', current_policies)
if name in current_policies:
exists = True
current_state = client.sys.read_acl_policy(name)
current_state = current_state.get('data', current_state).get('policy', current_state)

# Define desired rules
rules_file = params.get('rules_file')
if rules_file:
try:
desired_state = open(rules_file, 'r').read()
except Exception as e:
return {'changed': False,
'failed': True,
'msg': 'Error opening rules file <%s>: %s' % (rules_file, str(e))}
else:
desired_state = params.get('rules')

# Check required actions
if state == 'present' and not exists:
changed = True
elif state == 'absent' and exists:
changed = True
elif state == 'present' and exists:
if current_state != desired_state:
changed = True

if changed and not module.check_mode:
# create or update
if state == 'present':
client.sys.create_or_update_acl_policy(name, desired_state)
# delete
elif state == 'absent':
client.sys.delete_acl_policy(name)

return {
"changed": changed,
"diff": {
"before": current_state,
"after": desired_state,
},
}


if __name__ == '__main__':
main()
59 changes: 59 additions & 0 deletions ansible/modules/hashivault/hashivault_acl_policy_get.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ansible.module_utils.hashivault import hashivault_argspec
from ansible.module_utils.hashivault import hashivault_auth_client
from ansible.module_utils.hashivault import hashivault_init
from ansible.module_utils.hashivault import hashiwrapper

ANSIBLE_METADATA = {'status': ['stableinterface'], 'supported_by': 'community', 'version': '1.1'}
DOCUMENTATION = '''
---
module: hashivault_acl_policy_get
version_added: "2.1.0"
short_description: Hashicorp Vault ACL policy get module
description:
- Module to get an ACL policy from Hashicorp Vault.
options:
name:
description:
- policy name.
extends_documentation_fragment: hashivault
'''
EXAMPLES = '''
---
- hosts: localhost
tasks:
- hashivault_acl_policy_get:
name: 'annie'
register: 'vault_acl_policy_get'
- debug: msg="User policy is {{vault_acl_policy_get.policy}}"
'''


def main():
argspec = hashivault_argspec()
argspec['name'] = dict(required=True, type='str')
module = hashivault_init(argspec)
result = hashivault_acl_policy_get(module.params)
if result.get('failed'):
module.fail_json(**result)
else:
module.exit_json(**result)


@hashiwrapper
def hashivault_acl_policy_get(params):
name = params.get('name')
client = hashivault_auth_client(params)
policy = client.sys.read_acl_policy(name)
policy = policy.get('data', policy).get('policy', policy)
if policy is None:
result = {"changed": False, "rc": 1, "failed": True}
result['msg'] = u"Policy \"%s\" does not exist." % name
return result
else:
return {'rules': policy}


if __name__ == '__main__':
main()
49 changes: 49 additions & 0 deletions ansible/modules/hashivault/hashivault_acl_policy_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ansible.module_utils.hashivault import hashivault_argspec
from ansible.module_utils.hashivault import hashivault_auth_client
from ansible.module_utils.hashivault import hashivault_init
from ansible.module_utils.hashivault import hashiwrapper

ANSIBLE_METADATA = {'status': ['stableinterface'], 'supported_by': 'community', 'version': '1.1'}
DOCUMENTATION = '''
---
module: hashivault_acl_policy_list
version_added: "2.1.0"
short_description: Hashicorp Vault ACL policy list module
description:
- Module to list ACL policies in Hashicorp Vault.
extends_documentation_fragment: hashivault
'''
EXAMPLES = '''
---
- hosts: localhost
tasks:
- hashivault_acl_policy_list:
register: 'vault_acl_policy_list'
- debug: msg="Policies are {{vault_acl_policy_list.policy}}"
'''


def main():
argspec = hashivault_argspec()
module = hashivault_init(argspec)
result = hashivault_acl_policy_list(module.params)
if result.get('failed'):
module.fail_json(**result)
else:
module.exit_json(**result)


@hashiwrapper
def hashivault_acl_policy_list(params):
client = hashivault_auth_client(params)
current_policies = client.sys.list_acl_policies()
if isinstance(current_policies, dict):
current_policies = current_policies.get('data', current_policies)
current_policies = current_policies.get('keys', current_policies)
return {'policies': current_policies}


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions functional/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ ansible-playbook -vvv test_consul_role.yml
# ansible-playbook -v test_azure_config.yml
# ansible-playbook -v test_azure_role.yml cannot run without true azure connectivity
ansible-playbook -v test_policy.yml
ansible-playbook -v test_acl_policy.yml
ansible-playbook -v test_status.yml
ansible-playbook -v test_not_there.yml
ansible-playbook -v test_ephemeral.yml
Expand Down
143 changes: 143 additions & 0 deletions functional/test_acl_policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
- hosts: localhost
gather_facts: no
vars:
namespace: 'terry'
rules: >
path "secret/{{namespace}}/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/{{namespace}}" {
capabilities = ["list"]
}
expected: "{{rules | regex_replace('\n', '')}}"
bobs_rules: >
path "secret/bob/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/bob" {
capabilities = ["list"]
}
bobs_expected: "{{bobs_rules | regex_replace('\n', '') | regex_replace(' ', '')}}"
tasks:
- hashivault_acl_policy:
name: '{{namespace}}'
state: absent
- hashivault_acl_policy:
name: bob
state: absent

- hashivault_acl_policy_list:
register: 'vault_acl_policy_list'
- assert: { that: "vault_acl_policy_list is not changed" }
- assert: { that: "vault_acl_policy_list.rc == 0" }

- name: Delete a policy that doesn't exist and check that doesn't change or fail
hashivault_acl_policy:
name: '{{namespace}}'
state: absent
register: 'vault_acl_policy_delete'
- assert: { that: "vault_acl_policy_delete is not changed" }
- assert: { that: "vault_acl_policy_delete.rc == 0" }

- name: Set new policy
hashivault_acl_policy:
name: "{{namespace}}"
rules: "{{rules}}"
register: 'vault_acl_policy'
- assert: { that: "vault_acl_policy is changed" }
- assert: { that: "vault_acl_policy.rc == 0" }

- name: Set policy again and check that it doesn't change
hashivault_acl_policy:
name: "{{namespace}}"
rules: "{{rules}}"
register: 'vault_acl_policy_twice'
- assert: { that: "vault_acl_policy_twice is not changed" }
- assert: { that: "vault_acl_policy_twice.rc == 0" }

- name: Get policy and make sure it set properly
hashivault_acl_policy_get:
name: '{{namespace}}'
register: 'vault_acl_policy_get'
- assert: { that: "vault_acl_policy_get is not changed" }
- set_fact:
actual: "{{vault_acl_policy_get.rules | regex_replace('\n', '')}}"
- assert: { that: "expected == actual" }
- assert: { that: "vault_acl_policy_get.rc == 0" }

- name: Make sure our new policy is in list
hashivault_acl_policy_list:
register: 'vault_acl_policy_list'
- assert: { that: "vault_acl_policy_list is not changed" }
- fail: msg="policy {{namespace}} not in list"
when: namespace not in vault_acl_policy_list.policies
- assert: { that: "vault_acl_policy_list.rc == 0" }

- name: Set new policy from file
hashivault_acl_policy:
name: "{{namespace}}"
rules_file: "templates/policy_rules.hcl"
register: 'vault_acl_policy'
- assert: { that: "vault_acl_policy is changed" }
- assert: { that: "vault_acl_policy.rc == 0" }

- name: Validate file policy
hashivault_acl_policy_get:
name: "{{namespace}}"
register: 'vault_acl_policy_get'
- set_fact:
actual: "{{vault_acl_policy_get.rules | regex_replace('\n', '') | regex_replace(' ', '')}}"
- assert: { that: "bobs_expected == actual" }

- name: Get rid of our new policy
hashivault_acl_policy:
name: '{{namespace}}'
state: absent
register: 'vault_acl_policy_delete'
- assert: { that: "vault_acl_policy_delete is changed" }
- assert: { that: "vault_acl_policy_delete.rc == 0" }

- name: Make sure our new policy is gone
hashivault_acl_policy_list:
register: 'vault_acl_policy_list'
- assert: { that: "vault_acl_policy_list is not changed" }
- fail: msg="policy {{namespace}} in list"
when: namespace in vault_acl_policy_list.policies
- assert: { that: "vault_acl_policy_list.rc == 0" }

- name: Get bogus policy
hashivault_acl_policy_get:
name: '{{namespace}}bogus'
register: 'vault_acl_policy_get'
failed_when: False
- assert: { that: "vault_acl_policy_get is not changed" }
- assert: { that: "vault_acl_policy_get.rc == 1" }
- assert: { that: "vault_acl_policy_get.failed == False" }
- assert: { that: "vault_acl_policy_get.msg == 'Policy \"terrybogus\" does not exist.'" }

- name: Set new policy from file
hashivault_acl_policy:
name: bob
rules_file: "templates/policy_rules.hcl"
register: 'vault_acl_policy'
- assert: { that: "vault_acl_policy is changed" }
- assert: { that: "vault_acl_policy.rc == 0" }

- name: Get new from file policy and make sure it set properly
hashivault_acl_policy_get:
name: bob
register: 'vault_acl_policy_get'
- assert: { that: "vault_acl_policy_get is not changed" }
- set_fact:
actual: "{{vault_acl_policy_get.rules | regex_replace('\n', '') | regex_replace(' ', '')}}"
- assert: { that: "bobs_expected == actual" }
- assert: { that: "vault_acl_policy_get.rc == 0" }

- name: Delete our new policy from file
hashivault_acl_policy:
name: bob
state: absent
register: 'vault_acl_policy_delete'
- assert: { that: "vault_acl_policy_delete is changed" }
- assert: { that: "vault_acl_policy_delete.rc == 0" }
Loading