Exemple #1
0
    def code_grant_type(self, areq):
        # assert that the code is valid
        try:
            _info = self.sdb[areq["code"]]
        except KeyError:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Unknown access grant")
            return Response(err.to_json(),
                            content="application/json",
                            status="401 Unauthorized")

        authzreq = json.loads(_info['authzreq'])
        if 'code_verifier' in areq:
            try:
                _method = authzreq['code_challenge_method']
            except KeyError:
                _method = 'S256'

            resp = self.verify_code_challenge(areq['code_verifier'],
                                              authzreq['code_challenge'],
                                              _method)
            if resp:
                return resp

        if 'state' in areq:
            if self.sdb[areq['code']]['state'] != areq['state']:
                err = TokenErrorResponse(error="unauthorized_client")
                return Unauthorized(err.to_json(), content="application/json")

        resp = self.token_scope_check(areq, _info)
        if resp:
            return resp

        # If redirect_uri was in the initial authorization request
        # verify that the one given here is the correct one.
        if "redirect_uri" in _info:
            assert areq["redirect_uri"] == _info["redirect_uri"]

        issue_refresh = False
        if 'scope' in authzreq and 'offline_access' in authzreq['scope']:
            if authzreq['response_type'] == 'code':
                issue_refresh = True

        try:
            _tinfo = self.sdb.upgrade_to_token(areq["code"],
                                               issue_refresh=issue_refresh)
        except AccessCodeUsed:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Access grant used")
            return Response(err.to_json(),
                            content="application/json",
                            status="401 Unauthorized")

        logger.debug("_tinfo: %s" % _tinfo)

        atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo))

        logger.debug("AccessTokenResponse: %s" % atr)

        return Response(atr.to_json(), content="application/json")
Exemple #2
0
    def providerinfo_endpoint(self, **kwargs):
        _log_info = logger.info

        _log_info("@providerinfo_endpoint")
        try:
            _response = self.create_providerinfo()
            _log_info("provider_info_response: %s" % (_response.to_dict(), ))

            headers = [("Cache-Control", "no-store"), ("x-ffo", "bar")]
            if 'handle' in kwargs:
                (key, _) = kwargs['handle']
                if key.startswith(STR) and key.endswith(STR):
                    cookie = self.cookie_func(key, self.cookie_name, "pinfo",
                                              self.sso_ttl)
                    headers.append(cookie)

            resp = Response(_response.to_json(),
                            content="application/json",
                            headers=headers)
        except Exception:
            message = traceback.format_exception(*sys.exc_info())
            logger.error(message)
            resp = Response(message, content="html/text")

        return resp
Exemple #3
0
    def providerinfo_endpoint(self, handle="", **kwargs):
        _log_debug = logger.debug
        _log_info = logger.info

        _log_info("@providerinfo_endpoint")
        try:
            _response = self.conf_info

            #for endp in self.endpoints:
            #    _response[endp(None).name] = "%s%s" % (self.baseurl,
            # endp.etype)

            _log_info("provider_info_response: %s" % (_response.to_dict(), ))

            headers = [("Cache-Control", "no-store"), ("x-ffo", "bar")]
            if handle:
                (key, timestamp) = handle
                if key.startswith(STR) and key.endswith(STR):
                    cookie = self.cookie_func(key, self.cookie_name, "pinfo",
                                              self.sso_ttl)
                    headers.append(cookie)

            resp = Response(_response.to_json(),
                            content="application/json",
                            headers=headers)
        except Exception as err:
            message = traceback.format_exception(*sys.exc_info())
            logger.error(message)
            resp = Response(message, content="html/text")

        return resp
Exemple #4
0
    def __call__(self, cookie=None, policy_url=None, logo_url=None,
                 query="", **kwargs):
        """
        Put up the login form
        """
        if cookie:
            headers = [cookie]
        else:
            headers = []

        resp = Response(headers=headers)

        acr = None
        try:
            req = urlparse.parse_qs(query)
            acr = req["acr_values"][0]
        except:
            pass

        argv = {"login": "",
                "password": "",
                "action": "verify",
                "policy_url": policy_url,
                "logo_url": logo_url,
                "query": query,
                "acr" : acr}
        logger.info("do_authentication argv: %s" % argv)
        mte = self.template_lookup.get_template(self.mako_template)
        resp.message = mte.render(**argv)
        return resp
