add WebSSO support
* add support for relating with subordinate charms providing Service Provider functionality via apache2 authentication modules; * retrieve protocol, identity provider and user-facing name info from keystone service provider charm subordinates; * provide trusted dashboard information to keystone charm Change-Id: I15ca0dd1616ec12c7ad47dc05961b51bb45bb770
This commit is contained in:
parent
0707590487
commit
45be17c904
|
@ -44,6 +44,7 @@ from charmhelpers.core.host import pwgen
|
|||
|
||||
from base64 import b64decode
|
||||
import os
|
||||
import json
|
||||
|
||||
VALID_ENDPOINT_TYPES = {
|
||||
'PUBLICURL': 'publicURL',
|
||||
|
@ -282,3 +283,30 @@ class LocalSettingsContext(OSContextGenerator):
|
|||
key=lambda r: r[1]['priority'])]
|
||||
}
|
||||
return ctxt
|
||||
|
||||
|
||||
class WebSSOFIDServiceProviderContext(OSContextGenerator):
|
||||
interfaces = ['websso-fid-service-provider']
|
||||
|
||||
def __call__(self):
|
||||
websso_keys = ['protocol-name', 'idp-name', 'user-facing-name']
|
||||
|
||||
relations = []
|
||||
for rid in relation_ids("websso-fid-service-provider"):
|
||||
try:
|
||||
# the first unit will do - the assumption is that all
|
||||
# of them should advertise the same data. This needs
|
||||
# refactoring if juju gets per-application relation data
|
||||
# support
|
||||
unit = related_units(rid)[0]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
rdata = relation_get(unit=unit, rid=rid)
|
||||
if set(rdata).issuperset(set(websso_keys)):
|
||||
relations.append({k: json.loads(rdata[k])
|
||||
for k in websso_keys})
|
||||
# populate the context with data from one or more
|
||||
# service providers
|
||||
ctxt = {'websso_data': relations} if relations else {}
|
||||
return ctxt
|
||||
|
|
|
@ -31,6 +31,7 @@ from charmhelpers.core.hookenv import (
|
|||
is_leader,
|
||||
local_unit,
|
||||
WARNING,
|
||||
network_get,
|
||||
)
|
||||
from charmhelpers.fetch import (
|
||||
apt_update, apt_install,
|
||||
|
@ -152,6 +153,8 @@ def config_changed():
|
|||
open_port(80)
|
||||
open_port(443)
|
||||
|
||||
websso_trusted_dashboard_changed()
|
||||
|
||||
|
||||
@hooks.hook('identity-service-relation-joined')
|
||||
def keystone_joined(rel_id=None):
|
||||
|
@ -342,6 +345,47 @@ def db_changed():
|
|||
log('Not running neutron database migration, not leader')
|
||||
|
||||
|
||||
@hooks.hook('websso-fid-service-provider-relation-joined',
|
||||
'websso-fid-service-provider-relation-changed',
|
||||
'websso-fid-service-provider-relation-departed')
|
||||
@restart_on_change(restart_map(), stopstart=True, sleep=3)
|
||||
def websso_sp_changed():
|
||||
CONFIGS.write_all()
|
||||
|
||||
|
||||
@hooks.hook('websso-trusted-dashboard-relation-joined',
|
||||
'websso-trusted-dashboard-relation-changed')
|
||||
def websso_trusted_dashboard_changed():
|
||||
"""
|
||||
Provide L7 endpoint details for the dashboard and also
|
||||
handle any config changes that may affect those.
|
||||
"""
|
||||
relations = relation_ids('websso-trusted-dashboard')
|
||||
if not relations:
|
||||
return
|
||||
|
||||
# TODO: check for vault relation in order to determine url scheme
|
||||
tls_configured = config('ssl-key') or config('enforce-ssl')
|
||||
scheme = 'https://' if tls_configured else 'http://'
|
||||
|
||||
if config('dns-ha') or config('os-public-hostname'):
|
||||
hostname = config('os-public-hostname')
|
||||
elif config('vip'):
|
||||
hostname = config('vip')
|
||||
else:
|
||||
# use an ingress-address of a given unit as a fallback
|
||||
netinfo = network_get('websso-trusted-dashboard')
|
||||
hostname = netinfo['ingress-addresses'][0]
|
||||
|
||||
# provide trusted dashboard URL details
|
||||
for rid in relations:
|
||||
relation_set(relation_id=rid, relation_settings={
|
||||
"scheme": scheme,
|
||||
"hostname": hostname,
|
||||
"path": "/auth/websso/"
|
||||
})
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
hooks.execute(sys.argv)
|
||||
|
|
|
@ -93,7 +93,8 @@ CONFIG_FILES = OrderedDict([
|
|||
horizon_contexts.IdentityServiceContext(),
|
||||
context.SyslogContext(),
|
||||
horizon_contexts.LocalSettingsContext(),
|
||||
horizon_contexts.ApacheSSLContext()],
|
||||
horizon_contexts.ApacheSSLContext(),
|
||||
horizon_contexts.WebSSOFIDServiceProviderContext()],
|
||||
'services': ['apache2', 'memcached']
|
||||
}),
|
||||
(APACHE_CONF, {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -23,6 +23,8 @@ provides:
|
|||
dashboard-plugin:
|
||||
interface: dashboard-plugin
|
||||
scope: container
|
||||
websso-trusted-dashboard:
|
||||
interface: websso-trusted-dashboard
|
||||
requires:
|
||||
identity-service:
|
||||
interface: keystone
|
||||
|
@ -31,6 +33,8 @@ requires:
|
|||
scope: container
|
||||
shared-db:
|
||||
interface: mysql-shared
|
||||
websso-fid-service-provider:
|
||||
interface: websso-fid-service-provider
|
||||
peers:
|
||||
cluster:
|
||||
interface: openstack-dashboard-ha
|
||||
|
|
|
@ -991,3 +991,19 @@ ALLOWED_PRIVATE_SUBNET_CIDR = {'ipv4': [], 'ipv6': []}
|
|||
# 'phone_num': _('Phone Number'),
|
||||
#}
|
||||
{{ settings|join('\n\n') }}
|
||||
|
||||
{% if websso_data %}
|
||||
WEBSSO_ENABLED = True
|
||||
WEBSSO_CHOICES = (
|
||||
{% for provider_data in websso_data -%}
|
||||
("{{ '{}_{}'.format(provider_data['idp-name'], provider_data['protocol-name']) }}", "{{ provider_data['user-facing-name'] }}"),
|
||||
{% endfor -%}
|
||||
("credentials", _("Keystone Credentials"))
|
||||
)
|
||||
|
||||
WEBSSO_IDP_MAPPING = {
|
||||
{% for provider_data in websso_data -%}
|
||||
"{{ '{}_{}'.format(provider_data['idp-name'], provider_data['protocol-name']) }}": ("{{ provider_data['idp-name'] }}", "{{ provider_data['protocol-name'] }}"),
|
||||
{% endfor -%}
|
||||
}
|
||||
{% endif %}
|
||||
|
|
|
@ -650,3 +650,76 @@ class TestHorizonContexts(CharmTestCase):
|
|||
'BAR = False',
|
||||
'# horizon-plugin/0\n'
|
||||
'FOO = True']})
|
||||
|
||||
def test_WebSSOFIDServiceProviderContext(self):
|
||||
def relation_ids_side_effect(rname):
|
||||
return {
|
||||
'websso-fid-service-provider': [
|
||||
'websso-fid-service-provider:0',
|
||||
'websso-fid-service-provider:1',
|
||||
]
|
||||
}[rname]
|
||||
self.relation_ids.side_effect = relation_ids_side_effect
|
||||
|
||||
def related_units_side_effect(rid):
|
||||
return {
|
||||
'websso-fid-service-provider:0': [
|
||||
'keystone-saml-mellon-red/0',
|
||||
'keystone-saml-mellon-red/1',
|
||||
],
|
||||
'websso-fid-service-provider:1': [
|
||||
'keystone-saml-mellon-green/0',
|
||||
'keystone-saml-mellon-green/1',
|
||||
],
|
||||
}[rid]
|
||||
self.related_units.side_effect = related_units_side_effect
|
||||
|
||||
def relation_get_side_effect(unit, rid):
|
||||
return {
|
||||
'websso-fid-service-provider:0': {
|
||||
'keystone-saml-mellon-red/0': {
|
||||
'ingress-address': '10.0.0.10',
|
||||
'protocol-name': '"saml2"',
|
||||
'idp-name': '"red"',
|
||||
'user-facing-name': '"Red IDP"',
|
||||
},
|
||||
'keystone-saml-mellon-red/1': {
|
||||
'ingress-address': '10.0.0.11',
|
||||
'protocol-name': '"saml2"',
|
||||
'idp-name': '"red"',
|
||||
'user-facing-name': '"Red IDP"',
|
||||
},
|
||||
},
|
||||
'websso-fid-service-provider:1': {
|
||||
'keystone-saml-mellon-green/0': {
|
||||
'ingress-address': '10.0.0.12',
|
||||
'protocol-name': '"mapped"',
|
||||
'idp-name': '"green"',
|
||||
'user-facing-name': '"Green IDP"',
|
||||
},
|
||||
'keystone-saml-mellon-green/1': {
|
||||
'ingress-address': '10.0.0.13',
|
||||
'protocol-name': '"mapped"',
|
||||
'idp-name': '"green"',
|
||||
'user-facing-name': '"Green IDP"',
|
||||
},
|
||||
},
|
||||
}[rid][unit]
|
||||
self.relation_get.side_effect = relation_get_side_effect
|
||||
|
||||
self.assertEqual(
|
||||
horizon_contexts.WebSSOFIDServiceProviderContext()(),
|
||||
{
|
||||
'websso_data': [
|
||||
{
|
||||
'protocol-name': 'saml2',
|
||||
'idp-name': 'red',
|
||||
'user-facing-name': "Red IDP",
|
||||
},
|
||||
{
|
||||
'protocol-name': 'mapped',
|
||||
'idp-name': 'green',
|
||||
'user-facing-name': "Green IDP",
|
||||
},
|
||||
]
|
||||
})
|
||||
|
|
|
@ -260,7 +260,30 @@ class TestHorizonHooks(CharmTestCase):
|
|||
|
||||
@patch('horizon_hooks.keystone_joined')
|
||||
def test_config_changed_no_upgrade(self, _joined):
|
||||
self.relation_ids.return_value = ['identity/0']
|
||||
def relation_ids_side_effect(rname):
|
||||
return {
|
||||
'websso-trusted-dashboard': [
|
||||
'websso-trusted-dashboard:0',
|
||||
'websso-trusted-dashboard:1',
|
||||
],
|
||||
'identity-service': [
|
||||
'identity/0',
|
||||
],
|
||||
}[rname]
|
||||
self.relation_ids.side_effect = relation_ids_side_effect
|
||||
|
||||
def config_side_effect(key):
|
||||
return {
|
||||
'ssl-key': 'somekey',
|
||||
'enforce-ssl': True,
|
||||
'dns-ha': True,
|
||||
'os-public-hostname': 'dashboard.intranet.test',
|
||||
'prefer-ipv6': False,
|
||||
'action-managed-upgrade': False,
|
||||
'webroot': '/horizon',
|
||||
}[key]
|
||||
self.config.side_effect = config_side_effect
|
||||
|
||||
self.openstack_upgrade_available.return_value = False
|
||||
self._call_hook('config-changed')
|
||||
_joined.assert_called_with('identity/0')
|
||||
|
@ -331,3 +354,41 @@ class TestHorizonHooks(CharmTestCase):
|
|||
openstack_dir='/usr/share/openstack-dashboard',
|
||||
relation_id=None
|
||||
)
|
||||
|
||||
def test_websso_fid_service_provider_changed(self):
|
||||
self._call_hook('websso-fid-service-provider-relation-changed')
|
||||
self.CONFIGS.write_all.assert_called_with()
|
||||
|
||||
def test_websso_trusted_dashboard_changed(self):
|
||||
def relation_ids_side_effect(rname):
|
||||
return {
|
||||
'websso-trusted-dashboard': [
|
||||
'websso-trusted-dashboard:0',
|
||||
'websso-trusted-dashboard:1',
|
||||
]
|
||||
}[rname]
|
||||
self.relation_ids.side_effect = relation_ids_side_effect
|
||||
|
||||
def config_side_effect(key):
|
||||
return {
|
||||
'ssl-key': 'somekey',
|
||||
'enforce-ssl': True,
|
||||
'dns-ha': True,
|
||||
'os-public-hostname': 'dashboard.intranet.test',
|
||||
}[key]
|
||||
self.config.side_effect = config_side_effect
|
||||
self._call_hook('websso-trusted-dashboard-relation-changed')
|
||||
self.relation_set.assert_has_calls([
|
||||
call(relation_id='websso-trusted-dashboard:0',
|
||||
relation_settings={
|
||||
"scheme": "https://",
|
||||
"hostname": "dashboard.intranet.test",
|
||||
"path": "/auth/websso/",
|
||||
}),
|
||||
call(relation_id='websso-trusted-dashboard:1',
|
||||
relation_settings={
|
||||
"scheme": "https://",
|
||||
"hostname": "dashboard.intranet.test",
|
||||
"path": "/auth/websso/",
|
||||
}),
|
||||
])
|
||||
|
|
Loading…
Reference in New Issue