add create user parser

This commit is contained in:
Evgenii Alekseev 2021-08-27 03:15:50 +03:00
parent 3ee5f7f13e
commit 3e044fd006
6 changed files with 123 additions and 1 deletions

View File

@ -30,6 +30,8 @@ from ahriman.models.sign_settings import SignSettings
# pylint thinks it is bad idea, but get the fuck off
from ahriman.models.user_access import UserAccess
SubParserAction = argparse._SubParsersAction # pylint: disable=protected-access
@ -61,6 +63,7 @@ def _parser() -> argparse.ArgumentParser:
_set_check_parser(subparsers)
_set_clean_parser(subparsers)
_set_config_parser(subparsers)
_set_create_user_parser(subparsers)
_set_init_parser(subparsers)
_set_key_import_parser(subparsers)
_set_rebuild_parser(subparsers)
@ -138,6 +141,30 @@ def _set_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
return parser
def _set_create_user_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for create user subcommand
:param root: subparsers for the commands
:return: created argument parser
"""
parser = root.add_parser(
"create-user",
help="create user for web services",
description="create user for web services with password and role. In case if password was not entered it will be asked interactively",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("username", help="username for web service")
parser.add_argument("-r", "--role", help="user role", type=UserAccess, choices=UserAccess, default=UserAccess.Read)
parser.add_argument("-p", "--password", help="user password")
parser.set_defaults(
handler=handlers.CreateUser,
architecture=[""],
lock=None,
no_log=True,
no_report=True,
unsafe=True)
return parser
def _set_init_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for init subcommand

View File

@ -21,6 +21,7 @@ from ahriman.application.handlers.handler import Handler
from ahriman.application.handlers.add import Add
from ahriman.application.handlers.clean import Clean
from ahriman.application.handlers.create_user import CreateUser
from ahriman.application.handlers.dump import Dump
from ahriman.application.handlers.init import Init
from ahriman.application.handlers.key_import import KeyImport

View File

@ -0,0 +1,79 @@
#
# Copyright (c) 2021 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import argparse
import configparser
from getpass import getpass
from pathlib import Path
from typing import Type
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.user import User
class CreateUser(Handler):
"""
create user handler
"""
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration) -> None:
"""
callback for command line
:param args: command line args
:param architecture: repository architecture
:param configuration: configuration instance
"""
user = CreateUser.create_user(args, configuration)
CreateUser.create_configuration(user, configuration.include)
@staticmethod
def create_configuration(user: User, include_path: Path) -> None:
"""
put new user to configuration
:param user: user descriptor
:param include_path: path to directory with configuration includes
"""
target = include_path / "auth.ini"
configuration = configparser.ConfigParser()
configuration.read(target)
section = Configuration.section_name("auth", user.access.value)
configuration.add_section(section)
configuration.set(section, user.username, user.password)
with target.open("w") as ahriman_configuration:
configuration.write(ahriman_configuration)
@staticmethod
def create_user(args: argparse.Namespace, configuration: Configuration) -> User:
"""
create user descriptor from arguments
:param args: command line args
:param configuration: configuration instance
:return: built user descriptor
"""
user = User(args.username, args.password, args.role)
if user.password is None:
user.password = getpass()
user.password = user.generate_password(user.password, configuration.get("auth", "salt"))
return user

View File

@ -49,7 +49,9 @@ class WebClient(Client):
self.user = User.from_option(
configuration.get("web", "username", fallback=None),
configuration.get("web", "password", fallback=None))
self.__session = requests.session()
self.login()
@property
def _ahriman_url(self) -> str:

View File

@ -38,6 +38,8 @@ class User:
password: str
access: UserAccess
_HASHER = sha512_crypt
@classmethod
def from_option(cls: Type[User], username: Optional[str], password: Optional[str]) -> Optional[User]:
"""
@ -57,9 +59,19 @@ class User:
:param salt: salt for hashed password
:return: True in case if password matches, False otherwise
"""
verified: bool = sha512_crypt.verify(password + salt, self.password)
verified: bool = self._HASHER.verify(password + salt, self.password)
return verified
def generate_password(self, password: str, salt: str) -> str:
"""
generate hashed password from plain text
:param password: entered password
:param salt: salt for hashed password
:return: hashed string to store in configuration
"""
password_hash: str = self._HASHER.hash(password + salt)
return password_hash
def verify_access(self, required: UserAccess) -> bool:
"""
validate if user has access to requested resource

View File

@ -44,6 +44,7 @@ def client() -> Client:
def web_client(configuration: Configuration) -> WebClient:
"""
fixture for web client
:param configuration: configuration fixture
:return: web client test instance
"""
configuration.set("web", "port", 8080)