UiPasswordPlugin.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import string
  2. import random
  3. import time
  4. import json
  5. import re
  6. from Config import config
  7. from Plugin import PluginManager
  8. if "sessions" not in locals().keys(): # To keep sessions between module reloads
  9. sessions = {}
  10. @PluginManager.registerTo("UiRequest")
  11. class UiRequestPlugin(object):
  12. sessions = sessions
  13. last_cleanup = time.time()
  14. def route(self, path):
  15. if path.endswith("favicon.ico"):
  16. return self.actionFile("src/Ui/media/img/favicon.ico")
  17. else:
  18. if config.ui_password:
  19. if time.time() - self.last_cleanup > 60 * 60: # Cleanup expired sessions every hour
  20. self.cleanup()
  21. # Validate session
  22. session_id = self.getCookies().get("session_id")
  23. if session_id not in self.sessions: # Invalid session id, display login
  24. return self.actionLogin()
  25. return super(UiRequestPlugin, self).route(path)
  26. # Action: Login
  27. def actionLogin(self):
  28. template = open("plugins/UiPassword/login.html").read()
  29. self.sendHeader()
  30. posted = self.getPosted()
  31. if posted: # Validate http posted data
  32. if self.checkPassword(posted.get("password")):
  33. # Valid password, create session
  34. session_id = self.randomString(26)
  35. self.sessions[session_id] = {
  36. "added": time.time(),
  37. "keep": posted.get("keep")
  38. }
  39. # Redirect to homepage or referer
  40. url = self.env.get("HTTP_REFERER", "")
  41. if not url or re.sub("\?.*", "", url).endswith("/Login"):
  42. url = "/" + config.homepage
  43. cookie_header = ('Set-Cookie', "session_id=%s;path=/;max-age=2592000;" % session_id) # Max age = 30 days
  44. self.start_response('301 Redirect', [('Location', url), cookie_header])
  45. yield "Redirecting..."
  46. else:
  47. # Invalid password, show login form again
  48. template = template.replace("{result}", "bad_password")
  49. yield template
  50. def checkPassword(self, password):
  51. if password == config.ui_password:
  52. return True
  53. else:
  54. return False
  55. def randomString(self, chars):
  56. return ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(chars))
  57. @classmethod
  58. def cleanup(cls):
  59. cls.last_cleanup = time.time()
  60. for session_id, session in cls.sessions.items():
  61. if session["keep"] and time.time() - session["added"] > 60 * 60 * 24 * 60: # Max 60days for keep sessions
  62. del(cls.sessions[session_id])
  63. elif not session["keep"] and time.time() - session["added"] > 60 * 60 * 24: # Max 24h for non-keep sessions
  64. del(cls.sessions[session_id])
  65. # Action: Display sessions
  66. def actionSessions(self):
  67. self.sendHeader()
  68. yield "<pre>"
  69. yield json.dumps(self.sessions, indent=4)
  70. # Action: Logout
  71. def actionLogout(self):
  72. # Session id has to passed as get parameter or called without referer to avoid remote logout
  73. session_id = self.getCookies().get("session_id")
  74. if not self.env.get("HTTP_REFERER") or session_id == self.get.get("session_id"):
  75. if session_id in self.sessions:
  76. del self.sessions[session_id]
  77. self.start_response('301 Redirect', [
  78. ('Location', "/"),
  79. ('Set-Cookie', "session_id=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT")
  80. ])
  81. yield "Redirecting..."
  82. else:
  83. self.sendHeader()
  84. yield "Error: Invalid session id"
  85. @PluginManager.registerTo("ConfigPlugin")
  86. class ConfigPlugin(object):
  87. def createArguments(self):
  88. group = self.parser.add_argument_group("UiPassword plugin")
  89. group.add_argument('--ui_password', help='Password to access UiServer', default=None, metavar="password")
  90. return super(ConfigPlugin, self).createArguments()
  91. @PluginManager.registerTo("UiWebsocket")
  92. class UiWebsocketPlugin(object):
  93. def actionUiLogout(self, to):
  94. permissions = self.getPermissions(to)
  95. if "ADMIN" not in permissions:
  96. return self.response(to, "You don't have permission to run this command")
  97. session_id = self.request.getCookies().get("session_id", "")
  98. message = "<script>document.location.href = '/Logout?session_id=%s'</script>" % session_id
  99. self.cmd("notification", ["done", message])