Coverage for gws-app/gws/plugin/auth_method/basic/__init__.py: 96%

46 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-24 12:46 +0200

1"""HTTP Basic authorisation method.""" 

2 

3from typing import Optional 

4 

5import base64 

6 

7import gws 

8import gws.base.auth 

9import gws.base.web 

10 

11gws.ext.new.authMethod('basic') 

12 

13 

14class Config(gws.base.auth.method.Config): 

15 """HTTP-basic authorization options""" 

16 

17 realm: Optional[str] 

18 """Authentication realm.""" 

19 

20 

21class Object(gws.base.auth.method.Object): 

22 realm: str 

23 

24 def configure(self): 

25 self.uid = 'gws.plugin.auth_method.basic' 

26 self.realm = self.cfg('realm', default='Restricted Area') 

27 self.root.app.middlewareMgr.register(self, self.uid, depends_on=['auth']) 

28 

29 ## 

30 

31 def exit_middleware(self, req, res): 

32 if res.status == 403 and req.isGet: 

33 res.set_status(401) 

34 res.add_header('WWW-Authenticate', f'Basic realm={self.realm}, charset="UTF-8"') 

35 gws.log.debug(f'auth basic: redirect {res.status=}') 

36 

37 

38 def open_session(self, req): 

39 am = self.root.app.authMgr 

40 credentials = self._parse_header(req) 

41 if not credentials: 

42 return 

43 user = am.authenticate(self, credentials, req) 

44 if user: 

45 return am.create_transient_session(self, user) 

46 

47 def _parse_header(self, req: gws.WebRequester): 

48 h = req.header('Authorization') 

49 if not h: 

50 return 

51 

52 a = h.strip().split() 

53 if len(a) != 2 or a[0].lower() != 'basic': 

54 return 

55 

56 try: 

57 b = gws.u.to_str(base64.decodebytes(gws.u.to_bytes(a[1]))) 

58 except ValueError: 

59 return 

60 

61 c = b.split(':') 

62 if len(c) != 2: 

63 return 

64 

65 username = c[0].strip() 

66 if not username: 

67 return 

68 

69 return gws.Data(username=username, password=c[1])