Exemple #5
0
    def introspection_endpoint_(self, user, **kwargs):
        """
        The endpoint URI at which the resource server introspects an RPT
        presented to it by a client.
        """

        request = kwargs["request"]
        logger.debug("requestor: %s, request: %s" % (user, request))
        ir = IntrospectionRequest().from_json(request)
        adb = self.get_adb(kwargs["client_id"])
        try:
            try:
                # requestor = self.rpt[ir["token"]]["requestor"]
                perms = adb.permit.get_accepted_by_rpt(user, ir["token"])
            except KeyError:
                response = BadRequest()
            else:
                if perms:
                    irep = IntrospectionResponse(active=True,
                                                 exp=perms[0]["exp"],
                                                 permissions=perms)
                    logger.debug("response: %s" % irep.to_json())
                    response = Response(irep.to_json(),
                                        content="application/json")
                else:
                    logger.info("No permissions bound to this RPT")
                    response = BadRequest()
        except ToOld:
            logger.info("RPT expired")
            irep = IntrospectionResponse(valid=False)
            response = Response(irep.to_json(), content="application/json")
        except KeyError:
            response = BadRequest()

        return response
Exemple #6
0
def display_log(environ, start_response):
    path = environ.get('PATH_INFO', '').lstrip('/')
    if path == "log":
        tail = environ["REMOTE_ADDR"]
        path = os.path.join(path, tail)
    elif path == "logs":
        path = "log"

    if os.path.isfile(path):
        return static(environ, start_response, path)
    elif os.path.isdir(path):
        item = []
        for (dirpath, dirnames, filenames) in os.walk(path):
            if dirnames:
                item = [(fn, os.path.join(path, fn)) for fn in dirnames]
                break
            if filenames:
                item = [(fn, os.path.join(path, fn)) for fn in filenames]
                break

        item.sort()
        resp = Response(mako_template="logs.mako",
                        template_lookup=LOOKUP,
                        headers=[])
        argv = {"logs": item}

        return resp(environ, start_response, **argv)
    else:
        resp = Response("No saved logs")
        return resp(environ, start_response)
Exemple #7
0
    def token_endpoint(self, authn="", **kwargs):
        """
        This is where clients come to get their access tokens
        """

        _sdb = self.sdb

        logger.debug("- token -")
        body = kwargs["request"]
        logger.debug("body: %s" % sanitize(body))

        areq = AccessTokenRequest().deserialize(body, "urlencoded")

        try:
            self.client_authn(self, areq, authn)
        except FailedAuthentication as err:
            logger.error(err)
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug("AccessTokenRequest: %s" % sanitize(areq))

        try:
            assert areq["grant_type"] == "authorization_code"
        except AssertionError:
            err = TokenErrorResponse(error="invalid_request",
                                     error_description="Wrong grant type")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        # assert that the code is valid
        _info = _sdb[areq["code"]]

        resp = self.token_scope_check(areq, _info)
        if resp:
            return resp

        # If redirect_uri was in the initial authorization request
        # verify that the one given here is the correct one.
        if "redirect_uri" in _info:
            assert areq["redirect_uri"] == _info["redirect_uri"]

        try:
            _tinfo = _sdb.upgrade_to_token(areq["code"], issue_refresh=True)
        except AccessCodeUsed:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Access grant used")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug("_tinfo: %s" % sanitize(_tinfo))

        atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo))

        logger.debug("AccessTokenResponse: %s" % sanitize(atr))

        return Response(atr.to_json(), content="application/json")
Exemple #8
0
    def __call__(self, cookie=None, end_point_index=0, **kwargs):
        """Put up the login form."""
        resp = Response()

        argv = self.templ_arg_func(end_point_index, **kwargs)
        logger.info("do_authentication argv: %s" % sanitize(argv))
        mte = self.template_lookup.get_template(self.mako_template)
        resp.message = mte.render(**argv).decode("utf-8")
        return resp
Exemple #9
0
    def __call__(self, cookie=None, end_point_index=0, **kwargs):
        """
        Put up the login form
        """
        resp = Response()

        argv = self.templ_arg_func(end_point_index, **kwargs)
        logger.info("do_authentication argv: %s" % sanitize(argv))
        mte = self.template_lookup.get_template(self.mako_template)
        resp.message = mte.render(**argv).decode("utf-8")
        return resp
