big change to the webui auth system
added converting of old 1.1 config files added converting of old md5 passwords to sha1
This commit is contained in:
parent
2766c103e2
commit
af4c73a594
|
@ -28,3 +28,122 @@ AUTH_LEVEL_NORMAL = 5
|
||||||
AUTH_LEVEL_ADMIN = 10
|
AUTH_LEVEL_ADMIN = 10
|
||||||
|
|
||||||
AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL
|
AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL
|
||||||
|
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from twisted.internet.defer import Deferred
|
||||||
|
|
||||||
|
from deluge import component
|
||||||
|
from deluge.ui.web.json_api import JSONComponent, export
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class Auth(JSONComponent):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(Auth, self).__init__("Auth")
|
||||||
|
|
||||||
|
def _create_session(self, login='admin'):
|
||||||
|
m = hashlib.md5()
|
||||||
|
m.update(login)
|
||||||
|
m.update(str(time.time()))
|
||||||
|
m.update(str(random.getrandbits(40)))
|
||||||
|
m.update(m.hexdigest())
|
||||||
|
session_id = m.hexdigest()
|
||||||
|
|
||||||
|
log.debug("Creating session for %s", login)
|
||||||
|
config = component.get("DelugeWeb").config
|
||||||
|
|
||||||
|
if type(config["sessions"]) is list:
|
||||||
|
config.config["sessions"] = {}
|
||||||
|
|
||||||
|
config["sessions"][session_id] = {
|
||||||
|
"login": login
|
||||||
|
}
|
||||||
|
return session_id
|
||||||
|
|
||||||
|
@export
|
||||||
|
def change_password(self, new_password):
|
||||||
|
salt = hashlib.sha1(str(random.getrandbits(40))).hexdigest()
|
||||||
|
s = hashlib.sha1(salt)
|
||||||
|
s.update(new_password)
|
||||||
|
config = component.get("DelugeWeb").config
|
||||||
|
config["pwd_salt"] = salt
|
||||||
|
config["pwd_sha1"] = s.hexdigest()
|
||||||
|
log.debug("Changing password")
|
||||||
|
|
||||||
|
@export
|
||||||
|
def check_session(self, session_id):
|
||||||
|
d = Deferred()
|
||||||
|
config = component.get("DelugeWeb").config
|
||||||
|
d.callback(session_id in config["sessions"])
|
||||||
|
return d
|
||||||
|
|
||||||
|
@export
|
||||||
|
def delete_session(self, session_id):
|
||||||
|
d = Deferred()
|
||||||
|
config = component.get("DelugeWeb").config
|
||||||
|
del config["sessions"][session_id]
|
||||||
|
d.callback(True)
|
||||||
|
return d
|
||||||
|
|
||||||
|
@export
|
||||||
|
def login(self, password):
|
||||||
|
"""Method to allow the webui to authenticate
|
||||||
|
"""
|
||||||
|
config = component.get("DelugeWeb").config
|
||||||
|
d = Deferred()
|
||||||
|
|
||||||
|
if "old_pwd_md5" in config.config:
|
||||||
|
# We are using the 1.1 webui auth method
|
||||||
|
log.debug("Received a login via the 1.1 auth method")
|
||||||
|
from base64 import decodestring
|
||||||
|
m = hashlib.md5()
|
||||||
|
m.update(decodestring(config["old_pwd_salt"]))
|
||||||
|
m.update(password)
|
||||||
|
if m.digest() == decodestring(config["old_pwd_md5"]):
|
||||||
|
# We have a match, so we can create and return a session id.
|
||||||
|
d.callback(self._create_session())
|
||||||
|
|
||||||
|
# We also want to move the password over to sha1 and remove
|
||||||
|
# the old passwords from the config file.
|
||||||
|
self.change_password(password)
|
||||||
|
del config.config["old_pwd_salt"]
|
||||||
|
del config.config["old_pwd_md5"]
|
||||||
|
|
||||||
|
elif "pwd_md5" in config.config:
|
||||||
|
# We are using the 1.2-dev auth method
|
||||||
|
log.debug("Received a login via the 1.2-dev auth method")
|
||||||
|
m = hashlib.md5()
|
||||||
|
m.update(config["pwd_salt"])
|
||||||
|
m.update(password)
|
||||||
|
if m.hexdigest() == config['pwd_md5']:
|
||||||
|
# We have a match, so we can create and return a session id.
|
||||||
|
d.callback(self._create_session())
|
||||||
|
|
||||||
|
# We also want to move the password over to sha1 and remove
|
||||||
|
# the old passwords from the config file.
|
||||||
|
self.change_password(password)
|
||||||
|
del config.config["pwd_md5"]
|
||||||
|
del config.config["pwd_salt"]
|
||||||
|
|
||||||
|
elif "pwd_sha1" in config.config:
|
||||||
|
# We are using the 1.2 auth method
|
||||||
|
log.debug("Received a login via the 1.2 auth method")
|
||||||
|
s = hashlib.sha1()
|
||||||
|
s.update(config["pwd_salt"])
|
||||||
|
s.update(password)
|
||||||
|
if s.hexdigest() == config["pwd_sha1"]:
|
||||||
|
# We have a match, so we can create and return a session id.
|
||||||
|
d.callback(self._create_session())
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Can't detect which method we should be using so just deny
|
||||||
|
# access.
|
||||||
|
log.debug("Failed to detect the login method")
|
||||||
|
d.callback(False)
|
||||||
|
|
||||||
|
return d
|
|
@ -84,7 +84,7 @@ Copyright:
|
||||||
|
|
||||||
onLogin: function() {
|
onLogin: function() {
|
||||||
var passwordField = this.loginForm.items.get('password');
|
var passwordField = this.loginForm.items.get('password');
|
||||||
Deluge.Client.web.login(passwordField.getValue(), {
|
Deluge.Client.auth.login(passwordField.getValue(), {
|
||||||
success: function(result) {
|
success: function(result) {
|
||||||
if (result) {
|
if (result) {
|
||||||
Deluge.Events.fire('login');
|
Deluge.Events.fire('login');
|
||||||
|
@ -112,7 +112,7 @@ Copyright:
|
||||||
onLogout: function() {
|
onLogout: function() {
|
||||||
var session = Deluge.UI.cookies.get("session", false);
|
var session = Deluge.UI.cookies.get("session", false);
|
||||||
if (session) {
|
if (session) {
|
||||||
Deluge.Client.web.delete_session(session, {
|
Deluge.Client.auth.delete_session(session, {
|
||||||
success: function(result) {
|
success: function(result) {
|
||||||
Deluge.UI.cookies.clear("session");
|
Deluge.UI.cookies.clear("session");
|
||||||
this.show();
|
this.show();
|
||||||
|
@ -125,7 +125,7 @@ Copyright:
|
||||||
onBeforeShow: function() {
|
onBeforeShow: function() {
|
||||||
var session = Deluge.UI.cookies.get("session", false);
|
var session = Deluge.UI.cookies.get("session", false);
|
||||||
if (session) {
|
if (session) {
|
||||||
Deluge.Client.web.check_session(session, {
|
Deluge.Client.auth.check_session(session, {
|
||||||
success: function(result) {
|
success: function(result) {
|
||||||
if (result) {
|
if (result) {
|
||||||
Deluge.Events.fire('login');
|
Deluge.Events.fire('login');
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -25,10 +25,9 @@
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import base64
|
import base64
|
||||||
import random
|
|
||||||
import urllib
|
import urllib
|
||||||
import hashlib
|
|
||||||
import logging
|
import logging
|
||||||
|
import hashlib
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from types import FunctionType
|
from types import FunctionType
|
||||||
|
@ -39,12 +38,20 @@ from deluge import common, component
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.configmanager import ConfigManager
|
||||||
from deluge.ui import common as uicommon
|
from deluge.ui import common as uicommon
|
||||||
from deluge.ui.client import client, Client
|
from deluge.ui.client import client, Client
|
||||||
from deluge.ui.web.auth import *
|
|
||||||
from deluge.ui.web.common import _
|
from deluge.ui.web.common import _
|
||||||
json = common.json
|
json = common.json
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
AUTH_LEVEL_DEFAULT = None
|
||||||
|
|
||||||
|
class JSONComponent(component.Component):
|
||||||
|
def __init__(self, name, interval=1, depend=None):
|
||||||
|
super(JSONComponent, self).__init__(name, interval, depend)
|
||||||
|
self._json = component.get("JSON")
|
||||||
|
self._json.register_object(self, name)
|
||||||
|
|
||||||
def export(auth_level=AUTH_LEVEL_DEFAULT):
|
def export(auth_level=AUTH_LEVEL_DEFAULT):
|
||||||
"""
|
"""
|
||||||
Decorator function to register an object's method as an RPC. The object
|
Decorator function to register an object's method as an RPC. The object
|
||||||
|
@ -54,6 +61,10 @@ def export(auth_level=AUTH_LEVEL_DEFAULT):
|
||||||
:param auth_level: int, the auth level required to call this method
|
:param auth_level: int, the auth level required to call this method
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
global AUTH_LEVEL_DEFAULT
|
||||||
|
if AUTH_LEVEL_DEFAULT is None:
|
||||||
|
from deluge.ui.web.auth import AUTH_LEVEL_DEFAULT
|
||||||
|
|
||||||
def wrap(func, *args, **kwargs):
|
def wrap(func, *args, **kwargs):
|
||||||
func._json_export = True
|
func._json_export = True
|
||||||
func._json_auth_level = auth_level
|
func._json_auth_level = auth_level
|
||||||
|
@ -245,13 +256,6 @@ class JSON(resource.Resource, component.Component):
|
||||||
log.debug("Registering method: %s", name + "." + d)
|
log.debug("Registering method: %s", name + "." + d)
|
||||||
self._local_methods[name + "." + d] = getattr(obj, d)
|
self._local_methods[name + "." + d] = getattr(obj, d)
|
||||||
|
|
||||||
class JSONComponent(component.Component):
|
|
||||||
def __init__(self, name, interval=1, depend=None):
|
|
||||||
super(JSONComponent, self).__init__(name, interval, depend)
|
|
||||||
self._json = component.get("JSON")
|
|
||||||
self._json.register_object(self, name)
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_HOST = "127.0.0.1"
|
DEFAULT_HOST = "127.0.0.1"
|
||||||
DEFAULT_PORT = 58846
|
DEFAULT_PORT = 58846
|
||||||
|
|
||||||
|
@ -435,51 +439,6 @@ class WebApi(JSONComponent):
|
||||||
d.callback(True)
|
d.callback(True)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def _create_session(self, login='admin'):
|
|
||||||
m = hashlib.md5()
|
|
||||||
m.update(login)
|
|
||||||
m.update(str(time.time()))
|
|
||||||
m.update(str(random.getrandbits(999)))
|
|
||||||
m.update(m.hexdigest())
|
|
||||||
session_id = m.hexdigest()
|
|
||||||
|
|
||||||
config = component.get("DelugeWeb").config
|
|
||||||
config["sessions"][session_id] = {
|
|
||||||
"login": login
|
|
||||||
}
|
|
||||||
return session_id
|
|
||||||
|
|
||||||
@export
|
|
||||||
def check_session(self, session_id):
|
|
||||||
d = Deferred()
|
|
||||||
config = component.get("DelugeWeb").config
|
|
||||||
d.callback(session_id in config["sessions"])
|
|
||||||
return d
|
|
||||||
|
|
||||||
@export
|
|
||||||
def delete_session(self, session_id):
|
|
||||||
d = Deferred()
|
|
||||||
config = component.get("DelugeWeb").config
|
|
||||||
del config["sessions"][session_id]
|
|
||||||
d.callback(True)
|
|
||||||
return d
|
|
||||||
|
|
||||||
@export
|
|
||||||
def login(self, password):
|
|
||||||
"""Method to allow the webui to authenticate
|
|
||||||
"""
|
|
||||||
config = component.get("DelugeWeb").config
|
|
||||||
m = hashlib.md5()
|
|
||||||
m.update(config['pwd_salt'])
|
|
||||||
m.update(password)
|
|
||||||
d = Deferred()
|
|
||||||
if m.hexdigest() == config['pwd_md5']:
|
|
||||||
# Change this to return a session id
|
|
||||||
d.callback(self._create_session())
|
|
||||||
else:
|
|
||||||
d.callback(False)
|
|
||||||
return d
|
|
||||||
|
|
||||||
@export
|
@export
|
||||||
def get_hosts(self):
|
def get_hosts(self):
|
||||||
"""Return the hosts in the hostlist"""
|
"""Return the hosts in the hostlist"""
|
||||||
|
|
|
@ -43,6 +43,7 @@ from deluge.configmanager import ConfigManager
|
||||||
from deluge.log import setupLogger, LOG as _log
|
from deluge.log import setupLogger, LOG as _log
|
||||||
from deluge.ui import common as uicommon
|
from deluge.ui import common as uicommon
|
||||||
from deluge.ui.tracker_icons import TrackerIcons
|
from deluge.ui.tracker_icons import TrackerIcons
|
||||||
|
from deluge.ui.web.auth import Auth
|
||||||
from deluge.ui.web.common import Template
|
from deluge.ui.web.common import Template
|
||||||
from deluge.ui.web.json_api import JSON, WebApi
|
from deluge.ui.web.json_api import JSON, WebApi
|
||||||
from deluge.ui.web.pluginmanager import PluginManager
|
from deluge.ui.web.pluginmanager import PluginManager
|
||||||
|
@ -69,17 +70,24 @@ CONFIG_DEFAULTS = {
|
||||||
"port": 8112,
|
"port": 8112,
|
||||||
"enabled_plugins": [],
|
"enabled_plugins": [],
|
||||||
"theme": "slate",
|
"theme": "slate",
|
||||||
"pwd_salt": "16f65d5c79b7e93278a28b60fed2431e",
|
"pwd_salt": "c26ab3bbd8b137f99cd83c2c1c0963bcc1a35cad",
|
||||||
"pwd_md5": "2c9baa929ca38fb5c9eb5b054474d1ce",
|
"pwd_sha1": "2ce1a410bcdcc53064129b6d950f2e9fee4edc1e",
|
||||||
"base": "",
|
"base": "",
|
||||||
"sessions": {},
|
"sessions": {},
|
||||||
"sidebar_show_zero": False,
|
"sidebar_show_zero": False,
|
||||||
"sidebar_show_trackers": False,
|
"sidebar_show_trackers": False,
|
||||||
"show_keyword_search": False,
|
"show_keyword_search": False,
|
||||||
"show_sidebar": True,
|
"show_sidebar": True,
|
||||||
|
"cache_templates": False,
|
||||||
"https": False
|
"https": False
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OLD_CONFIG_KEYS = (
|
||||||
|
"port", "enabled_plugins", "base", "sidebar_show_zero",
|
||||||
|
"sidebar_show_trackers", "show_keyword_search", "show_sidebar",
|
||||||
|
"cache_templates", "https"
|
||||||
|
)
|
||||||
|
|
||||||
def rpath(path):
|
def rpath(path):
|
||||||
"""Convert a relative path into an absolute path relative to the location
|
"""Convert a relative path into an absolute path relative to the location
|
||||||
of this script.
|
of this script.
|
||||||
|
@ -341,10 +349,33 @@ class DelugeWeb(component.Component):
|
||||||
super(DelugeWeb, self).__init__("DelugeWeb")
|
super(DelugeWeb, self).__init__("DelugeWeb")
|
||||||
self.config = ConfigManager("web.conf", CONFIG_DEFAULTS)
|
self.config = ConfigManager("web.conf", CONFIG_DEFAULTS)
|
||||||
|
|
||||||
|
old_config = ConfigManager("webui06.conf")
|
||||||
|
if old_config.config:
|
||||||
|
# we have an old config file here to handle so we should move
|
||||||
|
# all the values across to the new config file, and then remove
|
||||||
|
# it.
|
||||||
|
for key in OLD_CONFIG_KEYS:
|
||||||
|
self.config[key] = old_config[key]
|
||||||
|
|
||||||
|
# We need to base64 encode the passwords since utf-8 can't handle
|
||||||
|
# them otherwise.
|
||||||
|
from base64 import encodestring
|
||||||
|
self.config["old_pwd_md5"] = encodestring(old_config["pwd_md5"])
|
||||||
|
self.config["old_pwd_salt"] = encodestring(old_config["pwd_salt"])
|
||||||
|
|
||||||
|
# Save our config and if it saved successfully then rename the
|
||||||
|
# old configuration file.
|
||||||
|
if self.config.save():
|
||||||
|
config_dir = os.path.dirname(old_config.config_file)
|
||||||
|
backup_path = os.path.join(config_dir, 'web.conf.old')
|
||||||
|
os.rename(old_config.config_file, backup_path)
|
||||||
|
del old_config
|
||||||
|
|
||||||
self.top_level = TopLevel()
|
self.top_level = TopLevel()
|
||||||
self.site = server.Site(self.top_level)
|
self.site = server.Site(self.top_level)
|
||||||
self.port = self.config["port"]
|
self.port = self.config["port"]
|
||||||
self.web_api = WebApi()
|
self.web_api = WebApi()
|
||||||
|
self.auth = Auth()
|
||||||
|
|
||||||
# Since twisted assigns itself all the signals may as well make
|
# Since twisted assigns itself all the signals may as well make
|
||||||
# use of it.
|
# use of it.
|
||||||
|
|
Loading…
Reference in New Issue