Coverage for src/django_otp_webauthn/utils.py: 25%

59 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-06-23 20:15 +0000

1from logging import Logger 

2from typing import TYPE_CHECKING, Optional 

3 

4from django.apps import apps 

5from django.core.exceptions import ImproperlyConfigured 

6from django.urls import reverse 

7from webauthn.helpers import exceptions as pywebauthn_exceptions 

8 

9from django_otp_webauthn import exceptions 

10from django_otp_webauthn.settings import app_settings 

11 

12if TYPE_CHECKING: 

13 from .models import AbstractWebAuthnAttestation, AbstractWebAuthnCredential 

14 

15 

16class rewrite_exceptions: 

17 """Context manager that swallows py_webauthn exceptions and raises 

18 appropriate django_otp_webauthn api exceptions that are handled nicely by rest 

19 framework. 

20 

21 To aid in debugging, this context manager accepts an optional logger 

22 argument that will be used to log the py_webauthn exceptions raised. 

23 

24 """ 

25 

26 def __init__(self, logger: Optional[Logger] = None): 

27 self.logger = logger 

28 

29 def log_exception(self, exc: Exception): 

30 if self.logger: 

31 self.logger.exception(exc) 

32 

33 def __enter__(self): 

34 pass 

35 

36 def __exit__(self, exc_type, exc_val, exc_tb): 

37 self.log_exception(exc_val) 

38 if exc_type is pywebauthn_exceptions.InvalidCBORData: 

39 raise exceptions.UnprocessableEntity(code="invalid_cbor", detail="Invalid CBOR data provided.") from exc_val 

40 elif exc_type is pywebauthn_exceptions.InvalidRegistrationResponse: 

41 raise exceptions.UnprocessableEntity( 

42 code="invalid_registration_response", 

43 detail="Invalid registration response provided.", 

44 ) from exc_val 

45 elif exc_type is pywebauthn_exceptions.InvalidAuthenticationResponse: 

46 raise exceptions.UnprocessableEntity( 

47 code="invalid_authentication_response", 

48 detail="Invalid authentication response provided.", 

49 ) from exc_val 

50 elif exc_type is pywebauthn_exceptions.InvalidJSONStructure: 

51 raise exceptions.UnprocessableEntity( 

52 code="invalid_json_structure", 

53 detail="There is a problem with the provided JSON data.", 

54 ) from exc_val 

55 elif exc_type is pywebauthn_exceptions.UnsupportedAlgorithm: 

56 raise exceptions.UnprocessableEntity( 

57 code="unsupported_algorithm", 

58 detail="The specified COSE algorithm is not supported by this server.", 

59 ) from exc_val 

60 elif ( 

61 exc_type is pywebauthn_exceptions.UnsupportedPublicKey 

62 or exc_type is pywebauthn_exceptions.InvalidPublicKeyStructure 

63 ): 

64 raise exceptions.UnprocessableEntity( 

65 code="unsupported_public_key", 

66 detail="The public key is malformed or not supported.", 

67 ) from exc_val 

68 elif exc_type is pywebauthn_exceptions.InvalidAuthenticatorDataStructure: 

69 raise exceptions.UnprocessableEntity( 

70 code="invalid_authenticator_data_structure", 

71 detail="The provided authenticator data is malformed.", 

72 ) from exc_val 

73 elif exc_type is pywebauthn_exceptions.InvalidCertificateChain: 

74 raise exceptions.UnprocessableEntity( 

75 code="invalid_certificate_chain", 

76 detail="The certificate chain in the attestation could not be validated.", 

77 ) from exc_val 

78 elif exc_type is pywebauthn_exceptions.UnsupportedEC2Curve: 

79 raise exceptions.UnprocessableEntity( 

80 code="unsupported_ec2_curve", detail="The EC2 curve is not supported." 

81 ) from exc_val 

82 elif exc_type is pywebauthn_exceptions.InvalidBackupFlags: 

83 raise exceptions.UnprocessableEntity( 

84 code="invalid_backup_flags", 

85 detail="Impossible backup flags combination was provided.", 

86 ) from exc_val 

87 return False 

88 

89 

90def get_exempt_urls() -> list: 

91 """Returns the list of urls that should be allowed without 2FA verification.""" 

92 return [ 

93 # Registration 

94 reverse("otp_webauthn:credential-registration-begin"), 

95 reverse("otp_webauthn:credential-registration-complete"), 

96 # Login 

97 reverse("otp_webauthn:credential-authentication-begin"), 

98 reverse("otp_webauthn:credential-authentication-complete"), 

99 ] 

100 

101 

102def get_credential_model() -> "AbstractWebAuthnCredential": 

103 """Returns the WebAuthnCredential model that is active in this project.""" 

104 # Inspired by Django's django.contrib.auth.get_user_model 

105 try: 

106 return apps.get_model(app_settings.OTP_WEBAUTHN_CREDENTIAL_MODEL, require_ready=False) 

107 except ValueError: 

108 raise ImproperlyConfigured("OTP_WEBAUTHN_CREDENTIAL_MODEL must be of the form 'app_label.model_name'") 

109 except LookupError: 

110 raise ImproperlyConfigured( 

111 f"OTP_WEBAUTHN_CREDENTIAL_MODEL refers to model '{app_settings.OTP_WEBAUTHN_CREDENTIAL_MODEL}' that has not been installed" 

112 ) 

113 

114 

115def get_attestation_model() -> "AbstractWebAuthnAttestation": 

116 """Returns the WebAuthnAttestation model that is active in this project.""" 

117 # Inspired by Django's django.contrib.auth.get_user_model 

118 try: 

119 return apps.get_model(app_settings.OTP_WEBAUTHN_ATTESTATION_MODEL, require_ready=False) 

120 except ValueError: 

121 raise ImproperlyConfigured("OTP_WEBAUTHN_ATTESTATION_MODEL must be of the form 'app_label.model_name'") 

122 except LookupError: 

123 raise ImproperlyConfigured( 

124 f"OTP_WEBAUTHN_ATTESTATION_MODEL refers to model '{app_settings.OTP_WEBAUTHN_ATTESTATION_MODEL}' that has not been installed" 

125 ) 

126 

127 

128def get_credential_model_string() -> str: 

129 """Returns the string representation of the WebAuthnCredential model that is 

130 active in this project.""" 

131 return app_settings.OTP_WEBAUTHN_CREDENTIAL_MODEL 

132 

133 

134def get_attestation_model_string() -> str: 

135 """Returns the string representation of the WebAuthnAttestation model 

136 that is active in this project.""" 

137 return app_settings.OTP_WEBAUTHN_ATTESTATION_MODEL