From ef5b735cd5a7887ebae965f5a4e197ecffe4c8db Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Tue, 10 Sep 2013 23:13:36 -0400 Subject: [PATCH] now w/o python module dependencies Signed-off-by: Brian Coca --- library/files/acl | 157 +++++++++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 64 deletions(-) diff --git a/library/files/acl b/library/files/acl index 57dbb838ae1..458a49dbd6f 100644 --- a/library/files/acl +++ b/library/files/acl @@ -35,10 +35,10 @@ options: - 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' ] + default: query + choices: [ 'query', '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 + - defines which operation you want to do. C(query) get the current acl C(present) sets/changes the acl, requires permissions field C(absent) deletes the acl, requires permissions field follow: required: false default: yes @@ -47,7 +47,10 @@ options: - if yes, dereferences symlinks and sets/gets attributes on symlink target, otherwise acts on symlink itself. author: Brian Coca notes: - - The "acl" module requires the posix1e module on the target machine and that acl is enabled on the target filesystem. + - The "acl" module requires that acl is enabled on the target filesystem and that the setfacl and getfacl binaries are installed. +author: Brian Coca +notes: + - The "acl" module requires the acl command line utilities be installed on the target machine and that acl is enabled on the target filesystem. ''' EXAMPLES = ''' @@ -55,37 +58,64 @@ EXAMPLES = ''' - acl: name=/etc/foo.conf # Grants joe read access to foo -- acl: name=/etc/foo.conf entry="u:joe:r" state=present +- acl: name=/etc/foo.conf entry="user:joe:r" state=present # Removes the acl for joe -- acl: name=/etc/foo.conf entry="u:joe:-" state=absent +- acl: name=/etc/foo.conf entry="user:joe:-" state=absent ''' -NO_PYLIBACL=False -try: - import posix1e -except: - NO_PYLIBACL=True -def gen_acl(module,entry): +def get_acl(module,path,entry,follow): + + cmd = [ module.get_bin_path('getfacl', True) ] + if not follow: + cmd.append('-h') + # prevents absolute path warnings and removes headers + cmd.append('-cp') + cmd.append(path) + + return _run_acl(module,cmd) + +def set_acl(module,path,entry,follow): + + cmd = [ module.get_bin_path('setfacl', True) ] + if not follow: + cmd.append('-h') + cmd.append('-m "%s"' % entry) + cmd.append(path) + + return _run_acl(module,cmd) + +def rm_acl(module,path,entry,follow): + + cmd = [ module.get_bin_path('setfacl', True) ] + if not follow: + cmd.append('-h') + entry = entry[0:entry.rfind(':')] + cmd.append('-x "%s"' % entry) + cmd.append(path) + + return _run_acl(module,cmd,False) + +def _run_acl(module,cmd,check_rc=True): + try: - return posix1e.ACL(text=entry) - except IOError, e: - module.fail_json(msg="Invalid entry: '%s', check that user/groups exist and permissions are correct" % entry) + (rc, out, err) = module.run_command(' '.join(cmd), check_rc=check_rc) + except Exception, e: + module.fail_json(msg=e.strerror) + + return out.splitlines() 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'), + state = dict(required=False, default='query', choices=[ 'query', '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') @@ -94,73 +124,72 @@ def main(): 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(":") != 2: - module.fail_json(msg="Invalid entry: '%s', it requires 3 sections divided by ':'" % entry) + if entry is None: + if state in ['present','absent']: + module.fail_json(msg="%s needs entry to be set" % state) + else: + if entry.count(":") != 2: + 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 + currentacl = get_acl(module,path,entry,follow) if (state == 'present'): - for newe in gen_acl(module, 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: + newe = entry.split(':') + matched = False + for oldentry in currentacl: + diff = False + olde = oldentry.split(':') + if olde[0] == newe[0]: + if newe[0] in ['user', 'group']: + if olde[1] == newe[1]: matched = True - if not str(olde.permset) == str(newe.permset): + if not olde[2] == newe[2]: diff = True - if diff: - newacl.delete_entry(olde) - newacl.append(newe) - changes=changes+1 - if matched: - break - if not matched: - newacl.append(newe) + else: + matched = True + if not olde[2] == newe[2]: + diff = True + if diff: changes=changes+1 + if not module.check_mode: + set_acl(module,path,entry,follow) + if matched: + break + if not matched: + changes=changes+1 + if not module.check_mode: + set_acl(module,path,entry,follow) msg="%s is present" % (entry) elif state == 'absent': - for rme in gen_acl(module, 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) + rme = entry.split(':') + for oldentry in currentacl: + olde = oldentry.split(':') + if olde[0] == rme[0]: + if rme[0] in ['user', 'group']: + if olde[1] == rme[1]: changes=changes+1 + if not module.check_mode: + rm_acl(module,path,entry,follow) break + else: + changes=changes+1 + if not module.check_mode: + rm_acl(module,path,entry,follow) + break msg="%s is absent" % (entry) else: msg="current acl" if changes > 0: - newacl.calc_mask() - if not newacl.valid(): - module.fail_json(msg="Invalid acl constructed: %s" % newacl.to_any_text()) - if not module.check_mode: - newacl.applyto(path) changed=True - res=newacl + currentacl = get_acl(module,path,entry,follow) msg="%s. %d entries changed" % (msg,changes) - module.exit_json(changed=changed, msg=msg, acl=res.to_any_text().split()) + module.exit_json(changed=changed, msg=msg, acl=currentacl) # this is magic, see lib/ansible/module_common.py #<>