#!/usr/bin/python # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . DOCUMENTATION = ''' --- module: acl version_added: "1.3" short_description: set and retrieve file acl description: - Sets and retrivies acl for a file options: name: required: true default: None description: - The full path of the file/object to get the facts of aliases: ['path'] entry: required: false default: None description: - The acl to set/remove. MUST always quote! In form of '::', qualifier may be empty for some types but type and perms are always requried. '-' can be used as placeholder when you don't care about permissions. state: required: false default: get choices: [ 'get', 'present', 'absent' ] description: - defines which operation you want to do. C(get) get the current acl C(present) sets/changes the acl, requires entry field C(absent) deletes the acl, requires entry field follow: required: false default: yes choices: [ 'yes', 'no' ] description: - if yes, dereferences symlinks and sets/gets attributes on symlink target, otherwise acts on symlink itself. author: Brian Coca notes: - The "acl" module requires that acl is enabled on the target filesystem and that the setfacl and getfacl binaries are installed. ''' EXAMPLES = ''' # Obtain the acl of /etc/foo.conf - acl: name=/etc/foo.conf # Grants joe read access to foo - acl: name=/etc/foo.conf entry="u:joe:r" state=present # Removes the acl for joe - acl: name=/etc/foo.conf entry="u:joe:-" state=absent ''' NO_PYLIBACL=False try: import posix1e except: NO_PYLIBACL=True def main(): module = AnsibleModule( argument_spec = dict( name = dict(required=True,aliases=['path']), entry = dict(required=False, default=None), state = dict(required=False, default='get', choices=[ 'get', 'present', 'absent' ], type='str'), follow = dict(required=False, type='bool', default=True), ), supports_check_mode=True, ) if NO_PYLIBACL: module.fail_json(msg="Could not import required module pylibacl (posix1e)") path = module.params.get('name') entry = module.params.get('entry') state = module.params.get('state') follow = module.params.get('follow') if not os.path.exists(path): module.fail_json(msg="path not found or not accessible!") if entry is None and state in ['present','absent']: module.fail_json(msg="%s needs entry to be set" % state) if entry.count(":") != 3: module.fail_json(msg="Invalid entry: '%s', it requires 3 sections divided by ':'" % entry) changed=False changes=0 msg = "" currentacl = posix1e.ACL(file=path) newacl = currentacl res = currentacl if (state == 'present'): for newe in posix1e.ACL(text=entry): matched = False for olde in currentacl: diff = False if olde.tag_type == newe.tag_type: if newe.tag_type in [ posix1e.ACL_GROUP, posix1e.ACL_USER ]: if olde.qualifier == newe.qualifier: matched = True if not str(olde.permset) == str(newe.permset): diff = True else: matched = True if not str(olde.permset) == str(newe.permset): diff = True if diff: newacl.delete_entry(olde) newacl.append(newe) changes=changes+1 if matched: break if not matched: newacl.append(newe) changes=changes+1 msg="%s is present" % (entry) elif state == 'absent': for rme in posix1e.ACL(text=entry): for olde in currentacl: if olde.tag_type == rme.tag_type: if rme.tag_type in [ posix1e.ACL_GROUP, posix1e.ACL_USER ]: if olde.qualifier == rme.qualifier: newacl.delete_entry(olde) changes=changes+1 break else: newacl.delete_entry(olde) changes=changes+1 break msg="%s is absent" % (entry) else: msg="current acl" if changes > 0: if not newacl.valid(): module.fail_json("Invalid acl constructed: %s" % newacl.to_any_text()) if not module.check_mode: newacl.applyto(path) changed=True res=newacl msg="%s. %d entries changed" % (msg,changes) module.exit_json(changed=changed, msg=msg, acl=res.to_any_text().split()) # this is magic, see lib/ansible/module_common.py #<> main()