prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Don't fetch LDAP user and groups on all API request when FIND_GROUP_PERMS is disabled
  ### NetBox version
  
  V3.0.9
  
  ### Feature type
  
  Change to existing functionality
  
  ### Proposed functionality
  
  Currently when using the LDAP backend for authentication, the AD is queried on every API request, regardless of other settings and regardless if the user is local or has been created by the LDAP backend. Additionally the LDAP cache built into django-auth-ldap does not function when using populate_user.
  
  As the user is not actually authenticated against the AD when using the API (the token is used), I propose that the local user and it's group assignments are used when FIND_GROUP_PERMISSIONS is disabled.
  
  I have a change ready for pull request if the issue is accepted.
  
  For more info, please see the discussion I created: https://github.com/netbox-community/netbox/discussions/7708
  
  This issue would also partly fix #6926 - it will not fix the caching, but the user who reported the issue is not using FIND_GROUP_PERMISSIONS.
  
  ### Use case
  
  The end goal is vastly improved API performance when using the LDAP backend in most cases.
  
  The above changes will result in the following changes for users:
  
  **Not using the LDAP backend:**
  
  No changes
  
  **FIND_GROUP_PERMS  = True:**
  
  No changes
  
  **MIRROR_GROUPS = True and FIND_GROUP_PERMS = True:**
  
  No changes
  
  **MIRROR_GROUPS = True and FIND_GROUP_PERMS = False:**
  
  Local user and group assignments will be used when calling the API and the user and groups are never reloaded from the LDAP server during API calls. This means that LDAP users utilizing the API will have to login to the web ui to update group memberships. The change also allows one to use locally created users to call the API with querying the LDAP server.
  
  **MIRROR_GROUPS = False and FIND_GROUP_PERMS = False:**
  
  The user performing the API request has to be locally assigned groups or have local user object permissions.
  
  ### Database changes
  
  No database changes
  
  ### External dependencies
  
  _No response_
  
  </issue>
  <code>
  [start of netbox/netbox/api/authentication.py]
  1 from django.conf import settings
  2 from rest_framework import authentication, exceptions
  3 from rest_framework.permissions import BasePermission, DjangoObjectPermissions, SAFE_METHODS
  4 
  5 from users.models import Token
  6 
  7 
  8 class TokenAuthentication(authentication.TokenAuthentication):
  9     """
  10     A custom authentication scheme which enforces Token expiration times.
  11     """
  12     model = Token
  13 
  14     def authenticate_credentials(self, key):
  15         model = self.get_model()
  16         try:
  17             token = model.objects.prefetch_related('user').get(key=key)
  18         except model.DoesNotExist:
  19             raise exceptions.AuthenticationFailed("Invalid token")
  20 
  21         # Enforce the Token's expiration time, if one has been set.
  22         if token.is_expired:
  23             raise exceptions.AuthenticationFailed("Token expired")
  24 
  25         if not token.user.is_active:
  26             raise exceptions.AuthenticationFailed("User inactive")
  27 
  28         # When LDAP authentication is active try to load user data from LDAP directory
  29         if settings.REMOTE_AUTH_BACKEND == 'netbox.authentication.LDAPBackend':
  30             from netbox.authentication import LDAPBackend
  31             ldap_backend = LDAPBackend()
  32             user = ldap_backend.populate_user(token.user.username)
  33             # If the user is found in the LDAP directory use it, if not fallback to the local user
  34             if user:
  35                 return user, token
  36 
  37         return token.user, token
  38 
  39 
  40 class TokenPermissions(DjangoObjectPermissions):
  41     """
  42     Custom permissions handler which extends the built-in DjangoModelPermissions to validate a Token's write ability
  43     for unsafe requests (POST/PUT/PATCH/DELETE).
  44     """
  45     # Override the stock perm_map to enforce view permissions
  46     perms_map = {
  47         'GET': ['%(app_label)s.view_%(model_name)s'],
  48         'OPTIONS': [],
  49         'HEAD': ['%(app_label)s.view_%(model_name)s'],
  50         'POST': ['%(app_label)s.add_%(model_name)s'],
  51         'PUT': ['%(app_label)s.change_%(model_name)s'],
  52         'PATCH': ['%(app_label)s.change_%(model_name)s'],
  53         'DELETE': ['%(app_label)s.delete_%(model_name)s'],
  54     }
  55 
  56     def __init__(self):
  57 
  58         # LOGIN_REQUIRED determines whether read-only access is provided to anonymous users.
  59         self.authenticated_users_only = settings.LOGIN_REQUIRED
  60 
  61         super().__init__()
  62 
  63     def _verify_write_permission(self, request):
  64 
  65         # If token authentication is in use, verify that the token allows write operations (for unsafe methods).
  66         if request.method in SAFE_METHODS or request.auth.write_enabled:
  67             return True
  68 
  69     def has_permission(self, request, view):
  70 
  71         # Enforce Token write ability
  72         if isinstance(request.auth, Token) and not self._verify_write_permission(request):
  73             return False
  74 
  75         return super().has_permission(request, view)
  76 
  77     def has_object_permission(self, request, view, obj):
  78 
  79         # Enforce Token write ability
  80         if isinstance(request.auth, Token) and not self._verify_write_permission(request):
  81             return False
  82 
  83         return super().has_object_permission(request, view, obj)
  84 
  85 
  86 class IsAuthenticatedOrLoginNotRequired(BasePermission):
  87     """
  88     Returns True if the user is authenticated or LOGIN_REQUIRED is False.
  89     """
  90     def has_permission(self, request, view):
  91         if not settings.LOGIN_REQUIRED:
  92             return True
  93         return request.user.is_authenticated
  94
  [end of netbox/netbox/api/authentication.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/netbox/netbox/api/authentication.py b/netbox/netbox/api/authentication.py
  --- a/netbox/netbox/api/authentication.py
  +++ b/netbox/netbox/api/authentication.py
  @@ -29,10 +29,13 @@
           if settings.REMOTE_AUTH_BACKEND == 'netbox.authentication.LDAPBackend':
               from netbox.authentication import LDAPBackend
               ldap_backend = LDAPBackend()
  -            user = ldap_backend.populate_user(token.user.username)
  -            # If the user is found in the LDAP directory use it, if not fallback to the local user
  -            if user:
  -                return user, token
  +
  +            # Load from LDAP if FIND_GROUP_PERMS is active
  +            if ldap_backend.settings.FIND_GROUP_PERMS:
  +                user = ldap_backend.populate_user(token.user.username)
  +                # If the user is found in the LDAP directory use it, if not fallback to the local user
  +                if user:
  +                    return user, token
   
           return token.user, token
  
verification_info: {"golden_diff": "diff --git a/netbox/netbox/api/authentication.py b/netbox/netbox/api/authentication.py\n--- a/netbox/netbox/api/authentication.py\n+++ b/netbox/netbox/api/authentication.py\n@@ -29,10 +29,13 @@\n         if settings.REMOTE_AUTH_BACKEND == 'netbox.authentication.LDAPBackend':\n             from netbox.authentication import LDAPBackend\n             ldap_backend = LDAPBackend()\n-            user = ldap_backend.populate_user(token.user.username)\n-            # If the user is found in the LDAP directory use it, if not fallback to the local user\n-            if user:\n-                return user, token\n+\n+            # Load from LDAP if FIND_GROUP_PERMS is active\n+            if ldap_backend.settings.FIND_GROUP_PERMS:\n+                user = ldap_backend.populate_user(token.user.username)\n+                # If the user is found in the LDAP directory use it, if not fallback to the local user\n+                if user:\n+                    return user, token\n \n         return token.user, token\n", "issue": "Don't fetch LDAP user and groups on all API request when FIND_GROUP_PERMS is disabled\n### NetBox version\n\nV3.0.9\n\n### Feature type\n\nChange to existing functionality\n\n### Proposed functionality\n\nCurrently when using the LDAP backend for authentication, the AD is queried on every API request, regardless of other settings and regardless if the user is local or has been created by the LDAP backend. Additionally the LDAP cache built into django-auth-ldap does not function when using populate_user.\r\n\r\nAs the user is not actually authenticated against the AD when using the API (the token is used), I propose that the local user and it's group assignments are used when FIND_GROUP_PERMISSIONS is disabled.\r\n\r\nI have a change ready for pull request if the issue is accepted.\r\n\r\nFor more info, please see the discussion I created: https://github.com/netbox-community/netbox/discussions/7708\r\n\r\nThis issue would also partly fix #6926 - it will not fix the caching, but the user who reported the issue is not using FIND_GROUP_PERMISSIONS.\n\n### Use case\n\nThe end goal is vastly improved API performance when using the LDAP backend in most cases.\r\n\r\nThe above changes will result in the following changes for users:\r\n\r\n**Not using the LDAP backend:**\r\n\r\nNo changes\r\n\r\n**FIND_GROUP_PERMS  = True:**\r\n\r\nNo changes\r\n\r\n**MIRROR_GROUPS = True and FIND_GROUP_PERMS = True:**\r\n\r\nNo changes\r\n\r\n**MIRROR_GROUPS = True and FIND_GROUP_PERMS = False:**\r\n\r\nLocal user and group assignments will be used when calling the API and the user and groups are never reloaded from the LDAP server during API calls. This means that LDAP users utilizing the API will have to login to the web ui to update group memberships. The change also allows one to use locally created users to call the API with querying the LDAP server.\r\n\r\n**MIRROR_GROUPS = False and FIND_GROUP_PERMS = False:**\r\n\r\nThe user performing the API request has to be locally assigned groups or have local user object permissions.\n\n### Database changes\n\nNo database changes\n\n### External dependencies\n\n_No response_\n", "before_files": [{"content": "from django.conf import settings\nfrom rest_framework import authentication, exceptions\nfrom rest_framework.permissions import BasePermission, DjangoObjectPermissions, SAFE_METHODS\n\nfrom users.models import Token\n\n\nclass TokenAuthentication(authentication.TokenAuthentication):\n    \"\"\"\n    A custom authentication scheme which enforces Token expiration times.\n    \"\"\"\n    model = Token\n\n    def authenticate_credentials(self, key):\n        model = self.get_model()\n        try:\n            token = model.objects.prefetch_related('user').get(key=key)\n        except model.DoesNotExist:\n            raise exceptions.AuthenticationFailed(\"Invalid token\")\n\n        # Enforce the Token's expiration time, if one has been set.\n        if token.is_expired:\n            raise exceptions.AuthenticationFailed(\"Token expired\")\n\n        if not token.user.is_active:\n            raise exceptions.AuthenticationFailed(\"User inactive\")\n\n        # When LDAP authentication is active try to load user data from LDAP directory\n        if settings.REMOTE_AUTH_BACKEND == 'netbox.authentication.LDAPBackend':\n            from netbox.authentication import LDAPBackend\n            ldap_backend = LDAPBackend()\n            user = ldap_backend.populate_user(token.user.username)\n            # If the user is found in the LDAP directory use it, if not fallback to the local user\n            if user:\n                return user, token\n\n        return token.user, token\n\n\nclass TokenPermissions(DjangoObjectPermissions):\n    \"\"\"\n    Custom permissions handler which extends the built-in DjangoModelPermissions to validate a Token's write ability\n    for unsafe requests (POST/PUT/PATCH/DELETE).\n    \"\"\"\n    # Override the stock perm_map to enforce view permissions\n    perms_map = {\n        'GET': ['%(app_label)s.view_%(model_name)s'],\n        'OPTIONS': [],\n        'HEAD': ['%(app_label)s.view_%(model_name)s'],\n        'POST': ['%(app_label)s.add_%(model_name)s'],\n        'PUT': ['%(app_label)s.change_%(model_name)s'],\n        'PATCH': ['%(app_label)s.change_%(model_name)s'],\n        'DELETE': ['%(app_label)s.delete_%(model_name)s'],\n    }\n\n    def __init__(self):\n\n        # LOGIN_REQUIRED determines whether read-only access is provided to anonymous users.\n        self.authenticated_users_only = settings.LOGIN_REQUIRED\n\n        super().__init__()\n\n    def _verify_write_permission(self, request):\n\n        # If token authentication is in use, verify that the token allows write operations (for unsafe methods).\n        if request.method in SAFE_METHODS or request.auth.write_enabled:\n            return True\n\n    def has_permission(self, request, view):\n\n        # Enforce Token write ability\n        if isinstance(request.auth, Token) and not self._verify_write_permission(request):\n            return False\n\n        return super().has_permission(request, view)\n\n    def has_object_permission(self, request, view, obj):\n\n        # Enforce Token write ability\n        if isinstance(request.auth, Token) and not self._verify_write_permission(request):\n            return False\n\n        return super().has_object_permission(request, view, obj)\n\n\nclass IsAuthenticatedOrLoginNotRequired(BasePermission):\n    \"\"\"\n    Returns True if the user is authenticated or LOGIN_REQUIRED is False.\n    \"\"\"\n    def has_permission(self, request, view):\n        if not settings.LOGIN_REQUIRED:\n            return True\n        return request.user.is_authenticated\n", "path": "netbox/netbox/api/authentication.py"}]}