Adam Tengler | b1ebaca | 2017-05-04 21:06:08 +0000 | [diff] [blame] | 1 | import io |
| 2 | import json |
| 3 | import logging |
Benjamin Drung | 5fe8041 | 2018-02-14 23:55:54 +0100 | [diff] [blame] | 4 | import sys |
Adam Tengler | b1ebaca | 2017-05-04 21:06:08 +0000 | [diff] [blame] | 5 | |
Petr Michalec | 073067a | 2017-10-27 15:37:46 +0200 | [diff] [blame] | 6 | LOG = logging.getLogger(__name__) |
Alexey Stupnikov | e124c3b | 2017-10-19 19:58:09 +0300 | [diff] [blame] | 7 | |
Kirill Bespalov | 4fe0d08 | 2017-06-29 19:02:13 +0300 | [diff] [blame] | 8 | import yaml |
Petr Michalec | 073067a | 2017-10-27 15:37:46 +0200 | [diff] [blame] | 9 | import yaml.constructor |
Kirill Bespalov | 4fe0d08 | 2017-06-29 19:02:13 +0300 | [diff] [blame] | 10 | |
Petr Michalec | 073067a | 2017-10-27 15:37:46 +0200 | [diff] [blame] | 11 | try: |
| 12 | # included in standard lib from Python 2.7 |
| 13 | from collections import OrderedDict |
| 14 | except ImportError: |
| 15 | # try importing the backported drop-in replacement |
| 16 | # it's available on PyPI |
| 17 | from ordereddict import OrderedDict |
| 18 | |
| 19 | # https://stackoverflow.com/questions/5121931/in-python-how-can-you-load-yaml-mappings-as-ordereddicts |
| 20 | class OrderedDictYAMLLoader(yaml.Loader): |
| 21 | """ |
| 22 | A YAML loader that loads mappings into ordered dictionaries. |
| 23 | """ |
| 24 | |
| 25 | def __init__(self, *args, **kwargs): |
| 26 | yaml.Loader.__init__(self, *args, **kwargs) |
| 27 | |
| 28 | self.add_constructor(u'tag:yaml.org,2002:map', type(self).construct_yaml_map) |
| 29 | self.add_constructor(u'tag:yaml.org,2002:omap', type(self).construct_yaml_map) |
| 30 | |
| 31 | def construct_yaml_map(self, node): |
| 32 | data = OrderedDict() |
| 33 | yield data |
| 34 | value = self.construct_mapping(node) |
| 35 | data.update(value) |
| 36 | |
| 37 | def construct_mapping(self, node, deep=False): |
| 38 | if isinstance(node, yaml.MappingNode): |
| 39 | self.flatten_mapping(node) |
| 40 | else: |
| 41 | raise yaml.constructor.ConstructorError(None, None, |
| 42 | 'expected a mapping node, but found %s' % node.id, node.start_mark) |
| 43 | |
| 44 | mapping = OrderedDict() |
| 45 | for key_node, value_node in node.value: |
| 46 | key = self.construct_object(key_node, deep=deep) |
| 47 | try: |
| 48 | hash(key) |
Benjamin Drung | 5fe8041 | 2018-02-14 23:55:54 +0100 | [diff] [blame] | 49 | except TypeError as exc: |
Petr Michalec | 073067a | 2017-10-27 15:37:46 +0200 | [diff] [blame] | 50 | raise yaml.constructor.ConstructorError('while constructing a mapping', |
| 51 | node.start_mark, 'found unacceptable key (%s)' % exc, key_node.start_mark) |
| 52 | value = self.construct_object(value_node, deep=deep) |
| 53 | mapping[key] = value |
| 54 | return mapping |
Adam Tengler | b1ebaca | 2017-05-04 21:06:08 +0000 | [diff] [blame] | 55 | |
| 56 | |
| 57 | def __virtual__(): |
| 58 | return True |
| 59 | |
| 60 | |
| 61 | def rule_list(path, **kwargs): |
| 62 | try: |
| 63 | with io.open(path, 'r') as file_handle: |
Petr Michalec | 073067a | 2017-10-27 15:37:46 +0200 | [diff] [blame] | 64 | rules = yaml.load(file_handle, OrderedDictYAMLLoader) or OrderedDict() |
Adam Tengler | b1ebaca | 2017-05-04 21:06:08 +0000 | [diff] [blame] | 65 | except Exception as e: |
Kirill Bespalov | 4fe0d08 | 2017-06-29 19:02:13 +0300 | [diff] [blame] | 66 | msg = "Unable to load policy file %s: %s" % (path, repr(e)) |
Adam Tengler | b1ebaca | 2017-05-04 21:06:08 +0000 | [diff] [blame] | 67 | LOG.debug(msg) |
| 68 | rules = {'Error': msg} |
| 69 | return rules |
| 70 | |
| 71 | |
| 72 | def rule_delete(name, path, **kwargs): |
| 73 | ret = {} |
| 74 | rules = __salt__['keystone_policy.rule_list'](path, **kwargs) |
| 75 | if 'Error' not in rules: |
| 76 | if name not in rules: |
| 77 | return ret |
| 78 | del rules[name] |
| 79 | try: |
| 80 | with io.open(path, 'w') as file_handle: |
Kirill Bespalov | 4fe0d08 | 2017-06-29 19:02:13 +0300 | [diff] [blame] | 81 | if path.endswith('json'): |
| 82 | serialized = json.dumps(rules, indent=4) |
| 83 | else: |
| 84 | serialized = yaml.safe_dump(rules, indent=4) |
Benjamin Drung | 5fe8041 | 2018-02-14 23:55:54 +0100 | [diff] [blame] | 85 | if sys.version_info[0] >= 3: |
| 86 | file_handle.write(serialized) |
| 87 | else: |
| 88 | file_handle.write(unicode(serialized)) |
Adam Tengler | b1ebaca | 2017-05-04 21:06:08 +0000 | [diff] [blame] | 89 | except Exception as e: |
Kirill Bespalov | 4fe0d08 | 2017-06-29 19:02:13 +0300 | [diff] [blame] | 90 | msg = "Unable to save policy file: %s" % repr(e) |
Adam Tengler | b1ebaca | 2017-05-04 21:06:08 +0000 | [diff] [blame] | 91 | LOG.error(msg) |
| 92 | return {'Error': msg} |
| 93 | ret = 'Rule {0} deleted'.format(name) |
| 94 | return ret |
| 95 | |
| 96 | |
| 97 | def rule_set(name, rule, path, **kwargs): |
| 98 | rules = __salt__['keystone_policy.rule_list'](path, **kwargs) |
| 99 | if 'Error' not in rules: |
| 100 | if name in rules and rules[name] == rule: |
| 101 | return {name: 'Rule %s already exists and is in correct state' % name} |
| 102 | rules.update({name: rule}) |
| 103 | try: |
| 104 | with io.open(path, 'w') as file_handle: |
Kirill Bespalov | 4fe0d08 | 2017-06-29 19:02:13 +0300 | [diff] [blame] | 105 | if path.endswith('json'): |
| 106 | serialized = json.dumps(rules, indent=4) |
| 107 | else: |
| 108 | serialized = yaml.safe_dump(rules, indent=4) |
Benjamin Drung | 5fe8041 | 2018-02-14 23:55:54 +0100 | [diff] [blame] | 109 | if sys.version_info[0] >= 3: |
| 110 | file_handle.write(serialized) |
| 111 | else: |
| 112 | file_handle.write(unicode(serialized)) |
Adam Tengler | b1ebaca | 2017-05-04 21:06:08 +0000 | [diff] [blame] | 113 | except Exception as e: |
Kirill Bespalov | 4fe0d08 | 2017-06-29 19:02:13 +0300 | [diff] [blame] | 114 | msg = "Unable to save policy file %s: %s" % (path, repr(e)) |
Adam Tengler | b1ebaca | 2017-05-04 21:06:08 +0000 | [diff] [blame] | 115 | LOG.error(msg) |
| 116 | return {'Error': msg} |
| 117 | return rule_get(name, path, **kwargs) |
| 118 | return rules |
| 119 | |
| 120 | |
| 121 | def rule_get(name, path, **kwargs): |
| 122 | ret = {} |
| 123 | rules = __salt__['keystone_policy.rule_list'](path, **kwargs) |
| 124 | if 'Error' in rules: |
| 125 | ret['Error'] = rules['Error'] |
| 126 | elif name in rules: |
| 127 | ret[name] = rules.get(name) |
| 128 | |
| 129 | return ret |
| 130 | |