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
|
||||
|
||||
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):
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, "AuthManager")
|
||||
self.__auth = {}
|
||||
self.__auth_modification_time = None
|
||||
|
||||
def start(self):
|
||||
self.__load_auth_file()
|
||||
|
@ -74,8 +93,10 @@ class AuthManager(component.Component):
|
|||
:returns: int, the auth level for this user
|
||||
:rtype: int
|
||||
|
||||
:raises AuthenticationRequired: if aditional details are required to authenticate
|
||||
:raises BadLoginError: if the username does not exist or password does not match
|
||||
:raises AuthenticationRequired: if aditional details are required to
|
||||
authenticate.
|
||||
:raises BadLoginError: if the username does not exist or password does
|
||||
not match.
|
||||
|
||||
"""
|
||||
if not username:
|
||||
|
@ -85,21 +106,60 @@ class AuthManager(component.Component):
|
|||
|
||||
self.__test_existing_account(username)
|
||||
|
||||
if self.__auth[username][0] == password:
|
||||
if self.__auth[username].password == password:
|
||||
# Return the users auth level
|
||||
return int(self.__auth[username][1])
|
||||
elif not password and self.__auth[username][0]:
|
||||
return self.__auth[username].auth_level
|
||||
elif not password and self.__auth[username].password:
|
||||
raise AuthenticationRequired("Password is required", username)
|
||||
else:
|
||||
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.
|
||||
"""
|
||||
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):
|
||||
if username not in self.__auth:
|
||||
|
@ -107,6 +167,7 @@ class AuthManager(component.Component):
|
|||
self.__load_auth_file()
|
||||
if username not in self.__auth:
|
||||
raise BadLoginError("Username does not exist")
|
||||
return True
|
||||
|
||||
def __create_localclient_account(self):
|
||||
"""
|
||||
|
@ -117,24 +178,28 @@ class AuthManager(component.Component):
|
|||
from hashlib import sha1 as sha_hash
|
||||
except ImportError:
|
||||
from sha import new as sha_hash
|
||||
return ("localclient:" + sha_hash(str(random.random())).hexdigest() +
|
||||
":" + str(AUTH_LEVEL_ADMIN) + "\n")
|
||||
self.__auth["localclient"] = Account(
|
||||
"localclient",
|
||||
sha_hash(str(random.random())).hexdigest(),
|
||||
AUTH_LEVEL_ADMIN
|
||||
)
|
||||
|
||||
def __load_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):
|
||||
localclient = self.__create_localclient_account()
|
||||
fd = open(auth_file, "w")
|
||||
fd.write(localclient)
|
||||
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)
|
||||
f = [localclient]
|
||||
else:
|
||||
# Load the auth file into a dictionary: {username: password, ...}
|
||||
self.__create_auth_file()
|
||||
self.__create_localclient_account()
|
||||
self.write_auth_file()
|
||||
|
||||
auth_file_modification_time = os.stat(auth_file).st_mtime
|
||||
if self.__auth_modification_time is None:
|
||||
self.__auth_modification_time = auth_file_modification_time
|
||||
elif self.__auth_modification_time == auth_file_modification_time:
|
||||
# File didn't change, no need for re-parsing's
|
||||
return
|
||||
|
||||
# Load the auth file into a dictionary: {username: Account(...)}
|
||||
f = open(auth_file, "r").readlines()
|
||||
|
||||
for line in f:
|
||||
|
@ -152,14 +217,36 @@ class AuthManager(component.Component):
|
|||
log.warning("Your auth entry for %s contains no auth level, "
|
||||
"using AUTH_LEVEL_DEFAULT(%s)..", username,
|
||||
AUTH_LEVEL_DEFAULT)
|
||||
level = AUTH_LEVEL_DEFAULT
|
||||
auth_level = AUTH_LEVEL_DEFAULT
|
||||
elif len(lsplit) == 3:
|
||||
username, password, level = lsplit
|
||||
username, password, auth_level = lsplit
|
||||
else:
|
||||
log.error("Your auth file is malformed: Incorrect number of fields!")
|
||||
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:
|
||||
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
|
||||
from deluge.event 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.pluginmanager import PluginManager
|
||||
from deluge.core.alertmanager import AlertManager
|
||||
|
@ -829,6 +829,9 @@ class Core(component.Component):
|
|||
"""
|
||||
return lt.version
|
||||
|
||||
@export(AUTH_LEVEL_ADMIN)
|
||||
@export(AUTH_LEVEL_DEFAULT)
|
||||
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
|
||||
|
||||
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):
|
||||
"""
|
||||
Emits the event to interested clients.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue