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:

Roundup Issue Tracker: http://roundup-tracker.org/