Merge changes from topic '3939'
* changes:
Add support of Keystone OIDC Federation
Rename websso to federation
diff --git a/README.rst b/README.rst
index a7f7082..1e0e822 100644
--- a/README.rst
+++ b/README.rst
@@ -86,6 +86,17 @@
admin_address: 10.0.0.20
admin_port: 8774
+Keystone with custom policies. Keys with specified rules are created or set to this value if they already exists. Keys with no value (like our "existing_rule") are deleted from the policy file.
+
+.. code-block:: yaml
+
+ keystone:
+ server:
+ enabled: true
+ policy:
+ new_rule: "rule:admin_required"
+ existing_rule:
+
Keystone memcached storage for tokens
.. code-block:: yaml
diff --git a/_grains/keystone_policy.py b/_grains/keystone_policy.py
new file mode 100644
index 0000000..2155b12
--- /dev/null
+++ b/_grains/keystone_policy.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+import salt.config
+import salt.loader
+
+
+def main():
+ path = "/etc/keystone/policy.json"
+ __opts__ = salt.config.minion_config('/etc/salt/minion')
+ keystone_policy_mod = salt.loader.raw_mod(__opts__, 'keystone_policy', None)
+ if keystone_policy_mod:
+ result = keystone_policy_mod['keystone_policy.rule_list'](path)
+ if result and 'Error' not in result:
+ return {'keystone_policy': result}
+ return {}
+
diff --git a/_modules/keystone_policy.py b/_modules/keystone_policy.py
new file mode 100644
index 0000000..05b9215
--- /dev/null
+++ b/_modules/keystone_policy.py
@@ -0,0 +1,68 @@
+import io
+import json
+import logging
+
+LOG = logging.getLogger(__name__)
+
+
+def __virtual__():
+ return True
+
+
+def rule_list(path, **kwargs):
+ try:
+ with io.open(path, 'r') as file_handle:
+ rules = json.loads(file_handle.read())
+ rules = {str(k): str(v) for (k, v) in rules.items()}
+ except Exception as e:
+ msg = "Unable to load policy JSON %s: %s" % (path, repr(e))
+ LOG.debug(msg)
+ rules = {'Error': msg}
+ return rules
+
+
+def rule_delete(name, path, **kwargs):
+ ret = {}
+ rules = __salt__['keystone_policy.rule_list'](path, **kwargs)
+ if 'Error' not in rules:
+ if name not in rules:
+ return ret
+ del rules[name]
+ try:
+ with io.open(path, 'w') as file_handle:
+ file_handle.write(unicode(json.dumps(rules, indent=4)))
+ except Exception as e:
+ msg = "Unable to save policy json: %s" % repr(e)
+ LOG.error(msg)
+ return {'Error': msg}
+ ret = 'Rule {0} deleted'.format(name)
+ return ret
+
+
+def rule_set(name, rule, path, **kwargs):
+ rules = __salt__['keystone_policy.rule_list'](path, **kwargs)
+ if 'Error' not in rules:
+ if name in rules and rules[name] == rule:
+ return {name: 'Rule %s already exists and is in correct state' % name}
+ rules.update({name: rule})
+ try:
+ with io.open(path, 'w') as file_handle:
+ file_handle.write(unicode(json.dumps(rules, indent=4)))
+ except Exception as e:
+ msg = "Unable to save policy JSON %s: %s" % (path, repr(e))
+ LOG.error(msg)
+ return {'Error': msg}
+ return rule_get(name, path, **kwargs)
+ return rules
+
+
+def rule_get(name, path, **kwargs):
+ ret = {}
+ rules = __salt__['keystone_policy.rule_list'](path, **kwargs)
+ if 'Error' in rules:
+ ret['Error'] = rules['Error']
+ elif name in rules:
+ ret[name] = rules.get(name)
+
+ return ret
+
diff --git a/_states/keystone_policy.py b/_states/keystone_policy.py
index 2d34e06..e7a4a6a 100644
--- a/_states/keystone_policy.py
+++ b/_states/keystone_policy.py
@@ -8,46 +8,74 @@
.. code-block:: yaml
-/etc/keystone/policy.json:
- keystone_policy.present:
- - override_data:
- override_key: override_value
- - formatter: json
+ my_rule_present:
+ keystone_policy.rule_present:
+ - name: rule_name
+ - rule: rule
+ - path: /etc/keystone/policy.json
+
+ my_rule_absent:
+ keystone_policy.rule_absent:
+ - name: rule_name
+ - path: /etc/keystone/policy.json
'''
import logging
-import json
log = logging.getLogger(__name__)
-JSON_LOCATION = '/etc/keystone/policy.json'
+
+def __virtual__():
+ return True
-def _deep_merge(dct, merge_dct):
- for k, v in merge_dct.iteritems():
- if (k in dct and isinstance(dct[k], dict)):
- _deep_merge(dct[k], merge_dct[k])
- else:
- dct[k] = merge_dct[k]
-
-
-def present(name, override_data={}, **kwargs):
+def rule_present(name, rule, path, **kwargs):
'''
- Ensures that given key present in policy.json file. This is a wrapper
- around file.serialize state with additional argument: override_data.
- Rest parameters of file.serialize can be safely used as well.
- Function reads contents of existing policy.json file into a python
- dictionary. User defined data populated to this dictionary using deep
- merge procedure.
-
- :param name: Name of the resource
- :param override_data: User defined data with overrides
+ Ensures that the policy rule exists
+
+ :param name: Rule name
+ :param rule: Rule
+ :param path: Path to policy file
'''
- with open(JSON_LOCATION) as policy_json:
- json_content = json.load(policy_json)
-
- _deep_merge(json_content, override_data)
-
- kwargs['dataset'] = json_content
- ret = __states__['file.serialize']('/etc/keystone/policy.json', **kwargs)
+ rule = rule or ""
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'Rule "{0}" already exists and is in correct state'.format(name)}
+ rule_check = __salt__['keystone_policy.rule_get'](name, path, **kwargs)
+ if not rule_check:
+ __salt__['keystone_policy.rule_set'](name, rule, path, **kwargs)
+ ret['comment'] = 'Rule {0} has been created'.format(name)
+ ret['changes']['Rule'] = 'Rule %s: "%s" has been created' % (name, rule)
+ elif 'Error' in rule_check:
+ ret['comment'] = rule_check.get('Error')
+ ret['result'] = False
+ elif rule_check[name] != rule:
+ __salt__['keystone_policy.rule_set'](name, rule, path, **kwargs)
+ ret['comment'] = 'Rule %s has been changed' % (name,)
+ ret['changes']['Old Rule'] = '%s: "%s"' % (name, rule_check[name])
+ ret['changes']['New Rule'] = '%s: "%s"' % (name, rule)
return ret
+
+
+def rule_absent(name, path, **kwargs):
+ '''
+ Ensures that the policy rule does not exist
+
+ :param name: Rule name
+ :param path: Path to policy file
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'Rule "{0}" is already absent'.format(name)}
+ rule_check = __salt__['keystone_policy.rule_get'](name, path, **kwargs)
+ if rule_check:
+ __salt__['keystone_policy.rule_delete'](name, path, **kwargs)
+ ret['comment'] = 'Rule {0} has been deleted'.format(name)
+ ret['changes']['Rule'] = 'Rule %s: "%s" has been deleted' % (name, rule_check[name])
+ elif 'Error' in rule_check:
+ ret['comment'] = rule_check.get('Error')
+ ret['result'] = False
+ return ret
+
diff --git a/keystone/files/keystone.conf b/keystone/files/keystone.conf
deleted file mode 100644
index e6c9de2..0000000
--- a/keystone/files/keystone.conf
+++ /dev/null
@@ -1,31 +0,0 @@
-{%- from "keystone/map.jinja" import client with context %}
-{%- for profile_name, identity in client.server.iteritems() %}
-
-{%- if identity.admin.get('protocol', 'http') == 'http' %}
-{%- set protocol = 'http' %}
-{%- else %}
-{%- set protocol = 'https' %}
-{%- endif %}
-
-{%- if identity.admin.get('api_version', '2') == '3' %}
-{%- set version = "v3" %}
-{%- else %}
-{%- set version = "v2.0" %}
-{%- endif %}
-
-{%- if identity.admin.user is defined %}
-
-{%- if identity.admin.token is not defined %}
-
-{{ profile_name }}:
- keystone.user: '{{ identity.admin.user }}'
- keystone.password: '{{ identity.admin.password }}'
- keystone.tenant: '{{ identity.admin.project }}'
- keystone.auth_url: '{{ protocol+'://'+identity.admin.host+':'+identity.admin.port|string+'/'+version }}'
- keystone.region_name: '{{ identity.admin.region_name }}'
-
-{%- endif %}
-
-{%- endif %}
-
-{%- endfor %}
diff --git a/keystone/meta/salt.yml b/keystone/meta/salt.yml
index 8961ec1..ccd9d91 100644
--- a/keystone/meta/salt.yml
+++ b/keystone/meta/salt.yml
@@ -6,21 +6,57 @@
priority: 510
control:
priority: 520
+grain:
+ keystone:
+ keystone_policy:
+ {%- set policy_grains = salt['keystone_policy.rule_list'](path="/etc/keystone/policy.json") %}
+ {{ policy_grains|yaml(False)|indent(6) }}
minion:
- {% if pillar.keystone.server is defined or pillar.keystone.client is defined %}
+ {%- if pillar.get('keystone', {}).get('server') or pillar.get('keystone', {}).get('client') %}
+ {%- from "keystone/map.jinja" import server with context %}
+ {%- from "keystone/map.jinja" import client with context %}
+
keystone:
- {%- if pillar.keystone.get('server', {'enabled': False}).enabled -%}
- {%- from "keystone/map.jinja" import server with context -%}
+ {%- if pillar.keystone.get('server', {'enabled': False}).enabled %}
keystone.token: '{{ server.service_token }}'
keystone.endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
- {%- else -%}
- {%- from "keystone/map.jinja" import client with context -%}
+ {%- else %}
+ {%- if client.get('server', {}).get('user') %}
keystone.user: '{{ client.server.user }}'
keystone.password: '{{ client.server.password }}'
keystone.tenant: '{{ client.server.tenant }}'
keystone.auth_url: 'http://{{ client.server.host }}:{{ client.server.public_port }}/v2.0/'
+ {%- endif %}
{%- endif %}
+
+ {#- Profile based metadata #}
+ {%- for profile_name, identity in client.get('server', {}).iteritems() %}
+ {%- if identity.admin.get('protocol', 'http') == 'http' %}
+ {%- set protocol = 'http' %}
+ {%- else %}
+ {%- set protocol = 'https' %}
+ {%- endif %}
+
+ {%- if identity.admin.get('api_version', '2') == '3' %}
+ {%- set version = "v3" %}
+ {%- else %}
+ {%- set version = "v2.0" %}
+ {%- endif %}
+
+ {%- if identity.admin.user is defined %}
+ {%- if identity.admin.token is not defined %}
+
+ {{ profile_name }}:
+ keystone.user: '{{ identity.admin.user }}'
+ keystone.password: '{{ identity.admin.password }}'
+ keystone.tenant: '{{ identity.admin.project }}'
+ keystone.auth_url: '{{ protocol+'://'+identity.admin.host+':'+identity.admin.port|string+'/'+version }}'
+ keystone.region_name: '{{ identity.admin.region_name }}'
+
+ {%- endif %}
+ {%- endif %}
+ {%- endfor %}
{%- endif %}
{#-
diff --git a/keystone/server.sls b/keystone/server.sls
index 05d10c0..b4de945 100644
--- a/keystone/server.sls
+++ b/keystone/server.sls
@@ -90,11 +90,15 @@
- service: keystone_service
{%- endif %}
-/etc/keystone/policy.json:
- keystone_policy.present:
- - override_data:
- {{ server.get('policy', {})|yaml }}
- - formatter: json
+{%- for name, rule in server.get('policy', {}).iteritems() %}
+
+{%- if rule != None %}
+
+rule_{{ name }}_present:
+ keystone_policy.rule_present:
+ - path: /etc/keystone/policy.json
+ - name: {{ name }}
+ - rule: {{ rule }}
- require:
- pkg: keystone_packages
{%- if not grains.get('noservices', False) %}
@@ -102,6 +106,23 @@
- service: keystone_service
{%- endif %}
+{%- else %}
+
+rule_{{ name }}_absent:
+ keystone_policy.rule_absent:
+ - path: /etc/keystone/policy.json
+ - name: {{ name }}
+ - require:
+ - pkg: keystone_packages
+ {%- if not grains.get('noservices', False) %}
+ - watch_in:
+ - service: keystone_service
+ {%- endif %}
+
+{%- endif %}
+
+{%- endfor %}
+
{%- if server.get("domain", {}) %}
/etc/keystone/domains:
diff --git a/metadata/service/support.yml b/metadata/service/support.yml
index 283ca7e..df136a7 100644
--- a/metadata/service/support.yml
+++ b/metadata/service/support.yml
@@ -6,7 +6,7 @@
heka:
enabled: true
sensu:
- enabled: true
+ enabled: false
sphinx:
enabled: true
config: