From 46dea95c119e590c73047c20399b81a516375c20 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Sun, 21 Jul 2013 11:18:31 -0400 Subject: [PATCH] initial draft acl module Signed-off-by: Brian Coca --- files/acl | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 files/acl diff --git a/files/acl b/files/acl new file mode 100644 index 00000000000..6ba1047b7e2 --- /dev/null +++ b/files/acl @@ -0,0 +1,156 @@ +#!/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 +''' + +try: + import posix1e +except: + module.fail_json(msg="Could not import required module pylibacl (posix1e)") + +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, + ) + 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()