include command to create a user
This commit is contained in:
		
							
								
								
									
										6
									
								
								openwebrx-admin.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6
									
								
								openwebrx-admin.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
from owrxadmin.__main__ import main
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    main()
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
from abc import ABC, abstractmethod
 | 
			
		||||
from owrx.config import CoreConfig
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
@@ -19,17 +20,32 @@ class Password(ABC):
 | 
			
		||||
            return CleartextPassword(d)
 | 
			
		||||
        raise PasswordException("invalid passord encoding: {0}".format(d["type"]))
 | 
			
		||||
 | 
			
		||||
    def __init__(self, pwinfo: dict):
 | 
			
		||||
        self.pwinfo = pwinfo
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def is_valid(self, inp: str):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def toJson(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CleartextPassword(Password):
 | 
			
		||||
    def __init__(self, pwinfo):
 | 
			
		||||
        if isinstance(pwinfo, str):
 | 
			
		||||
            self._value = pwinfo
 | 
			
		||||
        elif isinstance(pwinfo, dict):
 | 
			
		||||
            self._value = pwinfo["value"]
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError("invalid argument to ClearTextPassword()")
 | 
			
		||||
 | 
			
		||||
    def is_valid(self, inp: str):
 | 
			
		||||
        return self.pwinfo["value"] == inp
 | 
			
		||||
        return self._value == inp
 | 
			
		||||
 | 
			
		||||
    def toJson(self):
 | 
			
		||||
        return {
 | 
			
		||||
            "encoding": "string",
 | 
			
		||||
            "value": self._value
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class User(object):
 | 
			
		||||
@@ -38,6 +54,13 @@ class User(object):
 | 
			
		||||
        self.enabled = enabled
 | 
			
		||||
        self.password = password
 | 
			
		||||
 | 
			
		||||
    def toJson(self):
 | 
			
		||||
        return {
 | 
			
		||||
            "user": self.name,
 | 
			
		||||
            "enabled": self.enabled,
 | 
			
		||||
            "password": self.password.toJson()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserList(object):
 | 
			
		||||
    sharedInstance = None
 | 
			
		||||
@@ -51,30 +74,53 @@ class UserList(object):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.users = self._loadUsers()
 | 
			
		||||
 | 
			
		||||
    def _getUsersFile(self):
 | 
			
		||||
        config = CoreConfig()
 | 
			
		||||
        return "{data_directory}/users.json".format(data_directory=config.get_data_directory())
 | 
			
		||||
 | 
			
		||||
    def _loadUsers(self):
 | 
			
		||||
        for file in ["/etc/openwebrx/users.json", "users.json"]:
 | 
			
		||||
            try:
 | 
			
		||||
                f = open(file, "r")
 | 
			
		||||
        usersFile = self._getUsersFile()
 | 
			
		||||
        try:
 | 
			
		||||
            with open(usersFile, "r") as f:
 | 
			
		||||
                users_json = json.load(f)
 | 
			
		||||
                f.close()
 | 
			
		||||
 | 
			
		||||
                return {u.name: u for u in [self.buildUser(d) for d in users_json]}
 | 
			
		||||
            except FileNotFoundError:
 | 
			
		||||
                pass
 | 
			
		||||
            except json.JSONDecodeError:
 | 
			
		||||
                logger.exception("error while parsing users file %s", file)
 | 
			
		||||
                return {}
 | 
			
		||||
            except Exception:
 | 
			
		||||
                logger.exception("error while processing users from %s", file)
 | 
			
		||||
                return {}
 | 
			
		||||
        return {}
 | 
			
		||||
            return {u.name: u for u in [self._jsonToUser(d) for d in users_json]}
 | 
			
		||||
        except FileNotFoundError:
 | 
			
		||||
            return {}
 | 
			
		||||
        except json.JSONDecodeError:
 | 
			
		||||
            logger.exception("error while parsing users file %s", usersFile)
 | 
			
		||||
            return {}
 | 
			
		||||
        except Exception:
 | 
			
		||||
            logger.exception("error while processing users from %s", usersFile)
 | 
			
		||||
            return {}
 | 
			
		||||
 | 
			
		||||
    def buildUser(self, d):
 | 
			
		||||
    def _jsonToUser(self, d):
 | 
			
		||||
        if "user" in d and "password" in d and "enabled" in d:
 | 
			
		||||
            return User(d["user"], d["enabled"], Password.from_dict(d["password"]))
 | 
			
		||||
 | 
			
		||||
    def _userToJson(self, u):
 | 
			
		||||
        return u.toJson()
 | 
			
		||||
 | 
			
		||||
    def _store(self):
 | 
			
		||||
        usersFile = self._getUsersFile()
 | 
			
		||||
        users = [u.toJson() for u in self.users.values()]
 | 
			
		||||
        try:
 | 
			
		||||
            with open(usersFile, "w") as f:
 | 
			
		||||
                json.dump(users, f, indent=4)
 | 
			
		||||
        except Exception:
 | 
			
		||||
            logger.exception("error while writing users file %s", usersFile)
 | 
			
		||||
 | 
			
		||||
    def addUser(self, user: User):
 | 
			
		||||
        self[user.name] = user
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, item):
 | 
			
		||||
        return self.users[item]
 | 
			
		||||
 | 
			
		||||
    def __contains__(self, item):
 | 
			
		||||
        return item in self.users
 | 
			
		||||
 | 
			
		||||
    def __setitem__(self, key, value):
 | 
			
		||||
        if key in self.users:
 | 
			
		||||
            raise KeyError("User {user} already exists".format(user=key))
 | 
			
		||||
        self.users[key] = value
 | 
			
		||||
        self._store()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,22 @@
 | 
			
		||||
from owrx.version import openwebrx_version
 | 
			
		||||
from owrxadmin.commands import NewUserCommand
 | 
			
		||||
import argparse
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    print("OpenWebRX admin")
 | 
			
		||||
    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("--noninteractive", action="store_true", help="Don't ask for any user input (useful for automation)")
 | 
			
		||||
    parser.add_argument("-u", "--user")
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
    if args.command == "adduser":
 | 
			
		||||
        NewUserCommand().run(args)
 | 
			
		||||
    elif args.command == "removeuser":
 | 
			
		||||
        print("removing user")
 | 
			
		||||
    else:
 | 
			
		||||
        print("Unknown command: {command}".format(command=args.command))
 | 
			
		||||
        sys.exit(1)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								owrxadmin/commands.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								owrxadmin/commands.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
from abc import ABC, abstractmethod
 | 
			
		||||
from getpass import getpass
 | 
			
		||||
from owrx.users import UserList, User, CleartextPassword
 | 
			
		||||
import sys
 | 
			
		||||
import random
 | 
			
		||||
import string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Command(ABC):
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def run(self, args):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewUserCommand(Command):
 | 
			
		||||
    def run(self, args):
 | 
			
		||||
        if args.user:
 | 
			
		||||
            username = args.user
 | 
			
		||||
        else:
 | 
			
		||||
            if args.noninteractive:
 | 
			
		||||
                print("ERROR: User name not specified")
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
            else:
 | 
			
		||||
                username = input("Please enter the user name: ")
 | 
			
		||||
        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 note it down 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)
 | 
			
		||||
 | 
			
		||||
        print("Creating user {username}...".format(username=username))
 | 
			
		||||
        userList = UserList()
 | 
			
		||||
        user = User(name=username, enabled=True, password=CleartextPassword(password))
 | 
			
		||||
        userList.addUser(user)
 | 
			
		||||
 | 
			
		||||
    def getRandomPassword(self, length=10):
 | 
			
		||||
        printable = list(string.ascii_letters) + list(string.digits)
 | 
			
		||||
        return ''.join(random.choices(printable, k=length))
 | 
			
		||||
							
								
								
									
										11
									
								
								users.json
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								users.json
									
									
									
									
									
								
							@@ -1,11 +0,0 @@
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "user": "admin",
 | 
			
		||||
    "password": {
 | 
			
		||||
      "encoding": "string",
 | 
			
		||||
      "value": "password",
 | 
			
		||||
      "force_change": true
 | 
			
		||||
    },
 | 
			
		||||
    "enabled": true
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
		Reference in New Issue
	
	Block a user