Exemple #10
0
    def __call__(self, cookie=None, end_point_index=0, **kwargs):
        """
        Put up the login form
        """
        resp = Response()
        self.nerror = 0
        template_args = self.templ_arg_func(end_point_index, **kwargs)

        # mako_template_engine = self.template_lookup.get_template('totp_form.mako')
        mako_template_engine = self.template_lookup.get_template(self.mako_template)
        resp.message = mako_template_engine.render(**template_args).decode("utf-8")

        return resp
Exemple #11
0
def trace():
    resp = None
    for handler in logger.handlers:
        if isinstance(handler, TraceHandler):
            msg = [(e["created"], e["msg"]) for e in handler.buffer]
            resp = Response(json.dumps(msg))
            break

    # Should I flush the buffer ?
    #handler.flush()
    if resp:
        return resp
    else:
        return Response([])
Exemple #12
0
    def get_iss(self):
        resp = Response(mako_template="new_iss.mako",
                        template_lookup=self.lookup,
                        headers=[])

        args = {'base': ''}
        return resp(self.environ, self.start_response, **args)
Exemple #13
0
    def token_endpoint(self, dtype='urlencoded', **kwargs):
        atr = AccessTokenRequest().deserialize(kwargs["request"], dtype)
        resp = super(PoPProvider, self).token_endpoint(**kwargs)

        if "token_type" not in atr or atr["token_type"] != "pop":
            return resp

        client_public_key = base64.urlsafe_b64decode(
            atr["key"].encode("utf-8")).decode("utf-8")
        pop_key = json.loads(client_public_key)
        atr = AccessTokenResponse().deserialize(resp.message, method="json")
        data = self.sdb.read(atr["access_token"])

        jwt = {
            "iss": self.baseurl,
            "aud": self.baseurl,
            "exp": data["token_expires_at"],
            "nbf": int(time.time()),
            "cnf": {
                "jwk": pop_key
            }
        }
        _jws = JWS(jwt, alg="RS256").sign_compact(
            self.keyjar.get_signing_key(owner=""))
        self.access_tokens[_jws] = data["access_token"]

        atr["access_token"] = _jws
        atr["token_type"] = "pop"
        return Response(atr.to_json(), content="application/json")
Exemple #14
0
    def do_config(self, sid='', start_page='', params='', **args):
        resp = Response(mako_template="config.mako",
                        template_lookup=self.kwargs['lookup'],
                        headers=[])

        if sid:
            _url = os.path.join(self.base_url, sid)
        else:
            _url = self.base_url

        try:
            test_id = args['test_id']
        except KeyError:
            test_id = ''

        kwargs = {
            'start_page': start_page,
            'params': params,
            'issuer': _url,
            'profiles': list(self.kwargs['op_profiles'].keys()),
            'selected': self.selected,
            'sid': sid,
            'base': self.base_url,
            'test_id': test_id
        }
        return resp(self.inut.environ, self.inut.start_response, **kwargs)
Exemple #15
0
def sorry_response(environ, start_response, homepage, err):
    resp = Response(mako_template="sorry.mako",
                    template_lookup=LOOKUP,
                    headers=[])
    argv = {"htmlpage": homepage,
            "error": str(err)}
    return resp(environ, start_response, **argv)
Exemple #16
0
    def revocation_endpoint(self, authn='', request=None, **kwargs):
        """
        Implements RFC7009 allows a client to invalidate an access or refresh
        token.

        :param authn: Client Authentication information
        :param request: The revocation request
        :param kwargs:
        :return:
        """

        trr = TokenRevocationRequest().deserialize(request, "urlencoded")

        resp = self.get_token_info(authn, trr, 'revocation_endpoint')

        if isinstance(resp, Response):
            return resp
        else:
            client_id, token_type, _info = resp

        logger.info('{} token revocation: {}'.format(client_id, trr.to_dict()))

        try:
            self.sdb.token_factory[token_type].invalidate(trr['token'])
        except KeyError:
            return BadRequest()
        else:
            return Response('OK')
