diff options
Diffstat (limited to 'py-bin/jabberman.py')
-rw-r--r-- | py-bin/jabberman.py | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/py-bin/jabberman.py b/py-bin/jabberman.py new file mode 100644 index 0000000..11e3bdf --- /dev/null +++ b/py-bin/jabberman.py @@ -0,0 +1,392 @@ +#jabber manager + +import shelve, atexit, sha, hmac, random, os, time, re +import config +from ejabberdctl import EJabberdCtl + +class JabberUser: + def __init__(self, user_id): + self.user, self.domain = user_id.split("@") + self.accounts = [] + + def get_user_id(self): + return self.user + "@" + self.domain + + def get_default_jabber_id(self): + return self.user + "@jabber." + self.domain + + def is_active(self): + return hasattr(self, "password_hash") + + def check_password(self, password): + if not self.is_active(): + return False + return self.password_hash == self.__hash_password(password) + + def set_password(self, password): + self.password_hash = self.__hash_password(password) + + def __hash_password(self, password): + return sha.new(password).hexdigest() + + @staticmethod + def generate_token(): + data = str(random.getrandbits(256)) + str(time.time()*1000) + str(os.getpid()) + return "+" + hmac.new(config.the_secret, data, sha).hexdigest() + + def set_token(self, token): + self.token = token + + def validate_token(self, token): + if token[1:] == self.token[1:]: + if self.__is_token_expired(): + return (False, "Benutzerkonto bereits aktiviert.") + return (True, self) + else: + return (False, "Zugriff verweigert.") + + def __is_token_expired(self): + return self.token[0] != "+" + + def expire_token(self): + self.token = "-" + self.token[1:] + + def add_account(self, jabber_id): + self.accounts.append(jabber_id) + + def has_account(self, jabber_id): + return jabber_id in self.accounts + + def get_account_list(self): + return list(self.accounts) + + def get_extra_account_list(self): + default_acc = self.get_default_jabber_id() + return filter(lambda acc: acc != default_acc, self.accounts) + + def remove_account(self, jabber_id): + self.accounts.remove(jabber_id) + +class JabberAccount: + def __init__(self, jabber_id): + self.user, self.server = jabber_id.split("@") + + def get_jabber_id(self): + return self.user + "@" + self.server + +class JabberDB: + def __init__(self): + self.db = shelve.open(config.jabberdb_path, 'c') + atexit.register(self.db.close) + + def login_user(self, user_id, password): + user = self.__load_user(user_id) + if user and user.check_password(password): + return user + return None + + def generate_token(self, user_id): + if self.__load_user(user_id): + return (False, "Benutzer existiert bereits!") + + return (True, JabberUser.generate_token()) + + def prepare_user(self, user_id, token): + if self.__load_user(user_id): + return (False, "Benutzer existiert bereits!") + + user = JabberUser(user_id) + user.set_token(token) + self.__store_user(user) + + return (True, "Benutzer registriert, Aktivierung noch ausstehend.") + + def validate_token(self, user_id, token): + user = self.__load_user(user_id) + if not user: + return (False, "Zugriff verweigert.") + + return user.validate_token(token) + + def activate_user(self, user_id, password, token): + user = self.__load_user(user_id) + if not user: + return (False, "Zugriff verweigert.") + + ok, status = user.validate_token(token) + if not ok: + return (False, status) + + user.expire_token() + user.set_password(password) + self.__store_user(user) + + return (True, user) + + def add_account(self, user_id, jabber_id, check_only = False): + user = self.__load_user(user_id) + if not user: + return (False, "Zugriff verweigert.") + + account = self.__load_account(jabber_id) + if account: + return (False, "Sorry, Jabber Benutzerkonto %s bereits vergeben." % jabber_id) + + if check_only: + return (True, "Jabber kann hinzugefuegt werden.") + + account = JabberAccount(jabber_id) + self.__store_account(account) + user.add_account(jabber_id) + self.__store_user(user) + + return (True, "Jabber Konto hinzugefuegt.") + + def remove_account(self, user_id, jabber_id, check_only = False): + user = self.__load_user(user_id) + if (not user) or (not user.has_account(jabber_id)): + return (False, "Zugriff verweigert.") + + if check_only: + return (True, "Jabber darf geloescht werden.") + + self.__delete_account(jabber_id) + user.remove_account(jabber_id) + self.__store_user(user) + + return (True, "Jabber Konto geloescht.") + + def change_password(self, user_id, password): + user = self.__load_user(user_id) + if not user: + return (False, "Zugriff verweigert.") + + user.set_password(password) + self.__store_user(user) + return (True, "Passwort geaendert.") + + def __load_user(self, user_id): + return self.db.get("#usr#" + user_id) + + def __store_user(self, user): + self.db["#usr#" + user.get_user_id()] = user + + def __load_account(self, jabber_id): + return self.db.get("#acc#" + jabber_id) + + def __store_account(self, account): + self.db["#acc#" + account.get_jabber_id()] = account + + def __delete_account(self, jabber_id): + del(self.db["#acc#" + jabber_id]) + + +class JabberManager: + def __init__(self, session): + self.jadb = JabberDB() + self.session = session + self.current_user, self.authenticated = None, False + self.ejctl = EJabberdCtl() + + def get_user(self): + return self.current_user + + def authenticate(self): + if self.authenticated == True: + return True + if (not "uid" in self.session) or (not "pass" in self.session): + return (False, "Nicht angemeldet.") + ok, status_or_user = self.login( + self.session["uid"], self.session["pass"]) + return (ok, status_or_user) + + def login(self, user_id, password): + ok, status = self.check_user_id(user_id) + if not ok: + return (False, status) + + self.current_user = self.jadb.login_user(user_id, password) + if self.current_user: + self.__set_session(user_id, password = password) + else: + self.__clear_session() + return (False, "Benutzername oder Passwort falsch.") + + self.authenticated = True + return (True, self.current_user) + + def logout(self): + self.current_user, self.authenticated = None, False + self.__clear_session() + + def generate_token(self, user_id): + ok, status = self.check_user_id(user_id) + if not ok: + return (False, status) + + return self.jadb.generate_token(user_id) + + def prepare_user(self, user_id, token): + ok, status = self.check_user_id(user_id) + if not ok: + return (False, status) + + return self.jadb.prepare_user(user_id, token) + + def validate_token(self, user_id, token): + if user_id == "": + try: + user_id, token = self.session["uid"], self.session["tok"] + except Exception: + return (False, "Zugriff verweigert.") + + ok, status = self.check_user_id(user_id) + if not ok: + return (False, "Zugriff verweigert.") + + ok, status_or_user = self.jadb.validate_token(user_id, token) + if ok: + self.current_user = status_or_user + self.__set_session(user_id, token = token) + return (ok, status_or_user) + + def activate_user(self, password): + try: + user_id, token = self.session["uid"], self.session["tok"] + except Exception: + return (False, "Zugriff verweigert.") + ok, status = self.check_user_id(user_id) + if not ok: + return (False, "Zugriff verweigert.") + + ok, status_or_user = self.jadb.activate_user(user_id, password, token) + if ok: + self.current_user, self.authenticated = status_or_user, True + self.__set_session(user_id, password = password) + else: + self.__clear_session() + return (False, status_or_user) + + ok, status = self.add_account(self.current_user.get_default_jabber_id()) + if not ok: + #todo: handle this smarter somehow + return (False, status) + + return (True, status) + + def change_password(self, password): + if not self.authenticated: + return (False, "Zugriff verweigert.") + + user_id = self.current_user.get_user_id() + ok, status = self.jadb.change_password(user_id, password) + if ok: + self.__set_session(user_id, password = password) + else: + self.__clear_session() + return (False, status) + + for jabber_id in self.current_user.get_account_list(): + acc = JabberAccount(jabber_id) + if not self.ejctl.change_password(acc.user, acc.server, password): + msg = "Konnte Jaber Passwort fuer %s nicht setzen." % acc.get_jabber_id() + return (False, msg) + + return (True, "Passwort erfolgreich geaendert.") + + def is_acceptable_password(self, password, password2): + if password != password2: + return (False, "Passwoerter nicht identisch.") + if len(password) < config.min_password_length: + return (False, "Passwort ist zu kurz.") + if not re.match(config.password_re, password): + return (False, "Passwort enthaelt unerlaubte Zeichen.") + return (True, "Passwort OK.") + + def add_account(self, jabber_id): + if not self.authenticated: + return (False, "Zugriff verweigert.") + + ok, status = JabberManager.check_jabber_id(jabber_id) + if not ok: + return (False, status) + + acc = JabberAccount(jabber_id) + try: + password = self.session["pass"] + except Exception: + return (False, "Zugriff verweigert.") + + user_id = self.current_user.get_user_id() + ok, status = self.jadb.add_account(user_id, jabber_id, check_only = True) + if not ok: + return (False, status) + + if not self.ejctl.create_account(acc.user, acc.server, password): + return (False, "Konnte Konto %s nicht erstellen." % acc.get_jabber_id()) + + user_id = self.current_user.get_user_id() + return self.jadb.add_account(user_id, jabber_id) + + def remove_account(self, jabber_id): + if not self.authenticated: + return (False, "Zugriff verweigert.") + + ok, status = JabberManager.check_jabber_id(jabber_id) + if not ok: + return (False, "Zugriff verweigert.") + + user_id = self.current_user.get_user_id() + if jabber_id == self.current_user.get_default_jabber_id(): + return (False, "Hauptkonto darf nicht geloescht werden!") + + ok, status = self.jadb.remove_account(user_id, jabber_id, check_only = True) + if not ok: + return (False, status) + + acc = JabberAccount(jabber_id) + if not self.ejctl.remove_account(acc.user, acc.server): + return (False, "Konnte Konto %s nicht loeschen." % acc.get_jabber_id()) + + return self.jadb.remove_account(user_id, jabber_id) + + def __set_session(self, user_id, password = None, token = None): + self.__clear_session() + self.session["uid"] = user_id + if password: + self.session["pass"] = password + if token: + self.session["tok"] = token + + def __clear_session(self): + if self.session.get("uid"): + del(self.session["uid"]) + if self.session.get("pass"): + del(self.session["pass"]) + if self.session.get("token"): + del(self.session["tok"]) + + @staticmethod + def __check_account_form(acc_type, domains, account): + try: + user, domain = account.split("@") + except ValueError: + status = "Ungueltige %s Adresse. Erwartete Form: user@domain." % acc_type + return (False, status) + + if not re.match(config.user_re, user): + return (False, "Benutzername %s nicht erlaubt." % user) + if domain not in domains: + return (False, "Domain %s nicht erlaubt." % domain) + return (True, "%s Adresse akzeptiert." % acc_type) + + @staticmethod + def check_user_id(user_id): + return JabberManager.__check_account_form("E-Mail", config.mail_domains, user_id) + + @staticmethod + def check_jabber_id(jabber_id): + domains = map(lambda d: "jabber." + d, config.mail_domains) + config.extra_domains + return JabberManager.__check_account_form("Jabber", domains, jabber_id) + + |