diff --git a/Makefile b/Makefile index d9fed2d7bb..1e52e66944 100644 --- a/Makefile +++ b/Makefile @@ -256,6 +256,7 @@ generate_selfcertificate: scripts/auth_certificates ca $(AUTH_NAME) $(SSL_DAYS) scripts/auth_certificates user user $(SSL_DAYS) scripts/auth_certificates user admin $(SSL_DAYS) + scripts/auth_certificates user systemctl $(SSL_DAYS) ############################################################################### ############################ C BUILD/INSTALL TARGETS ########################## diff --git a/VERSION b/VERSION index 47c14db6b7..d40af75c6c 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ fledge_version=3.1.0 -fledge_schema=76 +fledge_schema=77 diff --git a/docs/quick_start/starting.rst b/docs/quick_start/starting.rst index 6e67b6f073..5ecb0821a3 100644 --- a/docs/quick_start/starting.rst +++ b/docs/quick_start/starting.rst @@ -18,13 +18,19 @@ For example, to start the Fledge system, open a session to the Fledge device and If authentication is enabled, which is the default mode for Fledge version 3.0 onward, then a number of the commands require authentication. Authentication can be accomplished by several means; - - Set the environment variable *USERNAME* to be the user name. + - Set the environment variable *FLEDGE_USER* to be the user name. - Pass the *-u* flag flag to the command to specify a user name. + - Create an authentication file + - If neither of the above are done the user will be prompted to enter a user name. -In both cases the user will be prompted to enter a password. It is possible, but not recommended, to set an environment variable *PASSWORD* or pass the *-p* flag on the command line, with the plain text version of the password. +.. note:: + + It is recommended to create an authentication file rather than pass parameters to the fledge command or set environment varaiables as both these methods can expose plain text user names, or passwords, to other users of the system. + +In both cases the user will be prompted to enter a password. It is possible, but not recommended, to set an environment variable *FLEDGE_PASSWORD* or pass the *-p* flag on the command line, with the plain text version of the password. .. code-block:: console @@ -47,6 +53,44 @@ It is also possible to use certificate based authentication to login to the syst .. note:: - Extreme caution should be taken when storing certificate files that they not be readable by any other user within the system. + Extreme caution should be taken when storing certificate files. They must not be readable by any other users within the system. Following a successful authentication attempt a time based token is issued that allows the user to run further commands, for a limited time, without the need to authenticate again. + +Authentication File +------------------- + +The prompting for username and password when using the *fledge* script can be bypassed if an authentication file is created. This is a file that should be created in a directory called *.fledge* in the user's home directory. + +The file created should be called *auth* and contains the credentials required to login. This may either be a username and password or the filename of a certficate to use to authenticate. + +.. note:: + + The *auth* file will only be read if the permissions on that file are set such that only the owner can read the file. + + .. code-block:: console + + $ chmod 600 ~/.fledge/auth + + In older versions of Fledge the *auth* file was simply called *~/.fledge*. If the older *.fledge* file exists it will still be used. + +An example *auth* file, using the default username and password would be as follows + +.. code-block:: console + + FLEDGE_USER=admin + FLEDGE_PASSWORD=fledge + +If using a certificate to authenticate the file would look as follow + +.. code-block:: console + + FLEDGE_CERT=~/.auth/user.cert + +The file name, minus the extension, should match the user name of the user. + +.. note:: + + In the above example the certificate has been placed in the .auth directory, this is not a requirement and the user name choose to place the certificate in any location that is convienent for them. However the certificate file should be protected sich that it can not be red or copied by other users. + + diff --git a/docs/securing_fledge.rst b/docs/securing_fledge.rst index b5525e3f87..2a6328e188 100644 --- a/docs/securing_fledge.rst +++ b/docs/securing_fledge.rst @@ -227,6 +227,27 @@ Fledge provides a mechanism to limit the age of passwords in use within the syst Whenever a user logs into Fledge the age of their password is checked against the maximum allowed password age. If their password has reached that age then the user is not logged in, but is instead forced to enter a new password. They must then login with that new password. In addition the system maintains a history of the last three passwords the user has used and prevents them being reused. +Management Script Login +----------------------- + +Fledge supports a number of mechanisms for user authentication when using the *fledge* script to manage startup, shutdown and other operations. + + - The default authentication mechanism will prompt the entry of user name and password interactively. + + - The user may set one or both of the FLEDGE_USER and FLEDGE_PASSWORD environment variables to automate the entry of the username or password. + + - The user may pass either or both of the *-u* and *-p* flags to set the username and password on the command line. + + - The user may pass the *-c* flag and a path to a certificate file that is used to authenticate the user. + + - The user creates an authentication file that contains the username and password + +.. note:: + + In all cases where a password is stored or passed to the script this is done in plain text. Care should be taken to protect these plain text passwords. In the case of the authentication file, the file permissions must be such that only the Linux user is able to read the file. + + Using a certificate to authenticate rather than username and password, alleviates the need to have plain text passwords stored on the system. However access to the certificate must be protected. The ability to access the certificate provides the ability to authenticate wit the Fledge instance without need for any further information. + User Management =============== @@ -346,7 +367,6 @@ To add a new certificate select the *Import* icon in the top right of the certif A dialog will appear that allows a key file and/or a certificate file to be selected and uploaded to the *Certificate Store*. An option allows to allow overwrite of an existing certificate. By default certificates may not be overwritten. - Custom Certificate Authority (CA) --------------------------------- @@ -370,12 +390,16 @@ After obtaining the certificates, modify the **Root CA Certificate** setting (cu If there are any intermediate certificates forming a chain, consolidate them into a single file named **intermediate.cert or intermediate.pem** and store it in the same directory specified earlier. -Generate a new auth certificates for user login ------------------------------------------------ +Generate a new certificates for user login +------------------------------------------ + +A default certificate authority (CA) certificate is available inside $FLEDGE_DATA/etc/certs and named as ca.cert. Also default admin, systemctl and non-admin certificates are available in the same location which will be used for Login with Certificate in Fledge i.e admin.cert, systemctl.cert and user.cert. See |Require User Login| + +.. note:: -Default ca certificate is available inside $FLEDGE_DATA/etc/certs and named as ca.cert. Also default admin and non-admin certs are available in the same location which will be used for Login with Certificate in Fledge i.e admin.cert, user.cert. See |Require User Login| + The systemctl.cert certificate is used by the Linux systemctl scripts to start, stop and monitor the state of the Fledge instance, using the status command, and should not be removed. -Below are the steps to create custom certificate along with existing fledge based ca signed for auth certificates. +Below are the steps to create custom certificates along with existing Fledge based CA signed for authentication certificates. **Using cURL** @@ -408,7 +432,7 @@ You may also refer the documentation of |REST API| cURL commands. If you are not .. note:: - Steps a (cert creation) and b (create user) can be executed in any order. + Steps a (cert creation) and b (create user) can be executed in either order. c) Now you can login with the newly created user **test**, with the following cURL diff --git a/extras/scripts/fledge.service b/extras/scripts/fledge.service index 4f79f14d73..cb270caa4b 100755 --- a/extras/scripts/fledge.service +++ b/extras/scripts/fledge.service @@ -55,6 +55,7 @@ fi FLEDGE_ROOT="/usr/local/fledge" FLEDGE_DATA="${FLEDGE_ROOT}/data" FLEDGE_USER=`ls -ld "${FLEDGE_DATA}" | awk '{print $3}'` +FLEDGE_CERT="${FLEDGE_ROOT}/data/etc/certs/systemctl.cert" PID_FILE="${FLEDGE_DATA}/var/run/fledge.core.pid" PID=0 @@ -68,7 +69,13 @@ get_pid() { fledge_start() { if [ "$IS_RHEL" = "" ]; then - sudo -u ${FLEDGE_USER} "${FLEDGE_ROOT}/bin/fledge" start > /dev/null + if [ -f "${FLEDGE_CERT}" ]; then + sudo -u ${FLEDGE_USER} "${FLEDGE_ROOT}/bin/fledge" -c "$FLEDGE_CERT}" start > /dev/null + else + sudo -u ${FLEDGE_USER} "${FLEDGE_ROOT}/bin/fledge" start > /dev/null + fi + elif [ -f "${FLEDGE_CERT}" ]; then + "${FLEDGE_ROOT}/bin/fledge" -c "${FLEDGE_CERT}" start > /dev/null else "${FLEDGE_ROOT}/bin/fledge" start > /dev/null fi @@ -76,7 +83,13 @@ fledge_start() { fledge_stop() { if [ "$IS_RHEL" = "" ]; then - sudo -u ${FLEDGE_USER} "${FLEDGE_ROOT}/bin/fledge" stop > /dev/null + if [ -f "${FLEDGE_CERT}" ]; then + sudo -u ${FLEDGE_USER} "${FLEDGE_ROOT}/bin/fledge" -c "${FLEDGE_CERT}" stop > /dev/null + else + sudo -u ${FLEDGE_USER} "${FLEDGE_ROOT}/bin/fledge" stop > /dev/null + fi + elif [ -f "${FLEDGE_CERT}" ]; then + "${FLEDGE_ROOT}/bin/fledge" -c "${FLEDGE_CERT}" stop > /dev/null else "${FLEDGE_ROOT}/bin/fledge" stop > /dev/null fi @@ -139,7 +152,7 @@ case "$1" in rm -f $PID_FILE exit 1 else - fledge_stop + kill -TERM $PID echo "Fledge stopped [$PID]" fi fi @@ -148,7 +161,19 @@ case "$1" in restart) - $0 fledge_stop + get_pid + if [ $PID -eq 0 ]; then + echo "Fledge not running" + else + ps -p $PID + if [ $? -eq 1 ]; then + echo "Fledge not running (process dead but PID file exists)" + rm -f $PID_FILE + else + kill -TERM $PID + echo "Fledge stopped [$PID]" + fi + fi $0 fledge_start ;; diff --git a/python/fledge/common/web/middleware.py b/python/fledge/common/web/middleware.py index d6ac996197..b2020be050 100644 --- a/python/fledge/common/web/middleware.py +++ b/python/fledge/common/web/middleware.py @@ -200,6 +200,10 @@ async def validate_requests(request): - same as normal user can do - All CRUD's privileges for control scripts - All CRUD's privileges for control pipelines + e) With "systemctl" based user role id = 6 only + - login and log out + - ping, health, audit, service, profile (GET call) + - shutdown (PUT call) """ user_id = request.user['id'] # Only URL's which are specific meant for Admin user @@ -207,6 +211,15 @@ async def validate_requests(request): # Special case: Allowed GET user for Control user if int(request.user["role_id"]) != 5 and str(request.rel_url) == '/fledge/user': raise web.HTTPForbidden + # Admin restrictions + if int(request.user["role_id"]) == 1: + # Admin cannot delete and update the systemctl user (userid = 3) + if request.method == 'DELETE': + if str(request.rel_url).startswith('/fledge/admin/3/delete'): + raise web.HTTPForbidden + elif request.method == 'PUT': + if str(request.rel_url).startswith('/fledge/admin/3'): + raise web.HTTPForbidden # Normal/Editor user if int(request.user["role_id"]) == 2 and request.method != 'GET': # Special case: Allowed control entrypoint update request and handling of rejection in its handler @@ -237,7 +250,23 @@ async def validate_requests(request): raise web.HTTPForbidden else: raise web.HTTPForbidden - + # Systemctl user + elif int(request.user["role_id"]) == 6: + if request.method == 'GET': + supported_endpoints = ['/fledge/ping', '/fledge/health/logging', '/fledge/health/storage', + '/fledge/user?id=3', '/fledge/audit?source=SRVFL'] + if not (str(request.rel_url).startswith(tuple(supported_endpoints)) or + str(request.rel_url).endswith('/fledge/service')): + raise web.HTTPForbidden + elif request.method == 'PUT': + supported_endpoints = ['/fledge/shutdown', '/logout'] + if not str(request.rel_url).endswith(tuple(supported_endpoints)): + raise web.HTTPForbidden + elif request.method == 'post': + if not str(request.rel_url).startswith('fledge/login'): + raise web.HTTPForbidden + else: + raise web.HTTPForbidden def check_firewall(req: web.Request) -> None: # FIXME: Need to check on other environments like AWS, docker; May be ideal with socket diff --git a/python/fledge/services/core/api/auth.py b/python/fledge/services/core/api/auth.py index fe795df0ed..fd01144aa6 100644 --- a/python/fledge/services/core/api/auth.py +++ b/python/fledge/services/core/api/auth.py @@ -436,6 +436,9 @@ async def create_user(request): if not (await is_valid_role(role_id)): msg = "Invalid role ID." raise web.HTTPBadRequest(reason=msg, body=json.dumps({"message": msg})) + if role_id == 6: + msg = "We cannot create a user with systemctl role." + raise web.HTTPBadRequest(reason=msg, body=json.dumps({"message": msg})) users = await User.Objects.all() unames = [u['uname'] for u in users] if username in unames: diff --git a/python/fledge/services/core/api/certificate_store.py b/python/fledge/services/core/api/certificate_store.py index 5c1d840c64..a43a197a0e 100644 --- a/python/fledge/services/core/api/certificate_store.py +++ b/python/fledge/services/core/api/certificate_store.py @@ -101,8 +101,8 @@ async def upload(request): cert_filename = cert_file.filename # default installed auth cert keys can be deleted, for matching/debugging disallow overwrite - if cert_filename in ['admin.cert', 'admin.key', 'user.cert', 'user.key', 'fledge.key', 'fledge.cert', 'ca.key', - 'ca.cert']: + if cert_filename in ['admin.cert', 'admin.key', 'user.cert', 'user.key', 'systemctl.cert', 'systemctl.key', + 'fledge.key', 'fledge.cert', 'ca.key', 'ca.cert']: if request.is_auth_optional: _logger.warning(FORBIDDEN_MSG) raise web.HTTPForbidden(reason=FORBIDDEN_MSG, body=json.dumps({"message": FORBIDDEN_MSG})) @@ -197,7 +197,7 @@ async def delete_certificate(request): if not file_name.endswith(valid_extensions): msg = "Accepted file extensions are {}".format(valid_extensions) raise web.HTTPBadRequest(reason=msg, body=json.dumps({"message": msg})) - if file_name in ['admin.cert', 'user.cert', 'fledge.key', 'fledge.cert', 'ca.key', 'ca.cert']: + if file_name in ['admin.cert', 'user.cert', 'systemctl.cert', 'fledge.key', 'fledge.cert', 'ca.key', 'ca.cert']: if request.is_auth_optional: _logger.warning(FORBIDDEN_MSG) raise web.HTTPForbidden(reason=FORBIDDEN_MSG, body=json.dumps({"message": FORBIDDEN_MSG})) diff --git a/python/fledge/services/core/server.py b/python/fledge/services/core/server.py index 754367683b..3417e87b8b 100755 --- a/python/fledge/services/core/server.py +++ b/python/fledge/services/core/server.py @@ -405,6 +405,9 @@ class Server: _alert_manager = None """ Alert Manager """ + _loop = None + """ Event loop reference for signal handlers """ + running_in_safe_mode = False """ Fledge running in Safe mode """ @@ -1048,12 +1051,89 @@ async def _get_alerts(cls): cls._alert_manager = AlertManager(cls._storage_client_async) await cls._alert_manager.get_all() + @classmethod + def _signal_handler(cls, signum, frame): + """Signal handler for graceful shutdown and restart + + Args: + signum: Signal number + frame: Current stack frame + """ + signal_name = signal.Signals(signum).name + + # Handle SIGHUP as restart, others as shutdown + if signum == signal.SIGHUP: + _logger.info("Received signal %s (%d), initiating graceful restart...", signal_name, signum) + if cls._loop and not cls._loop.is_closed(): + asyncio.run_coroutine_threadsafe(cls._signal_restart(), cls._loop) + else: + _logger.error("Event loop not available for graceful restart") + sys.exit(1) + else: + _logger.info("Received signal %s (%d), initiating graceful shutdown...", signal_name, signum) + if cls._loop and not cls._loop.is_closed(): + asyncio.run_coroutine_threadsafe(cls._signal_shutdown(), cls._loop) + else: + _logger.error("Event loop not available for graceful shutdown") + sys.exit(1) + + @classmethod + async def _signal_shutdown(cls): + """Immediate shutdown method for signal handlers (no delay) + + This method provides immediate shutdown without the 2-second delay + that's appropriate for HTTP API shutdowns but not for signal handling. + """ + try: + await cls._stop() + _logger.info("Stopping the Fledge Core event loop. Good Bye!") + cls._loop.stop() + except Exception as ex: + _logger.error("Error during signal shutdown: %s", str(ex)) + cls._loop.stop() + + @classmethod + async def _signal_restart(cls): + """Immediate restart method for signal handlers (no delay) + + This method provides immediate restart without the 2-second delay + that's appropriate for HTTP API restarts but not for signal handling. + """ + try: + await cls._stop() + _logger.info("Restarting Fledge Core...") + + # Allow some time for cleanup + await asyncio.sleep(1.0) + + # Remove safe-mode from sys.argv if present + if 'safe-mode' in sys.argv: + sys.argv.remove('safe-mode') + sys.argv.append('') + + # Restart the process + python3 = sys.executable + os.execl(python3, python3, *sys.argv) # Replaces current process, no return + except Exception as ex: + _logger.error("Error during signal restart: %s", str(ex)) + cls._loop.stop() + @classmethod def _start_core(cls, loop=None): if cls.running_in_safe_mode: _logger.info("Starting in SAFE MODE ...") else: _logger.info("Starting ...") + + # Store the loop reference for signal handlers + cls._loop = loop + + # Register signal handlers for graceful shutdown + signal.signal(signal.SIGHUP, cls._signal_handler) + signal.signal(signal.SIGTERM, cls._signal_handler) + signal.signal(signal.SIGINT, cls._signal_handler) + _logger.info("Signal handlers registered for SIGHUP, SIGTERM, and SIGINT") + try: host = cls._host diff --git a/scripts/fledge b/scripts/fledge index ad963deb1e..6c439fad67 100755 --- a/scripts/fledge +++ b/scripts/fledge @@ -181,7 +181,7 @@ fledge_reset() { find "$FLEDGE_DATA/etc/kerberos" -depth -type f -not -name "README.rst" -exec rm {} \; fi # Remove user etc/certs: extras, with exclusions - find "$FLEDGE_DATA/etc/certs" -depth -type f -not -name user.* -not -name fledge.* -not -name ca.* -not -name admin.* -exec rm {} \; + find "$FLEDGE_DATA/etc/certs" -depth -type f -not -name user.* -not -name fledge.* -not -name ca.* -not -name admin.* -not -name systemctl.* -exec rm {} \; # Remove user etc/ files: extras, with one exclusion find "$FLEDGE_DATA/etc/" -maxdepth 1 -type f -not -name storage.json -exec rm {} \; echo "Removed user data from $FLEDGE_DATA/etc" @@ -455,7 +455,7 @@ fledge_authenticate() { if [[ -z ${CERT+x} ]]; then if [[ -t "$fd" ]]; then # We have an interactive shell - if [ -z ${USERNAME+x} ]; then + if [ -z ${FLUSERNAME+x} ]; then read -p "Username: " USERNAME fi if [ -z ${PASSWORD+x} ]; then @@ -470,7 +470,7 @@ fledge_authenticate() { if [[ -f ${CERT} ]]; then result=`curl -T ${CERT} -X POST -k -s ${REST_API_URL}/fledge/login --insecure` else - payload='{ "username" : "'${USERNAME}'", "password" : "'${PASSWORD}'" }' + payload='{ "username" : "'${FLUSERNAME}'", "password" : "'${PASSWORD}'" }' result=`curl -X POST -k -s ${REST_API_URL}/fledge/login -d"$payload" || true` fi if [[ ! "$result" =~ "Logged in successfully" ]]; then @@ -798,6 +798,25 @@ fledge_healthcheck() { echo Healthcheck completed } +extract_credentials() { + file=$1 + # if $file is mode 0600 then fetch username and password + # from it if they are not already set + perm=`stat -c %A "$file"` + if [ "$perm" == "-rw-------" ]; then + . "$file" + if [ ! -z ${FLEDGE_USER+x} ]; then + FLUSERNAME=$FLEDGE_USER + fi + if [ ! -z ${FLEDGE_PASSWORD+x} ]; then + PASSWORD=$FLEDGE_PASSWORD + fi + if [ ! -z ${FLEDGE_CERT+x} ]; then + CERT=$FLEDGE_CERT + fi + fi +} + ### Main Logic ### # Set FLEDGE_DATA if it does not exist @@ -829,7 +848,7 @@ get_rest_api_url while getopts "u:p:c:h" option; do case "$option" in u) - USERNAME=${OPTARG} + FLUSERNAME=${OPTARG} ;; p) PASSWORD=${OPTARG} @@ -849,10 +868,10 @@ while getopts "u:p:c:h" option; do done shift $((OPTIND-1)) -if [ -z ${USERNAME+x} ]; then +if [ -z ${FLUSERNAME+x} ]; then # If username is not set on the command line use environment variable if set if [ ! -z ${FLEDGE_USER+x} ]; then - USERNAME=$FLEDGE_USER + FLUSERNAME=$FLEDGE_USER fi fi @@ -862,19 +881,19 @@ if [ -z ${PASSWORD+x} ]; then PASSWORD=$FLEDGE_PASSWORD fi fi +if [ -z ${CERT+x} ]; then + # If certificate is not set on the command line use environment variable if set + if [ ! -z ${FLEDGE_CERT+x} ]; then + CERT=$FLEDGE_CERT + fi +fi if [ -f ~/.fledge ] ; then - # if ~/.fledge is mode 0600 then fetch username and password - # from it if they are not already set - perm=`stat -c %A ~/.fledge` - if [ "$perm" == "-rw-------" ]; then - if [ -z ${USERNAME+x} ]; then - USERNAME=`awk -F: 'NR==1{ print $1 }' < ~/.fledge | cut -d"=" -f2` - fi - if [ -z ${PASSWORD+x} ]; then - PASSWORD=`awk -F: 'NR==2{ print $1 }' < ~/.fledge | cut -d"=" -f2` - fi - fi + extract_credentials ~/.fledge +fi + +if [ -d ~/.fledge ] && [ -f ~/.fledge/auth ] ; then + extract_credentials ~/.fledge/auth fi SAFE_MODE='' diff --git a/scripts/plugins/storage/postgres/downgrade/76.sql b/scripts/plugins/storage/postgres/downgrade/76.sql new file mode 100644 index 0000000000..224f6d1e05 --- /dev/null +++ b/scripts/plugins/storage/postgres/downgrade/76.sql @@ -0,0 +1,6 @@ +-- Delete the systemctl user along with its associated role +DELETE FROM fledge.users WHERE uname='systemctl'; +DELETE FROM fledge.roles WHERE name='systemctl'; +-- Reset auto increment +ALTER SEQUENCE fledge.users_id_seq RESTART WITH 3; +ALTER SEQUENCE fledge.roles_id_seq RESTART WITH 6; \ No newline at end of file diff --git a/scripts/plugins/storage/postgres/init.sql b/scripts/plugins/storage/postgres/init.sql index 25679d84f7..c7e2b6ac89 100644 --- a/scripts/plugins/storage/postgres/init.sql +++ b/scripts/plugins/storage/postgres/init.sql @@ -976,14 +976,16 @@ INSERT INTO fledge.roles ( name, description ) ('user', 'All CRUD operations and self profile management'), ('view', 'Only to view the configuration'), ('data-view', 'Only read the data in buffer'), - ('control', 'Same as editor can do and also have access for control scripts and pipelines'); + ('control', 'Same as editor can do and also have access for control scripts and pipelines'), + ('systemctl', 'It solely facilitates the execution of commands initiated by the fledge script'); -- Users DELETE FROM fledge.users; -INSERT INTO fledge.users ( uname, real_name, pwd, role_id, description ) - VALUES ('admin', 'Admin user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'admin user'), - ('user', 'Normal user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 2, 'normal user'); +INSERT INTO fledge.users ( uname, real_name, pwd, role_id, description, access_method) + VALUES ('admin', 'Admin user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'admin user', 'any'), + ('user', 'Normal user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 2, 'normal user', 'any'), + ('systemctl', 'Systemctl user', '', 6, 'User used by the systemctl scripts', 'cert'); -- User password history DELETE FROM fledge.user_pwd_history; diff --git a/scripts/plugins/storage/postgres/upgrade/77.sql b/scripts/plugins/storage/postgres/upgrade/77.sql new file mode 100644 index 0000000000..24679f4822 --- /dev/null +++ b/scripts/plugins/storage/postgres/upgrade/77.sql @@ -0,0 +1,7 @@ +-- Systemctl Role and User +INSERT INTO fledge.roles ( name, description ) + VALUES ('systemctl', 'It solely facilitates the execution of commands initiated by the fledge script'); + +INSERT INTO fledge.users ( uname, real_name, pwd, role_id, description, access_method) + VALUES ('systemctl', 'Systemctl user', '', 6, 'User used by the systemctl scripts', 'cert'); + diff --git a/scripts/plugins/storage/sqlite/downgrade/76.sql b/scripts/plugins/storage/sqlite/downgrade/76.sql new file mode 100644 index 0000000000..75be0557f6 --- /dev/null +++ b/scripts/plugins/storage/sqlite/downgrade/76.sql @@ -0,0 +1,6 @@ +-- Delete the systemctl user along with its associated role +DELETE FROM fledge.users WHERE uname='systemctl'; +DELETE FROM fledge.roles WHERE name ='systemctl'; +-- Reset auto increment +-- You cannot use ALTER TABLE for that. The autoincrement counter is stored in a separate table named "sqlite_sequence". You can modify the value there +UPDATE sqlite_sequence SET seq = 1 WHERE name IN ('users', 'roles'); diff --git a/scripts/plugins/storage/sqlite/init.sql b/scripts/plugins/storage/sqlite/init.sql index 53db939281..22b0e67f36 100644 --- a/scripts/plugins/storage/sqlite/init.sql +++ b/scripts/plugins/storage/sqlite/init.sql @@ -734,13 +734,15 @@ INSERT INTO fledge.roles ( name, description ) ('user', 'All CRUD operations and self profile management'), ('view', 'Only to view the configuration'), ('data-view', 'Only read the data in buffer'), - ('control', 'Same as editor can do and also have access for control scripts and pipelines'); + ('control', 'Same as editor can do and also have access for control scripts and pipelines'), + ('systemctl', 'It solely facilitates the execution of commands initiated by the fledge script'); -- Users DELETE FROM fledge.users; -INSERT INTO fledge.users ( uname, real_name, pwd, role_id, description ) - VALUES ('admin', 'Admin user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'admin user'), - ('user', 'Normal user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 2, 'normal user'); +INSERT INTO fledge.users ( uname, real_name, pwd, role_id, description, access_method ) + VALUES ('admin', 'Admin user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'admin user', 'any'), + ('user', 'Normal user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 2, 'normal user', 'any'), + ('systemctl', 'Systemctl user', '', 6, 'User used by the systemctl scripts', 'cert'); -- User password history DELETE FROM fledge.user_pwd_history; diff --git a/scripts/plugins/storage/sqlite/upgrade/77.sql b/scripts/plugins/storage/sqlite/upgrade/77.sql new file mode 100644 index 0000000000..561592e941 --- /dev/null +++ b/scripts/plugins/storage/sqlite/upgrade/77.sql @@ -0,0 +1,6 @@ +-- Systemctl Role and User +INSERT INTO fledge.roles ( name, description ) + VALUES ('systemctl', 'It solely facilitates the execution of commands initiated by the fledge script'); + +INSERT INTO fledge.users ( uname, real_name, pwd, role_id, description, access_method) + VALUES ('systemctl', 'Systemctl user', '', 6, 'User used by the systemctl scripts', 'cert'); diff --git a/scripts/plugins/storage/sqlitelb/downgrade/76.sql b/scripts/plugins/storage/sqlitelb/downgrade/76.sql new file mode 100644 index 0000000000..75be0557f6 --- /dev/null +++ b/scripts/plugins/storage/sqlitelb/downgrade/76.sql @@ -0,0 +1,6 @@ +-- Delete the systemctl user along with its associated role +DELETE FROM fledge.users WHERE uname='systemctl'; +DELETE FROM fledge.roles WHERE name ='systemctl'; +-- Reset auto increment +-- You cannot use ALTER TABLE for that. The autoincrement counter is stored in a separate table named "sqlite_sequence". You can modify the value there +UPDATE sqlite_sequence SET seq = 1 WHERE name IN ('users', 'roles'); diff --git a/scripts/plugins/storage/sqlitelb/init.sql b/scripts/plugins/storage/sqlitelb/init.sql index 9f9f147a97..ab88cc5b10 100644 --- a/scripts/plugins/storage/sqlitelb/init.sql +++ b/scripts/plugins/storage/sqlitelb/init.sql @@ -734,13 +734,15 @@ INSERT INTO fledge.roles ( name, description ) ('user', 'All CRUD operations and self profile management'), ('view', 'Only to view the configuration'), ('data-view', 'Only read the data in buffer'), - ('control', 'Same as editor can do and also have access for control scripts and pipelines'); + ('control', 'Same as editor can do and also have access for control scripts and pipelines'), + ('systemctl', 'It solely facilitates the execution of commands initiated by the fledge script'); -- Users DELETE FROM fledge.users; -INSERT INTO fledge.users ( uname, real_name, pwd, role_id, description ) - VALUES ('admin', 'Admin user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'admin user'), - ('user', 'Normal user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 2, 'normal user'); +INSERT INTO fledge.users ( uname, real_name, pwd, role_id, description, access_method ) + VALUES ('admin', 'Admin user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'admin user', 'any'), + ('user', 'Normal user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 2, 'normal user', 'any'), + ('systemctl', 'Systemctl user', '', 6, 'User used by the systemctl scripts', 'cert'); -- User password history DELETE FROM fledge.user_pwd_history; diff --git a/scripts/plugins/storage/sqlitelb/upgrade/77.sql b/scripts/plugins/storage/sqlitelb/upgrade/77.sql new file mode 100644 index 0000000000..561592e941 --- /dev/null +++ b/scripts/plugins/storage/sqlitelb/upgrade/77.sql @@ -0,0 +1,6 @@ +-- Systemctl Role and User +INSERT INTO fledge.roles ( name, description ) + VALUES ('systemctl', 'It solely facilitates the execution of commands initiated by the fledge script'); + +INSERT INTO fledge.users ( uname, real_name, pwd, role_id, description, access_method) + VALUES ('systemctl', 'Systemctl user', '', 6, 'User used by the systemctl scripts', 'cert'); diff --git a/tests/system/python/api/test_authentication.py b/tests/system/python/api/test_authentication.py index ba55a5c7da..a38ea8d63c 100644 --- a/tests/system/python/api/test_authentication.py +++ b/tests/system/python/api/test_authentication.py @@ -70,7 +70,10 @@ def test_login_username_admin(self, fledge_url): ('', {'users': [{'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', 'realName': 'Admin user', 'description': 'admin user'}, {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', - 'description': 'normal user'}]}), + 'description': 'normal user'}, + {'userId': 3, 'userName': 'systemctl', 'roleId': 6, 'accessMethod': 'cert', + 'realName': 'Systemctl user', 'description': 'User used by the systemctl scripts'} + ]}), ('?id=2', {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', 'description': 'normal user'}), ('?username=admin', @@ -101,34 +104,36 @@ def test_get_roles(self, fledge_url): {'id': 3, 'name': 'view', 'description': 'Only to view the configuration'}, {'id': 4, 'name': 'data-view', 'description': 'Only read the data in buffer'}, {'id': 5, 'name': 'control', 'description': - 'Same as editor can do and also have access for control scripts and pipelines'} + 'Same as editor can do and also have access for control scripts and pipelines'}, + {'id': 6, 'name': 'systemctl', 'description': + 'It solely facilitates the execution of commands initiated by the fledge script'} ]} == jdoc @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any1", "password": "User@123", "real_name": "AJ", "description": "Nerd user"}, - {'user': {'userName': 'any1', 'userId': 3, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', + {'user': {'userName': 'any1', 'userId': 4, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', 'description': 'Nerd user'}, 'message': 'any1 user has been created successfully.'}), ({"username": "admin1", "password": "F0gl@mp!", "role_id": 1}, - {'user': {'userName': 'admin1', 'userId': 4, 'roleId': 1, 'accessMethod': 'any', 'realName': '', + {'user': {'userName': 'admin1', 'userId': 5, 'roleId': 1, 'accessMethod': 'any', 'realName': '', 'description': ''}, 'message': 'admin1 user has been created successfully.'}), ({"username": "bogus", "password": "Fl3dG$", "role_id": 2}, - {'user': {'userName': 'bogus', 'userId': 5, 'roleId': 2, 'accessMethod': 'any', 'realName': '', + {'user': {'userName': 'bogus', 'userId': 6, 'roleId': 2, 'accessMethod': 'any', 'realName': '', 'description': ''}, 'message': 'bogus user has been created successfully.'}), ({"username": "view", "password": "V!3w@1", "role_id": 3, "real_name": "View", "description": "Only to view the configuration"}, {'user': { - 'userName': 'view', 'userId': 6, 'roleId': 3, 'accessMethod': 'any', 'realName': 'View', + 'userName': 'view', 'userId': 7, 'roleId': 3, 'accessMethod': 'any', 'realName': 'View', 'description': 'Only to view the configuration'}, 'message': 'view user has been created successfully.'}), ({"username": "dataView", "password": "DV!3w@1", "role_id": 4, "real_name": "DataView", "description": "Only read the data in buffer"}, {'user': { - 'userName': 'dataview', 'userId': 7, 'roleId': 4, 'accessMethod': 'any', 'realName': 'DataView', + 'userName': 'dataview', 'userId': 8, 'roleId': 4, 'accessMethod': 'any', 'realName': 'DataView', 'description': 'Only read the data in buffer'}, 'message': 'dataview user has been created successfully.'} ), ({"username": "control", "password": "C0ntrol!", "role_id": 5, "real_name": "Control", "description": "Same as editor can do and also have access for control scripts and pipelines"}, {'user': { - 'userName': 'control', 'userId': 8, 'roleId': 5, 'accessMethod': 'any', 'realName': 'Control', + 'userName': 'control', 'userId': 9, 'roleId': 5, 'accessMethod': 'any', 'realName': 'Control', 'description': 'Same as editor can do and also have access for control scripts and pipelines'}, 'message': 'control user has been created successfully.'}) ]) @@ -142,7 +147,7 @@ def test_create_user(self, fledge_url, form_data, expected_values): assert expected_values == jdoc def test_update_password(self, fledge_url): - uid = 3 + uid = 4 payload = {"current_password": "User@123", "new_password": "F0gl@mp1"} conn = http.client.HTTPConnection(fledge_url) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(payload), @@ -154,7 +159,7 @@ def test_update_password(self, fledge_url): assert {'message': 'Password has been updated successfully for user ID:<{}>.'.format(uid)} == jdoc def test_update_user(self, fledge_url): - uid = 5 + uid = 6 conn = http.client.HTTPConnection(fledge_url) payload = {"real_name": "Test Real", "description": "Test Desc", "access_method": "pwd"} conn.request("PUT", "/fledge/admin/{}".format(uid), body=json.dumps(payload), @@ -188,7 +193,7 @@ def test_update_me(self, fledge_url): assert payload['real_name'] == jdoc['realName'] def test_enable_user(self, fledge_url): - uid = 5 + uid = 6 # Fetch users list conn = http.client.HTTPConnection(fledge_url) conn.request("GET", "/fledge/user", headers={"authorization": TOKEN}) @@ -219,7 +224,7 @@ def test_enable_user(self, fledge_url): assert uid not in user_list def test_reset_user(self, fledge_url): - uid = 3 + uid = 4 payload = {"role_id": 1, "password": "F0gl@mp!"} conn = http.client.HTTPConnection(fledge_url) conn.request("PUT", "/fledge/admin/{}/reset".format(uid), body=json.dumps(payload), @@ -238,10 +243,10 @@ def test_create_user_cert(self, fledge_url, storage_plugin): assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) - user = jdoc["users"][6] + user = jdoc["users"][7] if storage_plugin == 'postgres': - user = jdoc["users"][4] - assert 8 == user["userId"] + user = jdoc["users"][5] + assert 9 == user["userId"] assert "control" == user["userName"] # Generate an Authentication Certificate for the control user. @@ -260,10 +265,10 @@ def test_create_user_cert(self, fledge_url, storage_plugin): assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) - user = jdoc["users"][6] + user = jdoc["users"][7] if storage_plugin == 'postgres': - user = jdoc["users"][4] - assert 8 == user["userId"] + user = jdoc["users"][5] + assert 9 == user["userId"] assert "control" == user["userName"] # Log in using the newly created certificate above @@ -283,7 +288,7 @@ def test_create_user_cert(self, fledge_url, storage_plugin): def test_delete_user(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("DELETE", "/fledge/admin/4/delete", headers={"authorization": TOKEN}) + conn.request("DELETE", "/fledge/admin/5/delete", headers={"authorization": TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() diff --git a/tests/system/python/api/test_endpoints_with_different_user_types.py b/tests/system/python/api/test_endpoints_with_different_user_types.py index 2fc389c104..4885de96d8 100644 --- a/tests/system/python/api/test_endpoints_with_different_user_types.py +++ b/tests/system/python/api/test_endpoints_with_different_user_types.py @@ -105,13 +105,13 @@ def test_login(self, fledge_url, wait_time): # health ("GET", "/fledge/health/storage", 200), ("GET", "/fledge/health/logging", 200), # user & roles - ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=3", 200), ("GET", "/fledge/user?id=2", 403), + ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=4", 200), ("GET", "/fledge/user?id=2", 403), ("GET", "/fledge/user?username={}".format(VIEW_USERNAME), 200), ("GET", "/fledge/user?username={}".format(CONTROL_USERNAME), 403), - ("GET", "/fledge/user?id={}&username={}".format(3, VIEW_USERNAME), 200), - ("GET", "/fledge/user?username={}&id={}".format(VIEW_USERNAME, 3), 200), + ("GET", "/fledge/user?id={}&username={}".format(4, VIEW_USERNAME), 200), + ("GET", "/fledge/user?username={}&id={}".format(VIEW_USERNAME, 4), 200), ("GET", "/fledge/user?username=admin&id=1", 403), - ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/3/password", 500), + ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/4/password", 500), ("GET", "/fledge/user/role", 403), # auth ("POST", "/fledge/login", 403), ("PUT", "/fledge/31/logout", 401), @@ -275,12 +275,12 @@ def test_login(self, fledge_url, wait_time): # health ("GET", "/fledge/health/storage", 403), ("GET", "/fledge/health/logging", 403), # user & roles - ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=4", 200), ("GET", "/fledge/user?id=1", 403), + ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=5", 200), ("GET", "/fledge/user?id=1", 403), ("GET", "/fledge/user?username={}".format(DATA_VIEW_USERNAME), 200), ("GET", "/fledge/user?username=user", 403), - ("GET", "/fledge/user?id={}&username={}".format(4, DATA_VIEW_USERNAME), 200), + ("GET", "/fledge/user?id={}&username={}".format(5, DATA_VIEW_USERNAME), 200), ("GET", "/fledge/user?id=1&username=admin", 403), - ("GET", "/fledge/user?username={}&id={}".format(DATA_VIEW_USERNAME, 4), 200), - ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/4/password", 500), + ("GET", "/fledge/user?username={}&id={}".format(DATA_VIEW_USERNAME, 5), 200), + ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/5/password", 500), ("GET", "/fledge/user/role", 403), # auth ("POST", "/fledge/login", 403), ("PUT", "/fledge/31/logout", 401), @@ -443,13 +443,13 @@ def test_login(self, fledge_url, wait_time): # health ("GET", "/fledge/health/storage", 200), ("GET", "/fledge/health/logging", 200), # user & roles - ("GET", "/fledge/user", 200), ("GET", "/fledge/user?id=5", 200), ("GET", "/fledge/user?id=1", 200), + ("GET", "/fledge/user", 200), ("GET", "/fledge/user?id=6", 200), ("GET", "/fledge/user?id=1", 200), ("GET", "/fledge/user?username={}".format(CONTROL_USERNAME), 200), ("GET", "/fledge/user?username=admin", 200), - ("GET", "/fledge/user?id={}&username={}".format(5, CONTROL_USERNAME), 200), - ("GET", "/fledge/user?username={}&id={}".format(CONTROL_USERNAME, 5), 200), - ("GET", "/fledge/user?username={}&id={}".format(VIEW_USERNAME, 3), 200), - ("GET", "/fledge/user?id={}&username={}".format(4, DATA_VIEW_USERNAME), 200), - ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 401), ("PUT", "/fledge/user/5/password", 500), + ("GET", "/fledge/user?id={}&username={}".format(6, CONTROL_USERNAME), 200), + ("GET", "/fledge/user?username={}&id={}".format(CONTROL_USERNAME, 6), 200), + ("GET", "/fledge/user?username={}&id={}".format(VIEW_USERNAME, 4), 200), + ("GET", "/fledge/user?id={}&username={}".format(5, DATA_VIEW_USERNAME), 200), + ("PUT", "/fledge/user", 500), ("PUT", "/fledge/user/1/password", 401), ("PUT", "/fledge/user/6/password", 500), ("GET", "/fledge/user/role", 403), # auth ("POST", "/fledge/login", 400), ("PUT", "/fledge/31/logout", 401), @@ -592,3 +592,173 @@ def test_logout_me(self, fledge_url): r = r.read().decode() jdoc = json.loads(r) assert jdoc['logout'] + +class TestAPIEndpointsWithSystemctlUserType: + def test_login(self, fledge_url, wait_time): + import os + time.sleep(wait_time * 2) + conn = http.client.HTTPConnection(fledge_url) + cert_file_path = os.path.join(os.path.expandvars('${FLEDGE_ROOT}'), 'data/etc/certs/systemctl.cert') + with open(cert_file_path, 'r') as f: + conn.request("POST", "/fledge/login", body=f) + r = conn.getresponse() + assert 200 == r.status + r = r.read().decode() + jdoc = json.loads(r) + assert "Logged in successfully." == jdoc['message'] + assert "token" in jdoc + assert not jdoc['admin'] + global TOKEN + TOKEN = jdoc["token"] + + @pytest.mark.parametrize(("method", "route_path", "http_status_code"), [ + # common + ("GET", "/fledge/ping", 200), # ("PUT", "/fledge/shutdown", 200), + # health + ("GET", "/fledge/health/storage", 200), ("GET", "/fledge/health/logging", 200), + # user & roles + ("GET", "/fledge/user", 403), ("GET", "/fledge/user?id=3", 200), ("GET", "/fledge/user?id=1", 403), + ("GET", "/fledge/user?username={}".format(CONTROL_USERNAME), 403), ("GET", "/fledge/user?username=admin", 403), + ("GET", "/fledge/user?id={}&username={}".format(6, CONTROL_USERNAME), 403), + ("GET", "/fledge/user?username={}&id={}".format(CONTROL_USERNAME, 6), 403), + ("GET", "/fledge/user?username={}&id={}".format(VIEW_USERNAME, 4), 403), + ("GET", "/fledge/user?id={}&username={}".format(5, DATA_VIEW_USERNAME), 403), + ("PUT", "/fledge/user", 403), ("PUT", "/fledge/user/1/password", 403), ("PUT", "/fledge/user/6/password", 403), + ("GET", "/fledge/user/role", 403), + # auth + ("GET", "/fledge/auth/ott", 403), # login and logout tests separately + # admin + ("POST", "/fledge/admin/user", 403), ("DELETE", "/fledge/admin/3/delete", 403), ("PUT", "/fledge/admin/3", 403), + ("PUT", "/fledge/admin/3/enable", 403), ("PUT", "/fledge/admin/3/reset", 403), + ("POST", "/fledge/admin/3/authcertificate", 403), + # category + ("GET", "/fledge/category", 403), ("POST", "/fledge/category", 403), ("GET", "/fledge/category/General", 403), + ("PUT", "/fledge/category/General", 403), ("DELETE", "/fledge/category/General", 403), + ("POST", "/fledge/category/General/children", 403), ("GET", "/fledge/category/General/children", 403), + ("DELETE", "/fledge/category/General/children/Advanced", 403), + ("DELETE", "/fledge/category/General/parent", 403), + ("GET", "/fledge/category/rest_api/allowPing", 403), ("PUT", "/fledge/category/rest_api/allowPing", 403), + ("DELETE", "/fledge/category/rest_api/allowPing/value", 403), + ("POST", "/fledge/category/rest_api/allowPing/upload", 403), + # schedule processes & schedules + ("GET", "/fledge/schedule/process", 403), ("POST", "/fledge/schedule/process", 403), + ("GET", "/fledge/schedule/process/purge", 403), + ("GET", "/fledge/schedule", 403), ("POST", "/fledge/schedule", 403), ("GET", "/fledge/schedule/type", 403), + ("GET", "/fledge/schedule/2176eb68-7303-11e7-8cf7-a6006ad3dba0", 403), + ("PUT", "/fledge/schedule/2176eb68-7303-11e7-8cf7-a6006ad3dba0/enable", 403), + ("PUT", "/fledge/schedule/2176eb68-7303-11e7-8cf7-a6006ad3dba0/disable", 403), + ("PUT", "/fledge/schedule/enable", 403), ("PUT", "/fledge/schedule/disable", 403), + ("POST", "/fledge/schedule/start/2176eb68-7303-11e7-8cf7-a6006ad3dba0", 403), + ("PUT", "/fledge/schedule/2176eb68-7303-11e7-8cf7-a6006ad3dba0", 403), + ("DELETE", "/fledge/schedule/d1631422-9ec6-11e7-abc4-cec278b6b50a", 403), + # tasks + ("GET", "/fledge/task", 403), ("GET", "/fledge/task/state", 403), ("GET", "/fledge/task/latest", 403), + ("GET", "/fledge/task/123", 403), ("PUT", "/fledge/task/123/cancel", 403), + ("POST", "/fledge/scheduled/task", 403), ("DELETE", "/fledge/scheduled/task/blah", 403), + # service + ("POST", "/fledge/service", 403), ("GET", "/fledge/service", 200), ("DELETE", "/fledge/service/blah", 403), + # ("GET", "/fledge/service/available", 200), -- checked manually and commented out only to avoid apt-update + ("GET", "/fledge/service/installed", 403), + ("PUT", "/fledge/service/Southbound/blah/update", 403), ("POST", "/fledge/service/blah/otp", 403), + # south & north + ("GET", "/fledge/south", 403), ("GET", "/fledge/north", 403), + # asset browse + ("GET", "/fledge/asset", 403), ("GET", "/fledge/asset/sinusoid", 403), + ("GET", "/fledge/asset/sinusoid/latest", 403), + ("GET", "/fledge/asset/sinusoid/summary", 403), ("GET", "/fledge/asset/sinusoid/sinusoid", 403), + ("GET", "/fledge/asset/sinusoid/sinusoid/summary", 403), ("GET", "/fledge/asset/sinusoid/sinusoid/series", 403), + ("GET", "/fledge/asset/sinusoid/bucket/1", 403), ("GET", "/fledge/asset/sinusoid/sinusoid/bucket/1", 403), + ("GET", "/fledge/structure/asset", 403), ("DELETE", "/fledge/asset", 403), + ("DELETE", "/fledge/asset/sinusoid", 403), + # asset tracker + ("GET", "/fledge/track", 403), ("GET", "/fledge/track/storage/assets", 403), + ("PUT", "/fledge/track/service/foo/asset/bar/event/Ingest", 403), + # statistics + ("GET", "/fledge/statistics", 403), ("GET", "/fledge/statistics/history", 403), + ("GET", "/fledge/statistics/rate?periods=1&statistics=FOO", 403), + # audit trail + ("POST", "/fledge/audit", 403), ("GET", "/fledge/audit", 403), ("GET", "/fledge/audit?source=SRVFL", 200), + ("GET", "/fledge/audit/logcode", 403), ("GET", "/fledge/audit/severity", 403), + # backup & restore + ("GET", "/fledge/backup", 403), ("POST", "/fledge/backup", 403), + ("POST", "/fledge/backup/upload", 403), + ("GET", "/fledge/backup/status", 403), ("GET", "/fledge/backup/123", 403), + ("DELETE", "/fledge/backup/123", 403), ("GET", "/fledge/backup/123/download", 403), + ("PUT", "/fledge/backup/123/restore", 403), + # package update + ("GET", "/fledge/update", 403), ("PUT", "/fledge/update", 403), + # certs store + ("GET", "/fledge/certificate", 403), ("POST", "/fledge/certificate", 403), + ("DELETE", "/fledge/certificate/user", 403), + # support bundle + ("GET", "/fledge/support", 403), ("GET", "/fledge/support/foo", 403), ("POST", "/fledge/support", 403), + # syslogs & package logs + ("GET", "/fledge/syslog", 403), ("GET", "/fledge/package/log", 403), ("GET", "/fledge/package/log/foo", 403), + ("GET", "/fledge/package/install/status", 403), + # plugins + ("GET", "/fledge/plugins/installed", 403), ("GET", "/fledge/plugins/available", 403), + ("PUT", "/fledge/plugins/south/sinusoid/update", 403), ("DELETE", "/fledge/plugins/south/sinusoid", 403), + ("POST", "/fledge/plugins", 403), ("GET", "/fledge/service/foo/persist", 403), + ("GET", "/fledge/service/foo/plugin/omf/data", 403), ("POST", "/fledge/service/foo/plugin/omf/data", 403), + ("DELETE", "/fledge/service/foo/plugin/omf/data", 403), + # filters + ("POST", "/fledge/filter", 403), ("PUT", "/fledge/filter/foo/pipeline", 403), + ("GET", "/fledge/filter/foo/pipeline", 403), ("GET", "/fledge/filter/bar", 403), ("GET", "/fledge/filter", 403), + ("DELETE", "/fledge/filter/foo/pipeline", 403), ("DELETE", "/fledge/filter/bar", 403), + # snapshots + ("GET", "/fledge/snapshot/plugins", 403), ("POST", "/fledge/snapshot/plugins", 403), + ("PUT", "/fledge/snapshot/plugins/1", 403), ("DELETE", "/fledge/snapshot/plugins/1", 403), + ("GET", "/fledge/snapshot/category", 403), ("POST", "/fledge/snapshot/category", 403), + ("PUT", "/fledge/snapshot/category/1", 403), ("DELETE", "/fledge/snapshot/category/1", 403), + ("GET", "/fledge/snapshot/schedule", 403), ("POST", "/fledge/snapshot/schedule", 403), + ("PUT", "/fledge/snapshot/schedule/1", 403), ("DELETE", "/fledge/snapshot/schedule/1", 403), + # repository + ("POST", "/fledge/repository", 403), + # ACL + ("POST", "/fledge/ACL", 403), ("GET", "/fledge/ACL", 403), ("GET", "/fledge/ACL/foo", 403), + ("PUT", "/fledge/ACL/foo", 403), ("DELETE", "/fledge/ACL/foo", 403), ("PUT", "/fledge/service/foo/ACL", 403), + ("DELETE", "/fledge/service/foo/ACL", 403), + # control script + ("POST", "/fledge/control/script", 403), ("GET", "/fledge/control/script", 403), + ("GET", "/fledge/control/script/foo", 403), ("PUT", "/fledge/control/script/foo", 403), + ("DELETE", "/fledge/control/script/foo", 403), ("POST", "/fledge/control/script/foo/schedule", 403), + # control pipeline + ("POST", "/fledge/control/pipeline", 403), ("GET", "/fledge/control/lookup", 403), + ("GET", "/fledge/control/pipeline", 403), ("GET", "/fledge/control/pipeline/1", 403), + ("PUT", "/fledge/control/pipeline/1", 403), ("DELETE", "/fledge/control/pipeline/1", 403), + # python packages + ("GET", "/fledge/python/packages", 403), ("POST", "/fledge/python/package", 403), + # notification + ("GET", "/fledge/notification", 403), ("GET", "/fledge/notification/plugin", 403), + ("GET", "/fledge/notification/type", 403), ("GET", "/fledge/notification/N1", 403), + ("POST", "/fledge/notification", 403), ("PUT", "/fledge/notification/N1", 403), + ("DELETE", "/fledge/notification/N1", 403), ("GET", "/fledge/notification/N1/delivery", 403), + ("POST", "/fledge/notification/N1/delivery", 403), ("GET", "/fledge/notification/N1/delivery/C1", 403), + ("DELETE", "/fledge/notification/N1/delivery/C1", 403), + # performance monitors + ("GET", "/fledge/monitors", 403), ("GET", "/fledge/monitors/SVC", 403), + ("GET", "/fledge/monitors/Svc/Counter", 403), ("DELETE", "/fledge/monitors", 403), + ("DELETE", "/fledge/monitors/SVC", 403), ("DELETE", "/fledge/monitors/Svc/Counter", 403), + # alerts + ("GET", "/fledge/alert", 403), ("DELETE", "/fledge/alert", 403), ("DELETE", "/fledge/alert/blah", 403), + # pipeline debugger + ("GET", "/fledge/service/name/debug?action=state", 403), + ("GET", "/fledge/service/name/debug?action=buffer", 403), + ("PUT", "/fledge/service/name/debug?action=buffer", 403), + ("PUT", "/fledge/service/{name}/debug?action=attach", 403) + ]) + def test_endpoints(self, fledge_url, method, route_path, http_status_code, storage_plugin): + conn = http.client.HTTPConnection(fledge_url) + conn.request(method, route_path, headers={"authorization": TOKEN}) + r = conn.getresponse() + assert http_status_code == r.status + r.read().decode() + + def test_logout_me(self, fledge_url): + conn = http.client.HTTPConnection(fledge_url) + conn.request("PUT", '/fledge/logout', headers={"authorization": TOKEN}) + r = conn.getresponse() + assert 200 == r.status + r = r.read().decode() + jdoc = json.loads(r) + assert jdoc['logout'] diff --git a/tests/system/python/api/test_passwords.py b/tests/system/python/api/test_passwords.py index 25dd69224c..70bb75fce7 100644 --- a/tests/system/python/api/test_passwords.py +++ b/tests/system/python/api/test_passwords.py @@ -79,7 +79,7 @@ def test_create_user(self, fledge_url, payload): assert '{} user has been created successfully.'.format(payload['username']) == jdoc['message'] def test_update_password(self, fledge_url): - uid = 4 + uid = 5 payload = {"current_password": "password", "new_password": "0123456"} conn = http.client.HTTPConnection(fledge_url) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(payload), @@ -113,7 +113,7 @@ def test_create_user(self, fledge_url, payload): assert '{} user has been created successfully.'.format(payload['username']) == jdoc['message'] def test_update_password(self, fledge_url): - uid = 6 + uid = 7 payload = {"current_password": "Passw0rd", "new_password": "13pAss1"} conn = http.client.HTTPConnection(fledge_url) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(payload), @@ -147,7 +147,7 @@ def test_create_user(self, fledge_url, payload): assert '{} user has been created successfully.'.format(payload['username']) == jdoc['message'] def test_update_password(self, fledge_url): - uid = 11 + uid = 12 payload = {"current_password": "paSSw0rd", "new_password": "13pAss1"} conn = http.client.HTTPConnection(fledge_url) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(payload), @@ -181,7 +181,7 @@ def test_create_user(self, fledge_url, payload): assert '{} user has been created successfully.'.format(payload['username']) == jdoc['message'] def test_update_password(self, fledge_url): - uid = 17 + uid = 18 payload = {"current_password": "Fl@3737", "new_password": "pAss@!1"} conn = http.client.HTTPConnection(fledge_url) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(payload), diff --git a/tests/system/python/packages/test_authentication.py b/tests/system/python/packages/test_authentication.py index f84ce217aa..cd06e9c272 100644 --- a/tests/system/python/packages/test_authentication.py +++ b/tests/system/python/packages/test_authentication.py @@ -44,7 +44,9 @@ {'id': 3, 'name': 'view', 'description': 'Only to view the configuration'}, {'id': 4, 'name': 'data-view', 'description': 'Only read the data in buffer'}, {'id': 5, 'name': 'control', - 'description': 'Same as editor can do and also have access for control scripts and pipelines'} + 'description': 'Same as editor can do and also have access for control scripts and pipelines'}, + {'id': 6, 'name': 'systemctl', 'description': + 'It solely facilitates the execution of commands initiated by the fledge script'} ]} @@ -411,7 +413,10 @@ def test_ping_with_allow_ping_false_with_certificate_token(self, fledge_url): ('', {'users': [{'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', 'realName': 'Admin user', 'description': 'admin user'}, {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', - 'description': 'normal user'}]}), + 'description': 'normal user'}, + {'userId': 3, 'userName': 'systemctl', 'roleId': 6, 'accessMethod': 'cert', + 'realName': 'Systemctl user', 'description': 'User used by the systemctl scripts'} + ]}), ('?id=2', {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', 'description': 'normal user'}), ('?username=admin', {'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', @@ -432,7 +437,10 @@ def test_get_users_with_password_token(self, fledge_url, query, expected_values) ('', {'users': [{'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', 'realName': 'Admin user', 'description': 'admin user'}, {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', - 'description': 'normal user'}]}), + 'description': 'normal user'}, + {'userId': 3, 'userName': 'systemctl', 'roleId': 6, 'accessMethod': 'cert', + 'realName': 'Systemctl user', 'description': 'User used by the systemctl scripts'} + ]}), ('?id=2', {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', 'description': 'normal user'}), ('?username=admin', {'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', @@ -469,10 +477,10 @@ def test_get_roles_with_certificate_token(self, fledge_url): @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any1", "password": "User@123", "real_name": "AJ", "description": "Nerd user"}, - {'user': {'userName': 'any1', 'userId': 3, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', + {'user': {'userName': 'any1', 'userId': 4, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', 'description': 'Nerd user'}, 'message': 'any1 user has been created successfully.'}), ({"username": "admin1", "password": "F0gl@mp!", "role_id": 1}, - {'user': {'userName': 'admin1', 'userId': 4, 'roleId': 1, 'accessMethod': 'any', 'realName': '', + {'user': {'userName': 'admin1', 'userId': 5, 'roleId': 1, 'accessMethod': 'any', 'realName': '', 'description': ''}, 'message': 'admin1 user has been created successfully.'}) ]) def test_create_user_with_password_token(self, fledge_url, form_data, expected_values): @@ -487,10 +495,10 @@ def test_create_user_with_password_token(self, fledge_url, form_data, expected_v @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any2", "password": "User@123", "real_name": "PG", "description": "Nerd user"}, - {'user': {'userName': 'any2', 'userId': 5, 'roleId': 2, 'accessMethod': 'any', 'realName': 'PG', + {'user': {'userName': 'any2', 'userId': 6, 'roleId': 2, 'accessMethod': 'any', 'realName': 'PG', 'description': 'Nerd user'}, 'message': 'any2 user has been created successfully.'}), ({"username": "admin2", "password": "F0gl@mp!", "role_id": 1}, - {'user': {'userName': 'admin2', 'userId': 6, 'roleId': 1, 'accessMethod': 'any', 'realName': '', + {'user': {'userName': 'admin2', 'userId': 7, 'roleId': 1, 'accessMethod': 'any', 'realName': '', 'description': ''}, 'message': 'admin2 user has been created successfully.'}) ]) def test_create_user_with_certificate_token(self, fledge_url, form_data, expected_values): @@ -519,7 +527,7 @@ def test_login_of_newly_created_user(self, fledge_url, form_data, expected_value assert expected_values == jdoc['message'] def test_update_password_with_password_token(self, fledge_url): - uid = 3 + uid = 4 data = {"current_password": "User@123", "new_password": "F0gl@mp1"} conn = http.client.HTTPConnection(fledge_url) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(data), @@ -531,7 +539,7 @@ def test_update_password_with_password_token(self, fledge_url): assert {'message': 'Password has been updated successfully for user ID:<{}>.'.format(uid)} == jdoc def test_update_password_with_certificate_token(self, fledge_url): - uid = 5 + uid = 6 data = {"current_password": "User@123", "new_password": "F0gl@mp2"} conn = http.client.HTTPConnection(fledge_url) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(data), @@ -557,23 +565,23 @@ def test_login_with_updated_password(self, fledge_url, form_data, expected_value def test_reset_user_with_password_token(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("PUT", "/fledge/admin/3/reset", body=json.dumps({"role_id": 1, "password": "F0gl@mp!#1"}), - headers={"authorization": PASSWORD_TOKEN}) + conn.request("PUT", "/fledge/admin/4/reset", body=json.dumps( + {"role_id": 1, "password": "F0gl@mp!#1"}), headers={"authorization": PASSWORD_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) - assert {'message': 'User with ID:<3> has been updated successfully.'} == jdoc + assert {'message': 'User with ID:<4> has been updated successfully.'} == jdoc def test_reset_user_with_certificate_token(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("PUT", "/fledge/admin/5/reset", body=json.dumps({"role_id": 1, "password": "F0gl@mp!#2"}), - headers={"authorization": CERT_TOKEN}) + conn.request("PUT", "/fledge/admin/6/reset", body=json.dumps( + {"role_id": 1, "password": "F0gl@mp!#2"}), headers={"authorization": CERT_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) - assert {'message': 'User with ID:<5> has been updated successfully.'} == jdoc + assert {'message': 'User with ID:<6> has been updated successfully.'} == jdoc @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any1", "password": "F0gl@mp!#1"}, LOGIN_SUCCESS_MSG), @@ -590,7 +598,7 @@ def test_login_with_resetted_password(self, fledge_url, form_data, expected_valu def test_delete_user_with_password_token(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("DELETE", "/fledge/admin/4/delete", headers={"authorization": PASSWORD_TOKEN}) + conn.request("DELETE", "/fledge/admin/5/delete", headers={"authorization": PASSWORD_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() @@ -599,7 +607,7 @@ def test_delete_user_with_password_token(self, fledge_url): def test_delete_user_with_certificate_token(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("DELETE", "/fledge/admin/6/delete", headers={"authorization": CERT_TOKEN}) + conn.request("DELETE", "/fledge/admin/7/delete", headers={"authorization": CERT_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() @@ -645,8 +653,8 @@ def test_admin_actions_forbidden_for_regular_user_with_pwd_token(self, fledge_ur _token = jdoc["token"] # Create User - conn.request("POST", "/fledge/admin/user", body=json.dumps({"username": "other", "password": "User@123"}), - headers={"authorization": _token}) + conn.request("POST", "/fledge/admin/user", body=json.dumps( + {"username": "other", "password": "User@123"}), headers={"authorization": _token}) r = conn.getresponse() assert 403 == r.status r = r.read().decode() @@ -682,8 +690,8 @@ def test_admin_actions_forbidden_for_regular_user_with_cert_token(self, fledge_u _token = jdoc["token"] # Create User - conn.request("POST", "/fledge/admin/user", body=json.dumps({"username": "other", "password": "User@123"}), - headers={"authorization": _token}) + conn.request("POST", "/fledge/admin/user", body=json.dumps( + {"username": "other", "password": "User@123"}), headers={"authorization": _token}) r = conn.getresponse() assert 403 == r.status r = r.read().decode() @@ -822,7 +830,10 @@ def test_ping_with_allow_ping_false(self, fledge_url): ('', {'users': [{'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', 'realName': 'Admin user', 'description': 'admin user'}, {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', - 'description': 'normal user'}]}), + 'description': 'normal user'}, + {'userId': 3, 'userName': 'systemctl', 'roleId': 6, 'accessMethod': 'cert', + 'realName': 'Systemctl user', 'description': 'User used by the systemctl scripts'} + ]}), ('?id=2', {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', 'description': 'normal user'}), ('?username=admin', {'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', @@ -850,10 +861,10 @@ def test_get_roles(self, fledge_url): @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any1", "password": "User@123", "real_name": "AJ", "description": "Nerd user"}, - {'user': {'userName': 'any1', 'userId': 3, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', + {'user': {'userName': 'any1', 'userId': 4, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', 'description': 'Nerd user'}, 'message': 'any1 user has been created successfully.'}), ({"username": "admin1", "password": "F0gl@mp!", "role_id": 1}, - {'user': {'userName': 'admin1', 'userId': 4, 'roleId': 1, 'accessMethod': 'any', 'realName': '', + {'user': {'userName': 'admin1', 'userId': 5, 'roleId': 1, 'accessMethod': 'any', 'realName': '', 'description': ''}, 'message': 'admin1 user has been created successfully.'}) ]) def test_create_user(self, fledge_url, form_data, expected_values): @@ -880,7 +891,7 @@ def test_login_of_newly_created_user(self, fledge_url, form_data, expected_value assert expected_values == jdoc['message'] def test_update_password(self, fledge_url): - uid = 3 + uid = 4 data = {"current_password": "User@123", "new_password": "F0gl@mp1"} conn = http.client.HTTPConnection(fledge_url) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(data), @@ -902,13 +913,13 @@ def test_login_with_updated_password(self, fledge_url): def test_reset_user(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("PUT", "/fledge/admin/3/reset", body=json.dumps({"role_id": 1, "password": "F0gl@mp!"}), + conn.request("PUT", "/fledge/admin/4/reset", body=json.dumps({"role_id": 1, "password": "F0gl@mp!"}), headers={"authorization": PASSWORD_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) - assert {'message': 'User with ID:<3> has been updated successfully.'} == jdoc + assert {'message': 'User with ID:<4> has been updated successfully.'} == jdoc def test_login_with_resetted_password(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) @@ -921,7 +932,7 @@ def test_login_with_resetted_password(self, fledge_url): def test_delete_user(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("DELETE", "/fledge/admin/4/delete", headers={"authorization": PASSWORD_TOKEN}) + conn.request("DELETE", "/fledge/admin/5/delete", headers={"authorization": PASSWORD_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() @@ -930,7 +941,8 @@ def test_delete_user(self, fledge_url): def test_login_of_deleted_user(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("POST", "/fledge/login", body=json.dumps({"username": "admin1", "password": "F0gl@mp!"})) + conn.request("POST", "/fledge/login", body=json.dumps( + {"username": "admin1", "password": "F0gl@mp!"})) r = conn.getresponse() assert 404 == r.status assert "User does not exist" == r.reason @@ -963,7 +975,8 @@ def test_admin_actions_forbidden_for_regular_user(self, fledge_url): _token = jdoc["token"] # Create User - conn.request("POST", "/fledge/admin/user", body=json.dumps({"username": "other", "password": "User@123"}), + conn.request("POST", "/fledge/admin/user", body=json.dumps( + {"username": "other", "password": "User@123"}), headers={"authorization": _token}) r = conn.getresponse() assert 403 == r.status @@ -1155,10 +1168,10 @@ def test_get_roles(self, fledge_url): @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any1", "password": "User@123", "real_name": "AJ", "description": "Nerd user"}, - {'user': {'userName': 'any1', 'userId': 3, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', + {'user': {'userName': 'any1', 'userId': 4, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', 'description': 'Nerd user'}, 'message': 'any1 user has been created successfully.'}), ({"username": "admin1", "password": "F0gl@mp!", "role_id": 1}, - {'user': {'userName': 'admin1', 'userId': 4, 'roleId': 1, 'accessMethod': 'any', 'realName': '', + {'user': {'userName': 'admin1', 'userId': 5, 'roleId': 1, 'accessMethod': 'any', 'realName': '', 'description': ''}, 'message': 'admin1 user has been created successfully.'}) ]) def test_create_user(self, fledge_url, form_data, expected_values): @@ -1172,7 +1185,7 @@ def test_create_user(self, fledge_url, form_data, expected_values): assert expected_values == jdoc def test_update_password(self, fledge_url): - uid = 3 + uid = 4 data = {"current_password": "User@123", "new_password": "F0gl@mp1"} conn = http.client.HTTPConnection(fledge_url) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(data), @@ -1185,17 +1198,17 @@ def test_update_password(self, fledge_url): def test_reset_user(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("PUT", "/fledge/admin/3/reset", body=json.dumps({"role_id": 1, "password": "F0gl@mp!"}), + conn.request("PUT", "/fledge/admin/4/reset", body=json.dumps({"role_id": 1, "password": "F0gl@mp!"}), headers={"authorization": CERT_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) - assert {'message': 'User with ID:<3> has been updated successfully.'} == jdoc + assert {'message': 'User with ID:<4> has been updated successfully.'} == jdoc def test_delete_user(self, fledge_url): conn = http.client.HTTPConnection(fledge_url) - conn.request("DELETE", "/fledge/admin/4/delete", headers={"authorization": CERT_TOKEN}) + conn.request("DELETE", "/fledge/admin/5/delete", headers={"authorization": CERT_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() @@ -1232,8 +1245,8 @@ def test_admin_actions_forbidden_for_regular_user(self, fledge_url): _token = jdoc["token"] # Create User - conn.request("POST", "/fledge/admin/user", body=json.dumps({"username": "other", "password": "User@123"}), - headers={"authorization": _token}) + conn.request("POST", "/fledge/admin/user", body=json.dumps( + {"username": "other", "password": "User@123"}), headers={"authorization": _token}) r = conn.getresponse() assert 403 == r.status r = r.read().decode() @@ -1540,7 +1553,10 @@ def test_ping_with_allow_ping_false_with_certificate_token(self): ('', {'users': [{'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', 'realName': 'Admin user', 'description': 'admin user'}, {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', - 'description': 'normal user'}]}), + 'description': 'normal user'}, + {'userId': 3, 'userName': 'systemctl', 'roleId': 6, 'accessMethod': 'cert', + 'realName': 'Systemctl user', 'description': 'User used by the systemctl scripts'} + ]}), ('?id=2', {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', 'description': 'normal user'}), ('?username=admin', {'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', @@ -1561,7 +1577,10 @@ def test_get_users_with_password_token(self, query, expected_values): ('', {'users': [{'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', 'realName': 'Admin user', 'description': 'admin user'}, {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', - 'description': 'normal user'}]}), + 'description': 'normal user'}, + {'userId': 3, 'userName': 'systemctl', 'roleId': 6, 'accessMethod': 'cert', + 'realName': 'Systemctl user', 'description': 'User used by the systemctl scripts'} + ]}), ('?id=2', {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', 'description': 'normal user'}), ('?username=admin', {'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', @@ -1598,10 +1617,10 @@ def test_get_roles_with_certificate_token(self): @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any1", "password": "User@123", "real_name": "AJ", "description": "Nerd user"}, - {'user': {'userName': 'any1', 'userId': 3, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', + {'user': {'userName': 'any1', 'userId': 4, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', 'description': 'Nerd user'}, 'message': 'any1 user has been created successfully.'}), ({"username": "admin1", "password": "F0gl@mp!", "role_id": 1}, - {'user': {'userName': 'admin1', 'userId': 4, 'roleId': 1, 'accessMethod': 'any', 'realName': '', + {'user': {'userName': 'admin1', 'userId': 5, 'roleId': 1, 'accessMethod': 'any', 'realName': '', 'description': ''}, 'message': 'admin1 user has been created successfully.'}) ]) def test_create_user_with_password_token(self, form_data, expected_values): @@ -1616,10 +1635,10 @@ def test_create_user_with_password_token(self, form_data, expected_values): @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any2", "password": "User@123", "real_name": "PG", "description": "Nerd user"}, - {'user': {'userName': 'any2', 'userId': 5, 'roleId': 2, 'accessMethod': 'any', 'realName': 'PG', + {'user': {'userName': 'any2', 'userId': 6, 'roleId': 2, 'accessMethod': 'any', 'realName': 'PG', 'description': 'Nerd user'}, 'message': 'any2 user has been created successfully.'}), ({"username": "admin2", "password": "F0gl@mp!", "role_id": 1}, - {'user': {'userName': 'admin2', 'userId': 6, 'roleId': 1, 'accessMethod': 'any', 'realName': '', + {'user': {'userName': 'admin2', 'userId': 7, 'roleId': 1, 'accessMethod': 'any', 'realName': '', 'description': ''}, 'message': 'admin2 user has been created successfully.'}) ]) def test_create_user_with_certificate_token(self, form_data, expected_values): @@ -1648,7 +1667,7 @@ def test_login_of_newly_created_user(self, form_data, expected_values): assert expected_values == jdoc['message'] def test_update_password_with_password_token(self): - uid = 3 + uid = 4 data = {"current_password": "User@123", "new_password": "F0gl@mp1"} conn = http.client.HTTPSConnection("localhost", 1995, context=context) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(data), @@ -1660,7 +1679,7 @@ def test_update_password_with_password_token(self): assert {'message': 'Password has been updated successfully for user ID:<{}>.'.format(uid)} == jdoc def test_update_password_with_certificate_token(self): - uid = 5 + uid = 6 data = {"current_password": "User@123", "new_password": "F0gl@mp2"} conn = http.client.HTTPSConnection("localhost", 1995, context=context) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(data), @@ -1686,23 +1705,23 @@ def test_login_with_updated_password(self, form_data, expected_values): def test_reset_user_with_password_token(self): conn = http.client.HTTPSConnection("localhost", 1995, context=context) - conn.request("PUT", "/fledge/admin/3/reset", body=json.dumps({"role_id": 1, "password": "F0gl@mp!#1"}), - headers={"authorization": PASSWORD_TOKEN}) + conn.request("PUT", "/fledge/admin/4/reset", body=json.dumps( + {"role_id": 1, "password": "F0gl@mp!#1"}), headers={"authorization": PASSWORD_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) - assert {'message': 'User with ID:<3> has been updated successfully.'} == jdoc + assert {'message': 'User with ID:<4> has been updated successfully.'} == jdoc def test_reset_user_with_certificate_token(self): conn = http.client.HTTPSConnection("localhost", 1995, context=context) - conn.request("PUT", "/fledge/admin/5/reset", body=json.dumps({"role_id": 1, "password": "F0gl@mp!#2"}), - headers={"authorization": CERT_TOKEN}) + conn.request("PUT", "/fledge/admin/6/reset", body=json.dumps( + {"role_id": 1, "password": "F0gl@mp!#2"}), headers={"authorization": CERT_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) - assert {'message': 'User with ID:<5> has been updated successfully.'} == jdoc + assert {'message': 'User with ID:<6> has been updated successfully.'} == jdoc @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any1", "password": "F0gl@mp!#1"}, LOGIN_SUCCESS_MSG), @@ -1719,7 +1738,7 @@ def test_login_with_resetted_password(self, form_data, expected_values): def test_delete_user_with_password_token(self): conn = http.client.HTTPSConnection("localhost", 1995, context=context) - conn.request("DELETE", "/fledge/admin/4/delete", headers={"authorization": PASSWORD_TOKEN}) + conn.request("DELETE", "/fledge/admin/5/delete", headers={"authorization": PASSWORD_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() @@ -1728,7 +1747,7 @@ def test_delete_user_with_password_token(self): def test_delete_user_with_certificate_token(self): conn = http.client.HTTPSConnection("localhost", 1995, context=context) - conn.request("DELETE", "/fledge/admin/6/delete", headers={"authorization": CERT_TOKEN}) + conn.request("DELETE", "/fledge/admin/7/delete", headers={"authorization": CERT_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() @@ -1811,8 +1830,8 @@ def test_admin_actions_forbidden_for_regular_user_with_cert_token(self): _token = jdoc["token"] # Create User - conn.request("POST", "/fledge/admin/user", body=json.dumps({"username": "other", "password": "User@123"}), - headers={"authorization": _token}) + conn.request("POST", "/fledge/admin/user", body=json.dumps( + {"username": "other", "password": "User@123"}), headers={"authorization": _token}) r = conn.getresponse() assert 403 == r.status r = r.read().decode() @@ -1951,7 +1970,10 @@ def test_ping_with_allow_ping_false(self): ('', {'users': [{'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', 'realName': 'Admin user', 'description': 'admin user'}, {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', - 'description': 'normal user'}]}), + 'description': 'normal user'}, + {'userId': 3, 'userName': 'systemctl', 'roleId': 6, 'accessMethod': 'cert', + 'realName': 'Systemctl user', 'description': 'User used by the systemctl scripts'} + ]}), ('?id=2', {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', 'description': 'normal user'}), ('?username=admin', {'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', @@ -1979,10 +2001,10 @@ def test_get_roles(self): @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any1", "password": "User@123", "real_name": "AJ", "description": "Nerd user"}, - {'user': {'userName': 'any1', 'userId': 3, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', + {'user': {'userName': 'any1', 'userId': 4, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', 'description': 'Nerd user'}, 'message': 'any1 user has been created successfully.'}), ({"username": "admin1", "password": "F0gl@mp!", "role_id": 1}, - {'user': {'userName': 'admin1', 'userId': 4, 'roleId': 1, 'accessMethod': 'any', 'realName': '', + {'user': {'userName': 'admin1', 'userId': 5, 'roleId': 1, 'accessMethod': 'any', 'realName': '', 'description': ''}, 'message': 'admin1 user has been created successfully.'}) ]) def test_create_user(self, form_data, expected_values): @@ -2009,7 +2031,7 @@ def test_login_of_newly_created_user(self, form_data, expected_values): assert expected_values == jdoc['message'] def test_update_password(self): - uid = 3 + uid = 4 data = {"current_password": "User@123", "new_password": "F0gl@mp1"} conn = http.client.HTTPSConnection("localhost", 1995, context=context) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(data), @@ -2031,13 +2053,13 @@ def test_login_with_updated_password(self): def test_reset_user(self): conn = http.client.HTTPSConnection("localhost", 1995, context=context) - conn.request("PUT", "/fledge/admin/3/reset", body=json.dumps({"role_id": 1, "password": "F0gl@mp!"}), - headers={"authorization": PASSWORD_TOKEN}) + conn.request("PUT", "/fledge/admin/4/reset", body=json.dumps( + {"role_id": 1, "password": "F0gl@mp!"}), headers={"authorization": PASSWORD_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) - assert {'message': 'User with ID:<3> has been updated successfully.'} == jdoc + assert {'message': 'User with ID:<4> has been updated successfully.'} == jdoc def test_login_with_resetted_password(self): conn = http.client.HTTPSConnection("localhost", 1995, context=context) @@ -2050,7 +2072,7 @@ def test_login_with_resetted_password(self): def test_delete_user(self): conn = http.client.HTTPSConnection("localhost", 1995, context=context) - conn.request("DELETE", "/fledge/admin/4/delete", headers={"authorization": PASSWORD_TOKEN}) + conn.request("DELETE", "/fledge/admin/5/delete", headers={"authorization": PASSWORD_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() @@ -2059,7 +2081,8 @@ def test_delete_user(self): def test_login_of_deleted_user(self): conn = http.client.HTTPSConnection("localhost", 1995, context=context) - conn.request("POST", "/fledge/login", body=json.dumps({"username": "admin1", "password": "F0gl@mp!"})) + conn.request("POST", "/fledge/login", body=json.dumps( + {"username": "admin1", "password": "F0gl@mp!"})) r = conn.getresponse() assert 404 == r.status assert "User does not exist" == r.reason @@ -2092,8 +2115,8 @@ def test_admin_actions_forbidden_for_regular_user(self): _token = jdoc["token"] # Create User - conn.request("POST", "/fledge/admin/user", body=json.dumps({"username": "other", "password": "User@123"}), - headers={"authorization": _token}) + conn.request("POST", "/fledge/admin/user", body=json.dumps( + {"username": "other", "password": "User@123"}), headers={"authorization": _token}) r = conn.getresponse() assert 403 == r.status r = r.read().decode() @@ -2261,7 +2284,10 @@ def test_ping_with_allow_ping_false(self): ('', {'users': [{'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', 'realName': 'Admin user', 'description': 'admin user'}, {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', - 'description': 'normal user'}]}), + 'description': 'normal user'}, + {'userId': 3, 'userName': 'systemctl', 'roleId': 6, 'accessMethod': 'cert', + 'realName': 'Systemctl user', 'description': 'User used by the systemctl scripts'} + ]}), ('?id=2', {'userId': 2, 'roleId': 2, 'userName': 'user', 'accessMethod': 'any', 'realName': 'Normal user', 'description': 'normal user'}), ('?username=admin', {'userId': 1, 'roleId': 1, 'userName': 'admin', 'accessMethod': 'any', @@ -2289,10 +2315,10 @@ def test_get_roles(self): @pytest.mark.parametrize(("form_data", "expected_values"), [ ({"username": "any1", "password": "User@123", "real_name": "AJ", "description": "Nerd user"}, - {'user': {'userName': 'any1', 'userId': 3, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', + {'user': {'userName': 'any1', 'userId': 4, 'roleId': 2, 'accessMethod': 'any', 'realName': 'AJ', 'description': 'Nerd user'}, 'message': 'any1 user has been created successfully.'}), ({"username": "admin1", "password": "F0gl@mp!", "role_id": 1}, - {'user': {'userName': 'admin1', 'userId': 4, 'roleId': 1, 'accessMethod': 'any', 'realName': '', + {'user': {'userName': 'admin1', 'userId': 5, 'roleId': 1, 'accessMethod': 'any', 'realName': '', 'description': ''}, 'message': 'admin1 user has been created successfully.'}) ]) def test_create_user(self, form_data, expected_values): @@ -2306,7 +2332,7 @@ def test_create_user(self, form_data, expected_values): assert expected_values == jdoc def test_update_password(self): - uid = 3 + uid = 4 data = {"current_password": "User@123", "new_password": "F0gl@mp1"} conn = http.client.HTTPSConnection("localhost", 1995, context=context) conn.request("PUT", "/fledge/user/{}/password".format(uid), body=json.dumps(data), @@ -2319,17 +2345,17 @@ def test_update_password(self): def test_reset_user(self): conn = http.client.HTTPSConnection("localhost", 1995, context=context) - conn.request("PUT", "/fledge/admin/3/reset", body=json.dumps({"role_id": 1, "password": "F0gl@mp!"}), - headers={"authorization": CERT_TOKEN}) + conn.request("PUT", "/fledge/admin/4/reset", body=json.dumps( + {"role_id": 1, "password": "F0gl@mp!"}), headers={"authorization": CERT_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() jdoc = json.loads(r) - assert {'message': 'User with ID:<3> has been updated successfully.'} == jdoc + assert {'message': 'User with ID:<4> has been updated successfully.'} == jdoc def test_delete_user(self): conn = http.client.HTTPSConnection("localhost", 1995, context=context) - conn.request("DELETE", "/fledge/admin/4/delete", headers={"authorization": CERT_TOKEN}) + conn.request("DELETE", "/fledge/admin/5/delete", headers={"authorization": CERT_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() @@ -2366,8 +2392,8 @@ def test_admin_actions_forbidden_for_regular_user(self): _token = jdoc["token"] # Create User - conn.request("POST", "/fledge/admin/user", body=json.dumps({"username": "other", "password": "User@123"}), - headers={"authorization": _token}) + conn.request("POST", "/fledge/admin/user", body=json.dumps( + {"username": "other", "password": "User@123"}), headers={"authorization": _token}) r = conn.getresponse() assert 403 == r.status r = r.read().decode()