Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/rest_framework_simplejwt/authentication.py: 72%

68 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from django.contrib.auth import get_user_model 

2from django.utils.translation import gettext_lazy as _ 

3from rest_framework import HTTP_HEADER_ENCODING, authentication 

4 

5from .exceptions import AuthenticationFailed, InvalidToken, TokenError 

6from .settings import api_settings 

7 

8AUTH_HEADER_TYPES = api_settings.AUTH_HEADER_TYPES 

9 

10if not isinstance(api_settings.AUTH_HEADER_TYPES, (list, tuple)): 10 ↛ 11line 10 didn't jump to line 11, because the condition on line 10 was never true

11 AUTH_HEADER_TYPES = (AUTH_HEADER_TYPES,) 

12 

13AUTH_HEADER_TYPE_BYTES = {h.encode(HTTP_HEADER_ENCODING) for h in AUTH_HEADER_TYPES} 

14 

15 

16class JWTAuthentication(authentication.BaseAuthentication): 

17 """ 

18 An authentication plugin that authenticates requests through a JSON web 

19 token provided in a request header. 

20 """ 

21 

22 www_authenticate_realm = "api" 

23 media_type = "application/json" 

24 

25 def __init__(self, *args, **kwargs): 

26 super().__init__(*args, **kwargs) 

27 self.user_model = get_user_model() 

28 

29 def authenticate(self, request): 

30 header = self.get_header(request) 

31 if header is None: 

32 return None 

33 

34 raw_token = self.get_raw_token(header) 

35 if raw_token is None: 35 ↛ 36line 35 didn't jump to line 36, because the condition on line 35 was never true

36 return None 

37 

38 validated_token = self.get_validated_token(raw_token) 

39 

40 return self.get_user(validated_token), validated_token 

41 

42 def authenticate_header(self, request): 

43 return '{} realm="{}"'.format( 

44 AUTH_HEADER_TYPES[0], 

45 self.www_authenticate_realm, 

46 ) 

47 

48 def get_header(self, request): 

49 """ 

50 Extracts the header containing the JSON web token from the given 

51 request. 

52 """ 

53 header = request.META.get(api_settings.AUTH_HEADER_NAME) 

54 

55 if isinstance(header, str): 

56 # Work around django test client oddness 

57 header = header.encode(HTTP_HEADER_ENCODING) 

58 

59 return header 

60 

61 def get_raw_token(self, header): 

62 """ 

63 Extracts an unvalidated JSON web token from the given "Authorization" 

64 header value. 

65 """ 

66 parts = header.split() 

67 

68 if len(parts) == 0: 68 ↛ 70line 68 didn't jump to line 70, because the condition on line 68 was never true

69 # Empty AUTHORIZATION header sent 

70 return None 

71 

72 if parts[0] not in AUTH_HEADER_TYPE_BYTES: 72 ↛ 74line 72 didn't jump to line 74, because the condition on line 72 was never true

73 # Assume the header does not contain a JSON web token 

74 return None 

75 

76 if len(parts) != 2: 76 ↛ 77line 76 didn't jump to line 77, because the condition on line 76 was never true

77 raise AuthenticationFailed( 

78 _("Authorization header must contain two space-delimited values"), 

79 code="bad_authorization_header", 

80 ) 

81 

82 return parts[1] 

83 

84 def get_validated_token(self, raw_token): 

85 """ 

86 Validates an encoded JSON web token and returns a validated token 

87 wrapper object. 

88 """ 

89 messages = [] 

90 for AuthToken in api_settings.AUTH_TOKEN_CLASSES: 90 ↛ 102line 90 didn't jump to line 102, because the loop on line 90 didn't complete

91 try: 

92 return AuthToken(raw_token) 

93 except TokenError as e: 

94 messages.append( 

95 { 

96 "token_class": AuthToken.__name__, 

97 "token_type": AuthToken.token_type, 

98 "message": e.args[0], 

99 } 

100 ) 

101 

102 raise InvalidToken( 

103 { 

104 "detail": _("Given token not valid for any token type"), 

105 "messages": messages, 

106 } 

107 ) 

108 

109 def get_user(self, validated_token): 

110 """ 

111 Attempts to find and return a user using the given validated token. 

112 """ 

113 try: 

114 user_id = validated_token[api_settings.USER_ID_CLAIM] 

115 except KeyError: 

116 raise InvalidToken(_("Token contained no recognizable user identification")) 

117 

118 try: 

119 user = self.user_model.objects.get(**{api_settings.USER_ID_FIELD: user_id}) 

120 except self.user_model.DoesNotExist: 

121 raise AuthenticationFailed(_("User not found"), code="user_not_found") 

122 

123 if not user.is_active: 123 ↛ 124line 123 didn't jump to line 124, because the condition on line 123 was never true

124 raise AuthenticationFailed(_("User is inactive"), code="user_inactive") 

125 

126 return user 

127 

128 

129class JWTStatelessUserAuthentication(JWTAuthentication): 

130 """ 

131 An authentication plugin that authenticates requests through a JSON web 

132 token provided in a request header without performing a database lookup to obtain a user instance. 

133 """ 

134 

135 def get_user(self, validated_token): 

136 """ 

137 Returns a stateless user object which is backed by the given validated 

138 token. 

139 """ 

140 if api_settings.USER_ID_CLAIM not in validated_token: 

141 # The TokenUser class assumes tokens will have a recognizable user 

142 # identifier claim. 

143 raise InvalidToken(_("Token contained no recognizable user identification")) 

144 

145 return api_settings.TOKEN_USER_CLASS(validated_token) 

146 

147 

148JWTTokenUserAuthentication = JWTStatelessUserAuthentication 

149 

150 

151def default_user_authentication_rule(user): 

152 # Prior to Django 1.10, inactive users could be authenticated with the 

153 # default `ModelBackend`. As of Django 1.10, the `ModelBackend` 

154 # prevents inactive users from authenticating. App designers can still 

155 # allow inactive users to authenticate by opting for the new 

156 # `AllowAllUsersModelBackend`. However, we explicitly prevent inactive 

157 # users from authenticating to enforce a reasonable policy and provide 

158 # sensible backwards compatibility with older Django versions. 

159 return user is not None and user.is_active