def continue_response(self): key = request.form['request_key'] relay_state = from_session('relay_state') if key and key in self.responses: _response = self.responses.pop(key) auth_req = self.ticket.pop(key) if 'confirm' in request.form: return _response, 200 elif 'delete' in request.form: destination = self.get_destination( auth_req, auth_req.issuer.text ) error_info = get_spid_error( AUTH_NO_CONSENT ) response = create_error_response( { 'response': { 'attrs': { 'in_response_to': auth_req.id, 'destination': destination } }, 'issuer': { 'attrs': { 'name_qualifier': 'something', }, 'text': self.server.config.entityid }, }, { 'status_code': error_info[0], 'status_message': error_info[1] } ).to_xml() self.app.logger.debug( 'Error response: \n{}'.format(response) ) key_file = self.server.config.key_file cert_file = self.server.config.cert_file pkey = open(key_file, 'rb').read() cert = open(cert_file, 'rb').read() response = sign_http_post(response, pkey, cert) rendered_template = render_template( 'form_http_post.html', **{ 'action': destination, 'relay_state': relay_state, 'message': response, 'message_type': 'SAMLResponse' } ) return rendered_template, 200 return render_template('403.html'), 403
def continue_response(self): key = request.form['request_key'] relay_state = from_session('relay_state') if key and key in self.responses: _response = self.responses.pop(key) auth_req = self.ticket.pop(key) if 'confirm' in request.form: return _response, 200 elif 'delete' in request.form: destination = self.get_destination( auth_req, auth_req.issuer.text ) error_info = get_spid_error( AUTH_NO_CONSENT ) response = create_error_response( { 'response': { 'attrs': { 'in_response_to': auth_req.id, 'destination': destination } }, 'issuer': { 'attrs': { 'name_qualifier': 'something', }, 'text': self._config.entity_id }, }, { 'status_code': error_info[0], 'status_message': error_info[1] } ).to_xml() logger.info( 'Error response: \n{}'.format(response) ) response = sign_http_post( response, self._config.idp_key, self._config.idp_certificate, ) rendered_template = render_template( 'form_http_post.html', **{ 'action': destination, 'relay_state': relay_state, 'message': base64.b64encode(response).decode('ascii'), 'message_type': 'SAMLResponse' } ) return rendered_template, 200 return render_template('403.html'), 403
def _build_failed_response(self, authn_request, destination, key, relay_state): """ Build and return a failed response """ error_info = get_spid_error(AUTH_NO_CONSENT) response = create_error_response( { 'response': { 'attrs': { 'in_response_to': authn_request.id, 'destination': destination } }, 'issuer': { 'attrs': { 'name_qualifier': self._config.entity_id, }, 'text': self._config.entity_id }, }, { 'status_code': error_info[0], 'status_message': error_info[1] }).to_xml() self.app.logger.debug('Error response: \n{}'.format(response)) response = sign_http_post( response, self._config.idp_key, self._config.idp_certificate, ) del self.ticket[key] rendered_template = render_template( 'form_http_post.html', **{ 'action': destination, 'relay_state': relay_state, 'message': response, 'message_type': 'SAMLResponse' }) return rendered_template
def login(self): """ Login endpoint (verify user credentials) """ key = from_session('request_key') relay_state = from_session('relay_state') logger.info('Request key: {}'.format(key)) if key and key in self.ticket: authn_request = self.ticket[key] sp_id = authn_request.issuer.text.strip() destination = self.get_destination(authn_request, sp_id) authn_context = authn_request.requested_authn_context spid_level = authn_context.authn_context_class_ref.text.strip() if request.method == 'GET': # inject extra data in form login based on spid level extra_challenge = self._verify_spid(level=spid_level, **{'key': key}) rendered_form = render_template( 'login.html', **{ 'action': url_for('login'), 'request_key': key, 'relay_state': relay_state, 'extra_challenge': extra_challenge, 'show_response_options': self._config.show_response_options, }) return rendered_form, 200 if 'confirm' in request.form: # verify optional challenge based on spid level verified = self._verify_spid(level=spid_level, verify=True, **{ 'key': key, 'data': request.form }) if verified: # verify user credentials user_id, user = self.user_manager.get( request.form['username'], request.form['password'], sp_id) if user_id is not None: # setup response _audience = sp_id _destination = destination _recipient_subj = destination _issuer_text = self._config.entity_id _pkey = self._config.idp_key _cert = self._config.idp_certificate # setup custom response elements (if any) wrong_destination = request.form.get( 'wrong_destination', False) if wrong_destination: _destination = '{}wrong/bad/'.format(_destination) wrong_relay_state = request.form.get( 'wrong_relay_state', False) if wrong_relay_state: relay_state = '{}wrong'.format(relay_state) wrong_audience = request.form.get( 'wrong_audience', False) if wrong_audience: _audience = '{}/wrong/bad/'.format(_audience) wrong_recipient_subj = request.form.get( 'wrong_recipient_subj', False) if wrong_recipient_subj: _recipient_subj = 'badrecipient' wrong_issuer = request.form.get('wrong_issuer', False) if wrong_issuer: _issuer_text = 'wrongissuer123' has_assertion = not request.form.get( 'no_assertion', False) bad_status_code = request.form.get( 'bad_status_code', False) wrong_conditions_notbefore = request.form.get( 'wrong_conditions_notbefore') wrong_conditions_notonorafter = request.form.get( 'wrong_conditions_notonorafter') wrong_subj_notonorafter = request.form.get( 'wrong_subj_notonorafter') wrong_subj_inresponseto = request.form.get( 'wrong_subj_inresponseto') custom_spid_level = request.form.get('spid_level') if custom_spid_level: spid_level = self._spid_levels[int( custom_spid_level)] sign_assertion = request.form.get( 'sign_assertion', False) sign_message = request.form.get('sign_message', False) custom_private_key = request.form.get('private_key') if custom_private_key: _pkey = bytes(custom_private_key.encode('utf-8')) custom_certificate = request.form.get('certificate') if custom_certificate: _pkey = bytes(custom_certificate.encode('utf-8')) _conditions = {'conditions': {'attrs': {}}} _subj_extra = {} if wrong_subj_inresponseto: _subj_extra[ 'in_response_to'] = 'inresponsetowron134' if wrong_conditions_notbefore: _conditions['conditions']['attrs'][ 'not_before'] = wrong_conditions_notbefore + ':00Z' if wrong_conditions_notonorafter: _conditions['conditions']['attrs'][ 'not_on_or_after'] = wrong_conditions_notonorafter + ':00Z' if wrong_subj_notonorafter: _subj_extra[ 'not_on_or_after'] = wrong_subj_notonorafter + ':00Z' _status_code = STATUS_AUTHN_FAILED if bad_status_code else STATUS_SUCCESS identity = user['attrs'].copy() logger.debug('Unfiltered data: {}'.format(identity)) atcs_idx = getattr( authn_request, 'attribute_consuming_service_index', None) logger.info( 'AttributeConsumingServiceIndex: {}'.format( atcs_idx)) sp_metadata = self._registry.get(sp_id) required = [] optional = [] if atcs_idx and sp_metadata: attrs = sp_metadata.attributes(atcs_idx) required = [el for el in attrs.get('required')] optional = [el for el in attrs.get('optional')] for attr_name, val in list(identity.items()): _type = self._attribute_type(attr_name) identity[attr_name] = (_type, val) _identity = self._filter_attributes( identity, required, optional) logger.debug('Filtered data: {}'.format(_identity)) _response_data = { 'response': { 'attrs': { 'in_response_to': authn_request.id, 'destination': _destination } }, 'issuer': { 'attrs': { 'name_qualifier': self._config.entity_id, }, 'text': _issuer_text }, 'name_id': { 'attrs': { 'name_qualifier': self._config.entity_id, } }, 'subject_confirmation_data': { 'attrs': { 'recipient': _recipient_subj } }, 'audience': { 'text': _audience }, 'authn_context_class_ref': { 'text': spid_level } } _response_data.update(_conditions) if _subj_extra: _response_data['subject_confirmation_data'][ 'attrs'].update(_subj_extra) response = create_response( _response_data, {'status_code': _status_code}, _identity.copy(), has_assertion=has_assertion) response = sign_http_post(response.to_xml(), _pkey, _cert, message=sign_message, assertion=sign_assertion) logger.info('Response: \n{}'.format(response)) rendered_template = render_template( 'form_http_post.html', **{ 'action': destination, 'relay_state': relay_state, 'message': base64.b64encode(response).decode('ascii'), 'message_type': 'SAMLResponse' }) self.responses[key] = rendered_template # Setup confirmation page data rendered_response = render_template( 'confirm.html', **{ 'destination_service': sp_id, 'lines': escape(response.decode('utf-8')).splitlines(), 'attrs': list(_identity.keys()), 'action': '/continue-response', 'request_key': key, 'show_response_options': self._config.show_response_options, }) return rendered_response, 200 elif 'delete' in request.form: error_info = get_spid_error(AUTH_NO_CONSENT) response = create_error_response( { 'response': { 'attrs': { 'in_response_to': authn_request.id, 'destination': destination } }, 'issuer': { 'attrs': { 'name_qualifier': self._config.entity_id, }, 'text': self._config.entity_id }, }, { 'status_code': error_info[0], 'status_message': error_info[1] }).to_xml() logger.info('Error response: \n{}'.format(response)) response = sign_http_post( response, self._config.idp_key, self._config.idp_certificate, ) del self.ticket[key] rendered_template = render_template( 'form_http_post.html', **{ 'action': destination, 'relay_state': relay_state, 'message': base64.b64encode(response).decode('ascii'), 'message_type': 'SAMLResponse' }) return rendered_template, 200 return render_template('403.html'), 403
def login(self): """ Login endpoint (verify user credentials) """ key = from_session('request_key') relay_state = from_session('relay_state') self.app.logger.debug('Request key: {}'.format(key)) if key and key in self.ticket: authn_request = self.ticket[key] sp_id = authn_request.issuer.text destination = self.get_destination(authn_request, sp_id) authn_context = authn_request.requested_authn_context spid_level = authn_context.authn_context_class_ref.text if request.method == 'GET': # inject extra data in form login based on spid level extra_challenge = self._verify_spid(level=spid_level, **{'key': key}) rendered_form = render_template( 'login.html', **{ 'action': url_for('login'), 'request_key': key, 'relay_state': relay_state, 'extra_challenge': extra_challenge } ) return rendered_form, 200 if 'confirm' in request.form: # verify optional challenge based on spid level verified = self._verify_spid( level=spid_level, verify=True, **{ 'key': key, 'data': request.form } ) if verified: # verify user credentials user_id, user = self.user_manager.get( request.form['username'], request.form['password'], sp_id ) if user_id is not None: # setup response identity = user['attrs'].copy() self.app.logger.debug( 'Unfiltered data: {}'.format(identity) ) atcs_idx = getattr(authn_request, 'attribute_consuming_service_index', None) self.app.logger.debug( 'AttributeConsumingServiceIndex: {}'.format( atcs_idx ) ) if atcs_idx: # TODO: Remove this pysaml2 dependency attrs = self.server.wants(sp_id, atcs_idx) required = [el.get('name') for el in attrs.get('required')] optional = [el.get('name') for el in attrs.get('optional')] else: required = [] optional = [] for k, v in identity.items(): if k in self._spid_main_fields: _type = self._spid_attributes['primary'][k] else: _type = self._spid_attributes['secondary'][k] identity[k] = (_type, v) _identity = {} for _key in required: _identity[_key] = identity[_key] for _key in optional: _identity[_key] = identity[_key] self.app.logger.debug( 'Filtered data: {}'.format(_identity) ) response_xmlstr = create_response( { 'response': { 'attrs': { 'in_response_to': authn_request.id, 'destination': destination } }, 'issuer': { 'attrs': { 'name_qualifier': self._config.entity_id, }, 'text': self._config.entity_id }, 'name_id': { 'attrs': { 'name_qualifier': self._config.entity_id, } }, 'subject_confirmation_data': { 'attrs': { 'recipient': destination } }, 'audience': { 'text': sp_id }, 'authn_context_class_ref': { 'text': spid_level } }, { 'status_code': STATUS_SUCCESS }, _identity.copy() ).to_xml() response = sign_http_post( response_xmlstr, self._config.idp_key, self._config.idp_certificate, ) self.app.logger.debug( 'Response: \n{}'.format(response) ) rendered_template = render_template( 'form_http_post.html', **{ 'action': destination, 'relay_state': relay_state, 'message': response, 'message_type': 'SAMLResponse' } ) self.responses[key] = rendered_template # Setup confirmation page data rendered_response = render_template( 'confirm.html', **{ 'destination_service': sp_id, 'lines': escape( response_xmlstr.decode('ascii') ).splitlines(), 'attrs': _identity.keys(), 'action': '/continue-response', 'request_key': key } ) return rendered_response, 200 elif 'delete' in request.form: error_info = get_spid_error( AUTH_NO_CONSENT ) response = create_error_response( { 'response': { 'attrs': { 'in_response_to': authn_request.id, 'destination': destination } }, 'issuer': { 'attrs': { 'name_qualifier': self._config.entity_id, }, 'text': self._config.entity_id }, }, { 'status_code': error_info[0], 'status_message': error_info[1] } ).to_xml() self.app.logger.debug( 'Error response: \n{}'.format(response) ) response = sign_http_post( response, self._config.idp_key, self._config.idp_certificate, ) del self.ticket[key] rendered_template = render_template( 'form_http_post.html', **{ 'action': destination, 'relay_state': relay_state, 'message': response, 'message_type': 'SAMLResponse' } ) return rendered_template, 200 return render_template('403.html'), 403