"""Base module IDM related classes and functions."""
from abc import ABC, abstractmethod
from argparse import ArgumentParser, Namespace
from logging import getLogger
from typing import List, Optional
from wmflib.exceptions import WmflibError
[docs]
class IdmValueError(WmflibError):
"""Raised by the IDM module value errors."""
# We make the description optional to deal with the following issue
# https://github.com/python/mypy/issues/9170
[docs]
def logoutd_args(description: Optional[str] = None, args: Optional[List] = None) -> Namespace:
"""Logout scripts common CLI for parsing the command line arguments.
When not using the higher level API :py:class:`wmflib.idm.LogoutdBase`, a user could just implement their own
script still using the same command line arguments processing, of the form::
from wmflib.idm import logoutd_args
args = logoutd_args('Some description')
# write your own script
Arguments:
description (str): the description to use
args (list): A list of arguments to use (used for testing)
Returns:
`argparse.Namespace`: The parsed argparser Namespace
"""
if description is None:
raise IdmValueError('Must provide a string description')
parser = ArgumentParser(description=description)
parser.add_argument('-v', '--verbose', action='count', default=0)
sub = parser.add_subparsers(dest='command', required=True)
query = sub.add_parser('query', help='display the status of logged-in users')
query.add_argument(
'-u',
'--uid',
required=True,
help='The uid of the user to use',
)
query.add_argument('-c', '--cn', required=True, help='The cn of the user to use')
sub.add_parser(
'logout',
parents=[query],
add_help=False,
help='display the status of logged in users',
)
sub.add_parser('list', help='list all active sessions')
return parser.parse_args(args)
[docs]
class LogoutdBase(ABC): # noqa: D300,D301 See https://github.com/PyCQA/pydocstyle/issues/542
"""Base class for a standardized API for logout scripts.
A simple logout.d python script would then be of the form::
from wmflib.idm import LogoutdBase
class MyLogoutd(LogoutdBase):
\"\"\"Some description.\"\"\"
def logout_user(self, user):
# logout the given user
def query_user(self, user):
# check the login status of a given user
def list(self):
# list all active login sessions
logoutd = MyLogoutd()
raise SystemExit(logoutd.run()) # This includes the parsing of command line arguments.
"""
user_identifier: str = 'cn'
def __init__(self, args: Optional[List] = None) -> None:
"""Init function.
Arguments:
args (list): A list of arguments to use (used for testing)
"""
self._args = logoutd_args(self.__doc__, args)
self._logger = getLogger('.'.join((self.__module__, self.__class__.__name__)))
@property
def user(self) -> str:
"""Return either common_name or uid with a preference for common_name.
Returns:
(str): representing the user
"""
return getattr(self._args, self.user_identifier)
[docs]
@abstractmethod
def logout_user(self, user: str) -> int:
"""Log out the specified user.
Arguments:
user (User): object representing the user
Returns:
(int): 0 if the users session was successfully cleared otherwise 1
"""
[docs]
@abstractmethod
def query_user(self, user: str) -> int:
"""Return status of logged in user.
Arguments:
user (User): object representing the user
Returns:
(int): 1 if the user is logged in otherwise 0
"""
[docs]
@abstractmethod
def list(self) -> int:
"""Return data of all logged in users.
Returns:
(int): 0 on success non-zero on fail
"""
[docs]
def run(self) -> int:
"""Execute the correct action.
Returns:
(int): exit code depends on command
"""
self._logger.debug('Running action: %s', self._args.command)
if self._args.command == 'query':
return self.query_user(self.user)
if self._args.command == 'logout':
return self.logout_user(self.user)
return self.list()