implement password reset command
This commit is contained in:
		@@ -102,6 +102,9 @@ class User(object):
 | 
			
		||||
            "password": self.password.toJson()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def setPassword(self, password: Password):
 | 
			
		||||
        self.password = password
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserList(object):
 | 
			
		||||
    sharedInstance = None
 | 
			
		||||
@@ -142,7 +145,7 @@ class UserList(object):
 | 
			
		||||
    def _userToJson(self, u):
 | 
			
		||||
        return u.toJson()
 | 
			
		||||
 | 
			
		||||
    def _store(self):
 | 
			
		||||
    def store(self):
 | 
			
		||||
        usersFile = self._getUsersFile()
 | 
			
		||||
        users = [u.toJson() for u in self.users.values()]
 | 
			
		||||
        try:
 | 
			
		||||
@@ -169,7 +172,7 @@ class UserList(object):
 | 
			
		||||
        if key not in self.users:
 | 
			
		||||
            raise KeyError("User {user} doesn't exist".format(user=key))
 | 
			
		||||
        del self.users[key]
 | 
			
		||||
        self._store()
 | 
			
		||||
        self.store()
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, item):
 | 
			
		||||
        return self.users[item]
 | 
			
		||||
@@ -181,4 +184,4 @@ class UserList(object):
 | 
			
		||||
        if key in self.users:
 | 
			
		||||
            raise KeyError("User {user} already exists".format(user=key))
 | 
			
		||||
        self.users[key] = value
 | 
			
		||||
        self._store()
 | 
			
		||||
        self.store()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
from owrx.version import openwebrx_version
 | 
			
		||||
from owrxadmin.commands import NewUser, DeleteUser
 | 
			
		||||
from owrxadmin.commands import NewUser, DeleteUser, ResetPassword
 | 
			
		||||
import argparse
 | 
			
		||||
import sys
 | 
			
		||||
import traceback
 | 
			
		||||
@@ -9,7 +9,7 @@ def main():
 | 
			
		||||
    print("OpenWebRX admin version {version}".format(version=openwebrx_version))
 | 
			
		||||
 | 
			
		||||
    parser = argparse.ArgumentParser()
 | 
			
		||||
    parser.add_argument("command", help="One of the following commands: adduser, removeuser")
 | 
			
		||||
    parser.add_argument("command", help="One of the following commands: adduser, removeuser, resetpassword")
 | 
			
		||||
    parser.add_argument(
 | 
			
		||||
        "--noninteractive", action="store_true", help="Don't ask for any user input (useful for automation)"
 | 
			
		||||
    )
 | 
			
		||||
@@ -21,6 +21,8 @@ def main():
 | 
			
		||||
        command = NewUser()
 | 
			
		||||
    elif args.command == "removeuser":
 | 
			
		||||
        command = DeleteUser()
 | 
			
		||||
    elif args.command == "resetpassword":
 | 
			
		||||
        command = ResetPassword()
 | 
			
		||||
    else:
 | 
			
		||||
        if not args.silent:
 | 
			
		||||
            print("Unknown command: {command}".format(command=args.command))
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,26 @@ class UserCommand(Command, metaclass=ABCMeta):
 | 
			
		||||
            else:
 | 
			
		||||
                return input("Please enter the user name: ")
 | 
			
		||||
 | 
			
		||||
    def getPassword(self, args, username):
 | 
			
		||||
        if args.noninteractive:
 | 
			
		||||
            print("Generating password for user {username}...".format(username=username))
 | 
			
		||||
            password = self.getRandomPassword()
 | 
			
		||||
            print('Password for {username} is "{password}".'.format(username=username, password=password))
 | 
			
		||||
            # TODO implement this threat
 | 
			
		||||
            print('This password is suitable for initial setup only, you will be asked to reset it on initial use.')
 | 
			
		||||
            print('This password cannot be recovered from the system, please copy it now.')
 | 
			
		||||
        else:
 | 
			
		||||
            password = getpass("Please enter the new password for {username}: ".format(username=username))
 | 
			
		||||
            confirm = getpass("Please confirm the new password: ")
 | 
			
		||||
            if password != confirm:
 | 
			
		||||
                print("ERROR: Password mismatch.")
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
        return password
 | 
			
		||||
 | 
			
		||||
    def getRandomPassword(self, length=10):
 | 
			
		||||
        printable = list(string.ascii_letters) + list(string.digits)
 | 
			
		||||
        return ''.join(random.choices(printable, k=length))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewUser(UserCommand):
 | 
			
		||||
    def run(self, args):
 | 
			
		||||
@@ -32,28 +52,12 @@ class NewUser(UserCommand):
 | 
			
		||||
        if username in userList:
 | 
			
		||||
            raise KeyError("User {username} already exists".format(username=username))
 | 
			
		||||
 | 
			
		||||
        if args.noninteractive:
 | 
			
		||||
            print("Generating password for user {username}...".format(username=username))
 | 
			
		||||
            password = self.getRandomPassword()
 | 
			
		||||
            print('Password for {username} is "{password}".'.format(username=username, password=password))
 | 
			
		||||
            # TODO implement this threat
 | 
			
		||||
            print('This password is suitable for initial setup only, you will be asked to reset it on initial use.')
 | 
			
		||||
            print('This password cannot be recovered from the system, please copy it now.')
 | 
			
		||||
        else:
 | 
			
		||||
            password = getpass("Please enter the password for {username}: ".format(username=username))
 | 
			
		||||
            confirm = getpass("Please confirm password: ")
 | 
			
		||||
            if password != confirm:
 | 
			
		||||
                print("ERROR: Password mismatch.")
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
        password = self.getPassword(args, username)
 | 
			
		||||
 | 
			
		||||
        print("Creating user {username}...".format(username=username))
 | 
			
		||||
        user = User(name=username, enabled=True, password=DefaultPasswordClass(password))
 | 
			
		||||
        userList.addUser(user)
 | 
			
		||||
 | 
			
		||||
    def getRandomPassword(self, length=10):
 | 
			
		||||
        printable = list(string.ascii_letters) + list(string.digits)
 | 
			
		||||
        return ''.join(random.choices(printable, k=length))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DeleteUser(UserCommand):
 | 
			
		||||
    def run(self, args):
 | 
			
		||||
@@ -61,3 +65,14 @@ class DeleteUser(UserCommand):
 | 
			
		||||
        print("Deleting user {username}...".format(username=username))
 | 
			
		||||
        userList = UserList()
 | 
			
		||||
        userList.deleteUser(username)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResetPassword(UserCommand):
 | 
			
		||||
    def run(self, args):
 | 
			
		||||
        username = self.getUser(args)
 | 
			
		||||
        password = self.getPassword(args, username)
 | 
			
		||||
        userList = UserList()
 | 
			
		||||
        userList[username].setPassword(DefaultPasswordClass(password))
 | 
			
		||||
        # this is a change to an object in the list, not the list itself
 | 
			
		||||
        # in this case, store() is explicit
 | 
			
		||||
        userList.store()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user