def conv_response(resp): if not isinstance(resp, Response): return as_bytes(resp) cookie = cherrypy.response.cookie for header, value in resp.headers: if header == 'Set-Cookie': cookie_obj = SimpleCookie(value) for name in cookie_obj: morsel = cookie_obj[name] cookie[name] = morsel.value for key in [ 'expires', 'path', 'comment', 'domain', 'max-age', 'secure', 'version' ]: if morsel[key]: cookie[name][key] = morsel[key] _stat = int(resp._status.split(' ')[0]) # if self.mako_lookup and self.mako_template: # argv["message"] = message # mte = self.mako_lookup.get_template(self.mako_template) # return [mte.render(**argv)] if _stat < 300: cherrypy.response.status = _stat for key, val in resp.headers: cherrypy.response.headers[key] = val return as_bytes(resp.message) elif 300 <= _stat < 400: raise cherrypy.HTTPRedirect(resp.message, status=_stat) else: raise cherrypy.HTTPError(_stat, message=resp.message)
def read(self, qiss, qtag, path=''): """ :param qiss: OP issuer qoute_plus converted :param qtag: test instance tag quote_plus converted :param path: The HTTP request path :return: A HTTP response """ try: typ, info = self.read_conf(qiss, qtag) except (TypeError, NoSuchFile): if not path: path = '{}/{}'.format(qiss, qtag) return cherrypy.HTTPError(404, 'Could not find {}'.format(path)) else: if info: if typ == 'json': cherrypy.response.headers[ 'Content-Type'] = 'application/json' return as_bytes(json.dumps(info)) else: return as_bytes(info) else: return cherrypy.HTTPError(404, 'Could not find {}'.format(path))
def decrypt(self, token=None, keys=None, alg=None, cek=None): if token: jwe = JWEnc().unpack(token) # header, ek, eiv, ctxt, tag = token.split(b".") # self.parse_header(header) elif self.jwt: token = jwe = self.jwt _alg = jwe.headers["alg"] if alg and alg != _alg: raise WrongEncryptionAlgorithm() # Find appropriate keys if keys: keys = self.pick_keys(keys, use="enc", alg=_alg) else: keys = self.pick_keys(self._get_keys(), use="enc", alg=_alg) if not keys and not cek: raise NoSuitableDecryptionKey(_alg) if _alg in ["RSA-OAEP", "RSA1_5"]: decrypter = JWE_RSA(**self._dict) elif _alg.startswith("A") and _alg.endswith("KW"): decrypter = JWE_SYM(self.msg, **self._dict) elif _alg.startswith("ECDH-ES"): # ECDH-ES Requires the Server ECDH-ES Key to be set if not keys: raise NoSuitableECDHKey(_alg) decrypter = JWE_EC(**self._dict) cek = decrypter.dec_setup(token, key=keys[0]) else: raise NotSupportedAlgorithm if cek: try: msg = decrypter.decrypt(as_bytes(token), None, cek=cek) self["cek"] = decrypter.cek if 'cek' in decrypter else None except (KeyError, DecryptionFailed): pass else: logger.debug("Decrypted message using exiting CEK") return msg for key in keys: _key = key.encryption_key(alg=_alg, private=False) try: msg = decrypter.decrypt(as_bytes(token), _key) self["cek"] = decrypter.cek if 'cek' in decrypter else None except (KeyError, DecryptionFailed): pass else: logger.debug( "Decrypted message using key with kid=%s" % key.kid) return msg raise DecryptionFailed( "No available key that could decrypt the message")
def index(self, iss=''): cherrypy.response.headers['Content-Type'] = 'application/jwt' if iss: if isinstance(iss, list): return as_bytes(self.bundle.create_signed_bundle(iss_list=iss)) else: return as_bytes( self.bundle.create_signed_bundle(iss_list=[iss])) else: return as_bytes(self.bundle.create_signed_bundle())
def generate_request_uris(self, request_dir): """ Need to generate a path that is unique for the OP combo. This is before there is a client_id. :return: A list of uris """ m = hashlib.sha256() m.update(as_bytes(self.provider_info['issuer'])) m.update(as_bytes(self.base_url)) frag = rndstr() return '{}{}/{}#{}'.format(self.base_url, request_dir, m.hexdigest(), frag)
def test_client_secret_jwt(self, client): client.token_endpoint = "https://example.com/token" client.provider_info = { 'issuer': 'https://example.com/', 'token_endpoint': "https://example.com/token" } csj = ClientSecretJWT(client) cis = AccessTokenRequest() csj.construct(cis, algorithm="HS256", authn_endpoint='userinfo') assert cis["client_assertion_type"] == JWT_BEARER assert "client_assertion" in cis cas = cis["client_assertion"] _jwt = JWT().unpack(cas) jso = _jwt.payload() assert _eq(jso.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert _jwt.headers == {'alg': 'HS256'} _rj = JWS() info = _rj.verify_compact( cas, [SYMKey(k=b64e(as_bytes(client.client_secret)))]) assert _eq(info.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert info['aud'] == [client.provider_info['issuer']]
def index(self, op, **kwargs): if cherrypy.request.method == "OPTIONS": cherrypy_cors.preflight(allowed_methods=["GET"], origins='*', allowed_headers='Authorization') else: try: authz = cherrypy.request.headers['Authorization'] except KeyError: authz = None try: assert authz.startswith("Bearer") except AssertionError: op.events.store(EV_FAULT, "Bad authorization token") cherrypy.HTTPError(400, "Bad authorization token") tok = authz[7:] try: _claims = op.claim_access_token[tok] except KeyError: op.events.store(EV_FAULT, "Bad authorization token") cherrypy.HTTPError(400, "Bad authorization token") else: # one time token del op.claim_access_token[tok] _info = Message(**_claims) jwt_key = op.keyjar.get_signing_key() op.events.store(EV_RESPONSE, _info.to_dict()) cherrypy.response.headers["content-type"] = 'application/jwt' return as_bytes(_info.to_jwt(key=jwt_key, algorithm="RS256"))
def session_unchange(self, **kwargs): try: _state = kwargs['state'] except KeyError: pass else: if _state == 'unchanged': self.tester.conv.events.store( 'SessionState', 'Session state verified unchanged') _index = self.tester.sh['index'] _index += 1 return self.tester.run_flow(self.tester.conv.test_id, index=_index) else: # display session_verify.html again self.session_checks['unchanged'] += 1 self.tester.conv.events.store( 'SessionState', 'Session check {} returned: {}'.format( self.session_checks['unchanged'], _state)) if self.session_checks['unchanged'] == self.max_checks: self.tester.conv.events.store(EV_FAULT, 'Session state not unchanged') return self.main_page() else: _msg = self.tester.inut.pre_html['session_verify.html'] _csi = self.tester.conv.entity.provider_info['check_session_iframe'] _mod_msg = _msg.replace("{check_session_iframe}", _csi) return as_bytes(_mod_msg)
def index(self): try: if self.sh.session_init(): return as_bytes(self.info.flow_list()) else: try: _url = "{}opresult#{}".format(self.kwargs['base_url'], self.sh["testid"][0]) cherrypy.HTTPRedirect(_url) except KeyError: return as_bytes(self.info.flow_list()) except cherrypy.HTTPRedirect: raise except Exception as err: exception_trace("display_test_list", err) cherrypy.HTTPError(message=str(err))
def session_change(self, **kwargs): try: _state = kwargs['state'] except KeyError: pass else: logger.debug('Session state: {}'.format(kwargs)) self.tester.conv.events.store( 'SessionState', 'Session check returned: {}'.format(_state)) if _state == 'changed': self.tester.conv.events.store(EV_CONDITION, State('Done', status=OK)) self.tester.store_result() self.opresult() else: # display after_logout.html again self.session_checks['changed'] += 1 logger.debug('{} session check'.format(self.session_checks[ 'changed'])) self.tester.conv.events.store( 'SessionState', 'Session check {} returned: {}'.format( self.session_checks['unchanged'], _state)) if self.session_checks['changed'] >= self.max_checks: self.tester.conv.events.store(EV_FAULT, 'Session state not changed') return self.main_page() else: _msg = self.tester.inut.pre_html['after_logout.html'] _csi = self.tester.conv.entity.provider_info[ 'check_session_iframe'] _mod_msg = _msg.replace("{check_session_iframe}", _csi) return as_bytes(_mod_msg)
def restart(self, iss, tag, ev): """ Restart a test instance :param iss: :param tag: :param ev: :return: """ logger.info('restart test tool: {} {}'.format(iss, tag)) uqp, qp = unquote_quote(iss, tag) url = self.app.run_test_instance(*qp) if isinstance(url, Response): return conv_response(None, url) if url: # redirect back to entity page loc = '{}entity/{}'.format(self.rest.base_url, qp[0]) raise cherrypy.HTTPRedirect(loc) else: args = { 'title': "Action Failed", 'base': self.baseurl, 'note': 'Could not restart your test instance'} _msg = self.html['message.html'].format(**args) return as_bytes(_msg)
def __init__(self, kty="oct", alg="", use="", kid="", key=None, x5c=None, x5t="", x5u="", k="", mtrl="", **kwargs): Key.__init__(self, kty, alg, use, kid, as_bytes(key), x5c, x5t, x5u, **kwargs) self.k = k if not self.key and self.k: if isinstance(self.k, str): self.k = self.k.encode("utf-8") self.key = b64d(bytes(self.k))
def list_tag(self, iiss): uqp, qp = unquote_quote(iiss) logger.info('List all tags for "{}"'.format(uqp[0])) iss = uqp[0] qiss = qp[0] try: fils = os.listdir(os.path.join(self.entpath, qiss)) except FileNotFoundError: logger.warning('No such Issuer exists') return b"No such Issuer exists" active = dict() tags = [] for fil in fils: tag = unquote_plus(fil) active[tag] = isrunning(iss, tag) tags.append(tag) logger.info('tags: {}'.format(tags)) self.assigned_ports.load() _msg = self.prehtml['list_tag.html'].format( item_table=item_table(qiss, tags, active, self.assigned_ports, self.test_tool_base), iss=iss, version=self.version ) return as_bytes(_msg)
def update(self, iss, tag, ev=None, **kwargs): """ Displays interface for updating configuration :param iss: Issuer ID :param tag: tag :param ev: Event instance :param kwargs: keyword arguments :return: """ logger.debug('update test tool configuration: {} {}'.format(iss, tag)) uqp, qp = unquote_quote(iss, tag) try: _format, _conf = self.rest.read_conf(qp[0], qp[1]) except TypeError: _msg = "No such test tool configuration" logger.info(_msg) else: logger.info('config: {}'.format(_conf)) dicts, state, multi, notes = update_config(_conf, self.tool_params) action = "{}/run/{}/{}".format('', qp[0], qp[1]) _msg = self.html['instance.html'].format( display=display(dicts, state, multi, notes, action), version=self.version ) return as_bytes(_msg)
def index(self, op, **kwargs): if cherrypy.request.method == "OPTIONS": cherrypy_cors.preflight( allowed_methods=["POST", "GET"], origins='*', allowed_headers=['Authorization', 'content-type']) else: store_request(op, 'Logout') logger.debug('LogoutRequest: {}'.format(kwargs)) op.events.store(EV_REQUEST, kwargs) # Normally the user would here be confronted with a logout # verification page. We skip that and assumes she said yes. _info = op.unpack_signed_jwt(kwargs['sjwt']) logger.debug("SJWT unpacked: {}".format(_info)) # Before continuing make sure only the channel under test is used # for cid, _c_info in op.cdb.items(): try: res = op.do_verified_logout(alla=True, **_info) except ConnectionRefusedError as err: logger.error(err) raise cherrypy.HTTPError(message="Connection Refused: {}".format(err)) except Exception as err: logger.exception(err) raise cherrypy.HTTPError(message="{}".format(err)) if res == {}: # Failed raise cherrypy.HTTPError( message="Backchannel logout failed. No Frontchannel logout defined") _iframes = [] if res: try: _iframes = res['iframe'] except (KeyError, TypeError): pass _state = _info.get("state") if _state: _redirect_uri = "{}?{}".format(_info['redirect_uri'], urlencode({"state":_info["state"]})) else: _redirect_uri = _info['redirect_uri'] _body = LOGOUT_HTML_BODY.replace('{size}', str(len(_iframes))) _body = _body.replace('{frames}', ''.join(_iframes)) _body = _body.replace('{timeout}', '30') _body = _body.replace('{postLogoutRedirectUri}', _redirect_uri) try: cookies = res['cookie'] except (KeyError, TypeError): pass else: for tag, val in cookies: logger.debug("Response cookie: %s", val) cherrypy.response.cookie.load(val) return as_bytes("\n".join([LOGOUT_HTML_HEAD, _body]))
def update(self, iss, tag, ev=None, **kwargs): """ Displays interface for updating configuration :param iss: Issuer ID :param tag: tag :param ev: Event instance :param kwargs: keyword arguments :return: """ logger.debug('update test tool configuration: {} {}'.format(iss, tag)) uqp, qp = unquote_quote(iss, tag) try: _format, _conf = self.rest.read_conf(qp[0], qp[1]) except TypeError: _msg = "No such test tool configuration" logger.info(_msg) else: logger.info('config: {}'.format(_conf)) dicts, state, multi, notes = update_config(_conf, self.tool_params) action = "{}/run/{}/{}".format('', qp[0], qp[1]) _msg = self.html['instance.html'].format(display=display( dicts, state, multi, notes, action), version=self.version) return as_bytes(_msg)
def __init__(self, base_url='', registration_info=None, flow_type='code', federation_entity=None, hash_seed="", scope=None, verify_ssl=False, **kwargs): self.federation_entity = federation_entity self.flow_type = flow_type self.registration_info = registration_info self.base_url = base_url self.hash_seed = as_bytes(hash_seed) self.scope = scope or ['openid'] self.verify_ssl = verify_ssl self.extra = kwargs self.access_token_response = AccessTokenResponse self.client_cls = client.Client self.authn_method = None self.issuer2rp = {} self.state2issuer = {} self.hash2issuer = {}
def create_callback(self, issuer): _hash = hashlib.sha256() _hash.update(self.hash_seed) _hash.update(as_bytes(issuer)) _hex = _hash.hexdigest() self.hash2issuer[_hex] = issuer return "{}/authz_cb/{}".format(self.base_url, _hex)
def restart(self, iss, tag, ev): """ Restart a test instance :param iss: :param tag: :param ev: :return: """ logger.info('restart test tool: {} {}'.format(iss, tag)) uqp, qp = unquote_quote(iss, tag) url = self.app.run_test_instance(*qp) if isinstance(url, Response): return conv_response(None, url) if url: # redirect back to entity page loc = '{}entity/{}'.format(self.rest.base_url, qp[0]) raise cherrypy.HTTPRedirect(loc) else: args = { 'title': "Action Failed", 'base': self.baseurl, 'note': 'Could not restart your test instance' } _msg = self.html['message.html'].format(**args) return as_bytes(_msg)
def get_client_id(cdb, req, authn): """ Verify the client and return the client id :param req: The request :param authn: Authentication information from the HTTP header :return: """ logger.debug("REQ: %s" % sanitize(req.to_dict())) if authn: if authn.startswith("Basic "): logger.debug("Basic auth") (_id, _secret) = base64.b64decode(authn[6:].encode("utf-8")).decode("utf-8").split(":") _bid = as_bytes(_id) _cinfo = None try: _cinfo = cdb[_id] except KeyError: try: _cinfo[_bid] except AttributeError: pass if not _cinfo: logger.debug("Unknown client_id") raise FailedAuthentication("Unknown client_id") else: if not valid_client_info(_cinfo): logger.debug("Invalid Client info") raise FailedAuthentication("Invalid Client") if _secret != _cinfo["client_secret"]: logger.debug("Incorrect secret") raise FailedAuthentication("Incorrect secret") else: if authn[:6].lower() == "bearer": logger.debug("Bearer auth") _token = authn[7:] else: raise FailedAuthentication("AuthZ type I don't know") try: _id = cdb[_token] except KeyError: logger.debug("Unknown access token") raise FailedAuthentication("Unknown access token") else: try: _id = str(req["client_id"]) if _id not in cdb: logger.debug("Unknown client_id") raise FailedAuthentication("Unknown client_id") if not valid_client_info(cdb[_id]): raise FailedAuthentication("Invalid client_id") except KeyError: raise FailedAuthentication("Missing client_id") return _id
def list_tag(self, iiss): uqp, qp = unquote_quote(iiss) logger.info('List all tags for "{}"'.format(uqp[0])) iss = uqp[0] qiss = qp[0] try: fils = os.listdir(os.path.join(self.entpath, qiss)) except FileNotFoundError: logger.warning('No such Issuer exists') return b"No such Issuer exists" active = dict() tags = [] for fil in fils: tag = unquote_plus(fil) active[tag] = isrunning(iss, tag) tags.append(tag) logger.info('tags: {}'.format(tags)) self.assigned_ports.load() _msg = self.prehtml['list_tag.html'].format(item_table=item_table( qiss, tags, active, self.assigned_ports, self.test_tool_base), iss=iss, version=self.version) return as_bytes(_msg)
def encrypt(self, key, iv="", cek="", **kwargs): _msg = as_bytes(self.msg) _args = self._dict try: _args["kid"] = kwargs["kid"] except KeyError: pass if 'params' in kwargs: if 'apu' in kwargs['params']: _args['apu'] = kwargs['params']['apu'] if 'apv' in kwargs['params']: _args['apv'] = kwargs['params']['apv'] if 'epk' in kwargs['params']: _args['epk'] = kwargs['params']['epk'] jwe = JWEnc(**_args) ctxt, tag, cek = super(JWE_EC, self).enc_setup(self["enc"], _msg, jwe.b64_encode_header(), cek, iv=iv) if 'encrypted_key' in kwargs: return jwe.pack(parts=[kwargs['encrypted_key'], iv, ctxt, tag]) return jwe.pack(parts=[iv, ctxt, tag])
def restart_instance(self, iss, tag, action='restart'): uqp, qp = unquote_quote(iss, tag) logger.info('{} iss="{}", tag="{}"'.format(action, uqp[0], uqp[1])) url = self.app.run_test_instance(qp[0], qp[1]) if isinstance(url, Response): return conv_response(None, url) if url: args = { 'title': "Action performed", 'base': self.baseurl, 'note': 'Your test instance "{iss}:{tag}" has been ' '{act} as <a href="{url}">{url}</a>'.format(iss=uqp[0], tag=uqp[1], url=url, act=action) } else: args = { 'title': "Action Failed", 'base': self.baseurl, 'note': 'Could not {} your test instance'.format(action) } _msg = self.html['message.html'].format(**args) return as_bytes(_msg)
def registration(self, **kwargs): logger.debug('Request headers: {}'.format(cherrypy.request.headers)) if cherrypy.request.method == "OPTIONS": cherrypy_cors.preflight( allowed_methods=["POST", "GET"], origins='*', allowed_headers=['Authorization', 'content-type']) elif cherrypy.request.method == "GET": _cinfo = self.op.cdb[kwargs['client_id']] for attr in ['redirect_uris', 'post_logout_redirect_uris']: try: _cinfo[attr] = unpack_redirect_uri(_cinfo[attr]) except KeyError: pass rr = RegistrationResponse(**_cinfo) cherrypy.response.headers['Content-Type'] = 'application/json' return as_bytes(json.dumps(rr.to_dict())) else: logger.debug('ClientRegistration kwargs: {}'.format(kwargs)) _request = None if cherrypy.request.process_request_body is True: _request = as_unicode(cherrypy.request.body.read()) logger.debug('request_body: {}'.format(_request)) try: if _request: resp = self.op.registration_endpoint(_request) else: resp = self.op.registration_endpoint(kwargs) except Exception as err: logger.error(err) raise cherrypy.HTTPError(message=str(err)) return conv_response(resp)
def index(self, op, **kwargs): if cherrypy.request.method == "OPTIONS": cherrypy_cors.preflight( allowed_methods=["GET"], origins='*', allowed_headers='Authorization') else: try: authz = cherrypy.request.headers['Authorization'] except KeyError: authz = None try: assert authz.startswith("Bearer") except AssertionError: op.events.store(EV_FAULT, "Bad authorization token") cherrypy.HTTPError(400, "Bad authorization token") tok = authz[7:] try: _claims = op.claim_access_token[tok] except KeyError: op.events.store(EV_FAULT, "Bad authorization token") cherrypy.HTTPError(400, "Bad authorization token") else: # one time token del op.claim_access_token[tok] _info = Message(**_claims) jwt_key = op.keyjar.get_signing_key() op.events.store(EV_RESPONSE, _info.to_dict()) cherrypy.response.headers["content-type"] = 'application/jwt' return as_bytes(_info.to_jwt(key=jwt_key, algorithm="RS256"))
def store(self, qiss, qtag, info): """ :param qiss: OP issuer qoute_plus converted :param qtag: test instance tag quote_plus converted :param info: test instance configuration as JSON document :return: HTTP Created is successful """ uqp, qp = unquote_quote(qiss, qtag) logger.info('Store config: iss="{}", tag="{}", info={}'.format( uqp[0], uqp[1], info)) # verify the soundness of the information if isinstance(info, dict): info = json.dumps(info) else: try: json.loads(info) except Exception as err: _desc = 'Bogus replacement info!: {}'.format(info) logger.error(_desc) return cherrypy.HTTPError(404, _desc) self.write(qiss, qtag, info) fname = '{}{}/{}'.format(self.base_url, qiss, qtag) cherrypy.response.status = 201 return as_bytes(fname)
def static(environ, start_response, path): logger.info("[static]sending: %s" % (path,)) headers = [] try: bytes = open(path, 'rb').read() if path.endswith(".ico"): headers.append(('Content-Type', "image/x-icon")) elif path.endswith(".html"): headers.append(('Content-Type', 'text/html')) elif path.endswith(".json"): headers.append(('Content-Type', 'application/json')) elif path.endswith(".txt"): headers.append(('Content-Type', 'text/plain')) elif path.endswith(".css"): headers.append(('Content-Type', 'text/css')) elif path.endswith(".tar"): headers.append(('Content-Type', 'application/x-tar')) else: headers.append(('Content-Type', 'text/plain')) start_response('200 OK', headers) try: text = as_unicode(bytes) text = as_bytes(text.encode('utf8')) except (ValueError, UnicodeDecodeError): text = bytes except AttributeError: text = bytes resp = do_response(Response, text) except IOError: resp = do_response(NotFound, path) return resp(environ, start_response)
def create(self, **kwargs): logger.info('create test tool configuration: {} {}'.format( kwargs['iss'], kwargs['tag'])) uqp, qp = unquote_quote(kwargs['iss'], kwargs['tag']) if not uqp[0].startswith('https://') and not uqp[0].startswith( 'http://'): err = 'issuer value must start with "https://" or "http://"' logger.error(err) return as_bytes('Sorry failed to create: {}'.format(err)) # construct profile try: profile = to_profile(kwargs) except KeyError as err: logger.error(err) return as_bytes('Sorry failed to create: {}'.format(err)) _ent_conf = create_model(profile, ent_info_path=self.ent_info_path) if not do_discovery(profile): _ent_conf['client']['provider_info']['issuer'] = kwargs['iss'] if not do_registration(profile): # need to create a redirect_uri, means I need to register a port _port = self.app.assigned_ports.register_port( kwargs['iss'], kwargs['tag']) if self.app.test_tool_base.endswith('/'): _base = self.app.test_tool_base[:-1] else: _base = self.app.test_tool_base _ent_conf['client']['registration_response'][ 'redirect_uris'] = '[ "{}:{}/authz_cb", "{}:{}/authz_post" ]'.format( _base, _port, _base, _port) _ent_conf['tool']['issuer'] = uqp[0] _ent_conf['tool']['tag'] = uqp[1] _ent_conf['tool']['profile'] = profile _ent_conf.update(from_profile(profile)) logger.info("Test tool config: {}".format(_ent_conf)) self.rest.write(qp[0], qp[1], _ent_conf) # Do a redirect raise cherrypy.HTTPRedirect('/action/update?iss={}&tag={}'.format( qp[0], qp[1]))
def index(self, item='', **kwargs): if cherrypy.request.method == "OPTIONS": cherrypy_cors.preflight( allowed_methods=["GET"], origins='*', allowed_headers=['Authorization', 'content-type']) else: return as_bytes(self.smsfs[item])
def create(self, **kwargs): logger.info( 'create test tool configuration: {} {}'.format(kwargs['iss'], kwargs['tag'])) uqp, qp = unquote_quote(kwargs['iss'], kwargs['tag']) if not uqp[0].startswith('https://') and not uqp[0].startswith('http://'): err = 'issuer value must start with "https://" or "http://"' logger.error(err) return as_bytes('Sorry failed to create: {}'.format(err)) # construct profile try: profile = to_profile(kwargs) except KeyError as err: logger.error(err) return as_bytes('Sorry failed to create: {}'.format(err)) _ent_conf = create_model(profile, ent_info_path=self.ent_info_path) if not do_discovery(profile): _ent_conf['client']['provider_info']['issuer'] = kwargs['iss'] if not do_registration(profile): # need to create a redirect_uri, means I need to register a port _port = self.app.assigned_ports.register_port(kwargs['iss'], kwargs['tag']) if self.app.test_tool_base.endswith('/'): _base = self.app.test_tool_base[:-1] else: _base = self.app.test_tool_base _ent_conf['client']['registration_response'][ 'redirect_uris'] = '[ "{}:{}/authz_cb", "{}:{}/authz_post" ]'.format(_base, _port, _base, _port) _ent_conf['tool']['issuer'] = uqp[0] _ent_conf['tool']['tag'] = uqp[1] _ent_conf['tool']['profile'] = profile _ent_conf.update(from_profile(profile)) logger.info("Test tool config: {}".format(_ent_conf)) self.rest.write(qp[0], qp[1], _ent_conf) # Do a redirect raise cherrypy.HTTPRedirect( '/action/update?iss={}&tag={}'.format(qp[0], qp[1]))
def index(self, key=''): cherrypy.response.headers['Content-Type'] = 'application/jwt' if key: try: return as_bytes(self.mds[key]) except KeyError: raise cherrypy.HTTPError(404, 'Could not find {}'.format(key)) else: raise cherrypy.HTTPError(400, 'Bad Request')
def index(self, iss, jwks, ms, **kwargs): _kj = KeyJar() _kj.import_jwks(json.loads(jwks), iss) op = Operator() try: _pi = op.unpack_metadata_statement(jwt_ms=ms, keyjar=_kj, cls=MetadataStatement) response = json.dumps(_pi.result.to_dict(), sort_keys=True, indent=2, separators=(',', ': ')) cherrypy.response.headers['Content-Type'] = 'text/plain' return as_bytes(response) except (RegistrationError, ParameterError, MissingSigningKey) as err: raise cherrypy.HTTPError( 400, as_bytes('Invalid Metadata statement: {}'.format(err)))
def conv_response(resp): if not isinstance(resp, Response): return as_bytes(resp) _stat = int(resp._status.split(' ')[0]) # if self.mako_lookup and self.mako_template: # argv["message"] = message # mte = self.mako_lookup.get_template(self.mako_template) # return [mte.render(**argv)] if _stat < 300: cherrypy.response.status = _stat for key, val in resp.headers: cherrypy.response.headers[key] = val return as_bytes(resp.message) elif 300 <= _stat < 400: raise cherrypy.HTTPRedirect(resp.message, status=_stat) else: raise cherrypy.HTTPError(_stat, message=resp.message)
def add_symmetric(self, issuer, key, usage=None): if issuer not in self.issuer_keys: self.issuer_keys[issuer] = [] _key = b64e(as_bytes(key)) if usage is None: self.issuer_keys[issuer].append(self.keybundle_cls([{"kty": "oct", "k": _key}])) else: for use in usage: self.issuer_keys[issuer].append(self.keybundle_cls([{"kty": "oct", "k": _key, "use": use}]))
def index(self, op): if cherrypy.request.method == "OPTIONS": logger.debug('Request headers: {}'.format( cherrypy.request.headers)) cherrypy_cors.preflight( allowed_methods=["GET"], allowed_headers=['Authorization', 'content-type'], allow_credentials=True, origins='*') else: store_request(op, 'ProviderInfo') try: resp = op.create_fed_providerinfo() except Exception as err: raise cherrypy.HTTPError(message=as_bytes(err)) cherrypy.response.headers['Content-Type'] = 'application/json' # return as_bytes(resp.message) return as_bytes(json.dumps(resp.to_dict()))
def display_exception(self, exception_trace=''): """ So far only one known special response type :param exception_trace: :return: Bytes """ txt = [80 * '*', '\n', BANNER, '\n', 80 * '*', '\n', '\n', '\n'] txt.extend(exception_trace) cherrypy.response.headers['Content-Type'] = 'text/plain' return as_bytes(txt)
def create_callback(self, issuer): _hash = hashlib.sha256() _hash.update(self.hash_seed) #_hash.update(rndstr(32)) _hash.update(as_bytes(issuer)) _hex = _hash.hexdigest() self.hash2issuer[_hex] = issuer return { 'code': "{}/authz_cb/{}".format(self.base_url, _hex), 'implicit': "{}/authz_im_cb/{}".format(self.base_url, _hex) }
def __call__(self, *args, **kwargs): _msg = self.inut.pre_html[self.pre_html] # Need to replace client_id, session_state, issuer and # session_change_url client_id = self.conv.entity.client_id _msg = _msg.replace('{client_id}', client_id) session_state = '' _msg = _msg.replace('{session_state}', session_state) issuer = self.conv.entity.provider_info['issuer'] _msg = _msg.replace('{issuer}', issuer) session_change_url = '' _msg = _msg.replace('{session_change_url}', session_change_url) return as_bytes(_msg)
def index(self): fils = os.listdir(self.entpath) # Remove examples try: fils.remove('https%3A%2F%2Fexample.com') except ValueError: pass _msg = self.prehtml['list_iss.html'].format( iss_table=iss_table('', fils), version=self.version ) return as_bytes(_msg)
def show_tag(self, iiss, itag): uqp, qp = unquote_quote(iiss, itag) logger.info('Show info on iss="{}", tag="{}"'.format(*uqp)) if find_test_instance(*uqp): active = '<div class="active"> Running </div>' else: active = '<div class="inactive"> Inactive </div>' info = open(os.path.join(self.entpath, *qp), 'r').read() _msg = self.prehtml['action.html'].format(path=qp[-1], active=active, display_info=info) return as_bytes(_msg)
def index(self, resource, rel): ev = init_events('/.well-known/webfinger', 'Test tool version:{}'.format(self.version)) ev.store(EV_REQUEST, Operation('WebFinger', resource=resource, rel=rel)) if rel != 'http://openid.net/specs/connect/1.0/issuer': ev.store(EV_FAULT, FailedOperation('Webfinger', error='unknown rel', rel=rel)) try: op_id, test_id = parse_resource(resource) except (ValueError, TypeError): logger.error('webfinger resource specification faulty') raise cherrypy.HTTPError( 400, 'webfinger resource specification faulty') else: write_events(ev, op_id, test_id) raise cherrypy.NotFound() try: op_id, test_id = parse_resource(resource) except (ValueError, TypeError): logger.error('webfinger resource specification faulty') raise cherrypy.HTTPError( 400, 'webfinger resource specification faulty') else: _path = '/'.join([op_id, test_id]) cnf = cherrypy.request.config subj = resource _base = cnf['base_url'] dummy = None # introducing an error if 'rp-discovery-webfinger-http-href' in resource: _base = _base.replace('https', 'http') if 'rp-discovery-webfinger-unknown-member' in resource: dummy = "foobar" if _base.endswith('/'): href = '{}{}'.format(_base, _path) else: href = '{}/{}'.format(_base, _path) ev.store(EV_RESPONSE, Operation('Webfinger', href=href, subj=resource, dummy=dummy)) write_events(ev, op_id, test_id) resp = self.srv.response(subj, href, dummy=dummy) cherrypy.response.headers['Content-Type'] = 'application/jrd+json' return as_bytes(resp)
def get_client_id(cdb, req, authn): """ Verify the client and return the client id :param req: The request :param authn: Authentication information from the HTTP header :return: """ logger.debug("REQ: %s" % sanitize(req.to_dict())) _secret = None if not authn: try: _id = str(req["client_id"]) except KeyError: raise FailedAuthentication("Missing client_id") elif authn.startswith("Basic "): logger.debug("Basic auth") (_id, _secret) = base64.b64decode(authn[6:].encode("utf-8")).decode("utf-8").split(":") # Either as string or encoded if _id not in cdb: _bid = as_bytes(_id) _id = _bid elif authn[:6].lower() == "bearer": logger.debug("Bearer auth") _token = authn[7:] try: _id = cdb[_token] except KeyError: logger.debug("Unknown access token") raise FailedAuthentication("Unknown access token") else: raise FailedAuthentication("AuthZ type I don't know") # We have the client_id by now, so let's verify it _cinfo = cdb.get(_id) if _cinfo is None: raise FailedAuthentication("Unknown client") if not valid_client_info(_cinfo): logger.debug("Invalid Client info") raise FailedAuthentication("Invalid Client") if _secret is not None: if _secret != _cinfo["client_secret"]: logger.debug("Incorrect secret") raise FailedAuthentication("Incorrect secret") # All should be good, so return it return _id
def encrypt(self, key, iv="", cek="", **kwargs): """ Produces a JWE using RSA algorithms :param key: RSA key :param context: :param iv: :param cek: :return: A jwe """ _msg = as_bytes(self.msg) if "zip" in self: if self["zip"] == "DEF": _msg = zlib.compress(_msg) else: raise ParameterError("Zip has unknown value: %s" % self["zip"]) kwarg_cek = cek or None _enc = self["enc"] cek, iv = self._generate_key_and_iv(_enc, cek, iv) self["cek"] = cek logger.debug("cek: %s, iv: %s" % ([c for c in cek], [c for c in iv])) _encrypt = RSAEncrypter(self.with_digest).encrypt _alg = self["alg"] if kwarg_cek: jwe_enc_key = '' elif _alg == "RSA-OAEP": jwe_enc_key = _encrypt(cek, key, 'pkcs1_oaep_padding') elif _alg == "RSA1_5": jwe_enc_key = _encrypt(cek, key) else: raise NotSupportedAlgorithm(_alg) jwe = JWEnc(**self.headers()) enc_header = jwe.b64_encode_header() ctxt, tag, key = self.enc_setup(_enc, _msg, enc_header, cek, iv) return jwe.pack(parts=[jwe_enc_key, iv, ctxt, tag])
def conv_response(op, resp): _stat = resp.status_code # if self.mako_lookup and self.mako_template: # argv["message"] = message # mte = self.mako_lookup.get_template(self.mako_template) # return [mte.render(**argv)] if _stat < 300: op.events.store(EV_RESPONSE, resp.message) cherrypy.response.status = _stat for key, val in resp.headers: cherrypy.response.headers[key] = val return as_bytes(resp.message) elif 300 <= _stat < 400: op.events.store('Redirect', resp.message) raise cherrypy.HTTPRedirect(resp.message, status=_stat) else: logger.debug("Error - Status:{}, message:{}".format(_stat, resp.message)) op.events.store(EV_FAULT, resp.message) raise cherrypy.HTTPError(_stat, message=resp.message)
def conv_response(events, resp): if not isinstance(resp, Response): return resp try: _stat = resp._status_code except AttributeError: # For backward compatibility _stat = int(resp._status.split(' ')[0]) if _stat < 300: events.store(EV_RESPONSE, resp.message) for key, val in resp.headers: cherrypy.response.headers[key] = val return as_bytes(resp.message) elif 300 <= _stat < 400: events.store('Redirect', resp.message) raise cherrypy.HTTPRedirect(resp.message) else: events.store(EV_FAULT, resp.message) raise cherrypy.HTTPError(_stat, resp.message)
def restart_instance(self, iss, tag, action='restart'): uqp, qp = unquote_quote(iss, tag) logger.info('{} iss="{}", tag="{}"'.format(action, uqp[0], uqp[1])) url = self.app.run_test_instance(qp[0], qp[1]) if isinstance(url, Response): return conv_response(None, url) if url: args = { 'title': "Action performed", 'base': self.baseurl, 'note': 'Your test instance "{iss}:{tag}" has been ' '{act} as <a href="{url}">{url}</a>'.format( iss=uqp[0], tag=uqp[1], url=url, act=action)} else: args = { 'title': "Action Failed", 'base': self.baseurl, 'note': 'Could not {} your test instance'.format(action)} _msg = self.html['message.html'].format(**args) return as_bytes(_msg)
def decrypt(self, token, keys=None, alg=None): jwe = JWEnc().unpack(token) # header, ek, eiv, ctxt, tag = token.split(b".") # self.parse_header(header) _alg = jwe.headers["alg"] if alg and alg != _alg: raise WrongEncryptionAlgorithm() if _alg in ["RSA-OAEP", "RSA1_5"]: decrypter = JWE_RSA(**self._dict) elif _alg.startswith("A") and _alg.endswith("KW"): decrypter = JWE_SYM(self.msg, **self._dict) else: raise NotSupportedAlgorithm if keys: keys = self._pick_keys(keys, use="enc", alg=_alg) else: keys = self._pick_keys(self._get_keys(), use="enc", alg=_alg) if not keys: raise NoSuitableDecryptionKey(_alg) for key in keys: _key = key.encryption_key(alg=_alg, private=False) try: msg = decrypter.decrypt(as_bytes(token), _key) except (KeyError, DecryptionFailed): pass else: logger.debug( "Decrypted message using key with kid=%s" % key.kid) return msg raise DecryptionFailed( "No available key that could decrypt the message")
def test_client_secret_jwt(self, client): client.token_endpoint = "https://example.com/token" client.provider_info = {'issuer': 'https://example.com/', 'token_endpoint': "https://example.com/token"} csj = ClientSecretJWT(client) cis = AccessTokenRequest() csj.construct(cis, algorithm="HS256", authn_endpoint='userinfo') assert cis["client_assertion_type"] == JWT_BEARER assert "client_assertion" in cis cas = cis["client_assertion"] _jwt = JWT().unpack(cas) jso = _jwt.payload() assert _eq(jso.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert _jwt.headers == {'alg': 'HS256'} _rj = JWS() info = _rj.verify_compact( cas, [SYMKey(k=b64e(as_bytes(client.client_secret)))]) assert _eq(info.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert info['aud'] == [client.provider_info['issuer']]
def __call__(self, *args, **kwargs): _msg = self.inut.pre_html[self.pre_html].format(**self.args()) return as_bytes(_msg)
def sha512_digest(msg): return hashlib.sha512(as_bytes(msg)).digest()
def index(self, profile): mandatory = [] optional = [] for fn in os.listdir(self.fdir): if fn.startswith('rp-') and fn.endswith('.json'): fname = os.path.join(self.fdir, fn) fp = open(fname, 'r') try: _info = json.load(fp) except Exception: continue fp.close() if "MTI" in _info and profile in _info['MTI']: _det_desc = replace_with_link(_info['detailed_description'], self.links) _exp_res = replace_with_link(_info['expected_result'], self.links) mandatory.append((fn[:-5], _det_desc, _exp_res, _info['group'])) else: try: rts = _info["capabilities"]["response_types_supported"] except KeyError: pass else: profs = [ABBR[x] for x in rts] if profile in profs: _det_desc = replace_with_link( _info['detailed_description'], self.links) _exp_res = replace_with_link( _info['expected_result'], self.links) optional.append((fn[:-5], _det_desc, _exp_res, _info['group'])) hl = self.headline.format(EXP[profile]) response = [ HTML_PRE, '<div class="panel panel-primary">', ' <div class="panel-heading">', ' <h3 class="panel-title">Mandatory</h3>', ' </div>', ' <div class="panel-body">' ] response.extend(test_list(mandatory, self.grps)) response += [ ' </div>', '</div>' '<div class="panel panel-primary">', ' <div class="panel-heading">', ' <h3 class="panel-title">Optional</h3>', ' </div>', ' <div class="panel-body">', ] response.extend(test_list(optional, self.grps)) response += [ ' </div>', '</div>', HTML_FOOTER.format(self.version), HTML_POST ] return as_bytes('\n'.join(response))
def sha384_digest(msg): return hashlib.sha384(as_bytes(msg)).digest()
def sha256_digest(msg): return hashlib.sha256(as_bytes(msg)).digest()