Exemple #17
0
    def refresh_token_grant_type(self, areq):
        at = self.token_handler.refresh_access_token(self.baseurl,
                                                     areq['access_token'],
                                                     'refresh_token')

        atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **at))
        return Response(atr.to_json(), content="application/json")
Exemple #18
0
    def load_keys(self, request, client_id, client_secret):
        try:
            self.keyjar.load_keys(request, client_id)
            try:
                n_keys = len(self.keyjar[client_id])
                msg = "Found {} keys for client_id={}"
                logger.debug(msg.format(n_keys, client_id))
            except KeyError:
                pass
        except Exception as err:
            msg = "Failed to load client keys: {}"
            logger.error(msg.format(sanitize(request.to_dict())))
            logger.error("%s", err)
            err = ClientRegistrationError(
                error="invalid_configuration_parameter",
                error_description="%s" % err)
            return Response(err.to_json(),
                            content="application/json",
                            status_code="400 Bad Request")

        # Add the client_secret as a symmetric key to the keyjar
        _kc = KeyBundle([{
            "kty": "oct",
            "key": client_secret,
            "use": "ver"
        }, {
            "kty": "oct",
            "key": client_secret,
            "use": "sig"
        }])
        try:
            self.keyjar[client_id].append(_kc)
        except KeyError:
            self.keyjar[client_id] = [_kc]
Exemple #19
0
def opbyuid(environ, start_response):
    resp = Response(mako_template="opbyuid.mako",
                    template_lookup=LOOKUP,
                    headers=[])
    argv = {
    }
    return resp(environ, start_response, **argv)
Exemple #20
0
    def __call__(self, cookie=None, end_point_index=0, **kwargs):
        """
        Put up the login form
        """
        # if cookie:
        #     headers = [cookie]
        # else:
        #     headers = []

        resp = Response()

        argv = self.templ_arg_func(end_point_index, **kwargs)
        logger.info("do_authentication argv: %s" % argv)
        mte = self.template_lookup.get_template(self.mako_template)
        resp.message = mte.render(**argv)
        return resp
Exemple #21
0
    def password_grant_type(self, areq):
        """
        Token authorization using Resource owner password credentials.

        RFC6749 section 4.3
        """
        # `Any` comparison tries a first broker, so we either hit an IndexError or get a method
        try:
            authn, authn_class_ref = self.pick_auth(areq, "any")
        except IndexError:
            err = TokenErrorResponse(error="invalid_grant")
            return Unauthorized(err.to_json(), content="application/json")
        identity, _ts = authn.authenticated_as(
            username=areq["username"], password=areq["password"]
        )
        if identity is None:
            err = TokenErrorResponse(error="invalid_grant")
            return Unauthorized(err.to_json(), content="application/json")
        # We are returning a token
        areq["response_type"] = ["token"]
        authn_event = AuthnEvent(
            identity["uid"],
            identity.get("salt", ""),
            authn_info=authn_class_ref,
            time_stamp=_ts,
        )
        sid = self.setup_session(areq, authn_event, self.cdb[areq["client_id"]])
        _at = self.sdb.upgrade_to_token(self.sdb[sid]["code"], issue_refresh=True)
        atr_class = self.server.message_factory.get_response_type("token_endpoint")
        atr = atr_class(**by_schema(atr_class, **_at))
        return Response(
            atr.to_json(), content="application/json", headers=OAUTH2_NOCACHE_HEADERS
        )
Exemple #22
0
    def providerinfo_endpoint(self, handle="", **kwargs):
        _log_info = logger.info

        _log_info("@providerinfo_endpoint")
        try:
            _response = self.create_providerinfo()
            msg = "provider_info_response: {}"
            _log_info(msg.format(sanitize(_response.to_dict())))
            if self.events:
                self.events.store("Protocol response", _response)

            headers = [("Cache-Control", "no-store")]
            if handle:
                (key, timestamp) = handle
                if key.startswith(STR) and key.endswith(STR):
                    cookie = self.cookie_func(key, self.cookie_name, "pinfo",
                                              self.sso_ttl)
                    headers.append(cookie)

            resp = Response(_response.to_json(),
                            content="application/json",
                            headers=headers)
        except Exception:
            message = traceback.format_exception(*sys.exc_info())
            logger.error(message)
            resp = error_response("service_error", message)

        return resp
