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
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 12:46 +0200
1"""Provider for the file-based authorization.
3This provider works with a local JSON file, which is expected to contain
4a list of user "records" (dicts).
6A record is required to contain fields ``login`` and ``password`` (hashed as per `gws.lib.password.encode`).
8Other fields, if given, are converted to respective `gws.User` properties.
9"""
11import getpass
13import gws
14import gws.base.auth
15import gws.lib.jsonx
16import gws.lib.password
18gws.ext.new.authProvider('file')
21class Config(gws.base.auth.provider.Config):
22 """File-based authorization provider."""
24 path: gws.FilePath
25 """Path to the users json file."""
28class Object(gws.base.auth.provider.Object):
29 path: str
30 db: list[dict]
31 dummyPassword: str
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))
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
44 found = [rec for rec in self.db if gws.lib.password.compare(username, rec['login'])]
46 if len(found) > 1:
47 raise gws.ForbiddenError(f'multiple entries for {username!r}')
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
55 if not gws.lib.password.check(password, found[0]['password']):
56 raise gws.ForbiddenError(f'wrong password for {username!r}')
58 return self._make_user(found[0])
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)
65 def _make_user(self, rec: dict):
66 user_rec = dict(rec)
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', '')
73 return gws.base.auth.user.from_record(self, user_rec)
75 @gws.ext.command.cli('authPassword')
76 def passwd(self, p: gws.EmptyRequest):
77 """Encode a password for the authorization file"""
79 while True:
80 p1 = getpass.getpass('Password: ')
81 p2 = getpass.getpass('Repeat : ')
83 if p1 != p2:
84 print('passwords do not match')
85 continue
87 p = gws.lib.password.encode(p1)
88 print(p)
89 break