include command to create a user
This commit is contained in:
parent
dd2f0629d3
commit
99fe232a21
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 abc import ABC, abstractmethod
|
||||||
|
from owrx.config import CoreConfig
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -19,17 +20,32 @@ class Password(ABC):
|
|||||||
return CleartextPassword(d)
|
return CleartextPassword(d)
|
||||||
raise PasswordException("invalid passord encoding: {0}".format(d["type"]))
|
raise PasswordException("invalid passord encoding: {0}".format(d["type"]))
|
||||||
|
|
||||||
def __init__(self, pwinfo: dict):
|
|
||||||
self.pwinfo = pwinfo
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def is_valid(self, inp: str):
|
def is_valid(self, inp: str):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def toJson(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CleartextPassword(Password):
|
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):
|
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):
|
class User(object):
|
||||||
@ -38,6 +54,13 @@ class User(object):
|
|||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
self.password = password
|
self.password = password
|
||||||
|
|
||||||
|
def toJson(self):
|
||||||
|
return {
|
||||||
|
"user": self.name,
|
||||||
|
"enabled": self.enabled,
|
||||||
|
"password": self.password.toJson()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class UserList(object):
|
class UserList(object):
|
||||||
sharedInstance = None
|
sharedInstance = None
|
||||||
@ -51,30 +74,53 @@ class UserList(object):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.users = self._loadUsers()
|
self.users = self._loadUsers()
|
||||||
|
|
||||||
def _loadUsers(self):
|
def _getUsersFile(self):
|
||||||
for file in ["/etc/openwebrx/users.json", "users.json"]:
|
config = CoreConfig()
|
||||||
try:
|
return "{data_directory}/users.json".format(data_directory=config.get_data_directory())
|
||||||
f = open(file, "r")
|
|
||||||
users_json = json.load(f)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
return {u.name: u for u in [self.buildUser(d) for d in users_json]}
|
def _loadUsers(self):
|
||||||
|
usersFile = self._getUsersFile()
|
||||||
|
try:
|
||||||
|
with open(usersFile, "r") as f:
|
||||||
|
users_json = json.load(f)
|
||||||
|
|
||||||
|
return {u.name: u for u in [self._jsonToUser(d) for d in users_json]}
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
return {}
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
logger.exception("error while parsing users file %s", file)
|
logger.exception("error while parsing users file %s", usersFile)
|
||||||
return {}
|
return {}
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("error while processing users from %s", file)
|
logger.exception("error while processing users from %s", usersFile)
|
||||||
return {}
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def buildUser(self, d):
|
def _jsonToUser(self, d):
|
||||||
if "user" in d and "password" in d and "enabled" in d:
|
if "user" in d and "password" in d and "enabled" in d:
|
||||||
return User(d["user"], d["enabled"], Password.from_dict(d["password"]))
|
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):
|
def __getitem__(self, item):
|
||||||
return self.users[item]
|
return self.users[item]
|
||||||
|
|
||||||
def __contains__(self, item):
|
def __contains__(self, item):
|
||||||
return item in self.users
|
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():
|
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
|
|
||||||
}
|
|
||||||
]
|
|
Loading…
Reference in New Issue
Block a user