diff --git a/include/util/taoserror.h b/include/util/taoserror.h index f8991fb99208..70c6ca0b7575 100644 --- a/include/util/taoserror.h +++ b/include/util/taoserror.h @@ -244,6 +244,7 @@ int32_t taosGetErrSize(); #define TSDB_CODE_TSC_SESS_MAX_CALL_VNODE_LIMIT TAOS_DEF_ERROR_CODE(0, 0x023D) #define TSDB_CODE_TSC_INVALID_TOKEN TAOS_DEF_ERROR_CODE(0, 0x023E) #define TSDB_CODE_TSC_INSTANCE_API_RATE_LIMIT TAOS_DEF_ERROR_CODE(0, 0x023F) +#define TSDB_CODE_TSC_PASS_CHANGED TAOS_DEF_ERROR_CODE(0, 0x0240) #define TSDB_CODE_TSC_INTERNAL_ERROR TAOS_DEF_ERROR_CODE(0, 0x02FF) // mnode-common diff --git a/source/client/inc/clientInt.h b/source/client/inc/clientInt.h index fb8ba5b912d0..d9ec2ace9f85 100644 --- a/source/client/inc/clientInt.h +++ b/source/client/inc/clientInt.h @@ -175,6 +175,7 @@ typedef struct SAppInfo { typedef struct { int32_t ver; + int8_t passChanged; void* param; __taos_notify_fn_t fp; } STscNotifyInfo; diff --git a/source/client/src/clientEnv.c b/source/client/src/clientEnv.c index dc0256f7a371..2fef6e17fa5d 100644 --- a/source/client/src/clientEnv.c +++ b/source/client/src/clientEnv.c @@ -580,6 +580,10 @@ int32_t createRequest(uint64_t connId, int32_t type, int64_t reqid, SRequestObj releaseTscObj(connId); TSC_ERR_JRET(TSDB_CODE_MND_USER_DISABLED); } + if (atomic_load_8(&pTscObj->passInfo.passChanged)) { + releaseTscObj(connId); + TSC_ERR_JRET(TSDB_CODE_TSC_PASS_CHANGED); + } SSyncQueryParam *interParam = taosMemoryCalloc(1, sizeof(SSyncQueryParam)); if (interParam == NULL) { releaseTscObj(connId); diff --git a/source/client/src/clientHb.c b/source/client/src/clientHb.c index f6a5056dacac..ebf1e2abe210 100644 --- a/source/client/src/clientHb.c +++ b/source/client/src/clientHb.c @@ -232,7 +232,7 @@ static int32_t hbUpdateUserAuthInfo(SAppHbMgr *pAppHbMgr, SUserAuthBatchRsp *bat } // update password version - if (pTscObj->passInfo.fp) { + { SPassInfo *passInfo = &pTscObj->passInfo; int32_t oldVer = 0; do { @@ -242,7 +242,10 @@ static int32_t hbUpdateUserAuthInfo(SAppHbMgr *pAppHbMgr, SUserAuthBatchRsp *bat } } while (atomic_val_compare_exchange_32(&passInfo->ver, oldVer, pRsp->passVer) != oldVer); if (oldVer < pRsp->passVer) { - (*passInfo->fp)(passInfo->param, &pRsp->passVer, TAOS_NOTIFY_PASSVER); + atomic_store_8(&passInfo->passChanged, 1); + if (passInfo->fp) { + (*passInfo->fp)(passInfo->param, &pRsp->passVer, TAOS_NOTIFY_PASSVER); + } tscDebug("update passVer of user %s from %d to %d, conn:%" PRIi64, pRsp->user, oldVer, atomic_load_32(&passInfo->ver), pTscObj->id); } diff --git a/source/util/src/terror.c b/source/util/src/terror.c index 3fa0e593f804..7f77a38409dc 100644 --- a/source/util/src/terror.c +++ b/source/util/src/terror.c @@ -197,6 +197,7 @@ TAOS_DEFINE_ERROR(TSDB_CODE_NOT_SUPPORTTED_IN_WINDOWS, "Operation not support TAOS_DEFINE_ERROR(TSDB_CODE_TSC_INVALID_TOTP_CODE, "Invalid TOTP code") TAOS_DEFINE_ERROR(TSDB_CODE_TSC_INVALID_TOKEN, "Invalid token") TAOS_DEFINE_ERROR(TSDB_CODE_TSC_INSTANCE_API_RATE_LIMIT, "instance register/list API rate limit exceeded") +TAOS_DEFINE_ERROR(TSDB_CODE_TSC_PASS_CHANGED, "Password has been changed from another session, please reconnect") TAOS_DEFINE_ERROR(TSDB_CODE_TSC_SESS_PER_USER_LIMIT, "reached the maximum sessions per user limit") TAOS_DEFINE_ERROR(TSDB_CODE_TSC_SESS_CONN_TIMEOUT, "reached the maximum connection timeout limit") diff --git a/test/cases/24-Users/test_user_basic.py b/test/cases/24-Users/test_user_basic.py index 5dd364520eda..2658c0c1d569 100644 --- a/test/cases/24-Users/test_user_basic.py +++ b/test/cases/24-Users/test_user_basic.py @@ -434,7 +434,33 @@ def do_user_manage(self): # self.subscribe_topic() # PRIV_TODO: topic print("do user manager ............. [passed]") - + + # + # ------------------- test_pass_change_detect ---------------- + # + def do_pass_change_detect(self): + tdLog.info("=============== test: password change detection") + tdSql.execute("CREATE USER upasschk PASS 'AAbb1122'") + conn = taos.connect(user='upasschk', password='AAbb1122') + cursor = conn.cursor() + cursor.execute("SELECT SERVER_VERSION()") + tdLog.info("conn1 query before pass change: success") + + tdSql.execute("ALTER USER upasschk PASS 'CCdd3344!!'") + tdLog.info("password changed via root conn, waiting for heartbeat...") + time.sleep(5) + + try: + cursor.execute("SELECT SERVER_VERSION()") + tdLog.exit("query should have failed after password change but succeeded") + except Exception as e: + tdLog.info(f"query after pass change failed as expected: {e}") + + cursor.close() + conn.close() + tdSql.execute("DROP USER upasschk") + print("do pass change detect ............. [passed]") + # # ------------------- main ---------------- # @@ -448,6 +474,7 @@ def test_user_basic(self): 5. Ensures proper error handling for invalid privilege values 6. Check create multi users and grant/revoke privilege for them 7. Subscribe topic with different user privileges + 8. Password change detection: after password changed, all requests on old connection fail Catalog: - User @@ -467,4 +494,5 @@ def test_user_basic(self): tdSql.execute("alter all dnodes 'enableAdvancedSecurity' '1'") self.do_user_basic() self.do_user_privilege_multi_users() - self.do_user_manage() \ No newline at end of file + self.do_user_manage() + self.do_pass_change_detect() \ No newline at end of file