서버의 인증 라이브러리를 django-rest-framework-simplejwt 4.6.0 버전으로 이전하고 있던 어느 평화로운 오후에, 느닷없이 다음과 같은 에러가 등장하셨다.
File "/**/account/serializers.py", line 57, in validate
if not api_settings.USER_AUTHENTICATION_RULE(self.user):
TypeError: 'str' object is not callable
코드를 분해해보니 drf-simplejwt가 앱 시작시에 불러오는 세팅값 중에 유저 유효성 검사 함수를 가리키는 USER_AUTHENTICATION_RULE
값이 함수로 올바르게 임포트되지 못하고 문자열로 세팅되는 버그가 있었다.
simplejwt는 세팅시에 DRF의 APISettings
클래스를 활용한다. 에러 메시지의 api_settings
역시 이 클래스(를 상속하는 클래스)의 인스턴스인데, api_settings.USER_AUTHENTICATION_RULE
구문에서 다음 함수가 실행된다.
def __getattr__(self, attr):
if attr not in self.defaults:
raise AttributeError("Invalid API setting: '%s'" % attr)
try:
# Check if present in user settings
val = self.user_settings[attr] [1]
except KeyError:
# Fall back to defaults
val = self.defaults[attr] [2]
# Coerce import strings into classes
if attr in self.import_strings: [3]
val = perform_import(val, attr) [4]
# Cache the result
self._cached_attrs.add(attr)
setattr(self, attr, val)
return val
USER_AUTHENTICATION_RULE
값이 있는지 확인 후'rest_framework_simplejwt.authentication.default_user_authentication_rule'
을 가져온다. self.import_strings
튜플에 'USER_AUTHENTICATION_RULE'
가 있으면 그런데 self.import_strings
의 디폴트 값인IMPORT_STRINGS
에 'USER_AUTHENTICATION_RULE'
가 누락돼 해당 값이 가리키는 함수가 아닌 값 자체를 반환하고 있었다.
따라서 해당 변수를 호출하면 타입에러가 발생하는 것이었다.
다음과 같이 해당 변수에 'USER_AUTHENTICATION_RULE'
값을 추가하면 해결된다.
IMPORT_STRINGS = (
'AUTH_TOKEN_CLASSES',
'TOKEN_USER_CLASS',
'USER_AUTHENTICATION_RULE',
)
현재 simplejwt의 깃헙레포(11/26)에는 누락된 변수가 추가되어 있으나, pip
로 설치되는 PyPI 4.6.0 버전(11/13)에는 값이 누락되어 에러가 발생한다.
언제가 나올 다음 버젼에서는 해당 문제가 수정되겠지만, 지금은 에러가 나는 TokenObtainPairSerializer
를 커스터마이징해 USER_AUTHENTICATION_RULE
의 디폴트 값인 default_user_authentication_rule
함수를 직접 호출하는 방식으로 해결하였다.
- if not api_settings.USER_AUTHENTICATION_RULE(self.user):
+ if not user_authentication_rule(self.user):