CorsPlugin.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import re
  2. import html
  3. import copy
  4. import os
  5. import gevent
  6. from Plugin import PluginManager
  7. from Translate import Translate
  8. plugin_dir = os.path.dirname(__file__)
  9. if "_" not in locals():
  10. _ = Translate(plugin_dir + "/languages/")
  11. def getCorsPath(site, inner_path):
  12. match = re.match("^cors-([A-Za-z0-9]{26,35})/(.*)", inner_path)
  13. if not match:
  14. raise Exception("Invalid cors path: %s" % inner_path)
  15. cors_address = match.group(1)
  16. cors_inner_path = match.group(2)
  17. if not "Cors:%s" % cors_address in site.settings["permissions"]:
  18. raise Exception("This site has no permission to access site %s" % cors_address)
  19. return cors_address, cors_inner_path
  20. @PluginManager.registerTo("UiWebsocket")
  21. class UiWebsocketPlugin(object):
  22. def hasSitePermission(self, address, cmd=None):
  23. if super(UiWebsocketPlugin, self).hasSitePermission(address, cmd=cmd):
  24. return True
  25. allowed_commands = [
  26. "fileGet", "fileList", "dirList", "fileRules", "optionalFileInfo",
  27. "fileQuery", "dbQuery", "userGetSettings", "siteInfo"
  28. ]
  29. if not "Cors:%s" % address in self.site.settings["permissions"] or cmd not in allowed_commands:
  30. return False
  31. else:
  32. return True
  33. # Add cors support for file commands
  34. def corsFuncWrapper(self, func_name, to, inner_path, *args, **kwargs):
  35. if inner_path.startswith("cors-"):
  36. cors_address, cors_inner_path = getCorsPath(self.site, inner_path)
  37. req_self = copy.copy(self)
  38. req_self.site = self.server.sites.get(cors_address) # Change the site to the merged one
  39. if not req_self.site:
  40. return {"error": "No site found"}
  41. func = getattr(super(UiWebsocketPlugin, req_self), func_name)
  42. back = func(to, cors_inner_path, *args, **kwargs)
  43. return back
  44. else:
  45. func = getattr(super(UiWebsocketPlugin, self), func_name)
  46. return func(to, inner_path, *args, **kwargs)
  47. def actionFileGet(self, to, inner_path, *args, **kwargs):
  48. return self.corsFuncWrapper("actionFileGet", to, inner_path, *args, **kwargs)
  49. def actionFileList(self, to, inner_path, *args, **kwargs):
  50. return self.corsFuncWrapper("actionFileList", to, inner_path, *args, **kwargs)
  51. def actionDirList(self, to, inner_path, *args, **kwargs):
  52. return self.corsFuncWrapper("actionDirList", to, inner_path, *args, **kwargs)
  53. def actionFileRules(self, to, inner_path, *args, **kwargs):
  54. return self.corsFuncWrapper("actionFileRules", to, inner_path, *args, **kwargs)
  55. def actionOptionalFileInfo(self, to, inner_path, *args, **kwargs):
  56. return self.corsFuncWrapper("actionOptionalFileInfo", to, inner_path, *args, **kwargs)
  57. def actionCorsPermission(self, to, address):
  58. if isinstance(address, list):
  59. addresses = address
  60. else:
  61. addresses = [address]
  62. button_title = _["Grant"]
  63. site_names = []
  64. site_addresses = []
  65. for address in addresses:
  66. site = self.server.sites.get(address)
  67. if site:
  68. site_name = site.content_manager.contents.get("content.json", {}).get("title", address)
  69. else:
  70. site_name = address
  71. # If at least one site is not downloaded yet, show "Grant & Add" instead
  72. button_title = _["Grant & Add"]
  73. if not (site and "Cors:" + address in self.permissions):
  74. # No site or no permission
  75. site_names.append(site_name)
  76. site_addresses.append(address)
  77. if len(site_names) == 0:
  78. return "ignored"
  79. self.cmd(
  80. "confirm",
  81. [_["This site requests <b>read</b> permission to: <b>%s</b>"] % ", ".join(map(html.escape, site_names)), button_title],
  82. lambda res: self.cbCorsPermission(to, site_addresses)
  83. )
  84. def cbCorsPermission(self, to, addresses):
  85. # Add permissions
  86. for address in addresses:
  87. permission = "Cors:" + address
  88. if permission not in self.site.settings["permissions"]:
  89. self.site.settings["permissions"].append(permission)
  90. self.site.saveSettings()
  91. self.site.updateWebsocket(permission_added=permission)
  92. self.response(to, "ok")
  93. for address in addresses:
  94. site = self.server.sites.get(address)
  95. if not site:
  96. gevent.spawn(self.server.site_manager.need, address)
  97. @PluginManager.registerTo("UiRequest")
  98. class UiRequestPlugin(object):
  99. # Allow to load cross origin files using /cors-address/file.jpg
  100. def parsePath(self, path):
  101. path_parts = super(UiRequestPlugin, self).parsePath(path)
  102. if "cors-" not in path: # Optimization
  103. return path_parts
  104. site = self.server.sites[path_parts["address"]]
  105. try:
  106. path_parts["address"], path_parts["inner_path"] = getCorsPath(site, path_parts["inner_path"])
  107. except Exception:
  108. return None
  109. return path_parts