You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
3.2 KiB

  1. import json
  2. from functools import wraps
  3. from django.contrib.auth.models import User
  4. from django.db.models import Q
  5. from django.http import HttpResponse, JsonResponse
  6. from hc.accounts.models import Project
  7. from hc.lib.jsonschema import ValidationError, validate
  8. def error(msg, status=400):
  9. return JsonResponse({"error": msg}, status=status)
  10. def authorize(f):
  11. @wraps(f)
  12. def wrapper(request, *args, **kwds):
  13. if "HTTP_X_API_KEY" in request.META:
  14. api_key = request.META["HTTP_X_API_KEY"]
  15. else:
  16. api_key = str(request.json.get("api_key", ""))
  17. if len(api_key) != 32:
  18. return error("missing api key", 401)
  19. try:
  20. request.project = Project.objects.get(api_key=api_key)
  21. except Project.DoesNotExist:
  22. return error("wrong api key", 401)
  23. return f(request, *args, **kwds)
  24. return wrapper
  25. def authorize_read(f):
  26. @wraps(f)
  27. def wrapper(request, *args, **kwds):
  28. if "HTTP_X_API_KEY" in request.META:
  29. api_key = request.META["HTTP_X_API_KEY"]
  30. else:
  31. api_key = str(request.json.get("api_key", ""))
  32. if len(api_key) != 32:
  33. return error("missing api key", 401)
  34. write_key_match = Q(api_key=api_key)
  35. read_key_match = Q(api_key_readonly=api_key)
  36. try:
  37. request.project = Project.objects.get(write_key_match | read_key_match)
  38. except Project.DoesNotExist:
  39. return error("wrong api key", 401)
  40. return f(request, *args, **kwds)
  41. return wrapper
  42. def validate_json(schema=None):
  43. """ Parse request json and validate it against `schema`.
  44. Put the parsed result in `request.json`.
  45. If schema is None then only parse and don't validate.
  46. Supports a limited subset of JSON schema spec.
  47. """
  48. def decorator(f):
  49. @wraps(f)
  50. def wrapper(request, *args, **kwds):
  51. if request.body:
  52. try:
  53. request.json = json.loads(request.body.decode())
  54. except ValueError:
  55. return error("could not parse request body")
  56. else:
  57. request.json = {}
  58. if schema:
  59. try:
  60. validate(request.json, schema)
  61. except ValidationError as e:
  62. return error("json validation error: %s" % e)
  63. return f(request, *args, **kwds)
  64. return wrapper
  65. return decorator
  66. def cors(*methods):
  67. methods = set(methods)
  68. methods.add("OPTIONS")
  69. methods_str = ", ".join(methods)
  70. def decorator(f):
  71. @wraps(f)
  72. def wrapper(request, *args, **kwds):
  73. if request.method == "OPTIONS":
  74. # Handle OPTIONS here
  75. response = HttpResponse(status=204)
  76. elif request.method in methods:
  77. response = f(request, *args, **kwds)
  78. else:
  79. response = HttpResponse(status=405)
  80. response["Access-Control-Allow-Origin"] = "*"
  81. response["Access-Control-Allow-Headers"] = "X-Api-Key"
  82. response["Access-Control-Allow-Methods"] = methods_str
  83. return response
  84. return wrapper
  85. return decorator