prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Bad error handling in /ttype/<type> endpoint
  We observed a bad error handling when requesting the `/ttype/<type>` endpoint.
  
  In specific, we faced the following error:
  ```
  [ERROR][privacyidea.app:1892] Exception on /ttype/push"}. [GET]
  ...
  AttributeError: 'NoneType' object has no attribute 'api_endpoint'
  ```
  Actually, we could fix the problem but it would be nice to fix this upstream right away.
  
  ### Top-level intent
  
  Access the `/ttype/<type>` endpoint.
  
  ### Steps to reproduce
  
  1. Query `/ttype/test` endpoint
  2. There will be a NoneType error in the logs.
  
  ### Expected outcome
  
  Proper error handling
  
  ### Actual outcome
  
  NoneType exception.
  
  ### Configuration
  
  * **privacyIDEA version**: v3.6.3
  * **Installation method**: (from Ubuntu packages, github, PyPI, ...)
  * **Python version**: 3
  * **Operating system**: linux
  * **Webserver**: apache
  
  
  ### Log file
  
  **Set PI_LOGLEVEL = logging.DEBUG in pi.cfg and take a look at the privacyidea.log!**
  **If appropriate, attach the log file or paste relevant portions.**
  
  
  </issue>
  <code>
  [start of privacyidea/api/ttype.py]
  1 # -*- coding: utf-8 -*-
  2 #
  3 # http://www.privacyidea.org
  4 # (c) Cornelius Kölbel, privacyidea.org
  5 #
  6 # 2015-09-01 Cornelius Kölbel, <cornelius@privacyidea.org>
  7 #            Initial writeup
  8 #
  9 # This code is free software; you can redistribute it and/or
  10 # modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  11 # License as published by the Free Software Foundation; either
  12 # version 3 of the License, or any later version.
  13 #
  14 # This code is distributed in the hope that it will be useful,
  15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 # GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  18 #
  19 # You should have received a copy of the GNU Affero General Public
  20 # License along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21 #
  22 """
  23 This API endpoint is a generic endpoint that can be used by any token
  24 type.
  25 
  26 The tokentype needs to implement a classmethod *api_endpoint* and can then be
  27 called by /ttype/<tokentype>.
  28 This way, each tokentype can create its own API without the need to change
  29 the core API.
  30 
  31 The TiQR Token uses this API to implement its special functionalities. See
  32 :ref:`code_tiqr_token`.
  33 """
  34 from flask import (Blueprint,
  35                    request)
  36 from .lib.utils import getParam
  37 from ..lib.log import log_with
  38 from flask import g, jsonify, current_app
  39 import logging
  40 from privacyidea.api.lib.utils import get_all_params
  41 from privacyidea.lib.policy import PolicyClass
  42 from privacyidea.lib.audit import getAudit
  43 from privacyidea.lib.config import (get_token_class, get_from_config,
  44                                     SYSCONF, ensure_no_config_object)
  45 from privacyidea.lib.user import get_user_from_param
  46 from privacyidea.lib.utils import get_client_ip
  47 import json
  48 
  49 log = logging.getLogger(__name__)
  50 
  51 ttype_blueprint = Blueprint('ttype_blueprint', __name__)
  52 
  53 
  54 @ttype_blueprint.before_request
  55 def before_request():
  56     """
  57     This is executed before the request
  58     """
  59     ensure_no_config_object()
  60     request.all_data = get_all_params(request)
  61     privacyidea_server = current_app.config.get("PI_AUDIT_SERVERNAME") or \
  62                          request.host
  63     # Create a policy_object, that reads the database audit settings
  64     # and contains the complete policy definition during the request.
  65     # This audit_object can be used in the postpolicy and prepolicy and it
  66     # can be passed to the innerpolicies.
  67     g.policy_object = PolicyClass()
  68     g.audit_object = getAudit(current_app.config)
  69     # access_route contains the ip adresses of all clients, hops and proxies.
  70     g.client_ip = get_client_ip(request,
  71                                 get_from_config(SYSCONF.OVERRIDECLIENT))
  72     g.serial = getParam(request.all_data, "serial") or None
  73     g.audit_object.log({"success": False,
  74                         "action_detail": "",
  75                         "client": g.client_ip,
  76                         "client_user_agent": request.user_agent.browser,
  77                         "privacyidea_server": privacyidea_server,
  78                         "action": "{0!s} {1!s}".format(request.method, request.url_rule),
  79                         "info": ""})
  80 
  81 
  82 @ttype_blueprint.route('/<ttype>', methods=['POST', 'GET'])
  83 @log_with(log)
  84 def token(ttype=None):
  85     """
  86     This is a special token function. Each token type can define an
  87     additional API call, that does not need authentication on the REST API
  88     level.
  89 
  90     :return: Token Type dependent
  91     """
  92     tokenc = get_token_class(ttype)
  93     res = tokenc.api_endpoint(request, g)
  94     serial = getParam(request.all_data, "serial")
  95     user = get_user_from_param(request.all_data)
  96     g.audit_object.log({"success": 1,
  97                         "user": user.login,
  98                         "realm": user.realm,
  99                         "serial": serial,
  100                         "token_type": ttype})
  101     if res[0] == "json":
  102         return jsonify(res[1])
  103     elif res[0] in ["html", "plain"]:
  104         return current_app.response_class(res[1], mimetype="text/{0!s}".format(res[0]))
  105     elif len(res) == 2:
  106         return current_app.response_class(json.dumps(res[1]),
  107                                           mimetype="application/{0!s}".format(res[0]))
  108     else:
  109         return current_app.response_class(res[1], mimetype="application/octet-binary",
  110                                           headers=res[2])
  111
  [end of privacyidea/api/ttype.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/privacyidea/api/ttype.py b/privacyidea/api/ttype.py
  --- a/privacyidea/api/ttype.py
  +++ b/privacyidea/api/ttype.py
  @@ -38,6 +38,7 @@
   from flask import g, jsonify, current_app
   import logging
   from privacyidea.api.lib.utils import get_all_params
  +from privacyidea.lib.error import ParameterError
   from privacyidea.lib.policy import PolicyClass
   from privacyidea.lib.audit import getAudit
   from privacyidea.lib.config import (get_token_class, get_from_config,
  @@ -90,6 +91,9 @@
       :return: Token Type dependent
       """
       tokenc = get_token_class(ttype)
  +    if tokenc is None:
  +        log.error(u"Invalid tokentype provided. ttype: {}".format(ttype.lower()))
  +        raise ParameterError(u"Invalid tokentype provided. ttype: {}".format(ttype.lower()))
       res = tokenc.api_endpoint(request, g)
       serial = getParam(request.all_data, "serial")
       user = get_user_from_param(request.all_data)
  
verification_info: {"golden_diff": "diff --git a/privacyidea/api/ttype.py b/privacyidea/api/ttype.py\n--- a/privacyidea/api/ttype.py\n+++ b/privacyidea/api/ttype.py\n@@ -38,6 +38,7 @@\n from flask import g, jsonify, current_app\n import logging\n from privacyidea.api.lib.utils import get_all_params\n+from privacyidea.lib.error import ParameterError\n from privacyidea.lib.policy import PolicyClass\n from privacyidea.lib.audit import getAudit\n from privacyidea.lib.config import (get_token_class, get_from_config,\n@@ -90,6 +91,9 @@\n     :return: Token Type dependent\n     \"\"\"\n     tokenc = get_token_class(ttype)\n+    if tokenc is None:\n+        log.error(u\"Invalid tokentype provided. ttype: {}\".format(ttype.lower()))\n+        raise ParameterError(u\"Invalid tokentype provided. ttype: {}\".format(ttype.lower()))\n     res = tokenc.api_endpoint(request, g)\n     serial = getParam(request.all_data, \"serial\")\n     user = get_user_from_param(request.all_data)\n", "issue": "Bad error handling in /ttype/<type> endpoint\nWe observed a bad error handling when requesting the `/ttype/<type>` endpoint.\r\n\r\nIn specific, we faced the following error:\r\n```\r\n[ERROR][privacyidea.app:1892] Exception on /ttype/push\"}. [GET]\r\n...\r\nAttributeError: 'NoneType' object has no attribute 'api_endpoint'\r\n```\r\nActually, we could fix the problem but it would be nice to fix this upstream right away.\r\n\r\n### Top-level intent\r\n\r\nAccess the `/ttype/<type>` endpoint.\r\n\r\n### Steps to reproduce\r\n\r\n1. Query `/ttype/test` endpoint\r\n2. There will be a NoneType error in the logs.\r\n\r\n### Expected outcome\r\n\r\nProper error handling\r\n\r\n### Actual outcome\r\n\r\nNoneType exception.\r\n\r\n### Configuration\r\n\r\n* **privacyIDEA version**: v3.6.3\r\n* **Installation method**: (from Ubuntu packages, github, PyPI, ...)\r\n* **Python version**: 3\r\n* **Operating system**: linux\r\n* **Webserver**: apache\r\n\r\n\r\n### Log file\r\n\r\n**Set PI_LOGLEVEL = logging.DEBUG in pi.cfg and take a look at the privacyidea.log!**\r\n**If appropriate, attach the log file or paste relevant portions.**\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# http://www.privacyidea.org\n# (c) Cornelius K\u00f6lbel, privacyidea.org\n#\n# 2015-09-01 Cornelius K\u00f6lbel, <cornelius@privacyidea.org>\n#            Initial writeup\n#\n# This code is free software; you can redistribute it and/or\n# modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE\n# License as published by the Free Software Foundation; either\n# version 3 of the License, or any later version.\n#\n# This code is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n#\n# You should have received a copy of the GNU Affero General Public\n# License along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\n\"\"\"\nThis API endpoint is a generic endpoint that can be used by any token\ntype.\n\nThe tokentype needs to implement a classmethod *api_endpoint* and can then be\ncalled by /ttype/<tokentype>.\nThis way, each tokentype can create its own API without the need to change\nthe core API.\n\nThe TiQR Token uses this API to implement its special functionalities. See\n:ref:`code_tiqr_token`.\n\"\"\"\nfrom flask import (Blueprint,\n                   request)\nfrom .lib.utils import getParam\nfrom ..lib.log import log_with\nfrom flask import g, jsonify, current_app\nimport logging\nfrom privacyidea.api.lib.utils import get_all_params\nfrom privacyidea.lib.policy import PolicyClass\nfrom privacyidea.lib.audit import getAudit\nfrom privacyidea.lib.config import (get_token_class, get_from_config,\n                                    SYSCONF, ensure_no_config_object)\nfrom privacyidea.lib.user import get_user_from_param\nfrom privacyidea.lib.utils import get_client_ip\nimport json\n\nlog = logging.getLogger(__name__)\n\nttype_blueprint = Blueprint('ttype_blueprint', __name__)\n\n\n@ttype_blueprint.before_request\ndef before_request():\n    \"\"\"\n    This is executed before the request\n    \"\"\"\n    ensure_no_config_object()\n    request.all_data = get_all_params(request)\n    privacyidea_server = current_app.config.get(\"PI_AUDIT_SERVERNAME\") or \\\n                         request.host\n    # Create a policy_object, that reads the database audit settings\n    # and contains the complete policy definition during the request.\n    # This audit_object can be used in the postpolicy and prepolicy and it\n    # can be passed to the innerpolicies.\n    g.policy_object = PolicyClass()\n    g.audit_object = getAudit(current_app.config)\n    # access_route contains the ip adresses of all clients, hops and proxies.\n    g.client_ip = get_client_ip(request,\n                                get_from_config(SYSCONF.OVERRIDECLIENT))\n    g.serial = getParam(request.all_data, \"serial\") or None\n    g.audit_object.log({\"success\": False,\n                        \"action_detail\": \"\",\n                        \"client\": g.client_ip,\n                        \"client_user_agent\": request.user_agent.browser,\n                        \"privacyidea_server\": privacyidea_server,\n                        \"action\": \"{0!s} {1!s}\".format(request.method, request.url_rule),\n                        \"info\": \"\"})\n\n\n@ttype_blueprint.route('/<ttype>', methods=['POST', 'GET'])\n@log_with(log)\ndef token(ttype=None):\n    \"\"\"\n    This is a special token function. Each token type can define an\n    additional API call, that does not need authentication on the REST API\n    level.\n\n    :return: Token Type dependent\n    \"\"\"\n    tokenc = get_token_class(ttype)\n    res = tokenc.api_endpoint(request, g)\n    serial = getParam(request.all_data, \"serial\")\n    user = get_user_from_param(request.all_data)\n    g.audit_object.log({\"success\": 1,\n                        \"user\": user.login,\n                        \"realm\": user.realm,\n                        \"serial\": serial,\n                        \"token_type\": ttype})\n    if res[0] == \"json\":\n        return jsonify(res[1])\n    elif res[0] in [\"html\", \"plain\"]:\n        return current_app.response_class(res[1], mimetype=\"text/{0!s}\".format(res[0]))\n    elif len(res) == 2:\n        return current_app.response_class(json.dumps(res[1]),\n                                          mimetype=\"application/{0!s}\".format(res[0]))\n    else:\n        return current_app.response_class(res[1], mimetype=\"application/octet-binary\",\n                                          headers=res[2])\n", "path": "privacyidea/api/ttype.py"}]}