Coverage for src/paperap/auth.py: 93%

30 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-20 13:17 -0400

1""" 

2---------------------------------------------------------------------------- 

3 

4 METADATA: 

5 

6 File: auth.py 

7 Project: paperap 

8 Created: 2025-03-04 

9 Version: 0.0.7 

10 Author: Jess Mann 

11 Email: jess@jmann.me 

12 Copyright (c) 2025 Jess Mann 

13 

14---------------------------------------------------------------------------- 

15 

16 LAST MODIFIED: 

17 

18 2025-03-04 By Jess Mann 

19 

20""" 

21 

22from __future__ import annotations 

23 

24from abc import ABC, abstractmethod 

25from typing import Annotated, Any, override 

26 

27import pydantic 

28from pydantic import ConfigDict, Field 

29 

30 

31class AuthBase(pydantic.BaseModel): 

32 """Base authentication class.""" 

33 

34 model_config = ConfigDict( 

35 str_strip_whitespace=True, 

36 validate_default=True, 

37 validate_assignment=True, 

38 ) 

39 

40 @abstractmethod 

41 def get_auth_headers(self) -> dict[str, str]: 

42 """Get authentication headers.""" 

43 raise NotImplementedError("get_auth_headers must be implemented by subclasses") 

44 

45 @abstractmethod 

46 def get_auth_params(self) -> dict[str, Any]: 

47 """Get authentication parameters for requests.""" 

48 raise NotImplementedError("get_auth_params must be implemented by subclasses") 

49 

50 

51class TokenAuth(AuthBase): 

52 """Authentication using a token.""" 

53 

54 # token length appears to be 40. Set to 30 just in case (will still catch egregious errors) 

55 token: Annotated[str, Field(min_length=30, max_length=75, pattern=r"^[a-zA-Z0-9]+$")] 

56 

57 @override 

58 def get_auth_headers(self) -> dict[str, str]: 

59 """Get the authorization headers.""" 

60 return {"Authorization": f"Token {self.token}"} 

61 

62 @override 

63 def get_auth_params(self) -> dict[str, Any]: 

64 """Get authentication parameters for requests.""" 

65 return {} 

66 

67 

68class BasicAuth(AuthBase): 

69 """Authentication using username and password.""" 

70 

71 username: str 

72 password: str 

73 

74 @override 

75 def get_auth_headers(self) -> dict[str, str]: 

76 """ 

77 Get headers for basic auth. 

78 

79 Basic auth is handled by the requests library, so no headers are needed here. 

80 """ 

81 return {} 

82 

83 @override 

84 def get_auth_params(self) -> dict[str, Any]: 

85 """Get authentication parameters for requests.""" 

86 return {"auth": (self.username, self.password)}