prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Improve the logging of SSH tokens
  If a user has many different SSH keys assigned on one machine for different ssh-users on this machine, this line gets logged for each SSH key, that is not used:
  
  https://github.com/privacyidea/privacyidea/blob/745a829d89fb2824d253e27b510027316c856245/privacyidea/lib/applications/ssh.py#L84
  
  We should change this to "debug".
  
  On the other hand we should add a log here 
  https://github.com/privacyidea/privacyidea/blob/745a829d89fb2824d253e27b510027316c856245/privacyidea/lib/applications/ssh.py#L73
  like
  
  ~~~~python
  log.info(u"Using SSH key {0!s} for user {1!s}".format(tokclass.token.serial, options.get("user")))
  ~~~~
  
  
  
  </issue>
  <code>
  [start of privacyidea/lib/applications/ssh.py]
  1 # -*- coding: utf-8 -*-
  2 #
  3 #  privacyIDEA
  4 #  Jul 18, 2014 Cornelius Kölbel
  5 #  License:  AGPLv3
  6 #  contact:  http://www.privacyidea.org
  7 #
  8 # This code is free software; you can redistribute it and/or
  9 # modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10 # License as published by the Free Software Foundation; either
  11 # version 3 of the License, or any later version.
  12 #
  13 # This code is distributed in the hope that it will be useful,
  14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 # GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  17 #
  18 # You should have received a copy of the GNU Affero General Public
  19 # License along with this program.  If not, see <http://www.gnu.org/licenses/>.
  20 #
  21 """
  22 This file is tested in tests/test_lib_machinetokens.py
  23 """
  24 from privacyidea.lib.applications import MachineApplicationBase
  25 import logging
  26 from privacyidea.lib.token import get_tokens
  27 log = logging.getLogger(__name__)
  28 
  29 
  30 class MachineApplication(MachineApplicationBase):
  31     """
  32     This is the application for SSH.
  33 
  34     Possible options:
  35         user
  36 
  37     """
  38     application_name = "ssh"
  39     '''as the authentication item is no sensitive information,
  40     we can set bulk_call to True. Thus the admin can call
  41     all public keys to distribute them via salt.
  42     FIXME: This is only true for SSH pub keys.
  43     If we would support OTP with SSH, this might be sensitive information!
  44     '''
  45     allow_bulk_call = True
  46 
  47     @staticmethod
  48     def get_authentication_item(token_type,
  49                                 serial,
  50                                 challenge=None, options=None,
  51                                 filter_param=None):
  52         """
  53         :param token_type: the type of the token. At the moment
  54                            we support the tokenype "sshkey"
  55         :param serial:     the serial number of the token.
  56         :return auth_item: Return the SSH pub keys.
  57         """
  58         options = options or {}
  59         ret = {}
  60         filter_param = filter_param or {}
  61         user_filter = filter_param.get("user")
  62         if token_type.lower() == "sshkey":
  63             toks = get_tokens(serial=serial, active=True)
  64             if len(toks) == 1:
  65                 # We return this entry, either if no user_filter is requested
  66                 #  or if the user_filter matches the user
  67                 if (user_filter and user_filter == options.get("user")) or \
  68                         not user_filter:
  69                     # tokenclass is a SSHkeyTokenClass
  70                     tokclass = toks[0]
  71                     # We just return the ssh public key, so that
  72                     # it can be included into authorized keys.
  73                     ret["sshkey"] = tokclass.get_sshkey()
  74                     # We return the username if the token is assigned to a
  75                     # user, so that this username could be used to save
  76                     # the ssh key accordingly
  77                     user_object = toks[0].user
  78                     if user_object:
  79                         uInfo = user_object.info
  80                         if "username" in uInfo:
  81                             ret["username"] = uInfo.get("username")
  82                     # ret["info"] = uInfo
  83                 else:
  84                     log.info("The requested user %s does not match the user "
  85                              "option (%s) of the SSH application." % (
  86                         user_filter, options.get("user")))
  87         else:
  88             log.info("Token %r, type %r is not supported by "
  89                      "SSH application module" % (serial, token_type))
  90 
  91         return ret
  92 
  93     @staticmethod
  94     def get_options():
  95         """
  96         returns a dictionary with a list of required and optional options
  97         """
  98         return {'required': [],
  99                 'optional': ['user']}
  100
  [end of privacyidea/lib/applications/ssh.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/lib/applications/ssh.py b/privacyidea/lib/applications/ssh.py
  --- a/privacyidea/lib/applications/ssh.py
  +++ b/privacyidea/lib/applications/ssh.py
  @@ -70,6 +70,8 @@
                       tokclass = toks[0]
                       # We just return the ssh public key, so that
                       # it can be included into authorized keys.
  +                    log.info(u"Using SSH key {0!s} for SSH user {1!s}".format(tokclass.token.serial,
  +                                                                              options.get("user")))
                       ret["sshkey"] = tokclass.get_sshkey()
                       # We return the username if the token is assigned to a
                       # user, so that this username could be used to save
  @@ -79,14 +81,13 @@
                           uInfo = user_object.info
                           if "username" in uInfo:
                               ret["username"] = uInfo.get("username")
  -                    # ret["info"] = uInfo
                   else:
  -                    log.info("The requested user %s does not match the user "
  -                             "option (%s) of the SSH application." % (
  +                    log.debug(u"The requested user {0!s} does not match the user "
  +                              "option ({0!s}) of the SSH application.".format(
                           user_filter, options.get("user")))
           else:
  -            log.info("Token %r, type %r is not supported by "
  -                     "SSH application module" % (serial, token_type))
  +            log.info(u"Token {0!r}, type {0!r} is not supported by "
  +                     "SSH application module".format(serial, token_type))
   
           return ret
  
verification_info: {"golden_diff": "diff --git a/privacyidea/lib/applications/ssh.py b/privacyidea/lib/applications/ssh.py\n--- a/privacyidea/lib/applications/ssh.py\n+++ b/privacyidea/lib/applications/ssh.py\n@@ -70,6 +70,8 @@\n                     tokclass = toks[0]\n                     # We just return the ssh public key, so that\n                     # it can be included into authorized keys.\n+                    log.info(u\"Using SSH key {0!s} for SSH user {1!s}\".format(tokclass.token.serial,\n+                                                                              options.get(\"user\")))\n                     ret[\"sshkey\"] = tokclass.get_sshkey()\n                     # We return the username if the token is assigned to a\n                     # user, so that this username could be used to save\n@@ -79,14 +81,13 @@\n                         uInfo = user_object.info\n                         if \"username\" in uInfo:\n                             ret[\"username\"] = uInfo.get(\"username\")\n-                    # ret[\"info\"] = uInfo\n                 else:\n-                    log.info(\"The requested user %s does not match the user \"\n-                             \"option (%s) of the SSH application.\" % (\n+                    log.debug(u\"The requested user {0!s} does not match the user \"\n+                              \"option ({0!s}) of the SSH application.\".format(\n                         user_filter, options.get(\"user\")))\n         else:\n-            log.info(\"Token %r, type %r is not supported by \"\n-                     \"SSH application module\" % (serial, token_type))\n+            log.info(u\"Token {0!r}, type {0!r} is not supported by \"\n+                     \"SSH application module\".format(serial, token_type))\n \n         return ret\n", "issue": "Improve the logging of SSH tokens\nIf a user has many different SSH keys assigned on one machine for different ssh-users on this machine, this line gets logged for each SSH key, that is not used:\r\n\r\nhttps://github.com/privacyidea/privacyidea/blob/745a829d89fb2824d253e27b510027316c856245/privacyidea/lib/applications/ssh.py#L84\r\n\r\nWe should change this to \"debug\".\r\n\r\nOn the other hand we should add a log here \r\nhttps://github.com/privacyidea/privacyidea/blob/745a829d89fb2824d253e27b510027316c856245/privacyidea/lib/applications/ssh.py#L73\r\nlike\r\n\r\n~~~~python\r\nlog.info(u\"Using SSH key {0!s} for user {1!s}\".format(tokclass.token.serial, options.get(\"user\")))\r\n~~~~\r\n\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n#  privacyIDEA\n#  Jul 18, 2014 Cornelius K\u00f6lbel\n#  License:  AGPLv3\n#  contact:  http://www.privacyidea.org\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 file is tested in tests/test_lib_machinetokens.py\n\"\"\"\nfrom privacyidea.lib.applications import MachineApplicationBase\nimport logging\nfrom privacyidea.lib.token import get_tokens\nlog = logging.getLogger(__name__)\n\n\nclass MachineApplication(MachineApplicationBase):\n    \"\"\"\n    This is the application for SSH.\n\n    Possible options:\n        user\n\n    \"\"\"\n    application_name = \"ssh\"\n    '''as the authentication item is no sensitive information,\n    we can set bulk_call to True. Thus the admin can call\n    all public keys to distribute them via salt.\n    FIXME: This is only true for SSH pub keys.\n    If we would support OTP with SSH, this might be sensitive information!\n    '''\n    allow_bulk_call = True\n\n    @staticmethod\n    def get_authentication_item(token_type,\n                                serial,\n                                challenge=None, options=None,\n                                filter_param=None):\n        \"\"\"\n        :param token_type: the type of the token. At the moment\n                           we support the tokenype \"sshkey\"\n        :param serial:     the serial number of the token.\n        :return auth_item: Return the SSH pub keys.\n        \"\"\"\n        options = options or {}\n        ret = {}\n        filter_param = filter_param or {}\n        user_filter = filter_param.get(\"user\")\n        if token_type.lower() == \"sshkey\":\n            toks = get_tokens(serial=serial, active=True)\n            if len(toks) == 1:\n                # We return this entry, either if no user_filter is requested\n                #  or if the user_filter matches the user\n                if (user_filter and user_filter == options.get(\"user\")) or \\\n                        not user_filter:\n                    # tokenclass is a SSHkeyTokenClass\n                    tokclass = toks[0]\n                    # We just return the ssh public key, so that\n                    # it can be included into authorized keys.\n                    ret[\"sshkey\"] = tokclass.get_sshkey()\n                    # We return the username if the token is assigned to a\n                    # user, so that this username could be used to save\n                    # the ssh key accordingly\n                    user_object = toks[0].user\n                    if user_object:\n                        uInfo = user_object.info\n                        if \"username\" in uInfo:\n                            ret[\"username\"] = uInfo.get(\"username\")\n                    # ret[\"info\"] = uInfo\n                else:\n                    log.info(\"The requested user %s does not match the user \"\n                             \"option (%s) of the SSH application.\" % (\n                        user_filter, options.get(\"user\")))\n        else:\n            log.info(\"Token %r, type %r is not supported by \"\n                     \"SSH application module\" % (serial, token_type))\n\n        return ret\n\n    @staticmethod\n    def get_options():\n        \"\"\"\n        returns a dictionary with a list of required and optional options\n        \"\"\"\n        return {'required': [],\n                'optional': ['user']}\n", "path": "privacyidea/lib/applications/ssh.py"}]}