Mercurial > p > roundup > code
comparison roundup/cgi/client.py @ 5934:db9bd45d50ad
Refactor jwt auth into authenticate_bearer_token() method on Client
The json web token authentication was buried inside determine
user. Refactored to put this authenticate_bearer_token method so it
can (hopefully) be overriden by an instance.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Sat, 19 Oct 2019 16:35:08 -0400 |
| parents | b40059d7036f |
| children | 1b50c2c5619a |
comparison
equal
deleted
inserted
replaced
| 5933:0bac8b9a0ecc | 5934:db9bd45d50ad |
|---|---|
| 950 if language: | 950 if language: |
| 951 self.setTranslator(TranslationService.get_translation( | 951 self.setTranslator(TranslationService.get_translation( |
| 952 language, | 952 language, |
| 953 tracker_home=self.instance.config["TRACKER_HOME"])) | 953 tracker_home=self.instance.config["TRACKER_HOME"])) |
| 954 | 954 |
| 955 def authenticate_bearer_token(self, challenge): | |
| 956 ''' authenticate the bearer token. Refactored from determine_user() | |
| 957 to alow it to be overridden if needed. | |
| 958 ''' | |
| 959 try: # will jwt import? | |
| 960 import jwt | |
| 961 except ImportError: | |
| 962 # no support for jwt, this is fine. | |
| 963 self.setHeader("WWW-Authenticate", "Basic") | |
| 964 raise LoginError('Support for jwt disabled.') | |
| 965 | |
| 966 secret = self.db.config.WEB_JWT_SECRET | |
| 967 if len(secret) < 32: | |
| 968 # no support for jwt, this is fine. | |
| 969 self.setHeader("WWW-Authenticate", "Basic") | |
| 970 raise LoginError('Support for jwt disabled by admin.') | |
| 971 | |
| 972 try: # handle jwt exceptions | |
| 973 token = jwt.decode(challenge, secret, | |
| 974 algorithms=['HS256'], | |
| 975 audience=self.db.config.TRACKER_WEB, | |
| 976 issuer=self.db.config.TRACKER_WEB) | |
| 977 except jwt.exceptions.InvalidTokenError as err: | |
| 978 self.setHeader("WWW-Authenticate", "Basic, Bearer") | |
| 979 self.make_user_anonymous() | |
| 980 raise LoginError(str(err)) | |
| 981 | |
| 982 return(token) | |
| 983 | |
| 955 def determine_user(self): | 984 def determine_user(self): |
| 956 """Determine who the user is""" | 985 """Determine who the user is""" |
| 957 self.opendb('admin') | 986 self.opendb('admin') |
| 958 | 987 |
| 959 # if we get a jwt, it includes the roles to be used for this session | 988 # if we get a jwt, it includes the roles to be used for this session |
| 1009 # try to seed with something harder to guess than | 1038 # try to seed with something harder to guess than |
| 1010 # just the time. If random is SystemRandom, | 1039 # just the time. If random is SystemRandom, |
| 1011 # this is a no-op. | 1040 # this is a no-op. |
| 1012 random_.seed("%s%s"%(password,time.time())) | 1041 random_.seed("%s%s"%(password,time.time())) |
| 1013 elif scheme.lower() == 'bearer': | 1042 elif scheme.lower() == 'bearer': |
| 1014 try: # will jwt import? | 1043 token = self.authenticate_bearer_token(challenge) |
| 1015 import jwt | 1044 |
| 1016 from roundup.hyperdb import iter_roles | 1045 from roundup.hyperdb import iter_roles |
| 1017 try: # handle jwt exceptions | 1046 |
| 1018 secret = self.db.config.WEB_JWT_SECRET | 1047 # if we got here token is valid, use the role |
| 1019 if len(secret) < 32: | 1048 # and sub claims. |
| 1020 # no support for jwt, this is fine. | 1049 try: |
| 1021 self.setHeader("WWW-Authenticate", "Basic") | 1050 # make sure to str(token['sub']) the subject. As decoded |
| 1022 raise LoginError('Support for jwt disabled by admin.') | 1051 # by json, it is unicode which thows an error when used |
| 1023 token = jwt.decode(challenge, secret, | 1052 # with 'nodeid in db' down the call chain. |
| 1024 algorithms=['HS256'], | 1053 user = self.db.user.get(str(token['sub']), 'username') |
| 1025 audience=self.db.config.TRACKER_WEB, | 1054 except IndexError: |
| 1026 issuer=self.db.config.TRACKER_WEB) | 1055 raise LoginError("Token subject is invalid.") |
| 1027 except jwt.exceptions.InvalidTokenError as err: | 1056 |
| 1028 self.setHeader("WWW-Authenticate", "Basic, Bearer") | 1057 # validate roles |
| 1029 self.make_user_anonymous() | 1058 all_rolenames = [ role[0] for role in self.db.security.role.items() ] |
| 1030 raise LoginError(str(err)) | 1059 for r in token['roles']: |
| 1031 | 1060 if r.lower() not in all_rolenames: |
| 1032 # if we got here token is valid, use the role | 1061 raise LoginError("Token roles are invalid.") |
| 1033 # and sub claims. | |
| 1034 try: | |
| 1035 # make sure to str(token['sub']) the subject. As decoded | |
| 1036 # by json, it is unicode which thows an error when used | |
| 1037 # with 'nodeid in db' down the call chain. | |
| 1038 user = self.db.user.get(str(token['sub']), 'username') | |
| 1039 except IndexError: | |
| 1040 raise LoginError("Token subject is invalid.") | |
| 1041 | |
| 1042 # validate roles | |
| 1043 all_rolenames = [ role[0] for role in self.db.security.role.items() ] | |
| 1044 for r in token['roles']: | |
| 1045 if r.lower() not in all_rolenames: | |
| 1046 raise LoginError("Token roles are invalid.") | |
| 1047 | 1062 |
| 1048 # will be used later to override the get_roles method | 1063 # will be used later to override the get_roles method |
| 1049 override_get_roles = \ | 1064 override_get_roles = \ |
| 1050 lambda self: iter_roles(','.join(token['roles'])) | 1065 lambda self: iter_roles(','.join(token['roles'])) |
| 1051 | |
| 1052 except ImportError: | |
| 1053 # no support for jwt, this is fine. | |
| 1054 self.setHeader("WWW-Authenticate", "Basic") | |
| 1055 raise LoginError('Support for jwt disabled.') | |
| 1056 | 1066 |
| 1057 # if user was not set by http authorization, try session lookup | 1067 # if user was not set by http authorization, try session lookup |
| 1058 if not user: | 1068 if not user: |
| 1059 user = self.session_api.get('user') | 1069 user = self.session_api.get('user') |
| 1060 if user: | 1070 if user: |
