Coverage for gws-app/gws/plugin/auth_provider/file/__init__.py: 81%

53 statements  

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

1"""Provider for the file-based authorization. 

2 

3This provider works with a local JSON file, which is expected to contain 

4a list of user "records" (dicts). 

5 

6A record is required to contain fields ``login`` and ``password`` (hashed as per `gws.lib.password.encode`). 

7 

8Other fields, if given, are converted to respective `gws.User` properties. 

9""" 

10 

11import getpass 

12 

13import gws 

14import gws.base.auth 

15import gws.lib.jsonx 

16import gws.lib.password 

17 

18gws.ext.new.authProvider('file') 

19 

20 

21class Config(gws.base.auth.provider.Config): 

22 """File-based authorization provider.""" 

23 

24 path: gws.FilePath 

25 """Path to the users json file.""" 

26 

27 

28class Object(gws.base.auth.provider.Object): 

29 path: str 

30 db: list[dict] 

31 dummyPassword: str 

32 

33 def configure(self): 

34 self.path = self.cfg('path') 

35 self.db = gws.lib.jsonx.from_path(self.path) 

36 self.dummyPassword = gws.lib.password.encode(gws.u.random_string(32)) 

37 

38 def authenticate(self, method, credentials): 

39 username = credentials.get('username') 

40 password = credentials.get('password') 

41 if not username or not password: 

42 return 

43 

44 found = [rec for rec in self.db if gws.lib.password.compare(username, rec['login'])] 

45 

46 if len(found) > 1: 

47 raise gws.ForbiddenError(f'multiple entries for {username!r}') 

48 

49 if not found: 

50 # verify against a dummy hash, so that the time spent here 

51 # does not reveal whether the login exists 

52 gws.lib.password.check(password, self.dummyPassword) 

53 return 

54 

55 if not gws.lib.password.check(password, found[0]['password']): 

56 raise gws.ForbiddenError(f'wrong password for {username!r}') 

57 

58 return self._make_user(found[0]) 

59 

60 def get_user(self, local_uid): 

61 for rec in self.db: 

62 if rec['login'] == local_uid: 

63 return self._make_user(rec) 

64 

65 def _make_user(self, rec: dict): 

66 user_rec = dict(rec) 

67 

68 login = user_rec.pop('login', '') 

69 user_rec['localUid'] = user_rec['loginName'] = login 

70 user_rec['displayName'] = user_rec.pop('name', login) 

71 user_rec.pop('password', '') 

72 

73 return gws.base.auth.user.from_record(self, user_rec) 

74 

75 @gws.ext.command.cli('authPassword') 

76 def passwd(self, p: gws.EmptyRequest): 

77 """Encode a password for the authorization file""" 

78 

79 while True: 

80 p1 = getpass.getpass('Password: ') 

81 p2 = getpass.getpass('Repeat : ') 

82 

83 if p1 != p2: 

84 print('passwords do not match') 

85 continue 

86 

87 p = gws.lib.password.encode(p1) 

88 print(p) 

89 break