def verify_token(token=None): """ Verifies the token for the user (if provided). If in production environment and token is not provided, gets user from the SSO headers and returns their token. Args: token: Optional[str] Returns: token: str Raises: ApiError. If not on production and token is not valid, returns an 'invalid_token' 403 error. If on production and user is not authenticated, returns a 'no_user' 403 error. """ failure_error = ApiError( "invalid_token", "Unable to decode the token you provided. Please re-authenticate", status_code=403) if not _is_production() and (token is None or 'user' not in g): g.user = UserModel.query.first() token = g.user.encode_auth_token() if token: try: token_info = UserModel.decode_auth_token(token) g.user = UserModel.query.filter_by(uid=token_info['sub']).first() except: raise failure_error if g.user is not None: return token_info else: raise failure_error # If there's no token and we're in production, get the user from the SSO headers and return their token if not token and _is_production(): uid = _get_request_uid(request) if uid is not None: db_user = UserModel.query.filter_by(uid=uid).first() if db_user is not None: g.user = db_user token = g.user.encode_auth_token().decode() token_info = UserModel.decode_auth_token(token) return token_info else: raise ApiError( "no_user", "User not found. Please login via the frontend app before accessing this feature.", status_code=403)
def test_auth_token(self): # Save the orginal timeout setting orig_ttl = float(app.config['TOKEN_AUTH_TTL_HOURS']) self.load_example_data() # Set the timeout to something else new_ttl = 4.0 app.config['TOKEN_AUTH_TTL_HOURS'] = new_ttl user_1 = UserModel(uid="dhf8r") expected_exp_1 = timegm( (datetime.utcnow() + timedelta(hours=new_ttl)).utctimetuple()) auth_token_1 = user_1.encode_auth_token() self.assertTrue(isinstance(auth_token_1, bytes)) self.assertEqual("dhf8r", user_1.decode_auth_token(auth_token_1).get("sub")) actual_exp_1 = user_1.decode_auth_token(auth_token_1).get("exp") self.assertTrue( expected_exp_1 - 1000 <= actual_exp_1 <= expected_exp_1 + 1000) # Set the timeout to something else neg_ttl = -0.01 app.config['TOKEN_AUTH_TTL_HOURS'] = neg_ttl user_2 = UserModel(uid="dhf8r") expected_exp_2 = timegm( (datetime.utcnow() + timedelta(hours=neg_ttl)).utctimetuple()) auth_token_2 = user_2.encode_auth_token() self.assertTrue(isinstance(auth_token_2, bytes)) with self.assertRaises(ApiError) as api_error: with self.assertRaises(jwt.exceptions.ExpiredSignatureError): user_2.decode_auth_token(auth_token_2) self.assertEqual(api_error.exception.status_code, 400, 'Should raise an API Error if token is expired') # Set the timeout back to where it was app.config['TOKEN_AUTH_TTL_HOURS'] = orig_ttl user_3 = UserModel(uid="dhf8r") expected_exp_3 = timegm( (datetime.utcnow() + timedelta(hours=new_ttl)).utctimetuple()) auth_token_3 = user_3.encode_auth_token() self.assertTrue(isinstance(auth_token_3, bytes)) actual_exp_3 = user_3.decode_auth_token(auth_token_1).get("exp") self.assertTrue( expected_exp_3 - 1000 <= actual_exp_3 <= expected_exp_3 + 1000)
def verify_token_admin(token=None): """ Verifies the token for the user (if provided) in non-production environment. If in production environment, checks that the user is in the list of authorized admins Args: token: Optional[str] Returns: token: str """ verify_token(token) if "user" in g and g.user.is_admin(): token = g.user.encode_auth_token() token_info = UserModel.decode_auth_token(token) return token_info
def test_auth_token(self): # Save the orginal timeout setting orig_ttl = float(app.config['TOKEN_AUTH_TTL_HOURS']) self.load_example_data() # Set the timeout to something else new_ttl = 4.0 app.config['TOKEN_AUTH_TTL_HOURS'] = new_ttl user_1 = UserModel(uid="dhf8r") expected_exp_1 = timegm( (datetime.utcnow() + timedelta(hours=new_ttl)).utctimetuple()) auth_token_1 = user_1.encode_auth_token() self.assertTrue(isinstance(auth_token_1, str)) self.assertEqual("dhf8r", user_1.decode_auth_token(auth_token_1).get("sub"))
def verify_token(token=None): """ Verifies the token for the user (if provided). If in production environment and token is not provided, gets user from the SSO headers and returns their token. Args: token: Optional[str] Returns: token: str Raises: ApiError. If not on production and token is not valid, returns an 'invalid_token' 403 error. If on production and user is not authenticated, returns a 'no_user' 403 error. """ failure_error = ApiError("invalid_token", "Unable to decode the token you provided. Please re-authenticate", status_code=403) if token: try: token_info = UserModel.decode_auth_token(token) g.user = UserModel.query.filter_by(uid=token_info['sub']).first() # If the user is valid, store the token for this session if g.user: g.token = token except: raise failure_error if g.user is not None: return token_info else: raise failure_error # If there's no token and we're in production, get the user from the SSO headers and return their token elif _is_production(): uid = _get_request_uid(request) if uid is not None: db_user = UserModel.query.filter_by(uid=uid).first() # If the user is valid, store the user and token for this session if db_user is not None: g.user = db_user token = g.user.encode_auth_token().decode() g.token = token token_info = UserModel.decode_auth_token(token) return token_info else: raise ApiError("no_user", "User not found. Please login via the frontend app before accessing this feature.", status_code=403) else: # Fall back to a default user if this is not production. g.user = UserModel.query.first() if not g.user: raise ApiError("no_user", "You are in development mode, but there are no users in the database. Add one, and it will use it.") token = g.user.encode_auth_token() token_info = UserModel.decode_auth_token(token) return token_info