Some account management work. Not yet complete.
This commit is contained in:
parent
342da12d0c
commit
8195421c99
|
@ -51,10 +51,29 @@ AUTH_LEVEL_ADMIN = 10
|
||||||
|
|
||||||
AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL
|
AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL
|
||||||
|
|
||||||
|
class Account(object):
|
||||||
|
__slots__ = ('username', 'password', 'auth_level')
|
||||||
|
def __init__(self, username, password, auth_level):
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.auth_level = auth_level
|
||||||
|
|
||||||
|
def data(self, include_private=True):
|
||||||
|
rv = self.__dict__.copy()
|
||||||
|
if not include_private:
|
||||||
|
rv['password'] = ''
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return ('<Account username="%(username)s" auth_level=%(auth_level)s>' %
|
||||||
|
self.__dict__)
|
||||||
|
|
||||||
|
|
||||||
class AuthManager(component.Component):
|
class AuthManager(component.Component):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
component.Component.__init__(self, "AuthManager")
|
component.Component.__init__(self, "AuthManager")
|
||||||
self.__auth = {}
|
self.__auth = {}
|
||||||
|
self.__auth_modification_time = None
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.__load_auth_file()
|
self.__load_auth_file()
|
||||||
|
@ -74,8 +93,10 @@ class AuthManager(component.Component):
|
||||||
:returns: int, the auth level for this user
|
:returns: int, the auth level for this user
|
||||||
:rtype: int
|
:rtype: int
|
||||||
|
|
||||||
:raises AuthenticationRequired: if aditional details are required to authenticate
|
:raises AuthenticationRequired: if aditional details are required to
|
||||||
:raises BadLoginError: if the username does not exist or password does not match
|
authenticate.
|
||||||
|
:raises BadLoginError: if the username does not exist or password does
|
||||||
|
not match.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not username:
|
if not username:
|
||||||
|
@ -85,21 +106,60 @@ class AuthManager(component.Component):
|
||||||
|
|
||||||
self.__test_existing_account(username)
|
self.__test_existing_account(username)
|
||||||
|
|
||||||
if self.__auth[username][0] == password:
|
if self.__auth[username].password == password:
|
||||||
# Return the users auth level
|
# Return the users auth level
|
||||||
return int(self.__auth[username][1])
|
return self.__auth[username].auth_level
|
||||||
elif not password and self.__auth[username][0]:
|
elif not password and self.__auth[username].password:
|
||||||
raise AuthenticationRequired("Password is required", username)
|
raise AuthenticationRequired("Password is required", username)
|
||||||
else:
|
else:
|
||||||
raise BadLoginError("Password does not match")
|
raise BadLoginError("Password does not match")
|
||||||
|
|
||||||
def get_known_accounts(self):
|
def get_known_accounts(self, include_private_data=False):
|
||||||
"""
|
"""
|
||||||
Returns a list of known deluge usernames.
|
Returns a list of known deluge usernames.
|
||||||
"""
|
"""
|
||||||
self.__load_auth_file()
|
self.__load_auth_file()
|
||||||
return self.__auth.keys()
|
rv = {}
|
||||||
|
for account in self.__auth.items():
|
||||||
|
rv[account.username] = account.data(include_private_data)
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def create_account(self, username, password='', auth_level=AUTH_LEVEL_DEFAULT):
|
||||||
|
if username in self.__auth:
|
||||||
|
raise Something()
|
||||||
|
self.__create_account(username, password, auth_level)
|
||||||
|
|
||||||
|
def update_account(self, username, password='', auth_level=AUTH_LEVEL_DEFAULT):
|
||||||
|
if username in self.__auth:
|
||||||
|
raise Something()
|
||||||
|
self.__create_account(username, password, auth_level)
|
||||||
|
|
||||||
|
def remove_account(self, username):
|
||||||
|
if username in self.__auth:
|
||||||
|
raise Something()
|
||||||
|
del self.__auth[username]
|
||||||
|
self.write_auth_file()
|
||||||
|
if component.get("RPCServer").get_session_user() == username:
|
||||||
|
# Force a client logout by the server
|
||||||
|
component.get("RPCServer").logout_current_session()
|
||||||
|
|
||||||
|
def write_auth_file(self):
|
||||||
|
old_auth_file = configmanager.get_config_dir("auth")
|
||||||
|
new_auth_file = old_auth_file + '.new'
|
||||||
|
fd = open(new_auth_file, "w")
|
||||||
|
for account in self.__auth.items():
|
||||||
|
fd.write(
|
||||||
|
"%(username)s:%(password)s:%(auth_level)s\n" % account.__dict__
|
||||||
|
)
|
||||||
|
fd.flush()
|
||||||
|
os.fsync(fd.fileno())
|
||||||
|
fd.close()
|
||||||
|
os.rename(new_auth_file, old_auth_file)
|
||||||
|
self.__load_auth_file()
|
||||||
|
|
||||||
|
def __add_account(self, username, password, auth_level):
|
||||||
|
self.__auth[username] = Account(username, password, auth_level)
|
||||||
|
self.write_auth_file()
|
||||||
|
|
||||||
def __test_existing_account(self, username):
|
def __test_existing_account(self, username):
|
||||||
if username not in self.__auth:
|
if username not in self.__auth:
|
||||||
|
@ -107,6 +167,7 @@ class AuthManager(component.Component):
|
||||||
self.__load_auth_file()
|
self.__load_auth_file()
|
||||||
if username not in self.__auth:
|
if username not in self.__auth:
|
||||||
raise BadLoginError("Username does not exist")
|
raise BadLoginError("Username does not exist")
|
||||||
|
return True
|
||||||
|
|
||||||
def __create_localclient_account(self):
|
def __create_localclient_account(self):
|
||||||
"""
|
"""
|
||||||
|
@ -117,24 +178,28 @@ class AuthManager(component.Component):
|
||||||
from hashlib import sha1 as sha_hash
|
from hashlib import sha1 as sha_hash
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from sha import new as sha_hash
|
from sha import new as sha_hash
|
||||||
return ("localclient:" + sha_hash(str(random.random())).hexdigest() +
|
self.__auth["localclient"] = Account(
|
||||||
":" + str(AUTH_LEVEL_ADMIN) + "\n")
|
"localclient",
|
||||||
|
sha_hash(str(random.random())).hexdigest(),
|
||||||
|
AUTH_LEVEL_ADMIN
|
||||||
|
)
|
||||||
|
|
||||||
def __load_auth_file(self):
|
def __load_auth_file(self):
|
||||||
auth_file = configmanager.get_config_dir("auth")
|
auth_file = configmanager.get_config_dir("auth")
|
||||||
# Check for auth file and create if necessary
|
# Check for auth file and create if necessary
|
||||||
if not os.path.exists(auth_file):
|
if not os.path.exists(auth_file):
|
||||||
localclient = self.__create_localclient_account()
|
self.__create_auth_file()
|
||||||
fd = open(auth_file, "w")
|
self.__create_localclient_account()
|
||||||
fd.write(localclient)
|
self.write_auth_file()
|
||||||
fd.flush()
|
|
||||||
os.fsync(fd.fileno())
|
auth_file_modification_time = os.stat(auth_file).st_mtime
|
||||||
fd.close()
|
if self.__auth_modification_time is None:
|
||||||
# Change the permissions on the file so only this user can read/write it
|
self.__auth_modification_time = auth_file_modification_time
|
||||||
os.chmod(auth_file, stat.S_IREAD | stat.S_IWRITE)
|
elif self.__auth_modification_time == auth_file_modification_time:
|
||||||
f = [localclient]
|
# File didn't change, no need for re-parsing's
|
||||||
else:
|
return
|
||||||
# Load the auth file into a dictionary: {username: password, ...}
|
|
||||||
|
# Load the auth file into a dictionary: {username: Account(...)}
|
||||||
f = open(auth_file, "r").readlines()
|
f = open(auth_file, "r").readlines()
|
||||||
|
|
||||||
for line in f:
|
for line in f:
|
||||||
|
@ -152,14 +217,36 @@ class AuthManager(component.Component):
|
||||||
log.warning("Your auth entry for %s contains no auth level, "
|
log.warning("Your auth entry for %s contains no auth level, "
|
||||||
"using AUTH_LEVEL_DEFAULT(%s)..", username,
|
"using AUTH_LEVEL_DEFAULT(%s)..", username,
|
||||||
AUTH_LEVEL_DEFAULT)
|
AUTH_LEVEL_DEFAULT)
|
||||||
level = AUTH_LEVEL_DEFAULT
|
auth_level = AUTH_LEVEL_DEFAULT
|
||||||
elif len(lsplit) == 3:
|
elif len(lsplit) == 3:
|
||||||
username, password, level = lsplit
|
username, password, auth_level = lsplit
|
||||||
else:
|
else:
|
||||||
log.error("Your auth file is malformed: Incorrect number of fields!")
|
log.error("Your auth file is malformed: Incorrect number of fields!")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.__auth[username.strip()] = (password.strip(), level)
|
username = username.strip()
|
||||||
|
password = password.strip()
|
||||||
|
try:
|
||||||
|
auth_level = int(auth_level)
|
||||||
|
except ValueError:
|
||||||
|
log.error("Your auth file is malformed: %r is not a valid auth "
|
||||||
|
"level" % auth_level)
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.__auth[username] = Account(username, password, auth_level)
|
||||||
|
|
||||||
if "localclient" not in self.__auth:
|
if "localclient" not in self.__auth:
|
||||||
open(auth_file, "a").write(self.__create_localclient_account())
|
self.__create_localclient_account()
|
||||||
|
self.write_auth_file()
|
||||||
|
|
||||||
|
|
||||||
|
def __create_auth_file(self):
|
||||||
|
auth_file = configmanager.get_config_dir("auth")
|
||||||
|
# Check for auth file and create if necessary
|
||||||
|
if not os.path.exists(auth_file):
|
||||||
|
fd = open(auth_file, "w")
|
||||||
|
fd.flush()
|
||||||
|
os.fsync(fd.fileno())
|
||||||
|
fd.close()
|
||||||
|
# Change the permissions on the file so only this user can read/write it
|
||||||
|
os.chmod(auth_file, stat.S_IREAD | stat.S_IWRITE)
|
||||||
|
|
|
@ -55,7 +55,7 @@ import deluge.common
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.event import *
|
from deluge.event import *
|
||||||
from deluge.error import *
|
from deluge.error import *
|
||||||
from deluge.core.authmanager import AUTH_LEVEL_ADMIN
|
from deluge.core.authmanager import AUTH_LEVEL_ADMIN, AUTH_LEVEL_DEFAULT
|
||||||
from deluge.core.torrentmanager import TorrentManager
|
from deluge.core.torrentmanager import TorrentManager
|
||||||
from deluge.core.pluginmanager import PluginManager
|
from deluge.core.pluginmanager import PluginManager
|
||||||
from deluge.core.alertmanager import AlertManager
|
from deluge.core.alertmanager import AlertManager
|
||||||
|
@ -829,6 +829,9 @@ class Core(component.Component):
|
||||||
"""
|
"""
|
||||||
return lt.version
|
return lt.version
|
||||||
|
|
||||||
@export(AUTH_LEVEL_ADMIN)
|
@export(AUTH_LEVEL_DEFAULT)
|
||||||
def get_known_accounts(self):
|
def get_known_accounts(self):
|
||||||
return self.authmanager.get_known_accounts()
|
auth_level = component.get("RPCServer").get_session_auth_level()
|
||||||
|
return self.authmanager.get_known_accounts(
|
||||||
|
include_private_data=(auth_level==AUTH_LEVEL_ADMIN)
|
||||||
|
)
|
||||||
|
|
|
@ -485,6 +485,12 @@ class RPCServer(component.Component):
|
||||||
"""
|
"""
|
||||||
return session_id in self.factory.authorized_sessions
|
return session_id in self.factory.authorized_sessions
|
||||||
|
|
||||||
|
def logout_current_session(self):
|
||||||
|
"""
|
||||||
|
Makes the current session invalid logging out the current account
|
||||||
|
"""
|
||||||
|
self.factory.protocol.connectionLost("Server logged out client")
|
||||||
|
|
||||||
def emit_event(self, event):
|
def emit_event(self, event):
|
||||||
"""
|
"""
|
||||||
Emits the event to interested clients.
|
Emits the event to interested clients.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue