1 |
1 |
import requests
|
2 |
2 |
import logging
|
3 |
3 |
import os
|
|
4 |
import time
|
|
5 |
import xml.etree.ElementTree as ET
|
4 |
6 |
|
5 |
7 |
from authentic2 import app_settings
|
6 |
8 |
from authentic2.compat_lasso import lasso
|
... | ... | |
27 |
29 |
class Command(hobo_deploy.Command):
|
28 |
30 |
help = 'Deploy multitenant authentic service from hobo'
|
29 |
31 |
|
|
32 |
backoff_factor = 5
|
|
33 |
|
30 |
34 |
def __init__(self, *args, **kwargs):
|
31 |
35 |
self.logger = logging.getLogger(__name__)
|
32 |
36 |
super(Command, self).__init__(*args, **kwargs)
|
... | ... | |
117 |
121 |
|
118 |
122 |
# create or update Service Providers
|
119 |
123 |
services = hobo_environment['services']
|
120 |
|
for service in services:
|
121 |
|
if not service.get('saml-sp-metadata-url'):
|
122 |
|
continue
|
123 |
|
sp_url = service['saml-sp-metadata-url']
|
124 |
|
try:
|
125 |
|
metadata_response = requests.get(
|
126 |
|
sp_url, verify=app_settings.A2_VERIFY_SSL, timeout=5)
|
127 |
|
metadata_response.raise_for_status()
|
128 |
|
except requests.exceptions.RequestException as e:
|
129 |
|
self.stderr.write(self.style.WARNING(
|
130 |
|
'Error registering %s: %r\n' % (sp_url, e)))
|
131 |
|
continue
|
132 |
|
metadata_text = metadata_response.text
|
133 |
|
|
134 |
|
provider, service_created = \
|
135 |
|
LibertyProvider.objects.get_or_create(
|
136 |
|
entity_id=sp_url,
|
137 |
|
protocol_conformance=lasso.PROTOCOL_SAML_2_0)
|
138 |
|
provider.name = service['title']
|
139 |
|
provider.slug = service['slug']
|
140 |
|
provider.federation_source = 'hobo'
|
141 |
|
provider.metadata = metadata_text
|
142 |
|
provider.metadata_url = service['saml-sp-metadata-url']
|
143 |
|
if service.get('variables', {}).get('ou-slug'):
|
144 |
|
ou, created = get_ou_model().objects.get_or_create(
|
145 |
|
slug=service['variables']['ou-slug'])
|
146 |
|
ou.name = service['variables']['ou-label']
|
147 |
|
ou.save()
|
148 |
|
else:
|
149 |
|
# if there are more than one w.c.s. service we will create an
|
150 |
|
# ou of the same name
|
151 |
|
ou = get_default_ou()
|
152 |
|
create_ou = False
|
153 |
|
if service_created and service['service-id'] == 'wcs':
|
154 |
|
for s in services:
|
155 |
|
if s['service-id'] != 'wcs':
|
156 |
|
continue
|
157 |
|
if s['slug'] == service['slug']:
|
158 |
|
continue
|
159 |
|
if LibertyProvider.objects.filter(
|
160 |
|
slug=s['slug']).exists():
|
161 |
|
create_ou = True
|
162 |
|
break
|
163 |
|
if create_ou:
|
|
124 |
retries = 0
|
|
125 |
loaded = 0
|
|
126 |
max_retries = 1 if self.redeploy else 5
|
|
127 |
while retries < max_retries:
|
|
128 |
for service in services:
|
|
129 |
if service.get('$done'):
|
|
130 |
continue
|
|
131 |
if not service.get('saml-sp-metadata-url'):
|
|
132 |
service['$done'] = True
|
|
133 |
loaded += 1
|
|
134 |
continue
|
|
135 |
sp_url = service['saml-sp-metadata-url']
|
|
136 |
metadata_text = None
|
|
137 |
try:
|
|
138 |
metadata_response = requests.get(
|
|
139 |
sp_url, verify=app_settings.A2_VERIFY_SSL, timeout=5)
|
|
140 |
metadata_response.raise_for_status()
|
|
141 |
# verify metadata is correct
|
|
142 |
if self.check_saml_metadata(metadata_response.text):
|
|
143 |
metadata_text = metadata_response.text
|
|
144 |
else:
|
|
145 |
service['$last-error'] = 'metadata is incorrect'
|
|
146 |
continue
|
|
147 |
except requests.exceptions.RequestException as e:
|
|
148 |
service['$last-error'] = str(e)
|
|
149 |
continue
|
|
150 |
metadata_text = metadata_response.text
|
|
151 |
|
|
152 |
provider, service_created = \
|
|
153 |
LibertyProvider.objects.get_or_create(
|
|
154 |
entity_id=sp_url,
|
|
155 |
protocol_conformance=lasso.PROTOCOL_SAML_2_0)
|
|
156 |
provider.name = service['title']
|
|
157 |
provider.slug = service['slug']
|
|
158 |
provider.federation_source = 'hobo'
|
|
159 |
provider.metadata = metadata_text
|
|
160 |
provider.metadata_url = service['saml-sp-metadata-url']
|
|
161 |
if service.get('variables', {}).get('ou-slug'):
|
164 |
162 |
ou, created = get_ou_model().objects.get_or_create(
|
165 |
|
name=service['title'])
|
166 |
|
if service_created or not provider.ou:
|
167 |
|
provider.ou = ou
|
168 |
|
provider.save()
|
169 |
|
if service_created:
|
170 |
|
service_provider = LibertyServiceProvider(
|
171 |
|
enabled=True, liberty_provider=provider,
|
172 |
|
sp_options_policy=policy,
|
173 |
|
users_can_manage_federations=False)
|
174 |
|
service_provider.save()
|
175 |
|
|
176 |
|
# add a superuser role for the service
|
177 |
|
Role = get_role_model()
|
178 |
|
name = _('Superuser of %s') % service['title']
|
179 |
|
su_role, created = Role.objects.get_or_create(
|
180 |
|
service=provider, slug='_a2-hobo-superuser',
|
181 |
|
defaults={'name': name})
|
182 |
|
if su_role.name != name:
|
183 |
|
su_role.name = name
|
184 |
|
su_role.save()
|
185 |
|
su_role.attributes.get_or_create(name='is_superuser',
|
186 |
|
kind='string',
|
187 |
|
value='true')
|
188 |
|
# pass the new attribute to the service
|
189 |
|
SAMLAttribute.objects.get_or_create(
|
190 |
|
name='is_superuser',
|
191 |
|
name_format='basic',
|
192 |
|
attribute_name='is_superuser',
|
193 |
|
object_id=provider.pk,
|
194 |
|
content_type=provider_type)
|
195 |
|
SAMLAttribute.objects.get_or_create(
|
196 |
|
name='role-slug',
|
197 |
|
name_format='basic',
|
198 |
|
attribute_name='a2_service_ou_role_uuids',
|
199 |
|
object_id=provider.pk,
|
200 |
|
content_type=provider_type)
|
201 |
|
# load skeleton if service is new
|
202 |
|
if service.get('template_name'):
|
203 |
|
# if there are more of the same servie, we will create an
|
204 |
|
# ou
|
205 |
|
self.load_skeleton(provider, service['service-id'],
|
206 |
|
service['template_name'])
|
|
163 |
slug=service['variables']['ou-slug'])
|
|
164 |
ou.name = service['variables']['ou-label']
|
|
165 |
ou.save()
|
|
166 |
else:
|
|
167 |
# if there are more than one w.c.s. service we will create an
|
|
168 |
# ou of the same name
|
|
169 |
ou = get_default_ou()
|
|
170 |
create_ou = False
|
|
171 |
if service_created and service['service-id'] == 'wcs':
|
|
172 |
for s in services:
|
|
173 |
if s['service-id'] != 'wcs':
|
|
174 |
continue
|
|
175 |
if s['slug'] == service['slug']:
|
|
176 |
continue
|
|
177 |
if LibertyProvider.objects.filter(
|
|
178 |
slug=s['slug']).exists():
|
|
179 |
create_ou = True
|
|
180 |
break
|
|
181 |
if create_ou:
|
|
182 |
ou, created = get_ou_model().objects.get_or_create(
|
|
183 |
name=service['title'])
|
|
184 |
if service_created or not provider.ou:
|
|
185 |
provider.ou = ou
|
|
186 |
provider.save()
|
|
187 |
if service_created:
|
|
188 |
service_provider = LibertyServiceProvider(
|
|
189 |
enabled=True, liberty_provider=provider,
|
|
190 |
sp_options_policy=policy,
|
|
191 |
users_can_manage_federations=False)
|
|
192 |
service_provider.save()
|
|
193 |
|
|
194 |
# add a superuser role for the service
|
|
195 |
Role = get_role_model()
|
|
196 |
name = _('Superuser of %s') % service['title']
|
|
197 |
su_role, created = Role.objects.get_or_create(
|
|
198 |
service=provider, slug='_a2-hobo-superuser',
|
|
199 |
defaults={'name': name})
|
|
200 |
if su_role.name != name:
|
|
201 |
su_role.name = name
|
|
202 |
su_role.save()
|
|
203 |
su_role.attributes.get_or_create(name='is_superuser',
|
|
204 |
kind='string',
|
|
205 |
value='true')
|
|
206 |
# pass the new attribute to the service
|
|
207 |
SAMLAttribute.objects.get_or_create(
|
|
208 |
name='is_superuser',
|
|
209 |
name_format='basic',
|
|
210 |
attribute_name='is_superuser',
|
|
211 |
object_id=provider.pk,
|
|
212 |
content_type=provider_type)
|
|
213 |
SAMLAttribute.objects.get_or_create(
|
|
214 |
name='role-slug',
|
|
215 |
name_format='basic',
|
|
216 |
attribute_name='a2_service_ou_role_uuids',
|
|
217 |
object_id=provider.pk,
|
|
218 |
content_type=provider_type)
|
|
219 |
# load skeleton if service is new
|
|
220 |
if service.get('template_name'):
|
|
221 |
# if there are more of the same servie, we will create an
|
|
222 |
# ou
|
|
223 |
self.load_skeleton(provider, service['service-id'],
|
|
224 |
service['template_name'])
|
|
225 |
service['$done'] = True
|
|
226 |
loaded += 1
|
|
227 |
|
|
228 |
if len(services) == loaded:
|
|
229 |
# it's finished no need to continue
|
|
230 |
break
|
|
231 |
|
|
232 |
# wait 5, 10, 20, 40, .. seconds
|
|
233 |
time.sleep(self.backoff_factor * (2 ** retries))
|
|
234 |
retries += 1
|
|
235 |
|
|
236 |
for service in services:
|
|
237 |
if not service.get('$done'):
|
|
238 |
last_error = service['$last-error']
|
|
239 |
sp_url = service['saml-sp-metadata-url']
|
|
240 |
self.stderr.write(self.style.WARNING('Error registering %s: %s\n' % (sp_url, last_error)))
|
207 |
241 |
|
208 |
242 |
def load_skeleton(self, provider, service_id, template_name,
|
209 |
243 |
create_ou=False):
|
... | ... | |
242 |
276 |
if roles:
|
243 |
277 |
Role.objects.bulk_create(roles)
|
244 |
278 |
Role.objects.get(uuid=roles[-1].uuid).save()
|
|
279 |
|
|
280 |
def check_saml_metadata(self, saml_metadata):
|
|
281 |
try:
|
|
282 |
root = ET.fromstring(saml_metadata)
|
|
283 |
except ET.ParseError:
|
|
284 |
return False
|
|
285 |
return root.tag == '{%s}EntityDescriptor' % lasso.SAML2_METADATA_HREF
|