From 157f100045f36c07aec926e492fd0244ae36c335 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Thu, 13 Mar 2025 22:40:08 +0000 Subject: [PATCH 01/22] FOGL-1499 Use certificate for systemctl authentication Signed-off-by: Mark Riddoch --- Makefile | 1 + VERSION | 2 +- docs/quick_start/starting.rst | 23 ++++++++++++++-- docs/securing_fledge.rst | 10 +++++++ extras/scripts/fledge.service | 5 ++++ scripts/fledge | 32 +++++++++++++++++------ scripts/plugins/storage/postgres/init.sql | 2 ++ scripts/plugins/storage/sqlite/init.sql | 2 ++ scripts/plugins/storage/sqlitelb/init.sql | 2 ++ 9 files changed, 68 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 4332fd5297..32d3d43fc5 100644 --- a/Makefile +++ b/Makefile @@ -258,6 +258,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 db6170b32c..95481a0993 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ fledge_version=2.6.0 -fledge_schema=75 +fledge_schema=76 diff --git a/docs/quick_start/starting.rst b/docs/quick_start/starting.rst index c4f27c18ae..12159b6c6d 100644 --- a/docs/quick_start/starting.rst +++ b/docs/quick_start/starting.rst @@ -18,13 +18,15 @@ 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* with the plain text version of the password. +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* with the plain text version of the password. .. code-block:: console @@ -38,3 +40,20 @@ In both cases the user will be prompted to enter a password. It is possible, but The *start*, *status* and *help* commands do not require authentication. 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 two lines, the first line is the username to use to login, the second line is the password for that user. + +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 + +.. note:: + + In older versions of Fledge the *auth* file was simply called *~/.fledge*. If the older *.fledge* file exists it will still be used. diff --git a/docs/securing_fledge.rst b/docs/securing_fledge.rst index e0d4018466..7d7bace858 100644 --- a/docs/securing_fledge.rst +++ b/docs/securing_fledge.rst @@ -222,6 +222,16 @@ 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. +Fledge Management Script Login +------------------------------ + +Fledge supports a number of mechanisms that for user login process when using the *fledge* script to manage Fledge startup, shutdown and other operations. + + - If no action is taken the user name and password prompts will be presented to the user when running the *fledge* script. + + - The user name set one or both of the FLEDGE_USER and FLEDGE_PASSWORD environment variables to automate the entry of the username or password. + + - The user name create an authentication file that contains the username and password User Management =============== diff --git a/extras/scripts/fledge.service b/extras/scripts/fledge.service index 4f79f14d73..a4b0267056 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 @@ -69,6 +70,8 @@ get_pid() { fledge_start() { if [ "$IS_RHEL" = "" ]; then sudo -u ${FLEDGE_USER} "${FLEDGE_ROOT}/bin/fledge" start > /dev/null + elif [ -f "$FLEDGE_CERT" ]; then + "${FLEDGE_ROOT}/bin/fledge" -c "${FLEDGE_CERT}" start > /dev/null else "${FLEDGE_ROOT}/bin/fledge" start > /dev/null fi @@ -77,6 +80,8 @@ fledge_start() { fledge_stop() { if [ "$IS_RHEL" = "" ]; then sudo -u ${FLEDGE_USER} "${FLEDGE_ROOT}/bin/fledge" stop > /dev/null + elif [ -f "$FLEDGE_CERT" ]; then + "${FLEDGE_ROOT}/bin/fledge" -c "${FLEDGE_CERT}" stop > /dev/null else "${FLEDGE_ROOT}/bin/fledge" stop > /dev/null fi diff --git a/scripts/fledge b/scripts/fledge index 08ab84cda2..726b8290fa 100755 --- a/scripts/fledge +++ b/scripts/fledge @@ -445,8 +445,8 @@ fledge_authenticate() { fd=0 if [[ -t "$fd" ]]; then # We have an interactive shell - if [ -z ${USERNAME+x} ]; then - read -p "Username: " USERNAME + if [ -z ${FLUSERNAME+x} ]; then + read -p "Username: " FLUSERNAME fi if [ -z ${PASSWORD+x} ]; then read -s -p "Password: " PASSWORD @@ -456,7 +456,7 @@ fledge_authenticate() { # Get/Updates the rest API URL get_rest_api_url - payload='{ "username" : "'${USERNAME}'", "password" : "'${PASSWORD}'" }' + payload='{ "username" : "'${FLUSERNAME}'", "password" : "'${PASSWORD}'" }' result=`curl -X POST -k -s ${REST_API_URL}/fledge/login -d"$payload" || true` if [[ ! "$result" =~ "Logged in successfully" ]]; then echo "failed" @@ -808,7 +808,7 @@ get_rest_api_url while getopts "u:p:" option; do case "$option" in u) - USERNAME=${OPTARG} + FLUSERNAME=${OPTARG} ;; p) PASSWORD=${OPTARG} @@ -817,10 +817,10 @@ while getopts "u:p:" 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 @@ -836,8 +836,8 @@ if [ -f ~/.fledge ] ; then # 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` + if [ -z ${FLUSERNAME+x} ]; then + FLUSERNAME=`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` @@ -845,6 +845,22 @@ if [ -f ~/.fledge ] ; then fi fi +if [ -d ~/.fledge ] ; then + if [ -f ~/.fledge/auth ]; then + # if ~/.fledge is mode 0600 then fetch username and password + # from it if they are not already set + perm=`stat -c %A ~/.fledge/auth` + if [ "$perm" == "-rw-------" ]; then + if [ -z ${FLUSERNAME+x} ]; then + FLUSERNAME=`awk -F: 'NR==1{ print $1 }' < ~/.fledge/auth | cut -d"=" -f2` + fi + if [ -z ${PASSWORD+x} ]; then + PASSWORD=`awk -F: 'NR==2{ print $1 }' < ~/.fledge/auth | cut -d"=" -f2` + fi + fi + fi +fi + SAFE_MODE='' # Handle commands case "$1" in diff --git a/scripts/plugins/storage/postgres/init.sql b/scripts/plugins/storage/postgres/init.sql index a8ebf87503..0e9d81126d 100644 --- a/scripts/plugins/storage/postgres/init.sql +++ b/scripts/plugins/storage/postgres/init.sql @@ -984,6 +984,8 @@ 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 ('systemctl', 'Systemctl user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'User used by the systemctl scripts', 'cert'); -- User password history DELETE FROM fledge.user_pwd_history; diff --git a/scripts/plugins/storage/sqlite/init.sql b/scripts/plugins/storage/sqlite/init.sql index 6735a41551..500f4e12f3 100644 --- a/scripts/plugins/storage/sqlite/init.sql +++ b/scripts/plugins/storage/sqlite/init.sql @@ -741,6 +741,8 @@ 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 ('systemctl', 'Systemctl user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'User used by the systemctl scripts', 'cert'); -- User password history DELETE FROM fledge.user_pwd_history; diff --git a/scripts/plugins/storage/sqlitelb/init.sql b/scripts/plugins/storage/sqlitelb/init.sql index 96c84ffd3a..a48ce56fad 100644 --- a/scripts/plugins/storage/sqlitelb/init.sql +++ b/scripts/plugins/storage/sqlitelb/init.sql @@ -741,6 +741,8 @@ 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 ('systemctl', 'Systemctl user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'User used by the systemctl scripts', 'cert'); -- User password history DELETE FROM fledge.user_pwd_history; From 3ca41949edbb86332f39c2820f6ee453a2eddf8b Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Fri, 14 Mar 2025 12:36:47 +0000 Subject: [PATCH 02/22] FOGL-1499 Update docs Signed-off-by: Mark Riddoch --- docs/securing_fledge.rst | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/docs/securing_fledge.rst b/docs/securing_fledge.rst index 7d7bace858..f2ee09a6e9 100644 --- a/docs/securing_fledge.rst +++ b/docs/securing_fledge.rst @@ -222,16 +222,25 @@ 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. -Fledge Management Script Login ------------------------------- +Management Script Login +----------------------- -Fledge supports a number of mechanisms that for user login process when using the *fledge* script to manage Fledge startup, shutdown and other operations. +Fledge supports a number of mechanisms for user authentication when using the *fledge* script to manage startup, shutdown and other operations. - - If no action is taken the user name and password prompts will be presented to the user when running the *fledge* script. + - The default authentication mechanism will prompt the entry of user name and password interactively. - - The user name 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 set one or both of the FLEDGE_USER and FLEDGE_PASSWORD environment variables to automate the entry of the username or password. + + - The user name pass either or both of the *-u* and *-p* flags to set the username and password on the command line. + + - The user name 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 the the 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 password. In the case of the authentication file, the file permissions must be such that only the Linux user is able to read the file. - - The user name create an authentication file that contains the username and password User Management =============== @@ -352,12 +361,16 @@ 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. -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 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. a) Create a new certificate for username. Let say **test** @@ -368,9 +381,9 @@ a) Create a new certificate for username. Let say **test** Here script arguments are: $1=user $2=FLEDGE_USERNAME $3=SSL_DAYS_EXPIRATION -And now you can find **test** cert inside $FLEDGE_DATA/etc/certs/ +And now you can find a **test** certificate inside the $FLEDGE_DATA/etc/certs/ directory. -b) Now, it's time to create user with name **test** (case sensitive). Also only admin can create user. Below are the cURL Commands +b) Now, it's time to create user with name **test** (case sensitive). Also only admin role users can create users in Fledge. Below are the cURL Commands .. code-block:: console @@ -385,7 +398,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 From 1538b4e8324f93b0db09d9ab5e6ed7f9de30a509 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Mon, 17 Mar 2025 09:07:29 +0000 Subject: [PATCH 03/22] Address review comments Signed-off-by: Mark Riddoch --- docs/quick_start/starting.rst | 8 ++++++ docs/securing_fledge.rst | 8 ++++-- scripts/fledge | 53 ++++++++++++++++++----------------- 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/docs/quick_start/starting.rst b/docs/quick_start/starting.rst index 12159b6c6d..40a8afbf35 100644 --- a/docs/quick_start/starting.rst +++ b/docs/quick_start/starting.rst @@ -57,3 +57,11 @@ The *auth* file will only be read if the permissions on that file are set such t .. note:: 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 + + admin + fledge + diff --git a/docs/securing_fledge.rst b/docs/securing_fledge.rst index f2ee09a6e9..94b10dc3a2 100644 --- a/docs/securing_fledge.rst +++ b/docs/securing_fledge.rst @@ -231,15 +231,17 @@ Fledge supports a number of mechanisms for user authentication when using the *f - 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 name pass either or both of the *-u* and *-p* flags to set the username and password on the command line. + - 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 name pass the *-c* flag and a path to a certificate file that is used to authenticate the user. + - 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 the the 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 password. In the case of the authentication file, the file permissions must be such that only the Linux user is able to read the file. + 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 password. 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 diff --git a/scripts/fledge b/scripts/fledge index 726b8290fa..d2d23caa57 100755 --- a/scripts/fledge +++ b/scripts/fledge @@ -777,6 +777,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 @@ -830,35 +849,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 ${FLUSERNAME+x} ]; then - FLUSERNAME=`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 ] ; then - if [ -f ~/.fledge/auth ]; then - # if ~/.fledge is mode 0600 then fetch username and password - # from it if they are not already set - perm=`stat -c %A ~/.fledge/auth` - if [ "$perm" == "-rw-------" ]; then - if [ -z ${FLUSERNAME+x} ]; then - FLUSERNAME=`awk -F: 'NR==1{ print $1 }' < ~/.fledge/auth | cut -d"=" -f2` - fi - if [ -z ${PASSWORD+x} ]; then - PASSWORD=`awk -F: 'NR==2{ print $1 }' < ~/.fledge/auth | cut -d"=" -f2` - fi - fi - fi +if [ -d ~/.fledge ] && [ -f ~/.fledge/auth ] ; then + extract_credentials "~/.fledge/auth" fi SAFE_MODE='' From ec907560aac47d4a9d1dbb93a5e7ea5f948c06c1 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Tue, 18 Mar 2025 15:30:20 +0000 Subject: [PATCH 04/22] Updates after review comments Signed-off-by: Mark Riddoch --- docs/quick_start/starting.rst | 12 ++++++------ scripts/fledge | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/quick_start/starting.rst b/docs/quick_start/starting.rst index 40a8afbf35..17b6ecf568 100644 --- a/docs/quick_start/starting.rst +++ b/docs/quick_start/starting.rst @@ -48,13 +48,13 @@ The prompting for username and password when using the *fledge* script can be by The file created should be called *auth* and contains two lines, the first line is the username to use to login, the second line is the password for that user. -The *auth* file will only be read if the permissions on that file are set such that only the owner can read the file. +.. note:: -.. code-block:: console + The *auth* file will only be read if the permissions on that file are set such that only the owner can read the file. - $ chmod 600 ~/.fledge/auth + .. code-block:: console -.. note:: + $ 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. @@ -62,6 +62,6 @@ An example *auth* file, using the default username and password would be as foll .. code-block:: console - admin - fledge + FLEDGE_USER=admin + FLEDGE_PASSWORD=fledge diff --git a/scripts/fledge b/scripts/fledge index d2d23caa57..a05ce3c29a 100755 --- a/scripts/fledge +++ b/scripts/fledge @@ -778,7 +778,7 @@ fledge_healthcheck() { } extract_credentials() { - file = $1 + 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"` @@ -857,11 +857,11 @@ if [ -z ${CERT+x} ]; then fi if [ -f ~/.fledge ] ; then - extract_credentials "~/.fledge" + extract_credentials ~/.fledge fi if [ -d ~/.fledge ] && [ -f ~/.fledge/auth ] ; then - extract_credentials "~/.fledge/auth" + extract_credentials ~/.fledge/auth fi SAFE_MODE='' From ac7961f4867413df9d7c84829b15a036543f7ca8 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Wed, 26 Mar 2025 14:28:17 +0000 Subject: [PATCH 05/22] Minor doc updates Signed-off-by: Mark Riddoch --- docs/securing_fledge.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/securing_fledge.rst b/docs/securing_fledge.rst index 94b10dc3a2..3b21a61d03 100644 --- a/docs/securing_fledge.rst +++ b/docs/securing_fledge.rst @@ -239,7 +239,7 @@ Fledge supports a number of mechanisms for user authentication when using the *f .. 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 password. In the case of the authentication file, the file permissions must be such that only the Linux user is able to read the file. + 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. @@ -370,7 +370,7 @@ A default certificate authority (CA) certificate is available inside $FLEDGE_DAT .. note:: - The systemctl.cert certificate is used by the Linux systemctl scripts to start, stop and monitor the state of the Fledge instance and should not be removed. + 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 certificates along with existing Fledge based CA signed for authentication certificates. From eb119227776e528a0ad3cb925bf19b295f70b178 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Wed, 2 Apr 2025 09:27:27 +0000 Subject: [PATCH 06/22] Updates following merge of FOGL-9639 Signed-off-by: Mark Riddoch --- docs/quick_start/starting.rst | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/quick_start/starting.rst b/docs/quick_start/starting.rst index 51abc61ad8..5ecb0821a3 100644 --- a/docs/quick_start/starting.rst +++ b/docs/quick_start/starting.rst @@ -26,6 +26,10 @@ If authentication is enabled, which is the default mode for Fledge version 3.0 o - If neither of the above are done the user will be prompted to enter a user name. +.. 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 @@ -49,7 +53,7 @@ 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. @@ -58,7 +62,7 @@ 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 two lines, the first line is the username to use to login, the second line is the password for that user. +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:: @@ -77,3 +81,16 @@ An example *auth* file, using the default username and password would be as foll 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. + + From 6b6a25786c38894665f563f5d8767a2974c4feeb Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Tue, 8 Apr 2025 14:58:55 +0000 Subject: [PATCH 07/22] Fix for non-RedHat systems Signed-off-by: Mark Riddoch --- extras/scripts/fledge.service | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extras/scripts/fledge.service b/extras/scripts/fledge.service index a4b0267056..ef471033b1 100755 --- a/extras/scripts/fledge.service +++ b/extras/scripts/fledge.service @@ -79,7 +79,11 @@ 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 From b4165ef8759b151c424bd026f87e522d8c862db1 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Tue, 8 Apr 2025 15:36:18 +0000 Subject: [PATCH 08/22] Add for start as well - not strickly required Signed-off-by: Mark Riddoch --- extras/scripts/fledge.service | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/extras/scripts/fledge.service b/extras/scripts/fledge.service index ef471033b1..e3fa418bb6 100755 --- a/extras/scripts/fledge.service +++ b/extras/scripts/fledge.service @@ -69,8 +69,12 @@ get_pid() { fledge_start() { if [ "$IS_RHEL" = "" ]; then - sudo -u ${FLEDGE_USER} "${FLEDGE_ROOT}/bin/fledge" start > /dev/null - elif [ -f "$FLEDGE_CERT" ]; then + 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 @@ -84,7 +88,7 @@ fledge_stop() { else sudo -u ${FLEDGE_USER} "${FLEDGE_ROOT}/bin/fledge" stop > /dev/null fi - elif [ -f "$FLEDGE_CERT" ]; then + elif [ -f "${FLEDGE_CERT}" ]; then "${FLEDGE_ROOT}/bin/fledge" -c "${FLEDGE_CERT}" stop > /dev/null else "${FLEDGE_ROOT}/bin/fledge" stop > /dev/null From 5d32a71502c90777cee416a166455860256009e3 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 24 Apr 2025 11:27:48 +0530 Subject: [PATCH 09/22] systemctl role addition along with schema changes Signed-off-by: ashish-jabble --- VERSION | 2 +- scripts/plugins/storage/postgres/init.sql | 5 +++-- scripts/plugins/storage/sqlite/init.sql | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 6d2d676d77..3bd5dff86f 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ fledge_version=3.0.0 -fledge_schema=76 +fledge_schema=77 diff --git a/scripts/plugins/storage/postgres/init.sql b/scripts/plugins/storage/postgres/init.sql index 0e9d81126d..164d132745 100644 --- a/scripts/plugins/storage/postgres/init.sql +++ b/scripts/plugins/storage/postgres/init.sql @@ -976,7 +976,8 @@ 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 @@ -985,7 +986,7 @@ 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 ('systemctl', 'Systemctl user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'User used by the systemctl scripts', 'cert'); + VALUES ('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/init.sql b/scripts/plugins/storage/sqlite/init.sql index 500f4e12f3..bc0bee89a1 100644 --- a/scripts/plugins/storage/sqlite/init.sql +++ b/scripts/plugins/storage/sqlite/init.sql @@ -734,7 +734,8 @@ 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; @@ -742,7 +743,7 @@ 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 ('systemctl', 'Systemctl user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'User used by the systemctl scripts', 'cert'); + VALUES ('systemctl', 'Systemctl user', '', 6, 'User used by the systemctl scripts', 'cert'); -- User password history DELETE FROM fledge.user_pwd_history; From 889ba2a9013cdd30df936b2839e8acfa06841f3c Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 24 Apr 2025 11:28:31 +0530 Subject: [PATCH 10/22] endpoints restriction as per systemctl user role Signed-off-by: ashish-jabble --- python/fledge/common/web/middleware.py | 31 +++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/python/fledge/common/web/middleware.py b/python/fledge/common/web/middleware.py index 04dbfeceb3..6bcc24a5ba 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 From bf3906458c625eec515f3dd7bca1a21294ac649d Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 24 Apr 2025 11:29:20 +0530 Subject: [PATCH 11/22] Restricted admin when we add user with systemctl role Signed-off-by: ashish-jabble --- python/fledge/services/core/api/auth.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/fledge/services/core/api/auth.py b/python/fledge/services/core/api/auth.py index 0c3dd50c81..e6a78a55ab 100644 --- a/python/fledge/services/core/api/auth.py +++ b/python/fledge/services/core/api/auth.py @@ -425,6 +425,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: From ab84b278d3af0f591ee126820de4a161262d85d6 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 24 Apr 2025 17:18:33 +0530 Subject: [PATCH 12/22] Authentication API tests updated Signed-off-by: ashish-jabble --- .../system/python/api/test_authentication.py | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/tests/system/python/api/test_authentication.py b/tests/system/python/api/test_authentication.py index ea2179ab26..b8d49beb12 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() From a48dc013a28adebc534de1c900c52a8b29307708 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 24 Apr 2025 17:19:12 +0530 Subject: [PATCH 13/22] Package based authetication tests updated Signed-off-by: ashish-jabble --- .../python/packages/test_authentication.py | 164 ++++++++++-------- 1 file changed, 95 insertions(+), 69 deletions(-) diff --git a/tests/system/python/packages/test_authentication.py b/tests/system/python/packages/test_authentication.py index b22ee49f07..88938fa0b9 100644 --- a/tests/system/python/packages/test_authentication.py +++ b/tests/system/python/packages/test_authentication.py @@ -40,7 +40,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'} ]} @@ -447,7 +449,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', @@ -468,7 +473,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', @@ -505,10 +513,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): @@ -523,10 +531,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): @@ -555,7 +563,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), @@ -567,7 +575,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), @@ -593,8 +601,8 @@ 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() @@ -603,8 +611,8 @@ def test_reset_user_with_password_token(self, fledge_url): 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() @@ -626,7 +634,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() @@ -635,7 +643,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() @@ -681,8 +689,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() @@ -718,8 +726,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() @@ -858,7 +866,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', @@ -886,10 +897,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): @@ -916,7 +927,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), @@ -938,7 +949,7 @@ 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 @@ -957,7 +968,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() @@ -966,7 +977,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 @@ -999,7 +1011,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 @@ -1191,10 +1204,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): @@ -1208,7 +1221,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), @@ -1221,7 +1234,7 @@ 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 @@ -1231,7 +1244,7 @@ def test_reset_user(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": CERT_TOKEN}) + conn.request("DELETE", "/fledge/admin/5/delete", headers={"authorization": CERT_TOKEN}) r = conn.getresponse() assert 200 == r.status r = r.read().decode() @@ -1268,8 +1281,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() @@ -1542,7 +1555,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', @@ -1563,7 +1579,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', @@ -1600,10 +1619,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): @@ -1618,10 +1637,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): @@ -1650,7 +1669,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), @@ -1662,7 +1681,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), @@ -1688,8 +1707,8 @@ 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() @@ -1698,8 +1717,8 @@ def test_reset_user_with_password_token(self): 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() @@ -1721,7 +1740,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() @@ -1730,7 +1749,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() @@ -1813,8 +1832,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() @@ -1953,7 +1972,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', @@ -1981,10 +2003,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): @@ -2011,7 +2033,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), @@ -2033,8 +2055,8 @@ 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() @@ -2052,7 +2074,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() @@ -2061,7 +2083,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 @@ -2094,8 +2117,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() @@ -2263,7 +2286,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', @@ -2291,10 +2317,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): @@ -2308,7 +2334,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), @@ -2321,8 +2347,8 @@ 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() @@ -2331,7 +2357,7 @@ def test_reset_user(self): 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() @@ -2368,8 +2394,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() From 976bd848c58902c79eacfd53d53031558234c6b8 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Thu, 24 Apr 2025 17:19:58 +0530 Subject: [PATCH 14/22] test all API endpoints with various users based tests have been updated Signed-off-by: ashish-jabble --- ...est_endpoints_with_different_user_types.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) 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 bc511ffee0..8cd64ed932 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 @@ -110,13 +110,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), @@ -280,12 +280,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), @@ -448,13 +448,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", 500), ("PUT", "/fledge/31/logout", 401), From 5af6906184e335959554f506aa163baf5afcd5b7 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 25 Apr 2025 11:58:51 +0530 Subject: [PATCH 15/22] test all endpoints with systemctl user and other fixes in tests Signed-off-by: ashish-jabble --- ...est_endpoints_with_different_user_types.py | 170 ++++++++++++++++++ .../python/packages/test_authentication.py | 16 +- 2 files changed, 178 insertions(+), 8 deletions(-) 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 8cd64ed932..65edfc0aa8 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 @@ -597,3 +597,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/packages/test_authentication.py b/tests/system/python/packages/test_authentication.py index 88938fa0b9..8f691206cb 100644 --- a/tests/system/python/packages/test_authentication.py +++ b/tests/system/python/packages/test_authentication.py @@ -607,7 +607,7 @@ def test_reset_user_with_password_token(self, fledge_url): 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) @@ -617,7 +617,7 @@ def test_reset_user_with_certificate_token(self, fledge_url): 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), @@ -955,7 +955,7 @@ def test_reset_user(self, fledge_url): 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) @@ -1240,7 +1240,7 @@ def test_reset_user(self, fledge_url): 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) @@ -1713,7 +1713,7 @@ def test_reset_user_with_password_token(self): 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) @@ -1723,7 +1723,7 @@ def test_reset_user_with_certificate_token(self): 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), @@ -2061,7 +2061,7 @@ def test_reset_user(self): 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) @@ -2353,7 +2353,7 @@ def test_reset_user(self): 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) From df7f375ffb24e0d9317e7a819683edfde280ce9f Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 25 Apr 2025 14:35:07 +0530 Subject: [PATCH 16/22] schema 76 has introduced both user and role management through systemctl Signed-off-by: ashish-jabble --- VERSION | 2 +- scripts/plugins/storage/postgres/init.sql | 7 +++---- scripts/plugins/storage/sqlite/init.sql | 9 ++++----- scripts/plugins/storage/sqlitelb/init.sql | 12 ++++++------ 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/VERSION b/VERSION index 3bd5dff86f..6d2d676d77 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ fledge_version=3.0.0 -fledge_schema=77 +fledge_schema=76 diff --git a/scripts/plugins/storage/postgres/init.sql b/scripts/plugins/storage/postgres/init.sql index 164d132745..99ea7afa2d 100644 --- a/scripts/plugins/storage/postgres/init.sql +++ b/scripts/plugins/storage/postgres/init.sql @@ -982,11 +982,10 @@ INSERT INTO fledge.roles ( name, description ) -- 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 ('systemctl', 'Systemctl user', '', 6, 'User used by the systemctl scripts', 'cert'); + 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/init.sql b/scripts/plugins/storage/sqlite/init.sql index bc0bee89a1..48a0e037fd 100644 --- a/scripts/plugins/storage/sqlite/init.sql +++ b/scripts/plugins/storage/sqlite/init.sql @@ -739,11 +739,10 @@ INSERT INTO fledge.roles ( name, description ) -- 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 ('systemctl', 'Systemctl user', '', 6, 'User used by the systemctl scripts', 'cert'); +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/init.sql b/scripts/plugins/storage/sqlitelb/init.sql index a48ce56fad..375582ad28 100644 --- a/scripts/plugins/storage/sqlitelb/init.sql +++ b/scripts/plugins/storage/sqlitelb/init.sql @@ -734,15 +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 ('systemctl', 'Systemctl user', '495f7f5b17c534dbeabab3da2287a934b32ed6876568563b04c312be49e8773299243abd3881d13112ccfb67c4fb3ec8231406474810e1f6eb347d61c63785d4:672169c60df24b76b6b94e78cad800f8', 1, 'User used by the systemctl scripts', 'cert'); +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; From bfabf9a81653989691cb9d283c9f1197ffb15175 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 25 Apr 2025 14:39:50 +0530 Subject: [PATCH 17/22] upgrade and downgrade scripts for schema 76 Signed-off-by: ashish-jabble --- scripts/plugins/storage/postgres/downgrade/75.sql | 6 ++++++ scripts/plugins/storage/postgres/upgrade/76.sql | 7 +++++++ scripts/plugins/storage/sqlite/downgrade/75.sql | 6 ++++++ scripts/plugins/storage/sqlite/upgrade/76.sql | 6 ++++++ scripts/plugins/storage/sqlitelb/downgrade/75.sql | 6 ++++++ scripts/plugins/storage/sqlitelb/upgrade/76.sql | 6 ++++++ 6 files changed, 37 insertions(+) create mode 100644 scripts/plugins/storage/postgres/downgrade/75.sql create mode 100644 scripts/plugins/storage/postgres/upgrade/76.sql create mode 100644 scripts/plugins/storage/sqlite/downgrade/75.sql create mode 100644 scripts/plugins/storage/sqlite/upgrade/76.sql create mode 100644 scripts/plugins/storage/sqlitelb/downgrade/75.sql create mode 100644 scripts/plugins/storage/sqlitelb/upgrade/76.sql diff --git a/scripts/plugins/storage/postgres/downgrade/75.sql b/scripts/plugins/storage/postgres/downgrade/75.sql new file mode 100644 index 0000000000..224f6d1e05 --- /dev/null +++ b/scripts/plugins/storage/postgres/downgrade/75.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/upgrade/76.sql b/scripts/plugins/storage/postgres/upgrade/76.sql new file mode 100644 index 0000000000..24679f4822 --- /dev/null +++ b/scripts/plugins/storage/postgres/upgrade/76.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/75.sql b/scripts/plugins/storage/sqlite/downgrade/75.sql new file mode 100644 index 0000000000..75be0557f6 --- /dev/null +++ b/scripts/plugins/storage/sqlite/downgrade/75.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/upgrade/76.sql b/scripts/plugins/storage/sqlite/upgrade/76.sql new file mode 100644 index 0000000000..561592e941 --- /dev/null +++ b/scripts/plugins/storage/sqlite/upgrade/76.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/75.sql b/scripts/plugins/storage/sqlitelb/downgrade/75.sql new file mode 100644 index 0000000000..75be0557f6 --- /dev/null +++ b/scripts/plugins/storage/sqlitelb/downgrade/75.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/upgrade/76.sql b/scripts/plugins/storage/sqlitelb/upgrade/76.sql new file mode 100644 index 0000000000..561592e941 --- /dev/null +++ b/scripts/plugins/storage/sqlitelb/upgrade/76.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'); From 74c4920d214eb3da738a4538447430e35f919e06 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 25 Apr 2025 14:51:59 +0530 Subject: [PATCH 18/22] reset cert updates Signed-off-by: ashish-jabble --- scripts/fledge | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fledge b/scripts/fledge index 5ca7401c6e..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" From b3e7523525e5f4c1ffc91b400eaf252225cbab1d Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 25 Apr 2025 15:00:30 +0530 Subject: [PATCH 19/22] cert store API updated Signed-off-by: ashish-jabble --- python/fledge/services/core/api/certificate_store.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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})) From a771efdd1f35e72e29275a744d2df565a94c2f29 Mon Sep 17 00:00:00 2001 From: ashish-jabble Date: Fri, 25 Apr 2025 16:09:24 +0530 Subject: [PATCH 20/22] password API tests updated Signed-off-by: ashish-jabble --- tests/system/python/api/test_passwords.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/system/python/api/test_passwords.py b/tests/system/python/api/test_passwords.py index 4f0a758cfa..6e67a897af 100644 --- a/tests/system/python/api/test_passwords.py +++ b/tests/system/python/api/test_passwords.py @@ -84,7 +84,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), @@ -118,7 +118,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), @@ -151,7 +151,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), @@ -185,7 +185,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), From 9391f4a1826e99758128057e4e73170f4c23a2b3 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Wed, 20 Aug 2025 13:37:26 +0000 Subject: [PATCH 21/22] Resolve new database version; Signed-off-by: Mark Riddoch --- VERSION | 2 +- scripts/plugins/storage/postgres/downgrade/{75.sql => 76.sql} | 0 scripts/plugins/storage/postgres/upgrade/{76.sql => 77.sql} | 0 scripts/plugins/storage/sqlite/downgrade/{75.sql => 76.sql} | 0 scripts/plugins/storage/sqlite/upgrade/{76.sql => 77.sql} | 0 scripts/plugins/storage/sqlitelb/downgrade/{75.sql => 76.sql} | 0 scripts/plugins/storage/sqlitelb/upgrade/{76.sql => 77.sql} | 0 7 files changed, 1 insertion(+), 1 deletion(-) rename scripts/plugins/storage/postgres/downgrade/{75.sql => 76.sql} (100%) rename scripts/plugins/storage/postgres/upgrade/{76.sql => 77.sql} (100%) rename scripts/plugins/storage/sqlite/downgrade/{75.sql => 76.sql} (100%) rename scripts/plugins/storage/sqlite/upgrade/{76.sql => 77.sql} (100%) rename scripts/plugins/storage/sqlitelb/downgrade/{75.sql => 76.sql} (100%) rename scripts/plugins/storage/sqlitelb/upgrade/{76.sql => 77.sql} (100%) diff --git a/VERSION b/VERSION index 6d2d676d77..3bd5dff86f 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ fledge_version=3.0.0 -fledge_schema=76 +fledge_schema=77 diff --git a/scripts/plugins/storage/postgres/downgrade/75.sql b/scripts/plugins/storage/postgres/downgrade/76.sql similarity index 100% rename from scripts/plugins/storage/postgres/downgrade/75.sql rename to scripts/plugins/storage/postgres/downgrade/76.sql diff --git a/scripts/plugins/storage/postgres/upgrade/76.sql b/scripts/plugins/storage/postgres/upgrade/77.sql similarity index 100% rename from scripts/plugins/storage/postgres/upgrade/76.sql rename to scripts/plugins/storage/postgres/upgrade/77.sql diff --git a/scripts/plugins/storage/sqlite/downgrade/75.sql b/scripts/plugins/storage/sqlite/downgrade/76.sql similarity index 100% rename from scripts/plugins/storage/sqlite/downgrade/75.sql rename to scripts/plugins/storage/sqlite/downgrade/76.sql diff --git a/scripts/plugins/storage/sqlite/upgrade/76.sql b/scripts/plugins/storage/sqlite/upgrade/77.sql similarity index 100% rename from scripts/plugins/storage/sqlite/upgrade/76.sql rename to scripts/plugins/storage/sqlite/upgrade/77.sql diff --git a/scripts/plugins/storage/sqlitelb/downgrade/75.sql b/scripts/plugins/storage/sqlitelb/downgrade/76.sql similarity index 100% rename from scripts/plugins/storage/sqlitelb/downgrade/75.sql rename to scripts/plugins/storage/sqlitelb/downgrade/76.sql diff --git a/scripts/plugins/storage/sqlitelb/upgrade/76.sql b/scripts/plugins/storage/sqlitelb/upgrade/77.sql similarity index 100% rename from scripts/plugins/storage/sqlitelb/upgrade/76.sql rename to scripts/plugins/storage/sqlitelb/upgrade/77.sql From d702a6a6f2a42c7017fc337f59d1f7b9ad09bc21 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Thu, 2 Oct 2025 09:21:06 +0000 Subject: [PATCH 22/22] FOGL-1499 Add graceful shutdoen on SIGTERM and SIGINT reception and restart on SIGHUP. Update services to use signal method to shutdown to avoid authentication issues. Signed-off-by: Mark Riddoch --- extras/scripts/fledge.service | 16 +++++- python/fledge/services/core/server.py | 80 +++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/extras/scripts/fledge.service b/extras/scripts/fledge.service index e3fa418bb6..cb270caa4b 100755 --- a/extras/scripts/fledge.service +++ b/extras/scripts/fledge.service @@ -152,7 +152,7 @@ case "$1" in rm -f $PID_FILE exit 1 else - fledge_stop + kill -TERM $PID echo "Fledge stopped [$PID]" fi fi @@ -161,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/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