client_authentication.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. from base64 import b64encode
  2. from hmac import HMAC
  3. from time import time
  4. from urllib.parse import quote, urlencode
  5. from fate_flow.settings import HTTP_APP_KEY, HTTP_SECRET_KEY, MAX_TIMESTAMP_INTERVAL
  6. from fate_flow.entity import RetCode
  7. from fate_flow.hook import HookManager
  8. from fate_flow.hook.common.parameters import ClientAuthenticationReturn, ClientAuthenticationParameters
  9. @HookManager.register_client_authentication_hook
  10. def authentication(parm: ClientAuthenticationParameters) -> ClientAuthenticationReturn:
  11. if not (HTTP_APP_KEY and HTTP_SECRET_KEY):
  12. return ClientAuthenticationReturn(code=RetCode.AUTHENTICATION_ERROR,
  13. message=f"settings HTTP_APP_KEY and HTTP_SECRET_KEY is None")
  14. requirement_parm = ['TIMESTAMP', 'NONCE', 'APP_KEY', 'SIGNATURE']
  15. for _p in requirement_parm:
  16. if not parm.headers.get(_p):
  17. return ClientAuthenticationReturn(code=RetCode.AUTHENTICATION_ERROR,
  18. message=f"requirement headers parameters: {requirement_parm}")
  19. try:
  20. timestamp = int(parm.headers['TIMESTAMP']) / 1000
  21. except Exception:
  22. return ClientAuthenticationReturn(code=RetCode.AUTHENTICATION_ERROR, message="Invalid TIMESTAMP")
  23. now = time()
  24. if not now - MAX_TIMESTAMP_INTERVAL < timestamp < now + MAX_TIMESTAMP_INTERVAL:
  25. return ClientAuthenticationReturn(
  26. code=RetCode.AUTHENTICATION_ERROR,
  27. message=f'TIMESTAMP is more than {MAX_TIMESTAMP_INTERVAL} seconds away from the server time'
  28. )
  29. if not parm.headers['NONCE']:
  30. return ClientAuthenticationReturn(
  31. code=RetCode.AUTHENTICATION_ERROR,
  32. message='Invalid NONCE'
  33. )
  34. if parm.headers['APP_KEY'] != HTTP_APP_KEY:
  35. return ClientAuthenticationReturn(
  36. code=RetCode.AUTHENTICATION_ERROR,
  37. message='Unknown APP_KEY'
  38. )
  39. # authentication
  40. signature = b64encode(HMAC(HTTP_SECRET_KEY.encode('ascii'), b'\n'.join([
  41. parm.headers['TIMESTAMP'].encode('ascii'),
  42. parm.headers['NONCE'].encode('ascii'),
  43. parm.headers['APP_KEY'].encode('ascii'),
  44. parm.full_path.rstrip('?').encode('ascii'),
  45. parm.data if parm.json else b'',
  46. # quote_via: `urllib.parse.quote` replaces spaces with `%20`
  47. # safe: unreserved characters from rfc3986
  48. urlencode(sorted(parm.form.items()), quote_via=quote, safe='-._~').encode('ascii')
  49. if parm.form else b'',
  50. ]), 'sha1').digest()).decode('ascii')
  51. if signature != parm.headers['SIGNATURE']:
  52. return ClientAuthenticationReturn(
  53. code=RetCode.AUTHENTICATION_ERROR,
  54. message='signature authentication failed'
  55. )
  56. return ClientAuthenticationReturn()