Exemple #23
0
    def load_keys(self, request, client_id, client_secret):
        try:
            self.keyjar.load_keys(request, client_id)
            try:
                logger.debug("keys for %s: [%s]" % (client_id, ",".join(
                    ["%s" % x for x in self.keyjar[client_id]])))
            except KeyError:
                pass
        except Exception as err:
            logger.error("Failed to load client keys: %s" % request.to_dict())
            logger.error("%s", err)
            err = ClientRegistrationError(
                error="invalid_configuration_parameter",
                error_description="%s" % err)
            return Response(err.to_json(),
                            content="application/json",
                            status="400 Bad Request")

        # Add the client_secret as a symmetric key to the keyjar
        _kc = KeyBundle([{
            "kty": "oct",
            "key": client_secret,
            "use": "ver"
        }, {
            "kty": "oct",
            "key": client_secret,
            "use": "sig"
        }])
        try:
            self.keyjar[client_id].append(_kc)
        except KeyError:
            self.keyjar[client_id] = [_kc]
Exemple #24
0
    def do_put(self, path, info):
        """
        UPDATE: PUT /{collection}/{id}

        """

        _name = self.resource_name(path)
        try:
            f = open(_name, "w")
        except IOError:
            return ErrorResponse(error="not_allowed")

        head, tail = os.path.split(_name)

        try:
            _ji = json.loads(info)
        except ValueError:
            return ErrorResponse(error="not_json")

        try:
            assert _ji["_id"] == tail
        except KeyError:
            _ji["_id"] = tail
        except AssertionError:
            return ErrorResponse(error="not_allowed")

        f.write(json.dumps(_ji))
        f.close()
        return Response(json.dumps({"_id": tail}),
                        headers=[("Location",
                                  "%s/%s" % (self.baseurl, self.url(_name)))])
Exemple #25
0
    def delete_instance(self, iss, tag):
        resp = Response(mako_template="new_instance.mako",
                        template_lookup=self.lookup,
                        headers=[])

        arg = {'base': ''}
        return resp(self.environ, self.start_response, **arg)
Exemple #26
0
    def providerinfo_endpoint(self, handle="", **kwargs):
        """
        The Provider info endpoint. A request for provider info should be
        handled by this method. It will work as well for requests from
        federation aware RPs as for non-federation aware RPs.

        :param handle: (key, timestamp) tuple used at cookie construction
        :param kwargs: Extra key word arguments.
        :return: Provider Info response
        """
        logger.info("@providerinfo_endpoint")
        try:
            _response = self.create_fed_providerinfo()
            msg = "provider_info_response: {}"
            logger.info(msg.format(sanitize(_response.to_dict())))
            if self.events:
                self.events.store('Protocol response', _response)

            headers = [("Cache-Control", "no-store"), ("x-ffo", "bar")]
            if handle:
                (key, timestamp) = handle
                if key.startswith(STR) and key.endswith(STR):
                    cookie = self.cookie_func(key, self.cookie_name, "pinfo",
                                              self.sso_ttl)
                    headers.append(cookie)

            resp = Response(_response.to_json(),
                            content="application/json",
                            headers=headers)
        except Exception:
            message = traceback.format_exception(*sys.exc_info())
            logger.error(message)
            resp = error('service_error', message)

        return resp
Exemple #27
0
def flow_list(environ, start_response, flows, done):
    resp = Response(mako_template="flowlist.mako",
                    template_lookup=LOOKUP,
                    headers=[])
    argv = {"base": KW_ARGS["conf"].BASE, "flows": flows, "done": done}

    return resp(environ, start_response, **argv)
Exemple #28
0
    def response(self, binding, http_args, query):
        cookie = self.create_cookie(
            '{"'
            + self.CONST_QUERY
            + '": "'
            + base64.b64encode(query.encode("ascii")).decode("ascii")
            + '" , "'
            + self.CONST_HASIDP
            + '": "True" }',
            self.CONST_SAML_COOKIE,
            self.CONST_SAML_COOKIE,
        )
        if binding == BINDING_HTTP_ARTIFACT:
            resp = SeeOther()  # type: Response
        elif binding == BINDING_HTTP_REDIRECT:
            for param, value in http_args["headers"]:
                if param == "Location":
                    resp = SeeOther(str(value), headers=[cookie])
                    break
            else:
                raise ServiceErrorException("Parameter error")
        else:
            http_args["headers"].append(cookie)
            resp = Response(http_args["data"], headers=http_args["headers"])

        return resp
