MultiuserPlugin.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import re
  2. import sys
  3. from Config import config
  4. from Plugin import PluginManager
  5. from Crypt import CryptBitcoin
  6. import UserPlugin
  7. @PluginManager.registerTo("UiRequest")
  8. class UiRequestPlugin(object):
  9. def __init__(self, *args, **kwargs):
  10. self.user_manager = sys.modules["User.UserManager"].user_manager
  11. super(UiRequestPlugin, self).__init__(*args, **kwargs)
  12. # Create new user and inject user welcome message if necessary
  13. # Return: Html body also containing the injection
  14. def actionWrapper(self, path, extra_headers=None):
  15. match = re.match("/(?P<address>[A-Za-z0-9\._-]+)(?P<inner_path>/.*|$)", path)
  16. if not match:
  17. return False
  18. inner_path = match.group("inner_path").lstrip("/")
  19. html_request = "." not in inner_path or inner_path.endswith(".html") # Only inject html to html requests
  20. user_created = False
  21. if html_request:
  22. user = self.getCurrentUser() # Get user from cookie
  23. if not user: # No user found by cookie
  24. user = self.user_manager.create()
  25. user_created = True
  26. if user_created:
  27. if not extra_headers:
  28. extra_headers = []
  29. extra_headers.append(('Set-Cookie', "master_address=%s;path=/;max-age=2592000;" % user.master_address)) # = 30 days
  30. loggedin = self.get.get("login") == "done"
  31. back_generator = super(UiRequestPlugin, self).actionWrapper(path, extra_headers) # Get the wrapper frame output
  32. if not back_generator: # Wrapper error or not string returned, injection not possible
  33. return False
  34. if user_created:
  35. back = back_generator.next()
  36. master_seed = user.master_seed
  37. # Inject the welcome message
  38. inject_html = """
  39. <!-- Multiser plugin -->
  40. <style>
  41. .masterseed { font-size: 95%; background-color: #FFF0AD; padding: 5px 8px; margin: 9px 0px }
  42. </style>
  43. <script>
  44. hello_message = "<b>Hello, welcome to ZeroProxy!</b><div style='margin-top: 8px'>A new, unique account created for you:</div>"
  45. hello_message+= "<div class='masterseed'>{master_seed}</div> <div>This is your private key, <b>save it</b>, so you can login next time.</div><br>"
  46. hello_message+= "<a href='#' class='button' style='margin-left: 0px'>Ok, Saved it!</a> or <a href='#Login' onclick='wrapper.ws.cmd(\\"userLoginForm\\", []); return false'>Login</a><br><br>"
  47. hello_message+= "<small>This site allows you to browse ZeroNet content, but if you want to secure your account <br>"
  48. hello_message+= "and help to make a better network, then please run your own <a href='https://github.com/HelloZeroNet/ZeroNet' target='_blank'>ZeroNet client</a>.</small>"
  49. setTimeout(function() {
  50. wrapper.notifications.add("hello", "info", hello_message)
  51. delete(hello_message)
  52. }, 1000)
  53. </script>
  54. </body>
  55. </html>
  56. """.replace("\t", "")
  57. inject_html = inject_html.replace("{master_seed}", master_seed) # Set the master seed in the message
  58. return iter([re.sub("</body>\s*</html>\s*$", inject_html, back)]) # Replace the </body></html> tags with the injection
  59. elif loggedin:
  60. back = back_generator.next()
  61. inject_html = """
  62. <!-- Multiser plugin -->
  63. <script>
  64. setTimeout(function() {
  65. wrapper.notifications.add("login", "done", "Hello again!<br><small>You have been logged in successfully</small>", 5000)
  66. }, 1000)
  67. </script>
  68. </body>
  69. </html>
  70. """.replace("\t", "")
  71. return iter([re.sub("</body>\s*</html>\s*$", inject_html, back)]) # Replace the </body></html> tags with the injection
  72. else: # No injection necessary
  73. return back_generator
  74. # Get the current user based on request's cookies
  75. # Return: User object or None if no match
  76. def getCurrentUser(self):
  77. cookies = self.getCookies()
  78. user = None
  79. if "master_address" in cookies:
  80. users = self.user_manager.list()
  81. user = users.get(cookies["master_address"])
  82. return user
  83. @PluginManager.registerTo("UiWebsocket")
  84. class UiWebsocketPlugin(object):
  85. # Let the page know we running in multiuser mode
  86. def formatServerInfo(self):
  87. server_info = super(UiWebsocketPlugin, self).formatServerInfo()
  88. server_info["multiuser"] = True
  89. if "ADMIN" in self.site.settings["permissions"]:
  90. server_info["master_address"] = self.user.master_address
  91. return server_info
  92. # Show current user's master seed
  93. def actionUserShowMasterSeed(self, to):
  94. if "ADMIN" not in self.site.settings["permissions"]:
  95. return self.response(to, "Show master seed not allowed")
  96. message = "<b style='padding-top: 5px; display: inline-block'>Your unique private key:</b>"
  97. message += "<div style='font-size: 84%%; background-color: #FFF0AD; padding: 5px 8px; margin: 9px 0px'>%s</div>" % self.user.master_seed
  98. message += "<small>(Save it, you can access your account using this information)</small>"
  99. self.cmd("notification", ["info", message])
  100. # Logout user
  101. def actionUserLogout(self, to):
  102. if "ADMIN" not in self.site.settings["permissions"]:
  103. return self.response(to, "Logout not allowed")
  104. message = "<b>You have been logged out.</b> <a href='#Login' class='button' onclick='wrapper.ws.cmd(\"userLoginForm\", []); return false'>Login to another account</a>"
  105. message += "<script>document.cookie = 'master_address=; expires=Thu, 01 Jan 1970 00:00:00 UTC'</script>"
  106. self.cmd("notification", ["done", message, 1000000]) # 1000000 = Show ~forever :)
  107. # Delete from user_manager
  108. user_manager = sys.modules["User.UserManager"].user_manager
  109. if self.user.master_address in user_manager.users:
  110. if not config.multiuser_local:
  111. del user_manager.users[self.user.master_address]
  112. self.response(to, "Successful logout")
  113. else:
  114. self.response(to, "User not found")
  115. # Show login form
  116. def actionUserLoginForm(self, to):
  117. self.cmd("prompt", ["<b>Login</b><br>Your private key:", "password", "Login"], self.responseUserLogin)
  118. # Login form submit
  119. def responseUserLogin(self, master_seed):
  120. user_manager = sys.modules["User.UserManager"].user_manager
  121. user = user_manager.get(CryptBitcoin.privatekeyToAddress(master_seed))
  122. if not user:
  123. user = user_manager.create(master_seed=master_seed)
  124. if user.master_address:
  125. message = "Successfull login, reloading page..."
  126. message += "<script>document.cookie = 'master_address=%s;path=/;max-age=2592000;'</script>" % user.master_address
  127. message += "<script>wrapper.reload('login=done')</script>"
  128. self.cmd("notification", ["done", message])
  129. else:
  130. self.cmd("notification", ["error", "Error: Invalid master seed"])
  131. self.actionUserLoginForm(0)
  132. # Disable not Multiuser safe functions
  133. def actionSiteDelete(self, to, *args, **kwargs):
  134. if not config.multiuser_local:
  135. self.cmd("notification", ["info", "This function is disabled on this proxy"])
  136. else:
  137. return super(UiWebsocketPlugin, self).actionSiteDelete(to, *args, **kwargs)
  138. def actionConfigSet(self, to, *args, **kwargs):
  139. if not config.multiuser_local:
  140. self.cmd("notification", ["info", "This function is disabled on this proxy"])
  141. else:
  142. return super(UiWebsocketPlugin, self).actionConfigSet(to, *args, **kwargs)
  143. def actionServerShutdown(self, to, *args, **kwargs):
  144. if not config.multiuser_local:
  145. self.cmd("notification", ["info", "This function is disabled on this proxy"])
  146. else:
  147. return super(UiWebsocketPlugin, self).actionServerShutdown(to, *args, **kwargs)
  148. def actionServerUpdate(self, to, *args, **kwargs):
  149. if not config.multiuser_local:
  150. self.cmd("notification", ["info", "This function is disabled on this proxy"])
  151. else:
  152. return super(UiWebsocketPlugin, self).actionServerUpdate(to, *args, **kwargs)
  153. def actionSiteClone(self, to, *args, **kwargs):
  154. if not config.multiuser_local:
  155. self.cmd("notification", ["info", "This function is disabled on this proxy"])
  156. else:
  157. return super(UiWebsocketPlugin, self).actionSiteClone(to, *args, **kwargs)
  158. @PluginManager.registerTo("ConfigPlugin")
  159. class ConfigPlugin(object):
  160. def createArguments(self):
  161. group = self.parser.add_argument_group("Multiuser plugin")
  162. group.add_argument('--multiuser_local', help="Enable unsafe Ui functions and write users to disk", action='store_true')
  163. return super(ConfigPlugin, self).createArguments()