#!/usr/bin/python2 # Authors: # Petr Viktorin # John Dennis # Martin Kosek # # Copyright (C) 2011 Red Hat # see file 'COPYING' for use and warranty information # # This program 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. # # This program 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 this program. If not, see . # Test the managed permission ACIs against a known-good ACI list # to ensure that changes aren't made lightly. # Can either regenerate ACI.txt, or validate against it. from __future__ import print_function import sys import difflib from argparse import ArgumentParser import ignore_import_errors # pylint: disable=unused-import from ipalib import api from ipapython.dn import DN from ipapython.ipaldap import LDAPClient def parse_options(): parser = ArgumentParser() parser.add_argument('--validate', dest='validate', action='store_true', default=False, help='Validate the API vs the stored API') parser.add_argument('filename', nargs='?', default='ACI.txt', help='File to create or validate, default: ACI.txt') options = parser.parse_args() return options def generate_aci_lines(api): """Yields ACI lines as they appear in ACI.txt, with trailing newline""" update_plugin = api.Updater['update_managed_permissions'] perm_plugin = api.Object['permission'] fake_ldap = LDAPClient('', force_schema_updates=False, no_schema=True) for name, template, obj in update_plugin.get_templates(): dn = perm_plugin.get_dn(name) entry = fake_ldap.make_entry(dn) update_plugin.update_entry( obj=obj, entry=entry, template=template, anonymous_read_aci=None, is_new=True, ) aci = perm_plugin.make_aci(entry) yield 'dn: %s\n' % entry.single_value['ipapermlocation'] yield 'aci: %s\n' % aci check_member_attrs(name, template) def check_member_attrs(name, template): """Check that member* attrs are always present together for read ldap2._process_memberofindirect reads all these attributes together; if the user doesn't have rights to one of them, the entire entry is left out and memberofindirect processing returns wrong a result. So we need all of them be readable. """ checked_attrs = ['member', 'memberuser', 'memberhost'] perm_attrs = template.get('ipapermdefaultattr', ()) flags = [(a in perm_attrs) for a in checked_attrs] if 'read' in template['ipapermright'] and any(flags) and not all(flags): raise AssertionError("'%s' includes some but not all of %s" % (name, checked_attrs)) def main(options): api.bootstrap( in_server=True, in_tree=True, debug=False, verbose=0, validate_api=True, enable_ra=True, mode='developer', plugins_on_demand=False, basedn=DN('dc=ipa,dc=example'), realm='IPA.EXAMPLE', domain="example.com", ) from ipaserver.install.plugins import update_managed_permissions api.add_module(update_managed_permissions) api.finalize() output_lines = list(generate_aci_lines(api)) if options.validate: with open(options.filename) as file: file_lines = file.readlines() if file_lines != output_lines: diff = list(difflib.unified_diff( file_lines, output_lines, fromfile='existing %s' % options.filename, tofile='new result', )) for line in diff: print(line, end=' ') print(file=sys.stderr) print('Managed permission ACI validation failed.', file=sys.stderr) print('Re-check permission changes and run `makeaci`.', file=sys.stderr) exit('%s validation failed' % options.filename) else: with open(options.filename, 'w') as file: file.writelines(output_lines) if __name__ == '__main__': options = parse_options() main(options)