Exemple #29
0
    def __call__(self, cookie=None, end_point_index=0, **kwargs):
        """
        Put up the login form
        """
        # if cookie:
        #     headers = [cookie]
        # else:
        #     headers = []

        resp = Response()

        argv = self.templ_arg_func(end_point_index, **kwargs)
        logger.info("do_authentication argv: %s" % argv)
        mte = self.template_lookup.get_template(self.mako_template)
        resp.message = mte.render(**argv)
        return resp
Exemple #30
0
    def token_endpoint(self, authn="", **kwargs):
        """
        This is where clients come to get their access tokens
        """

        logger.debug("- token -")
        body = kwargs["request"]
        logger.debug("body: %s" % body)

        areq = AccessTokenRequest().deserialize(body, "urlencoded")

        try:
            self.client_authn(self, areq, authn)
        except FailedAuthentication as err:
            logger.error(err)
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(),
                            content="application/json",
                            status_code=401)

        logger.debug("AccessTokenRequest: %s" % areq)

        _grant_type = areq["grant_type"]
        if _grant_type == "authorization_code":
            return self.code_grant_type(areq)
        elif _grant_type == 'client_credentials':
            return self.client_credentials_grant_type(areq)
        elif _grant_type == 'password':
            return self.password_grant_type(areq)
        elif _grant_type == 'refresh_token':
            return self.refresh_token_grant_type(areq)
        else:
            raise UnSupported('grant_type: {}'.format(_grant_type))
Exemple #31
0
    def delete_instance(self, parts, pid=0, app=None):
        lp = [unquote_plus(p) for p in parts]
        qp = [quote_plus(p) for p in lp]
        _key = app.assigned_ports.make_key(*lp)

        if pid:
            kill_process(pid)
            del app.running_processes[_key]

        os.unlink(os.path.join(self.entpath, *qp))
        # Remove issuer if out of tags
        if not os.listdir(os.path.join(self.entpath, qp[0])):
            os.rmdir(os.path.join(self.entpath, qp[0]))

        del app.assigned_ports[_key]

        resp = Response(mako_template='message.mako',
                        template_lookup=self.lookup,
                        headers=[])

        args = {
            'title':
            "Action performed",
            'base':
            self.baseurl,
            'note':
            'Your test tool instance <em>{} {}</em> has been '
            'removed'.format(*lp)
        }
        return resp(self.environ, self.start_response, **args)
Exemple #32
0
    def introspection_endpoint(self, authn='', request=None, **kwargs):
        """
        Implements RFC7662

        :param authn: Client Authentication information
        :param request: The introspection request
        :param kwargs:
        :return:
        """

        tir = TokenIntrospectionRequest().deserialize(request, "urlencoded")

        resp = self.get_token_info(authn, tir, 'introspection_endpoint')

        if isinstance(resp, Response):
            return resp
        else:
            client_id, token_type, _info = resp

        logger.info('{} token introspection: {}'.format(
            client_id, tir.to_dict()))

        ir = TokenIntrospectionResponse(
            active=self.sdb.token_factory[token_type].is_valid(_info),
            **_info.to_dict())

        ir.weed()

        return Response(ir.to_json(), content="application/json")
Exemple #33
0
    def restart_instance(self, app, part):
        _iss = unquote_plus(part[0])
        _tag = unquote_plus(part[1])
        url = app.run_test_instance(quote_plus(_iss), quote_plus(_tag))
        if isinstance(url, Response):
            return url(self.environ, self.start_response)

        resp = Response(mako_template='message.mako',
                        template_lookup=self.lookup,
                        headers=[])

        if url:
            args = {
                'title':
                "Action performed",
                'base':
                self.baseurl,
                'note':
                'Your test instance "{iss}:{tag}" has been '
                'restarted as <a href="{url}">{url}</a>'.format(iss=_iss,
                                                                tag=_tag,
                                                                url=url)
            }
        else:
            args = {
                'title': "Action Failed",
                'base': self.baseurl,
                'note': 'Could not restart your test instance'
            }

        return resp(self.environ, self.start_response, **args)