mirror of
https://github.com/taosdata/TDengine
synced 2026-05-24 10:09:01 +00:00
feat[TS-7529]: support TOTP authentication (#33852)
This commit is contained in:
parent
ea2e280e14
commit
d4e29c97fc
35 changed files with 796 additions and 130 deletions
|
|
@ -138,7 +138,8 @@ Below are the business error codes for each module.
|
|||
| 0x8000022E | No available execution node | No available query execution node | Check the current query policy configuration, ensure available Qnode if needed |
|
||||
| 0x8000022F | Table is not a supertable | Table name in the statement is not a supertable | Check if the table name used in the statement is a supertable |
|
||||
| 0x80000230 | Stmt cache error | STMT/STMT2 internal cache error | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80000231 | Tsc internal error | TSC internal error | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80000238 | Invalid TOTP code | Invalid TOTP code | Check and enter the correct TOTP code |
|
||||
| 0x800002FF | Tsc internal error | TSC internal error | Preserve the scene and logs, report issue on GitHub |
|
||||
|
||||
#### mnode
|
||||
|
||||
|
|
@ -179,6 +180,7 @@ Below are the business error codes for each module.
|
|||
| 0x80000355 | Too many users | (Enterprise only) Exceeding user limit | Adjust configuration |
|
||||
| 0x80000357 | Authentication failure | Incorrect password | Confirm if the operation is correct |
|
||||
| 0x80000358 | User not available | User does not exist | Confirm if the operation is correct |
|
||||
| 0x8000035B | Wrong TOTP code | TOTP code not provided or wrong TOTP code | Check and enter the correct TOTP code |
|
||||
| 0x80000360 | STable already exists | Internal error | Report issue |
|
||||
| 0x80000361 | STable not exist | Internal error | Report issue |
|
||||
| 0x80000364 | Too many tags | Too many tags | Cannot be modified, code-level restriction |
|
||||
|
|
@ -533,19 +535,19 @@ Below are the business error codes for each module.
|
|||
| 0x8000268A | Cols function's first param must be a select function that output a single row | The first parameter of the cols function should be a selection function | Check and correct the SQL statement |
|
||||
| 0x8000268B | Invalid using alias for cols function | Illegal cols function alias | Check and correct the SQL statement |
|
||||
| 0x8000268C | Join primary key col must be timestamp type | Join primary key data type error | Check and correct the SQL statement |
|
||||
| 0x8000268D | Invalid virtual table's ref column | Create/Update Virtual table using incorrect data source column | Check and correct the SQL statement |
|
||||
| 0x8000268E | Invalid table type | Incorrect Table type | Check and correct the SQL statement |
|
||||
| 0x8000268F | Invalid ref column type | Virtual table's column type and data source column's type are different | Check and correct the SQL statement |
|
||||
| 0x80002690 | Create child table using virtual super table | Create non-virtual child table using virtual super table | Check and correct the SQL statement |
|
||||
| 0x80002696 | Invalid sliding offset | Invalid sliding offset | Check and correct the SQL statement |
|
||||
| 0x80002697 | Invalid interval offset | Invalid interval offset | Check and correct the SQL statement |
|
||||
| 0x80002698 | Invalid extend value | Invalid extend value | Check and correct the SQL statement |
|
||||
| 0x80002699 | Algorithm ID too long, max length is 63 character | Invalid algorithm id value | Check and correct the SQL statement |
|
||||
| 0x8000269A | Algorithm name too long, max length is 63 character | Invalid algorithm name value | Check and correct the SQL statement |
|
||||
| 0x8000269B | Algorithm description too long, max length is 127 character | Invalid algorithm description value | Check and correct the SQL statement |
|
||||
| 0x8000269C | Algorithm type too long, max length is 63 character | Invalid algorithm type value | Check and correct the SQL statement |
|
||||
| 0x8000269D | Algorithm OpenSSL name too long, max length is 63 character | Invalid algorithm OpenSSL name value | Check and correct the SQL statement |
|
||||
| 0x8000269E | Option duplicated | Option is only allowed to appear once but appeared twice or more | Check and correct the SQL statement |
|
||||
| 0x8000268D | Invalid virtual table's ref column | Create/Update Virtual table using incorrect data source column | Check and correct the SQL statement |
|
||||
| 0x8000268E | Invalid table type | Incorrect Table type | Check and correct the SQL statement |
|
||||
| 0x8000268F | Invalid ref column type | Virtual table's column type and data source column's type are different | Check and correct the SQL statement |
|
||||
| 0x80002690 | Create child table using virtual super table | Create non-virtual child table using virtual super table | Check and correct the SQL statement |
|
||||
| 0x80002696 | Invalid sliding offset | Invalid sliding offset | Check and correct the SQL statement |
|
||||
| 0x80002697 | Invalid interval offset | Invalid interval offset | Check and correct the SQL statement |
|
||||
| 0x80002698 | Invalid extend value | Invalid extend value | Check and correct the SQL statement |
|
||||
| 0x80002699 | Algorithm ID too long, max length is 63 character | Invalid algorithm id value | Check and correct the SQL statement |
|
||||
| 0x8000269A | Algorithm name too long, max length is 63 character | Invalid algorithm name value | Check and correct the SQL statement |
|
||||
| 0x8000269B | Algorithm description too long, max length is 127 character | Invalid algorithm description value | Check and correct the SQL statement |
|
||||
| 0x8000269C | Algorithm type too long, max length is 63 character | Invalid algorithm type value | Check and correct the SQL statement |
|
||||
| 0x8000269D | Algorithm OpenSSL name too long, max length is 63 character | Invalid algorithm OpenSSL name value | Check and correct the SQL statement |
|
||||
| 0x8000269E | Option duplicated | Option is only allowed to appear once but appeared twice or more | Check and correct the SQL statement |
|
||||
| 0x8000269F | Invalid option value | Invalid option value | Check and correct the SQL statement |
|
||||
| 0x800026A0 | Option value too long | Option value too long | Check and correct the SQL statement |
|
||||
| 0x800026A1 | Option value too short | Option value too short | Check and correct the SQL statement |
|
||||
|
|
@ -555,10 +557,10 @@ Below are the business error codes for each module.
|
|||
| 0x80002700 | Planner internal error | Internal error in planner | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002701 | Expect ts equal | JOIN condition validation failed | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002702 | Cross join not support | CROSS JOIN not supported | Check and correct the SQL statement |
|
||||
| 0x80002704 | Planner slot key not found | Planner cannot find slotId during making physic plan | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002705 | Planner invalid table type | Planner get invalid table type | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002706 | Planner invalid query control plan type | Planner get invalid query control plan type during making physic plan | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002707 | Planner invalid window type | Planner get invalid window type during making physic plan | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002704 | Planner slot key not found | Planner cannot find slotId during making physic plan | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002705 | Planner invalid table type | Planner get invalid table type | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002706 | Planner invalid query control plan type | Planner get invalid query control plan type during making physic plan | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002707 | Planner invalid window type | Planner get invalid window type during making physic plan | Preserve the scene and logs, report issue on GitHub |
|
||||
|
||||
#### function
|
||||
|
||||
|
|
|
|||
|
|
@ -136,7 +136,8 @@ TSDB 错误码包括 taosc 客户端和服务端,所有语言的连接器无
|
|||
| 0x8000022E | No available execution node | 没有可用的查询执行节点 | 检查当前 query policy 配置,如果需要有 Qnode 参与确保系统中存在可用的 Qnode 节点 |
|
||||
| 0x8000022F | Table is not a super table | 当前语句中的表名不是超级表 | 检查当前语句中所用表名是否是超级表 |
|
||||
| 0x80000230 | Stmt cache error | STMT/STMT2 内部缓存出错 | 保留现场和日志,github 上报 issue |
|
||||
| 0x80000231 | Tsc internal error | TSC 内部错误 | 保留现场和日志,github 上报 issue |
|
||||
| 0x80000238 | Invalid TOTP code | 输入的 TOTP 验证码格式错误 | 检查并重新输入正确的 TOTP 验证码 |
|
||||
| 0x800002FF | Tsc internal error | TSC 内部错误 | 保留现场和日志,github 上报 issue |
|
||||
|
||||
#### mnode
|
||||
|
||||
|
|
@ -164,7 +165,7 @@ TSDB 错误码包括 taosc 客户端和服务端,所有语言的连接器无
|
|||
| 0x80000332 | Vgroup does not exist | 内部错误 | 上报 issue |
|
||||
| 0x80000333 | Cannot drop mnode which is leader | 操作节点为 leader | 确认操作是否正确 |
|
||||
| 0x80000334 | Out of dnodes | dnode 节点数量不够 | 增加 dnode 节点 |
|
||||
| 0x80000335 | Cluster cfg inconsistent | 配置不一致 | 检查 dnode 节点与 mnode 节点配置是否一致。检查方式:1.节点启动时,在日志中输出 2.使用 show variables |
|
||||
| 0x80000335 | Cluster cfg inconsistent | 配置不一致 | 检查 dnode 节点与 mnode 节点配置是否一致。检查方式:1.节点启动时,在日志中输出 2.使用 show variables |
|
||||
| 0x8000033B | Cluster id not match | 节点配置数据不一致 | 检查各节点 data/dnode/dnodes.json 文件中的 clusterid |
|
||||
| 0x80000340 | Account already exists | (仅企业版)内部错误 | 上报 issue |
|
||||
| 0x80000342 | Invalid account options | (仅企业版)该操作不支持 | 确认操作是否正确 |
|
||||
|
|
@ -177,6 +178,7 @@ TSDB 错误码包括 taosc 客户端和服务端,所有语言的连接器无
|
|||
| 0x80000355 | Too many users | (仅企业版)用户数量超限 | 调整配置 |
|
||||
| 0x80000357 | Authentication failure | 密码不正确 | 确认操作是否正确 |
|
||||
| 0x80000358 | User not available | 用户不存在 | 确认操作是否正确 |
|
||||
| 0x8000035B | Wrong TOTP code | 未提供或提供了错误的 TOTP 验证码 | 检查并输入正确的 TOTP 验证码 |
|
||||
| 0x80000360 | STable already exists | 内部错误 | 上报 issue |
|
||||
| 0x80000361 | STable not exist | 内部错误 | 上报 issue |
|
||||
| 0x80000364 | Too many tags | tag 数量太多 | 不能修改,代码级别限制 |
|
||||
|
|
@ -481,7 +483,7 @@ TSDB 错误码包括 taosc 客户端和服务端,所有语言的连接器无
|
|||
| 0x80002635 | Incorrect TIMESTAMP value | 主键时间戳列值非法 | 检查并修正 SQL 语句 |
|
||||
| 0x80002637 | soffset/offset can not be less than 0 | soffset/offset 值非法 | 检查并修正 SQL 语句 |
|
||||
| 0x80002638 | slimit/soffset only available for PARTITION/GROUP BY query | slimit/soffset 只支持 PARTITION BY/GROUP BY 语句 | 检查并修正 SQL 语句 |
|
||||
| 0x80002639 | Invalid topic query | 不支持的 TOPIC 查询语法 | 检查并修正 SQL 语句 |
|
||||
| 0x80002639 | Invalid topic query | 不支持的 TOPIC 查询语法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000263A | Cannot drop super table in batch | 不支持批量删除超级表 | 检查并修正 SQL 语句 |
|
||||
| 0x8000263B | Start(end) time of query range required or time range too large | 窗口个数超出限制 | 检查并修正 SQL 语句 |
|
||||
| 0x8000263C | Duplicated column names | 列名称重复 | 检查并修正 SQL 语句 |
|
||||
|
|
@ -538,12 +540,12 @@ TSDB 错误码包括 taosc 客户端和服务端,所有语言的连接器无
|
|||
| 0x80002696 | Invalid sliding offset | sliding 窗口偏移量非法 | 检查并修正 SQL 语句 |
|
||||
| 0x80002697 | Invalid interval offset | interval 窗口偏移量非法 | 检查并修正 SQL 语句 |
|
||||
| 0x80002698 | Invalid extend value | extend 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x80002699 | Algorithm ID too long, max length is 63 character | Algorithm ID 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269A | Algorithm name too long, max length is 63 character | Algorithm name 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269B | Algorithm description too long, max length is 127 character | Algorithm description 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269C | Algorithm type too long, max length is 63 character | Algorithm type 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269D | Algorithm OpenSSL name too long, max length is 63 character | Algorithm OpenSSL name 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269E | Option duplicated | 只允许出现一次的选项出现了多次 | 检查并修正 SQL 语句 |
|
||||
| 0x80002699 | Algorithm ID too long, max length is 63 character | Algorithm ID 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269A | Algorithm name too long, max length is 63 character | Algorithm name 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269B | Algorithm description too long, max length is 127 character | Algorithm description 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269C | Algorithm type too long, max length is 63 character | Algorithm type 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269D | Algorithm OpenSSL name too long, max length is 63 character | Algorithm OpenSSL name 参数非法 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269E | Option duplicated | 只允许出现一次的选项出现了多次 | 检查并修正 SQL 语句 |
|
||||
| 0x8000269F | Invalid option value | 选项的值非法 | 检查并修正 SQL 语句 |
|
||||
| 0x800026A0 | Option value too long | 选项的值太长 | 检查并修正 SQL 语句 |
|
||||
| 0x800026A1 | Option value too short | 选项的值太短 | 检查并修正 SQL 语句 |
|
||||
|
|
@ -556,7 +558,7 @@ TSDB 错误码包括 taosc 客户端和服务端,所有语言的连接器无
|
|||
| 0x80002704 | Planner slot key not found | 生成物理计划时查找不到 slotId | 保留现场和日志,github 上报 issue |
|
||||
| 0x80002705 | Planner invalid table type | 计划器生成计划时得到了错误的表类型 | 保留现场和日志,github 上报 issue |
|
||||
| 0x80002706 | Planner invalid query control plan type | 计划器生成 dynamic query control 计划时得到的类型不正确 | 保留现场和日志,github 上报 issue |
|
||||
| 0x80002707 | Planner invalid window type | 计划器生成物理计划时得到了错误的窗口类型 | 保留现场和日志,github 上报 issue |
|
||||
| 0x80002707 | Planner invalid window type | 计划器生成物理计划时得到了错误的窗口类型 | 保留现场和日志,github 上报 issue |
|
||||
|
||||
#### function
|
||||
|
||||
|
|
|
|||
|
|
@ -204,6 +204,9 @@ DLL_EXPORT TAOS *taos_connect_auth(const char *ip, const char *user, const char
|
|||
* @return TAOS* connection handle on success; NULL if unsupported or on error
|
||||
*/
|
||||
DLL_EXPORT TAOS *taos_connect_with_dsn(const char *dsn);
|
||||
DLL_EXPORT TAOS *taos_connect_totp(const char *ip, const char *user, const char *pass, const char* totp, const char *db, uint16_t port);
|
||||
DLL_EXPORT int taos_connect_test(const char *ip, const char *user, const char *pass, const char* totp, const char *db, uint16_t port);
|
||||
DLL_EXPORT TAOS *taos_connect_token(const char *ip, const char *token, const char *db, uint16_t port);
|
||||
DLL_EXPORT void taos_close(TAOS *taos);
|
||||
|
||||
DLL_EXPORT const char *taos_data_type(int type);
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ enum {
|
|||
CONN_TYPE__QUERY = 1,
|
||||
CONN_TYPE__TMQ,
|
||||
CONN_TYPE__UDFD,
|
||||
CONN_TYPE__AUTH_TEST, // only for test authentication
|
||||
CONN_TYPE__MAX,
|
||||
};
|
||||
|
||||
|
|
@ -1277,6 +1278,7 @@ typedef struct {
|
|||
char passwd[TSDB_PASSWORD_LEN];
|
||||
int64_t startTime;
|
||||
char sVer[TSDB_VERSION_LEN];
|
||||
int32_t totpCode;
|
||||
} SConnectReq;
|
||||
|
||||
int32_t tSerializeSConnectReq(void* buf, int32_t bufLen, SConnectReq* pReq);
|
||||
|
|
@ -1290,7 +1292,6 @@ typedef struct {
|
|||
int8_t superUser;
|
||||
int8_t sysInfo;
|
||||
int8_t connType;
|
||||
int8_t mustChangePass;
|
||||
SEpSet epSet;
|
||||
int32_t svrTimestamp;
|
||||
int32_t passVer;
|
||||
|
|
|
|||
|
|
@ -126,7 +126,8 @@ typedef enum EFunctionType {
|
|||
FUNCTION_TYPE_FIND_IN_SET,
|
||||
FUNCTION_TYPE_LIKE_IN_SET,
|
||||
FUNCTION_TYPE_REGEXP_IN_SET,
|
||||
|
||||
FUNCTION_TYPE_GENERATE_TOTP_SECRET,
|
||||
FUNCTION_TYPE_GENERATE_TOTP_CODE,
|
||||
|
||||
// conversion function
|
||||
FUNCTION_TYPE_CAST = 2000,
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ int32_t crc32Function(SScalarParam* pInput, int32_t inputNum, SScalarParam* pOut
|
|||
int32_t findInSetFunction(SScalarParam* pInput, int32_t inputNum, SScalarParam* pOutput);
|
||||
int32_t likeInSetFunction(SScalarParam* pInput, int32_t inputNum, SScalarParam* pOutput);
|
||||
int32_t regexpInSetFunction(SScalarParam* pInput, int32_t inputNum, SScalarParam* pOutput);
|
||||
int32_t generateTotpSecretFunction(SScalarParam* pInput, int32_t inputNum, SScalarParam* pOutput);
|
||||
int32_t generateTotpCodeFunction(SScalarParam* pInput, int32_t inputNum, SScalarParam* pOutput);
|
||||
|
||||
/* Conversion functions */
|
||||
int32_t castFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput);
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ int32_t taosGetErrSize();
|
|||
#define TSDB_CODE_TSC_FAIL_GENERATE_JSON TAOS_DEF_ERROR_CODE(0, 0x0235)
|
||||
#define TSDB_CODE_TSC_STMT_BIND_NUMBER_ERROR TAOS_DEF_ERROR_CODE(0, 0x0236)
|
||||
#define TSDB_CODE_NOT_SUPPORTTED_IN_WINDOWS TAOS_DEF_ERROR_CODE(0, 0x0237)
|
||||
#define TSDB_CODE_TSC_INVALID_TOTP_CODE TAOS_DEF_ERROR_CODE(0, 0x0238)
|
||||
#define TSDB_CODE_TSC_INTERNAL_ERROR TAOS_DEF_ERROR_CODE(0, 0x02FF)
|
||||
|
||||
// mnode-common
|
||||
|
|
@ -304,7 +305,7 @@ int32_t taosGetErrSize();
|
|||
#define TSDB_CODE_MND_USER_NOT_AVAILABLE TAOS_DEF_ERROR_CODE(0, 0x0358)
|
||||
#define TSDB_CODE_MND_PRIVILEDGE_EXIST TAOS_DEF_ERROR_CODE(0, 0x0359)
|
||||
#define TSDB_CODE_MND_USER_PASSWORD_REUSE TAOS_DEF_ERROR_CODE(0, 0x035A)
|
||||
// #define TSDB_CODE_MND_USER_TIME_RANGE_CONFLICT TAOS_DEF_ERROR_CODE(0, 0x035B)
|
||||
#define TSDB_CODE_MND_WRONG_TOTP_CODE TAOS_DEF_ERROR_CODE(0, 0x035B)
|
||||
#define TSDB_CODE_MND_TOO_MANY_USER_IP_RANGE TAOS_DEF_ERROR_CODE(0, 0x035C)
|
||||
#define TSDB_CODE_MND_TOO_MANY_USER_TIME_RANGE TAOS_DEF_ERROR_CODE(0, 0x035D)
|
||||
|
||||
|
|
|
|||
53
include/util/totp.h
Normal file
53
include/util/totp.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _TD_UTIL_TOTP_H_
|
||||
#define _TD_UTIL_TOTP_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// format the TOTP code with leading zeros to match the specified digit length.
|
||||
// return the number of characters written, excluding the null terminator.
|
||||
int taosFormatTotp(int32_t totp, int digits, char *buffer, size_t size);
|
||||
|
||||
// generate TOTP code for given secret at current time.
|
||||
// secret is a byte array, not a base32 string.
|
||||
// return the generated TOTP code, or -1 on error.
|
||||
int32_t taosGenerateTotpCode(const uint8_t *secret, size_t secretLen, int digits);
|
||||
|
||||
// verify TOTP code for given secret at current time with allowed window.
|
||||
// secret is a byte array, not a base32 string.
|
||||
// return 1 if the code is correct, 0 otherwise.
|
||||
int taosVerifyTotpCode(const uint8_t *secret, size_t secretLen, int32_t userCode, int digits, int window);
|
||||
|
||||
// generate TOTP secret from seed
|
||||
// if seedLen is 0, the seed is treated as a null-terminated string and its length is determined using strlen.
|
||||
// secret is a byte array, not a base32 string.
|
||||
// return the length of the generated secret, or -1 on error.
|
||||
int taosGenerateTotpSecret(const char *seed, size_t seedLen, uint8_t *secret, size_t secretLen);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*_TD_UTIL_TOTP_H_*/
|
||||
|
|
@ -381,6 +381,9 @@ bool taosIsBigChar(char c);
|
|||
bool taosIsSmallChar(char c);
|
||||
bool taosIsNumberChar(char c);
|
||||
bool taosIsSpecialChar(char c);
|
||||
// check if the string is a complex string, a complex string contains
|
||||
// at least 3 types of characters: upper, lower, digit, special
|
||||
bool taosIsComplexString(const char* str);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,8 +186,8 @@ typedef struct STscObj {
|
|||
SAppInstInfo* pAppInfo;
|
||||
SHashObj* pRequests;
|
||||
SPassInfo passInfo;
|
||||
SWhiteListInfo whiteListInfo;
|
||||
SWhiteListInfo dateTimeWhiteListInfo;
|
||||
SWhiteListInfo whiteListInfo; // ip white list info
|
||||
SWhiteListInfo dateTimeWhiteListInfo; // date time white list info
|
||||
STscNotifyInfo userDroppedInfo;
|
||||
SOptionInfo optionInfo;
|
||||
} STscObj;
|
||||
|
|
@ -394,7 +394,7 @@ typedef struct AsyncArg {
|
|||
bool persistConnForSpecificMsg(void* parenct, tmsg_t msgType);
|
||||
void processMsgFromServer(void* parent, SRpcMsg* pMsg, SEpSet* pEpSet);
|
||||
|
||||
int32_t taos_connect_internal(const char* ip, const char* user, const char* pass, const char* auth, const char* db,
|
||||
int32_t taos_connect_internal(const char* ip, const char* user, const char* pass, const char* auth, const char* totp, const char* db,
|
||||
uint16_t port, int connType, STscObj** pObj);
|
||||
|
||||
int32_t parseSql(SRequestObj* pRequest, bool topicQuery, SQuery** pQuery, SStmtCallback* pStmtCb);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
#include "tversion.h"
|
||||
|
||||
static int32_t initEpSetFromCfg(const char* firstEp, const char* secondEp, SCorEpSet* pEpSet);
|
||||
static int32_t buildConnectMsg(SRequestObj* pRequest, SMsgSendInfo** pMsgSendInfo);
|
||||
static int32_t buildConnectMsg(SRequestObj* pRequest, SMsgSendInfo** pMsgSendInfo, int32_t totpCode);
|
||||
|
||||
void setQueryRequest(int64_t rId) {
|
||||
SRequestObj* pReq = acquireRequest(rId);
|
||||
|
|
@ -143,10 +143,10 @@ void cleanupAppInfo() {
|
|||
tscInfo("cluster instance map cleaned");
|
||||
}
|
||||
|
||||
static int32_t taosConnectImpl(const char* user, const char* auth, const char* db, __taos_async_fn_t fp, void* param,
|
||||
static int32_t taosConnectImpl(const char* user, const char* auth, int32_t totpCode, const char* db, __taos_async_fn_t fp, void* param,
|
||||
SAppInstInfo* pAppInfo, int connType, STscObj** pTscObj);
|
||||
|
||||
int32_t taos_connect_internal(const char* ip, const char* user, const char* pass, const char* auth, const char* db,
|
||||
int32_t taos_connect_internal(const char* ip, const char* user, const char* pass, const char* auth, const char* totp, const char* db,
|
||||
uint16_t port, int connType, STscObj** pObj) {
|
||||
TSC_ERR_RET(taos_init());
|
||||
if (!validateUserName(user)) {
|
||||
|
|
@ -175,6 +175,15 @@ int32_t taos_connect_internal(const char* ip, const char* user, const char* pass
|
|||
tstrncpy(secretEncrypt, auth, tListLen(secretEncrypt));
|
||||
}
|
||||
|
||||
int32_t totpCode = -1;
|
||||
if (totp != NULL) {
|
||||
char *endptr = NULL;
|
||||
totpCode = taosStr2Int32(totp, &endptr, 10);
|
||||
if (endptr == totp || *endptr != '\0' || totpCode < 0 || totpCode > 999999) {
|
||||
TSC_ERR_RET(TSDB_CODE_TSC_INVALID_TOTP_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
SCorEpSet epSet = {0};
|
||||
if (ip) {
|
||||
TSC_ERR_RET(initEpSetFromCfg(ip, NULL, &epSet));
|
||||
|
|
@ -267,7 +276,7 @@ _return:
|
|||
tscError("failed to unlock app info, code:%s", tstrerror(TAOS_SYSTEM_ERROR(code)));
|
||||
return code;
|
||||
}
|
||||
return taosConnectImpl(user, &secretEncrypt[0], localDb, NULL, NULL, *pInst, connType, pObj);
|
||||
return taosConnectImpl(user, &secretEncrypt[0], totpCode, localDb, NULL, NULL, *pInst, connType, pObj);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1688,7 +1697,7 @@ int32_t initEpSetFromCfg(const char* firstEp, const char* secondEp, SCorEpSet* p
|
|||
return 0;
|
||||
}
|
||||
|
||||
int32_t taosConnectImpl(const char* user, const char* auth, const char* db, __taos_async_fn_t fp, void* param,
|
||||
int32_t taosConnectImpl(const char* user, const char* auth, int32_t totpCode, const char* db, __taos_async_fn_t fp, void* param,
|
||||
SAppInstInfo* pAppInfo, int connType, STscObj** pTscObj) {
|
||||
*pTscObj = NULL;
|
||||
int32_t code = createTscObj(user, auth, db, connType, pAppInfo, pTscObj);
|
||||
|
|
@ -1711,7 +1720,7 @@ int32_t taosConnectImpl(const char* user, const char* auth, const char* db, __ta
|
|||
}
|
||||
|
||||
SMsgSendInfo* body = NULL;
|
||||
code = buildConnectMsg(pRequest, &body);
|
||||
code = buildConnectMsg(pRequest, &body, totpCode);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
destroyTscObj(*pTscObj);
|
||||
return code;
|
||||
|
|
@ -1739,15 +1748,22 @@ int32_t taosConnectImpl(const char* user, const char* auth, const char* db, __ta
|
|||
taos_close_internal(*pTscObj);
|
||||
*pTscObj = NULL;
|
||||
return terrno;
|
||||
} else {
|
||||
tscInfo("conn:0x%" PRIx64 ", connection is opening, connId:%u, dnodeConn:%p, QID:0x%" PRIx64, (*pTscObj)->id,
|
||||
(*pTscObj)->connId, (*pTscObj)->pAppInfo->pTransporter, pRequest->requestId);
|
||||
destroyRequest(pRequest);
|
||||
}
|
||||
if (connType == CONN_TYPE__AUTH_TEST) {
|
||||
terrno = TSDB_CODE_SUCCESS;
|
||||
destroyRequest(pRequest);
|
||||
taos_close_internal(*pTscObj);
|
||||
*pTscObj = NULL;
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
tscInfo("conn:0x%" PRIx64 ", connection is opening, connId:%u, dnodeConn:%p, QID:0x%" PRIx64, (*pTscObj)->id,
|
||||
(*pTscObj)->connId, (*pTscObj)->pAppInfo->pTransporter, pRequest->requestId);
|
||||
destroyRequest(pRequest);
|
||||
return code;
|
||||
}
|
||||
|
||||
static int32_t buildConnectMsg(SRequestObj* pRequest, SMsgSendInfo** pMsgSendInfo) {
|
||||
static int32_t buildConnectMsg(SRequestObj* pRequest, SMsgSendInfo** pMsgSendInfo, int32_t totpCode) {
|
||||
*pMsgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo));
|
||||
if (*pMsgSendInfo == NULL) {
|
||||
return terrno;
|
||||
|
|
@ -1781,6 +1797,7 @@ static int32_t buildConnectMsg(SRequestObj* pRequest, SMsgSendInfo** pMsgSendInf
|
|||
connectReq.connType = pObj->connType;
|
||||
connectReq.pid = appInfo.pid;
|
||||
connectReq.startTime = appInfo.startTime;
|
||||
connectReq.totpCode = totpCode;
|
||||
|
||||
tstrncpy(connectReq.app, appInfo.appName, sizeof(connectReq.app));
|
||||
tstrncpy(connectReq.user, pObj->user, sizeof(connectReq.user));
|
||||
|
|
@ -1990,6 +2007,56 @@ _exit:
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
TAOS *taos_connect_totp(const char *ip, const char *user, const char *pass, const char* totp, const char *db, uint16_t port) {
|
||||
tscInfo("try to connect to %s:%u by totp, user:%s db:%s", ip, port, user, db);
|
||||
if (user == NULL) {
|
||||
user = TSDB_DEFAULT_USER;
|
||||
}
|
||||
|
||||
if (pass == NULL) {
|
||||
pass = TSDB_DEFAULT_PASS;
|
||||
}
|
||||
|
||||
STscObj *pObj = NULL;
|
||||
int32_t code = taos_connect_internal(ip, user, pass, NULL, totp, db, port, CONN_TYPE__QUERY, &pObj);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
int64_t *rid = taosMemoryCalloc(1, sizeof(int64_t));
|
||||
if (NULL == rid) {
|
||||
tscError("out of memory when taos connect to %s:%u, user:%s db:%s", ip, port, user, db);
|
||||
return NULL;
|
||||
}
|
||||
*rid = pObj->id;
|
||||
return (TAOS *)rid;
|
||||
} else {
|
||||
terrno = code;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int taos_connect_test(const char *ip, const char *user, const char *pass, const char* totp, const char *db, uint16_t port) {
|
||||
tscInfo("try to connect to %s:%u by totp, user:%s db:%s", ip, port, user, db);
|
||||
if (user == NULL) {
|
||||
user = TSDB_DEFAULT_USER;
|
||||
}
|
||||
|
||||
if (pass == NULL) {
|
||||
pass = TSDB_DEFAULT_PASS;
|
||||
}
|
||||
|
||||
STscObj *pObj = NULL;
|
||||
return taos_connect_internal(ip, user, pass, NULL, totp, db, port, CONN_TYPE__AUTH_TEST, &pObj);
|
||||
}
|
||||
|
||||
|
||||
TAOS *taos_connect_token(const char *ip, const char *token, const char *db, uint16_t port) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
TAOS* taos_connect_auth(const char* ip, const char* user, const char* auth, const char* db, uint16_t port) {
|
||||
tscInfo("try to connect to %s:%u by auth, user:%s db:%s", ip, port, user, db);
|
||||
if (user == NULL) {
|
||||
|
|
@ -2002,7 +2069,7 @@ TAOS* taos_connect_auth(const char* ip, const char* user, const char* auth, cons
|
|||
}
|
||||
|
||||
STscObj* pObj = NULL;
|
||||
int32_t code = taos_connect_internal(ip, user, NULL, auth, db, port, CONN_TYPE__QUERY, &pObj);
|
||||
int32_t code = taos_connect_internal(ip, user, NULL, auth, NULL, db, port, CONN_TYPE__QUERY, &pObj);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
int64_t* rid = taosMemoryCalloc(1, sizeof(int64_t));
|
||||
if (NULL == rid) {
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ TAOS *taos_connect(const char *ip, const char *user, const char *pass, const cha
|
|||
}
|
||||
|
||||
STscObj *pObj = NULL;
|
||||
int32_t code = taos_connect_internal(ip, user, pass, NULL, db, port, CONN_TYPE__QUERY, &pObj);
|
||||
int32_t code = taos_connect_internal(ip, user, pass, NULL, NULL, db, port, CONN_TYPE__QUERY, &pObj);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
int64_t *rid = taosMemoryCalloc(1, sizeof(int64_t));
|
||||
if (NULL == rid) {
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@ int32_t processConnectRsp(void* param, SDataBuf* pMsg, int32_t code) {
|
|||
goto End;
|
||||
}
|
||||
|
||||
if (pTscObj->connType == CONN_TYPE__AUTH_TEST) {
|
||||
// auth test connection, no need to process connect rsp
|
||||
goto End;
|
||||
}
|
||||
|
||||
SConnectRsp connectRsp = {0};
|
||||
if (tDeserializeSConnectRsp(pMsg->pData, pMsg->len, &connectRsp) != 0) {
|
||||
code = TSDB_CODE_TSC_INVALID_VERSION;
|
||||
|
|
|
|||
|
|
@ -1841,7 +1841,7 @@ tmq_t* tmq_consumer_new(tmq_conf_t* conf, char* errstr, int32_t errstrLen) {
|
|||
}
|
||||
|
||||
// init connection
|
||||
code = taos_connect_internal(conf->ip, user, pass, NULL, NULL, conf->port, CONN_TYPE__TMQ, &pTmq->pTscObj);
|
||||
code = taos_connect_internal(conf->ip, user, pass, NULL, NULL, NULL, conf->port, CONN_TYPE__TMQ, &pTmq->pTscObj);
|
||||
if (code) {
|
||||
terrno = code;
|
||||
tqErrorC("consumer:0x%" PRIx64 " setup failed since %s, groupId:%s", pTmq->consumerId, terrstr(), pTmq->groupId);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "osSemaphore.h"
|
||||
#include "taoserror.h"
|
||||
#include "thash.h"
|
||||
#include "totp.h"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
||||
|
|
@ -338,6 +339,62 @@ TEST(clientCase, connect_Test) {
|
|||
taos_close(pConn);
|
||||
}
|
||||
|
||||
|
||||
TEST(clientCase, connect_totp_Test) {
|
||||
taos_options(TSDB_OPTION_CONFIGDIR, "~/first/cfg");
|
||||
TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0);
|
||||
if (pConn == NULL) {
|
||||
(void)printf("failed to connect to server, reason:%s\n", taos_errstr(NULL));
|
||||
}
|
||||
|
||||
uint8_t secret[64] = {0};
|
||||
size_t secretLen = taosGenerateTotpSecret("AAbb1122", 8, secret, sizeof(secret));
|
||||
|
||||
TAOS_RES* pRes = taos_query(pConn, "create user totp_u pass 'taosdata' totpseed 'AAbb1122'");
|
||||
if (taos_errno(pRes) != 0) {
|
||||
(void)printf("error in create user, reason:%s\n", taos_errstr(pRes));
|
||||
}
|
||||
taos_free_result(pRes);
|
||||
taos_close(pConn);
|
||||
|
||||
|
||||
int totpCode = taosGenerateTotpCode(secret, secretLen, 6);
|
||||
pConn = taos_connect_totp("localhost", "totp_u", "taosdata", "123456", NULL, 0);
|
||||
if (pConn != NULL) {
|
||||
(void)printf("connect to server with wrong totp");
|
||||
taos_close(pConn);
|
||||
}
|
||||
|
||||
int code = taos_connect_test("localhost", "totp_u", "taosdata", "123456", NULL, 0);
|
||||
if (code != TSDB_CODE_MND_WRONG_TOTP_CODE) {
|
||||
(void)printf("test connect to server with wrong totp return wrong code:%d\n", code);
|
||||
taos_close(pConn);
|
||||
}
|
||||
|
||||
char totp[16] = {0};
|
||||
(void)taosFormatTotp(totpCode, 6, totp, sizeof(totp));
|
||||
pConn = taos_connect_totp("localhost", "totp_u", "taosdata", totp, NULL, 0);
|
||||
if (pConn == NULL) {
|
||||
(void)printf("failed to connect to server with totp, reason:%s\n", taos_errstr(NULL));
|
||||
}
|
||||
|
||||
pRes = taos_query(pConn, "show users");
|
||||
if (taos_errno(pRes) != 0) {
|
||||
(void)printf("error in create user, reason:%s\n", taos_errstr(pRes));
|
||||
}
|
||||
taos_free_result(pRes);
|
||||
|
||||
taos_close(pConn);
|
||||
|
||||
totpCode = taosGenerateTotpCode(secret, secretLen, 6);
|
||||
(void)taosFormatTotp(totpCode, 6, totp, sizeof(totp));
|
||||
code = taos_connect_test("localhost", "totp_u", "taosdata", totp, NULL, 0);
|
||||
if (code != 0) {
|
||||
(void)printf("test connect to server with correct totp return wrong code:%d\n", code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(clientCase, connect_with_dsn_Test) {
|
||||
TAOS* pConn = taos_connect_with_dsn(NULL);
|
||||
ASSERT_EQ(pConn, nullptr);
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ extern void (*fp_taos_cleanup)(void);
|
|||
extern int (*fp_taos_options)(TSDB_OPTION option, const void *arg, ...);
|
||||
extern int (*fp_taos_options_connection)(TAOS *taos, TSDB_OPTION_CONNECTION option, const void *arg, ...);
|
||||
extern TAOS *(*fp_taos_connect)(const char *ip, const char *user, const char *pass, const char *db, uint16_t port);
|
||||
extern TAOS *(*fp_taos_connect_totp)(const char *ip, const char *user, const char *pass, const char* totp, const char *db, uint16_t port);
|
||||
extern int (*fp_taos_connect_test)(const char *ip, const char *user, const char *pass, const char* totp, const char *db, uint16_t port);
|
||||
extern TAOS *(*fp_taos_connect_token)(const char *ip, const char *token, const char *db, uint16_t port);
|
||||
extern TAOS *(*fp_taos_connect_auth)(const char *ip, const char *user, const char *auth, const char *db, uint16_t port);
|
||||
extern TAOS *(*fp_taos_connect_with_dsn)(const char *dsn);
|
||||
extern void (*fp_taos_close)(TAOS *taos);
|
||||
|
|
|
|||
|
|
@ -95,6 +95,9 @@ int32_t taosDriverInit(EDriverType driverType) {
|
|||
LOAD_FUNC(fp_taos_options, "taos_options");
|
||||
LOAD_FUNC(fp_taos_options_connection, "taos_options_connection");
|
||||
LOAD_FUNC(fp_taos_connect, "taos_connect");
|
||||
LOAD_FUNC(fp_taos_connect_totp, "taos_connect_totp");
|
||||
LOAD_FUNC(fp_taos_connect_token, "taos_connect_token");
|
||||
LOAD_FUNC(fp_taos_connect_test, "taos_connect_test");
|
||||
LOAD_FUNC(fp_taos_connect_auth, "taos_connect_auth");
|
||||
LOAD_FUNC(fp_taos_connect_with_dsn, "taos_connect_with_dsn");
|
||||
LOAD_FUNC(fp_taos_close, "taos_close");
|
||||
|
|
|
|||
|
|
@ -154,6 +154,39 @@ TAOS *taos_connect(const char *ip, const char *user, const char *pass, const cha
|
|||
return (*fp_taos_connect)(ip, user, pass, db, port);
|
||||
}
|
||||
|
||||
TAOS *taos_connect_totp(const char *ip, const char *user, const char *pass, const char* totp, const char *db, uint16_t port) {
|
||||
if (taos_init() != 0) {
|
||||
terrno = TSDB_CODE_DLL_NOT_LOAD;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CHECK_PTR(fp_taos_connect_totp);
|
||||
return (*fp_taos_connect_totp)(ip, user, pass, totp, db, port);
|
||||
}
|
||||
|
||||
int taos_connect_test(const char *ip, const char *user, const char *pass, const char* totp, const char *db, uint16_t port) {
|
||||
if (taos_init() != 0) {
|
||||
return TSDB_CODE_DLL_NOT_LOAD;
|
||||
}
|
||||
if (tsDriver == NULL) {
|
||||
return TSDB_CODE_DLL_NOT_LOAD;
|
||||
}
|
||||
if (fp_taos_connect_test == NULL) {
|
||||
return TSDB_CODE_DLL_FUNC_NOT_LOAD;
|
||||
}
|
||||
return (*fp_taos_connect_test)(ip, user, pass, totp, db, port);
|
||||
}
|
||||
|
||||
TAOS *taos_connect_token(const char *ip, const char *token, const char *db, uint16_t port) {
|
||||
if (taos_init() != 0) {
|
||||
terrno = TSDB_CODE_DLL_NOT_LOAD;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CHECK_PTR(fp_taos_connect_token);
|
||||
return (*fp_taos_connect_token)(ip, token, db, port);
|
||||
}
|
||||
|
||||
TAOS *taos_connect_auth(const char *ip, const char *user, const char *auth, const char *db, uint16_t port) {
|
||||
if (taos_init() != 0) {
|
||||
terrno = TSDB_CODE_DLL_NOT_LOAD;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ void (*fp_taos_cleanup)(void) = NULL;
|
|||
int (*fp_taos_options)(TSDB_OPTION option, const void *arg, ...) = NULL;
|
||||
int (*fp_taos_options_connection)(TAOS *taos, TSDB_OPTION_CONNECTION option, const void *arg, ...) = NULL;
|
||||
TAOS *(*fp_taos_connect)(const char *ip, const char *user, const char *pass, const char *db, uint16_t port) = NULL;
|
||||
TAOS *(*fp_taos_connect_totp)(const char *ip, const char *user, const char *pass, const char* totp, const char *db, uint16_t port) = NULL;
|
||||
int (*fp_taos_connect_test)(const char *ip, const char *user, const char *pass, const char* totp, const char *db, uint16_t port) = NULL;
|
||||
TAOS *(*fp_taos_connect_token)(const char *ip, const char *token, const char *db, uint16_t port) = NULL;
|
||||
TAOS *(*fp_taos_connect_auth)(const char *ip, const char *user, const char *auth, const char *db, uint16_t port) = NULL;
|
||||
TAOS *(*fp_taos_connect_with_dsn)(const char *dsn) = NULL;
|
||||
void (*fp_taos_close)(TAOS *taos) = NULL;
|
||||
|
|
|
|||
|
|
@ -9358,6 +9358,7 @@ int32_t tSerializeSConnectReq(void *buf, int32_t bufLen, SConnectReq *pReq) {
|
|||
TAOS_CHECK_EXIT(tEncodeCStrWithLen(&encoder, pReq->passwd, TSDB_PASSWORD_LEN));
|
||||
TAOS_CHECK_EXIT(tEncodeI64(&encoder, pReq->startTime));
|
||||
TAOS_CHECK_EXIT(tEncodeCStr(&encoder, pReq->sVer));
|
||||
TAOS_CHECK_EXIT(tEncodeI32(&encoder, pReq->totpCode));
|
||||
tEndEncode(&encoder);
|
||||
|
||||
_exit:
|
||||
|
|
@ -9390,6 +9391,12 @@ int32_t tDeserializeSConnectReq(void *buf, int32_t bufLen, SConnectReq *pReq) {
|
|||
TAOS_CHECK_EXIT(TSDB_CODE_VERSION_NOT_COMPATIBLE);
|
||||
}
|
||||
TAOS_CHECK_EXIT(tDecodeCStrTo(&decoder, pReq->sVer));
|
||||
// Check the client version from version 3.4.0.0
|
||||
if (tDecodeIsEnd(&decoder)) {
|
||||
tDecoderClear(&decoder);
|
||||
TAOS_CHECK_EXIT(TSDB_CODE_VERSION_NOT_COMPATIBLE);
|
||||
}
|
||||
TAOS_CHECK_EXIT(tDecodeI32(&decoder, &pReq->totpCode));
|
||||
tEndDecode(&decoder);
|
||||
|
||||
_exit:
|
||||
|
|
@ -9422,7 +9429,6 @@ int32_t tSerializeSConnectRsp(void *buf, int32_t bufLen, SConnectRsp *pRsp) {
|
|||
TAOS_CHECK_EXIT(tSerializeSMonitorParas(&encoder, &pRsp->monitorParas));
|
||||
TAOS_CHECK_EXIT(tEncodeI8(&encoder, pRsp->enableAuditDelete));
|
||||
TAOS_CHECK_EXIT(tEncodeI64(&encoder, pRsp->timeWhiteListVer));
|
||||
TAOS_CHECK_EXIT(tEncodeI8(&encoder, pRsp->mustChangePass));
|
||||
tEndEncode(&encoder);
|
||||
|
||||
_exit:
|
||||
|
|
@ -9481,10 +9487,8 @@ int32_t tDeserializeSConnectRsp(void *buf, int32_t bufLen, SConnectRsp *pRsp) {
|
|||
}
|
||||
if (!tDecodeIsEnd(&decoder)) {
|
||||
TAOS_CHECK_EXIT(tDecodeI64(&decoder, &pRsp->timeWhiteListVer));
|
||||
TAOS_CHECK_EXIT(tDecodeI8(&decoder, &pRsp->mustChangePass));
|
||||
} else {
|
||||
pRsp->timeWhiteListVer = 0;
|
||||
pRsp->mustChangePass = 0;
|
||||
}
|
||||
|
||||
tEndDecode(&decoder);
|
||||
|
|
|
|||
|
|
@ -291,6 +291,7 @@ static const SSysDbTableSchema userUsersSchema[] = {
|
|||
{.name = "sysinfo", .bytes = 1, .type = TSDB_DATA_TYPE_TINYINT, .sysInfo = true},
|
||||
{.name = "createdb", .bytes = 1, .type = TSDB_DATA_TYPE_TINYINT, .sysInfo = true},
|
||||
{.name = "create_time", .bytes = 8, .type = TSDB_DATA_TYPE_TIMESTAMP, .sysInfo = true},
|
||||
{.name = "totp", .bytes = 1, .type = TSDB_DATA_TYPE_TINYINT, .sysInfo = true},
|
||||
{.name = "allowed_host", .bytes = TSDB_PRIVILEDGE_HOST_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = true},
|
||||
{.name = "allowed_datetime", .bytes = TSDB_PRIVILEDGE_HOST_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = true},
|
||||
};
|
||||
|
|
@ -302,6 +303,7 @@ static const SSysDbTableSchema userUsersFullSchema[] = {
|
|||
{.name = "sysinfo", .bytes = 1, .type = TSDB_DATA_TYPE_TINYINT, .sysInfo = true},
|
||||
{.name = "createdb", .bytes = 1, .type = TSDB_DATA_TYPE_TINYINT, .sysInfo = true},
|
||||
{.name = "create_time", .bytes = 8, .type = TSDB_DATA_TYPE_TIMESTAMP, .sysInfo = true},
|
||||
{.name = "totp", .bytes = 1, .type = TSDB_DATA_TYPE_TINYINT, .sysInfo = true},
|
||||
{.name = "change_pass", .bytes = 1, .type = TSDB_DATA_TYPE_TINYINT, .sysInfo = true},
|
||||
{.name = "encrypted_pass", .bytes = TSDB_PASSWORD_LEN + VARSTR_HEADER_SIZE, .type = TSDB_DATA_TYPE_VARCHAR, .sysInfo = true},
|
||||
{.name = "session_per_user", .bytes = 4, .type = TSDB_DATA_TYPE_INT, .sysInfo = true},
|
||||
|
|
|
|||
|
|
@ -404,8 +404,8 @@ typedef struct {
|
|||
|
||||
|
||||
typedef struct {
|
||||
int64_t lastLoginTime; // in seconds
|
||||
int64_t lastFailedLoginTime; // in seconds
|
||||
int32_t lastLoginTime; // in seconds
|
||||
int32_t lastFailedLoginTime; // in seconds
|
||||
int32_t failedLoginCount;
|
||||
} SLoginInfo;
|
||||
|
||||
|
|
@ -422,7 +422,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
char pass[TSDB_PASSWORD_LEN];
|
||||
int64_t setTime; // password set time, in seconds
|
||||
int32_t setTime; // password set time, in seconds
|
||||
} SUserPassword;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ int64_t mndGetUserTimeWhiteListVer(SMnode *pMnode, SUserObj *pUser);
|
|||
|
||||
void mndGetUserLoginInfo(const char *user, SLoginInfo *pLoginInfo);
|
||||
void mndSetUserLoginInfo(const char *user, const SLoginInfo *pLoginInfo);
|
||||
bool mndIsTotpEnabledUser(SUserObj *pUser);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "mndView.h"
|
||||
#include "tglobal.h"
|
||||
#include "tversion.h"
|
||||
#include "totp.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
|
|
@ -293,12 +294,40 @@ static int32_t mndCountUserConns(SMnode *pMnode, const char *user) {
|
|||
|
||||
|
||||
|
||||
static bool constTimeEq(const char *a, const char *b, size_t len) {
|
||||
volatile uint8_t res = 0;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
res |= a[i] ^ b[i];
|
||||
static int32_t verifyPassword(SUserObj* pUser, const char* inputPass) {
|
||||
int32_t code = 0;
|
||||
|
||||
const char* currPass = pUser->passwords[0].pass;
|
||||
char pass[TSDB_PASSWORD_LEN] = {0};
|
||||
(void)memcpy(pass, inputPass, TSDB_PASSWORD_LEN);
|
||||
pass[TSDB_PASSWORD_LEN - 1] = 0;
|
||||
|
||||
if (pUser->passEncryptAlgorithm != 0) {
|
||||
if (pUser->passEncryptAlgorithm != tsiEncryptPassAlgorithm) {
|
||||
return TSDB_CODE_DNODE_INVALID_ENCRYPTKEY;
|
||||
}
|
||||
code = mndEncryptPass(pass, pUser->salt, NULL);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return res == 0;
|
||||
|
||||
// constant time comparison to prevent timing attack
|
||||
volatile uint8_t res = 0;
|
||||
for (size_t i = 0; i < sizeof(pass) - 1; i++) {
|
||||
res |= pass[i] ^ currPass[i];
|
||||
}
|
||||
|
||||
return (res == 0) ? TSDB_CODE_SUCCESS: TSDB_CODE_MND_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool verifyTotp(SUserObj *pUser, int32_t totpCode) {
|
||||
if (!mndIsTotpEnabledUser(pUser)) {
|
||||
return true;
|
||||
}
|
||||
return taosVerifyTotpCode(pUser->totpsecret, sizeof(pUser->totpsecret), totpCode, 6, 1) != 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -336,11 +365,11 @@ static int32_t mndProcessConnectReq(SRpcMsg *pReq) {
|
|||
goto _OVER;
|
||||
}
|
||||
|
||||
int64_t now = taosGetTimestampSec();
|
||||
int32_t now = taosGetTimestampSec();
|
||||
if (pUser->passwordLifeTime > 0 && pUser->passwordGraceTime >= 0) {
|
||||
int64_t lifeTime = now - pUser->passwords[0].setTime;
|
||||
int64_t maxLifeTime = pUser->passwordLifeTime + pUser->passwordGraceTime;
|
||||
if (lifeTime >= maxLifeTime) {
|
||||
int32_t age = now - pUser->passwords[0].setTime;
|
||||
int32_t maxLifeTime = pUser->passwordLifeTime + pUser->passwordGraceTime;
|
||||
if (age >= maxLifeTime) {
|
||||
mGError("user:%s, failed to login from %s since password expired", pReq->info.conn.user, ip);
|
||||
code = TSDB_CODE_MND_USER_PASSWORD_EXPIRED;
|
||||
goto _OVER;
|
||||
|
|
@ -378,24 +407,22 @@ static int32_t mndProcessConnectReq(SRpcMsg *pReq) {
|
|||
}
|
||||
}
|
||||
|
||||
char tmpPass[TSDB_PASSWORD_LEN] = {0};
|
||||
(void)memcpy(tmpPass, connReq.passwd, TSDB_PASSWORD_LEN);
|
||||
tmpPass[TSDB_PASSWORD_LEN - 1] = 0;
|
||||
|
||||
if (pUser->passEncryptAlgorithm != 0) {
|
||||
if (pUser->passEncryptAlgorithm != tsiEncryptPassAlgorithm) {
|
||||
code = TSDB_CODE_DNODE_INVALID_ENCRYPTKEY;
|
||||
goto _OVER;
|
||||
}
|
||||
TAOS_CHECK_GOTO(mndEncryptPass(tmpPass, pUser->salt, NULL), NULL, _OVER);
|
||||
}
|
||||
|
||||
if (tsMndSkipGrant) {
|
||||
loginInfo.lastLoginTime= now;
|
||||
} else if (constTimeEq(tmpPass, pUser->passwords[0].pass, sizeof(tmpPass) - 1)) {
|
||||
if (connReq.connType != CONN_TYPE__AUTH_TEST) {
|
||||
mndSetUserLoginInfo(pReq->info.conn.user, &loginInfo);
|
||||
}
|
||||
} else if (!verifyTotp(pUser, connReq.totpCode)) {
|
||||
mGError("user:%s, failed to login from %s since wrong TOTP code, input:%06d", pReq->info.conn.user, ip, connReq.totpCode);
|
||||
code = TSDB_CODE_MND_WRONG_TOTP_CODE;
|
||||
goto _OVER;
|
||||
} else if ((code = verifyPassword(pUser, connReq.passwd)) == TSDB_CODE_SUCCESS) {
|
||||
loginInfo.failedLoginCount = 0;
|
||||
loginInfo.lastLoginTime= now;
|
||||
} else {
|
||||
if (connReq.connType != CONN_TYPE__AUTH_TEST) {
|
||||
mndSetUserLoginInfo(pReq->info.conn.user, &loginInfo);
|
||||
}
|
||||
} else if (code == TSDB_CODE_MND_AUTH_FAILURE) {
|
||||
mGError("user:%s, failed to login from %s since pass not match, input:%s", pReq->info.conn.user, ip, connReq.passwd);
|
||||
if (pUser->failedLoginAttempts >= 0) {
|
||||
if (loginInfo.failedLoginCount >= pUser->failedLoginAttempts) {
|
||||
|
|
@ -405,11 +432,12 @@ static int32_t mndProcessConnectReq(SRpcMsg *pReq) {
|
|||
loginInfo.failedLoginCount++;
|
||||
loginInfo.lastFailedLoginTime = now;
|
||||
}
|
||||
code = TSDB_CODE_MND_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
mndSetUserLoginInfo(pReq->info.conn.user, &loginInfo);
|
||||
if (code != 0) {
|
||||
if (connReq.connType != CONN_TYPE__AUTH_TEST) {
|
||||
mndSetUserLoginInfo(pReq->info.conn.user, &loginInfo);
|
||||
}
|
||||
goto _OVER;
|
||||
} else {
|
||||
mGError("user:%s, failed to login from %s since %s", pReq->info.conn.user, ip, tstrerror(code));
|
||||
goto _OVER;
|
||||
}
|
||||
|
||||
|
|
@ -430,6 +458,11 @@ static int32_t mndProcessConnectReq(SRpcMsg *pReq) {
|
|||
TAOS_CHECK_GOTO(mndCheckDbPrivilege(pMnode, pReq->info.conn.user, MND_OPER_READ_OR_WRITE_DB, pDb), NULL, _OVER);
|
||||
}
|
||||
|
||||
if (connReq.connType == CONN_TYPE__AUTH_TEST) {
|
||||
code = 0;
|
||||
goto _OVER;
|
||||
}
|
||||
|
||||
pConn = mndCreateConn(pMnode, pReq->info.conn.user, connReq.connType, &pReq->info.conn.cliAddr, connReq.pid,
|
||||
connReq.app, connReq.startTime, connReq.sVer);
|
||||
if (pConn == NULL) {
|
||||
|
|
@ -446,7 +479,6 @@ static int32_t mndProcessConnectReq(SRpcMsg *pReq) {
|
|||
connectRsp.clusterId = pMnode->clusterId;
|
||||
connectRsp.connId = pConn->id;
|
||||
connectRsp.connType = connReq.connType;
|
||||
connectRsp.mustChangePass = mndMustChangePassword(pUser) ? 1 : 0;
|
||||
connectRsp.dnodeNum = mndGetDnodeSize(pMnode);
|
||||
connectRsp.svrTimestamp = taosGetTimestampSec();
|
||||
connectRsp.passVer = pUser->passVersion;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "mndTopic.h"
|
||||
#include "mndTrans.h"
|
||||
#include "tbase64.h"
|
||||
#include "totp.h"
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
|
@ -956,7 +957,7 @@ static void dropOldPasswords(SUserObj *pUser) {
|
|||
reuseMax = 1; // keep at least one password
|
||||
}
|
||||
|
||||
int64_t now = taosGetTimestampSec();
|
||||
int32_t now = taosGetTimestampSec();
|
||||
int32_t index = reuseMax;
|
||||
while(index < pUser->numOfPasswords) {
|
||||
SUserPassword *pPass = &pUser->passwords[index];
|
||||
|
|
@ -1208,7 +1209,7 @@ SSdbRaw *mndUserActionEncode(SUserObj *pUser) {
|
|||
SDB_SET_INT32(pRaw, dataPos, pUser->numOfPasswords, _OVER)
|
||||
for (int32_t i = 0; i < pUser->numOfPasswords; i++) {
|
||||
SDB_SET_BINARY(pRaw, dataPos, pUser->passwords[i].pass, sizeof(pUser->passwords[i].pass), _OVER)
|
||||
SDB_SET_INT64(pRaw, dataPos, pUser->passwords[i].setTime, _OVER)
|
||||
SDB_SET_INT32(pRaw, dataPos, pUser->passwords[i].setTime, _OVER)
|
||||
}
|
||||
SDB_SET_BINARY(pRaw, dataPos, pUser->salt, sizeof(pUser->salt), _OVER)
|
||||
|
||||
|
|
@ -1449,7 +1450,7 @@ static SSdbRow *mndUserActionDecode(SSdbRaw *pRaw) {
|
|||
}
|
||||
for (int32_t i = 0; i < pUser->numOfPasswords; ++i) {
|
||||
SDB_GET_BINARY(pRaw, dataPos, pUser->passwords[i].pass, sizeof(pUser->passwords[i].pass), _OVER);
|
||||
SDB_GET_INT64(pRaw, dataPos, &pUser->passwords[i].setTime, _OVER);
|
||||
SDB_GET_INT32(pRaw, dataPos, &pUser->passwords[i].setTime, _OVER);
|
||||
}
|
||||
SDB_GET_BINARY(pRaw, dataPos, pUser->salt, sizeof(pUser->salt), _OVER)
|
||||
}
|
||||
|
|
@ -1458,7 +1459,7 @@ static SSdbRow *mndUserActionDecode(SSdbRaw *pRaw) {
|
|||
SDB_GET_INT64(pRaw, dataPos, &pUser->createdTime, _OVER)
|
||||
SDB_GET_INT64(pRaw, dataPos, &pUser->updateTime, _OVER)
|
||||
if (sver < USER_VER_SUPPORT_ADVANCED_SECURITY) {
|
||||
pUser->passwords[0].setTime = pUser->updateTime / 1000;
|
||||
pUser->passwords[0].setTime = (int32_t)(pUser->updateTime / 1000);
|
||||
}
|
||||
|
||||
SDB_GET_INT8(pRaw, dataPos, &pUser->superUser, _OVER)
|
||||
|
|
@ -2165,7 +2166,10 @@ static int32_t mndCreateUser(SMnode *pMnode, char *acct, SCreateUserReq *pCreate
|
|||
tstrncpy(userObj.user, pCreate->user, TSDB_USER_LEN);
|
||||
tstrncpy(userObj.acct, acct, TSDB_USER_LEN);
|
||||
if (pCreate->totpseed[0] != 0) {
|
||||
// TODO: generate totp seed
|
||||
int len = taosGenerateTotpSecret(pCreate->totpseed, 0, userObj.totpsecret, sizeof(userObj.totpsecret));
|
||||
if (len < 0) {
|
||||
TAOS_CHECK_GOTO(TSDB_CODE_PAR_INVALID_OPTION_VALUE, &lino, _OVER);
|
||||
}
|
||||
}
|
||||
|
||||
userObj.createdTime = taosGetTimestampMs();
|
||||
|
|
@ -2333,7 +2337,6 @@ _OVER:
|
|||
}
|
||||
|
||||
|
||||
|
||||
static int32_t mndCheckPasswordFmt(const char *pwd) {
|
||||
if (strcmp(pwd, "taosdata") == 0) {
|
||||
return 0;
|
||||
|
|
@ -2357,26 +2360,26 @@ static int32_t mndCheckPasswordFmt(const char *pwd) {
|
|||
return TSDB_CODE_PAR_NAME_OR_PASSWD_TOO_LONG;
|
||||
}
|
||||
|
||||
int32_t upper = 0, lower = 0, number = 0, special = 0;
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
if (taosIsBigChar(pwd[i])) {
|
||||
upper = 1;
|
||||
} else if (taosIsSmallChar(pwd[i])) {
|
||||
lower = 1;
|
||||
} else if (taosIsNumberChar(pwd[i])) {
|
||||
number = 1;
|
||||
} else if (taosIsSpecialChar(pwd[i])) {
|
||||
special = 1;
|
||||
} else {
|
||||
return TSDB_CODE_MND_INVALID_PASS_FORMAT;
|
||||
}
|
||||
if (taosIsComplexString(pwd)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (upper + lower + number + special < 3) {
|
||||
return TSDB_CODE_MND_INVALID_PASS_FORMAT;
|
||||
return TSDB_CODE_MND_INVALID_PASS_FORMAT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int32_t mndCheckTotpSeedFmt(const char *seed) {
|
||||
int32_t len = strlen(seed);
|
||||
if (len < TSDB_USER_TOTPSEED_MIN_LEN) {
|
||||
return TSDB_CODE_PAR_OPTION_VALUE_TOO_SHORT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (taosIsComplexString(seed)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return TSDB_CODE_PAR_INVALID_OPTION_VALUE;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2565,6 +2568,11 @@ static int32_t mndProcessCreateUserReq(SRpcMsg *pReq) {
|
|||
TAOS_CHECK_GOTO(code, &lino, _OVER);
|
||||
}
|
||||
|
||||
if (createReq.totpseed[0] != 0) {
|
||||
code = mndCheckTotpSeedFmt(createReq.totpseed);
|
||||
TAOS_CHECK_GOTO(code, &lino, _OVER);
|
||||
}
|
||||
|
||||
code = mndAcquireUser(pMnode, createReq.user, &pUser);
|
||||
if (pUser != NULL) {
|
||||
TAOS_CHECK_GOTO(TSDB_CODE_MND_USER_ALREADY_EXIST, &lino, _OVER);
|
||||
|
|
@ -3201,7 +3209,11 @@ static int32_t mndProcessAlterUserReq(SRpcMsg *pReq) {
|
|||
}
|
||||
|
||||
if (alterReq.hasTotpseed) {
|
||||
// TODO: totp seed to secret
|
||||
if (alterReq.totpseed[0] == 0) { // clear totp secret
|
||||
memset(newUser.totpsecret, 0, sizeof(newUser.totpsecret));
|
||||
} else if (taosGenerateTotpSecret(alterReq.totpseed, 0, newUser.totpsecret, sizeof(newUser.totpsecret)) < 0) {
|
||||
TAOS_CHECK_GOTO(TSDB_CODE_PAR_INVALID_OPTION_VALUE, &lino, _OVER);
|
||||
}
|
||||
}
|
||||
|
||||
if (alterReq.hasEnable) {
|
||||
|
|
@ -3739,6 +3751,17 @@ _exit:
|
|||
TAOS_RETURN(code);
|
||||
}
|
||||
|
||||
|
||||
bool mndIsTotpEnabledUser(SUserObj *pUser) {
|
||||
for (int32_t i = 0; i < sizeof(pUser->totpsecret); i++) {
|
||||
if (pUser->totpsecret[i] != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int32_t mndRetrieveUsers(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock *pBlock, int32_t rows) {
|
||||
SMnode *pMnode = pReq->info.node;
|
||||
SSdb *pSdb = pMnode->pSdb;
|
||||
|
|
@ -3783,6 +3806,11 @@ static int32_t mndRetrieveUsers(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock *pBl
|
|||
pColInfo = taosArrayGet(pBlock->pDataBlock, cols);
|
||||
COL_DATA_SET_VAL_GOTO((const char *)&pUser->createdTime, false, pUser, pShow->pIter, _exit);
|
||||
|
||||
cols++;
|
||||
pColInfo = taosArrayGet(pBlock->pDataBlock, cols);
|
||||
flag = mndIsTotpEnabledUser(pUser) ? 1 : 0;
|
||||
COL_DATA_SET_VAL_GOTO((const char *)&flag, false, pUser, pShow->pIter, _exit);
|
||||
|
||||
cols++;
|
||||
|
||||
int32_t tlen = convertIpWhiteListToStr(pUser, &buf);
|
||||
|
|
@ -3886,6 +3914,11 @@ static int32_t mndRetrieveUsersFull(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock
|
|||
pColInfo = taosArrayGet(pBlock->pDataBlock, cols);
|
||||
COL_DATA_SET_VAL_GOTO((const char *)&pUser->createdTime, false, pUser, pShow->pIter, _exit);
|
||||
|
||||
cols++;
|
||||
pColInfo = taosArrayGet(pBlock->pDataBlock, cols);
|
||||
flag = mndIsTotpEnabledUser(pUser) ? 1 : 0;
|
||||
COL_DATA_SET_VAL_GOTO((const char *)&flag, false, pUser, pShow->pIter, _exit);
|
||||
|
||||
cols++;
|
||||
pColInfo = taosArrayGet(pBlock->pDataBlock, cols);
|
||||
COL_DATA_SET_VAL_GOTO((const char *)&pUser->changePass, false, pUser, pShow->pIter, _exit);
|
||||
|
|
|
|||
|
|
@ -6751,6 +6751,50 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
|
|||
.sprocessFunc = NULL,
|
||||
.finalizeFunc = NULL
|
||||
},
|
||||
{
|
||||
.name = "generate_totp_secret",
|
||||
.type = FUNCTION_TYPE_GENERATE_TOTP_SECRET,
|
||||
.classification = FUNC_MGT_SCALAR_FUNC | FUNC_MGT_STRING_FUNC,
|
||||
.parameters = {.minParamNum = 1,
|
||||
.maxParamNum = 1,
|
||||
.paramInfoPattern = 1,
|
||||
.inputParaInfo[0][0] = {.isLastParam = true,
|
||||
.startParam = 1,
|
||||
.endParam = 1,
|
||||
.validDataType = FUNC_PARAM_SUPPORT_VARCHAR_TYPE | FUNC_PARAM_SUPPORT_NCHAR_TYPE,
|
||||
.validNodeType = FUNC_PARAM_SUPPORT_EXPR_NODE,
|
||||
.paramAttribute = FUNC_PARAM_NO_SPECIFIC_ATTRIBUTE,
|
||||
.valueRangeFlag = FUNC_PARAM_NO_SPECIFIC_VALUE,},
|
||||
.outputParaInfo = {.validDataType = FUNC_PARAM_SUPPORT_VARCHAR_TYPE | FUNC_PARAM_SUPPORT_NCHAR_TYPE}},
|
||||
.translateFunc = translateOutFirstIn,
|
||||
.getEnvFunc = NULL,
|
||||
.initFunc = NULL,
|
||||
.sprocessFunc = generateTotpSecretFunction,
|
||||
.finalizeFunc = NULL,
|
||||
},
|
||||
{
|
||||
// this function generates TOTP code based on the TOTP secret,
|
||||
// it is not documented and only used for testing purpose to verify the correctness of TOTP code generation.
|
||||
.name = "generate_totp_code",
|
||||
.type = FUNCTION_TYPE_GENERATE_TOTP_CODE,
|
||||
.classification = FUNC_MGT_SCALAR_FUNC | FUNC_MGT_STRING_FUNC,
|
||||
.parameters = {.minParamNum = 1,
|
||||
.maxParamNum = 1,
|
||||
.paramInfoPattern = 1,
|
||||
.inputParaInfo[0][0] = {.isLastParam = true,
|
||||
.startParam = 1,
|
||||
.endParam = 1,
|
||||
.validDataType = FUNC_PARAM_SUPPORT_VARCHAR_TYPE | FUNC_PARAM_SUPPORT_NCHAR_TYPE,
|
||||
.validNodeType = FUNC_PARAM_SUPPORT_EXPR_NODE,
|
||||
.paramAttribute = FUNC_PARAM_NO_SPECIFIC_ATTRIBUTE,
|
||||
.valueRangeFlag = FUNC_PARAM_NO_SPECIFIC_VALUE,},
|
||||
.outputParaInfo = {.validDataType = FUNC_PARAM_SUPPORT_VARCHAR_TYPE}},
|
||||
.translateFunc = translateOutFirstIn,
|
||||
.getEnvFunc = NULL,
|
||||
.initFunc = NULL,
|
||||
.sprocessFunc = generateTotpCodeFunction,
|
||||
.finalizeFunc = NULL,
|
||||
},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ user_enabled(A) ::= ENABLE NK_INTEGER(B).
|
|||
|
||||
%type user_option { SUserOptions* }
|
||||
user_option(A) ::= TOTPSEED NK_STRING(B). { A = mergeUserOptions(pCxt, NULL, NULL); setUserOptionsTotpseed(pCxt, A, &B); }
|
||||
user_option(A) ::= TOTPSEED NULL. { A = mergeUserOptions(pCxt, NULL, NULL); setUserOptionsTotpseed(pCxt, A, NULL); }
|
||||
user_option(A) ::= user_enabled(B). { A = mergeUserOptions(pCxt, NULL, NULL); A->enable = B; A->hasEnable = true; }
|
||||
user_option(A) ::= SYSINFO NK_INTEGER(B). { A = mergeUserOptions(pCxt, NULL, NULL); A->sysinfo = taosStr2Int8(B.z, NULL, 10); A->hasSysinfo = true; }
|
||||
user_option(A) ::= IS_IMPORT NK_INTEGER(B). { A = mergeUserOptions(pCxt, NULL, NULL); A->isImport = taosStr2Int8(B.z, NULL, 10); A->hasIsImport = true; }
|
||||
|
|
|
|||
|
|
@ -121,31 +121,11 @@ static bool isValidSimplePassword(const char* password) {
|
|||
|
||||
|
||||
|
||||
static bool isComplexString(const char* str) {
|
||||
int hasUpper = 0, hasLower = 0, hasDigit = 0, hasSpecial = 0;
|
||||
|
||||
for (char c = *str; c != 0; c = *(++str)) {
|
||||
if (taosIsBigChar(c)) {
|
||||
hasUpper = 1;
|
||||
} else if (taosIsSmallChar(c)) {
|
||||
hasLower = 1;
|
||||
} else if (taosIsNumberChar(c)) {
|
||||
hasDigit = 1;
|
||||
} else if (taosIsSpecialChar(c)) {
|
||||
hasSpecial = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return (hasUpper + hasLower + hasDigit + hasSpecial) >= 3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool isValidStrongPassword(const char* password) {
|
||||
if (strcmp(password, "taosdata") == 0) {
|
||||
return true;
|
||||
}
|
||||
return isComplexString(password);
|
||||
return taosIsComplexString(password);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4275,6 +4255,11 @@ void setUserOptionsTotpseed(SAstCreateContext* pCxt, SUserOptions* pUserOptions,
|
|||
}
|
||||
pUserOptions->hasTotpseed = true;
|
||||
|
||||
if (pTotpseed == NULL) { // clear TOTP secret
|
||||
memset(pUserOptions->totpseed, 0, sizeof(pUserOptions->totpseed));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pTotpseed->n >= sizeof(pUserOptions->totpseed) * 2) {
|
||||
pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_OPTION_VALUE_TOO_LONG, "TOTPSEED", sizeof(pUserOptions->totpseed));
|
||||
return;
|
||||
|
|
@ -4347,7 +4332,7 @@ static bool isValidUserOptions(SAstCreateContext* pCxt, const SUserOptions* opts
|
|||
return false;
|
||||
}
|
||||
|
||||
if (opts->hasTotpseed && !isComplexString(opts->totpseed)) {
|
||||
if (opts->hasTotpseed && opts->totpseed[0] != 0 && !taosIsComplexString(opts->totpseed)) {
|
||||
pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_OPTION_VALUE, "TOTPSEED");
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "tjson.h"
|
||||
#include "ttime.h"
|
||||
#include "tcompare.h"
|
||||
#include "totp.h"
|
||||
|
||||
typedef float (*_float_fn)(float);
|
||||
typedef float (*_float_fn_2)(float, float);
|
||||
|
|
@ -1723,6 +1724,166 @@ _return:
|
|||
return code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int32_t base32Encode(const uint8_t *in, int32_t inLen, char *out) {
|
||||
int buffer = 0, bits = 0;
|
||||
int outLen = 0;
|
||||
|
||||
// process all input bytes
|
||||
for (int i = 0; i < inLen; i++) {
|
||||
buffer = (buffer << 8) | in[i];
|
||||
bits += 8;
|
||||
|
||||
while (bits >= 5) {
|
||||
int v = (buffer >> (bits - 5)) & 0x1F;
|
||||
out[outLen++] = (v >= 26) ? (v - 26 + '2') : (v + 'A');
|
||||
bits -= 5;
|
||||
}
|
||||
}
|
||||
|
||||
// process remaining bits
|
||||
if (bits > 0) {
|
||||
int v = (buffer << (5 - bits)) & 0x1F;
|
||||
out[outLen++] = (v >= 26) ? (v - 26 + '2') : (v + 'A');
|
||||
}
|
||||
|
||||
out[outLen] = '\0';
|
||||
return outLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32_t generateTotpSecretFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
||||
SColumnInfoData *pInputData = pInput->columnData;
|
||||
SColumnInfoData *pOutputData = pOutput->columnData;
|
||||
|
||||
char secret[64] = {0};
|
||||
int32_t bufLen = (sizeof(secret) * 8) / 5 + 4 + VARSTR_HEADER_SIZE + 1;
|
||||
char *pOutputBuf = taosMemoryMalloc(bufLen);
|
||||
if (!pOutputBuf) {
|
||||
qError("generate_totp_secret function alloc memory failed");
|
||||
return terrno;
|
||||
}
|
||||
for (int32_t i = 0; i < pInput->numOfRows; ++i) {
|
||||
if (colDataIsNull_s(pInputData, i)) {
|
||||
colDataSetNULL(pOutputData, i);
|
||||
continue;
|
||||
}
|
||||
char *input = colDataGetData(pInput[0].columnData, i);
|
||||
|
||||
int len = taosGenerateTotpSecret(varDataVal(input), varDataLen(input), secret, sizeof(secret));
|
||||
if (len < 0) {
|
||||
taosMemoryFree(pOutputBuf);
|
||||
SCL_ERR_RET(TSDB_CODE_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
char *output = pOutputBuf;
|
||||
len = base32Encode(secret, len, varDataVal(output));
|
||||
|
||||
varDataSetLen(output, len);
|
||||
int32_t code = colDataSetVal(pOutputData, i, output, false);
|
||||
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
taosMemoryFree(pOutputBuf);
|
||||
SCL_ERR_RET(code);
|
||||
}
|
||||
}
|
||||
|
||||
pOutput->numOfRows = pInput->numOfRows;
|
||||
taosMemoryFree(pOutputBuf);
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int32_t base32Decode(const char *in, int32_t inLen, uint8_t *out) {
|
||||
int buffer = 0, bits = 0;
|
||||
int32_t outLen = 0;
|
||||
|
||||
for (int32_t i = 0; i < inLen; i++) {
|
||||
char c = in[i];
|
||||
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
c -= 'a';
|
||||
} else if (c >= 'A' && c <= 'Z') {
|
||||
c -= 'A';
|
||||
} else if (c >= '2' && c <= '7') {
|
||||
c = c - '2' + 26;
|
||||
} else if (c == '=') {
|
||||
break; // padding character
|
||||
} else {
|
||||
return -1; // invalid character
|
||||
}
|
||||
buffer = (buffer << 5) | c;
|
||||
bits += 5;
|
||||
if (bits >= 8) {
|
||||
out[outLen++] = (buffer >> (bits - 8)) & 0xFF;
|
||||
bits -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
return outLen; // success
|
||||
}
|
||||
|
||||
|
||||
// this function generates TOTP code based on the TOTP secret,
|
||||
// it is not documented and only used for testing purpose to verify the correctness of
|
||||
// TOTP code generation.
|
||||
int32_t generateTotpCodeFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
||||
SColumnInfoData *pInputData = pInput->columnData;
|
||||
SColumnInfoData *pOutputData = pOutput->columnData;
|
||||
char *output = taosMemoryMalloc(VARSTR_HEADER_SIZE + 6 + 1);
|
||||
if (!output) {
|
||||
qError("generate_totp code function alloc memory failed");
|
||||
return terrno;
|
||||
}
|
||||
|
||||
uint8_t secret[64] = {0};
|
||||
int32_t secretLen = 0;
|
||||
|
||||
for (int32_t i = 0; i < pInput->numOfRows; ++i) {
|
||||
if (colDataIsNull_s(pInputData, i)) {
|
||||
colDataSetNULL(pOutputData, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
char *in = colDataGetData(pInput[0].columnData, i);
|
||||
if (varDataLen(in) > 100) {
|
||||
colDataSetNULL(pOutputData, i);
|
||||
qError("the %d secret is too long", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
secretLen = base32Decode(varDataVal(in), varDataLen(in), secret);
|
||||
if (secretLen <= 0) {
|
||||
qError("failed to decode the %d base32 secret", i);
|
||||
colDataSetNULL(pOutputData, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t totp = taosGenerateTotpCode(secret, secretLen, 6);
|
||||
if (totp < 0) {
|
||||
qError("failed to generate the %d totp code", i);
|
||||
colDataSetNULL(pOutputData, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
int len = taosFormatTotp(totp, 6, varDataVal(output), 7);
|
||||
varDataSetLen(output, len);
|
||||
int32_t code = colDataSetVal(pOutputData, i, output, false);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
taosMemoryFree(output);
|
||||
SCL_ERR_RET(code);
|
||||
}
|
||||
}
|
||||
|
||||
pOutput->numOfRows = pInput->numOfRows;
|
||||
taosMemoryFree(output);
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int32_t md5Function(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
||||
SColumnInfoData *pInputData = pInput->columnData;
|
||||
SColumnInfoData *pOutputData = pOutput->columnData;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ if(TD_LINUX)
|
|||
DEP_ext_xxhash(util)
|
||||
DEP_ext_lzma2(util)
|
||||
endif()
|
||||
if (NOT ${TD_WINDOWS})
|
||||
DEP_ext_ssl(util)
|
||||
endif()
|
||||
DEP_ext_geos(util)
|
||||
if(${BUILD_PCRE2}) # {
|
||||
DEP_ext_pcre2(util)
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_COLUMN_REF, "Invalid column refere
|
|||
TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_SLIDING_OFFSET, "Invalid sliding offset")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_INTERVAL_OFFSET, "Invalid interval offset")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_NOT_SUPPORTTED_IN_WINDOWS, "Operation not supported in windows")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_TSC_INVALID_TOTP_CODE, "Invalid TOTP code")
|
||||
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_TSC_INTERNAL_ERROR, "Internal error")
|
||||
|
||||
|
|
@ -235,7 +236,7 @@ TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_ALTER_OPER, "Invalid alter operati
|
|||
TAOS_DEFINE_ERROR(TSDB_CODE_MND_AUTH_FAILURE, "Authentication failure")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_MND_PRIVILEDGE_EXIST, "User already have this priviledge")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_MND_USER_PASSWORD_REUSE, "Password reuse detected")
|
||||
//TAOS_DEFINE_ERROR(TSDB_CODE_MND_USER_TIME_RANGE_CONFLICT, "Date time black/white list conflict")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_MND_WRONG_TOTP_CODE, "Wrong TOTP code")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_MND_TOO_MANY_USER_IP_RANGE, "Too many ranges in IP white list")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_MND_TOO_MANY_USER_TIME_RANGE, "Too many ranges in date time white list")
|
||||
|
||||
|
|
|
|||
135
source/util/src/totp.c
Normal file
135
source/util/src/totp.c
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define _DEFAULT_SOURCE
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include "ttime.h"
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
static int32_t doGenerateTotp(const uint8_t *secret, size_t secretLen, uint64_t tm, int digits) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int taosGenerateTotpSecret(const char *seed, size_t seedLen, uint8_t *secret, size_t secretLen) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // WINDOWS
|
||||
|
||||
// for OpenSSL HMAC functions
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
// doGenerateTotp generates a TOTP code based on the provided secret and time.
|
||||
// secret is a byte array, not a base32 string
|
||||
static int32_t doGenerateTotp(const uint8_t *secret, size_t secretLen, uint64_t tm, int digits) {
|
||||
if (secretLen < 16) {
|
||||
return -1; // secret is too short
|
||||
}
|
||||
|
||||
// convert timestamp to big-endian if system is little-endian
|
||||
uint32_t endianness = 0xdeadbeef;
|
||||
if ((*(const uint8_t *)&endianness) == 0xef) {
|
||||
tm = ((tm & 0x00000000ffffffff) << 32) | ((tm & 0xffffffff00000000) >> 32);
|
||||
tm = ((tm & 0x0000ffff0000ffff) << 16) | ((tm & 0xffff0000ffff0000) >> 16);
|
||||
tm = ((tm & 0x00ff00ff00ff00ff) << 8) | ((tm & 0xff00ff00ff00ff00) >> 8);
|
||||
};
|
||||
|
||||
// calculate HMAC-SHA1
|
||||
uint8_t hmacResult[EVP_MAX_MD_SIZE];
|
||||
unsigned int hmacLen;
|
||||
if (HMAC(EVP_sha1(), secret, secretLen, (const uint8_t *)&tm, sizeof(tm), hmacResult, &hmacLen) == NULL) {
|
||||
return -1; // HMAC calculation failed
|
||||
}
|
||||
|
||||
// use the lower 4 bits of the last byte as offset
|
||||
int offset = hmacResult[hmacLen - 1] & 0x0F;
|
||||
|
||||
int32_t binary = (hmacResult[offset] & 0x7F) << 24;
|
||||
binary |= (hmacResult[offset + 1] & 0xFF) << 16;
|
||||
binary |= (hmacResult[offset + 2] & 0xFF) << 8;
|
||||
binary |= hmacResult[offset + 3] & 0xFF;
|
||||
|
||||
return binary % (int32_t)pow(10, digits);
|
||||
}
|
||||
|
||||
|
||||
// generate TOTP secret from seed
|
||||
// secret is a byte array, not a base32 string
|
||||
int taosGenerateTotpSecret(const char *seed, size_t seedLen, uint8_t *secret, size_t secretLen) {
|
||||
// TOTP secret requires at least 16 characters for adequate security
|
||||
if (secretLen < 16) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seedLen == 0) {
|
||||
seedLen = strlen(seed);
|
||||
}
|
||||
|
||||
// calculate HMAC-SHA1
|
||||
uint8_t hmacResult[EVP_MAX_MD_SIZE];
|
||||
unsigned int hmacLen;
|
||||
if (HMAC(EVP_sha256(), NULL, 0, seed, seedLen, hmacResult, &hmacLen) == NULL) {
|
||||
return 0; // HMAC calculation failed
|
||||
}
|
||||
|
||||
if (hmacLen > secretLen) {
|
||||
hmacLen = secretLen;
|
||||
}
|
||||
(void)memcpy(secret, hmacResult, hmacLen);
|
||||
|
||||
return hmacLen;
|
||||
}
|
||||
|
||||
#endif // WINDOWS
|
||||
|
||||
|
||||
// formatTotp formats the TOTP code with leading zeros to match the specified digit length.
|
||||
int taosFormatTotp(int32_t totp, int digits, char *buffer, size_t size) {
|
||||
return snprintf(buffer, size, "%0*d", digits, totp);
|
||||
}
|
||||
|
||||
// generate TOTP code for given secret at current time
|
||||
// secret is a byte array, not a base32 string
|
||||
int32_t taosGenerateTotpCode(const uint8_t *secret, size_t secretLen, int digits) {
|
||||
if (secretLen < 16) {
|
||||
return -1; // secret is too short
|
||||
}
|
||||
|
||||
uint64_t now = taosGetTimestampSec() / 30;
|
||||
return doGenerateTotp(secret, secretLen, now, digits);
|
||||
}
|
||||
|
||||
// verify TOTP code for given secret at current time with allowed window
|
||||
// secret is a byte array, not a base32 string
|
||||
int taosVerifyTotpCode(const uint8_t *secret, size_t secretLen, int32_t userCode, int digits, int window) {
|
||||
if (secretLen < 16) {
|
||||
return 0; // secret is too short
|
||||
}
|
||||
|
||||
uint64_t now = taosGetTimestampSec() / 30;
|
||||
for (int i = -window; i <= window; i++) {
|
||||
if (doGenerateTotp(secret, secretLen, now + i, digits) == userCode) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -588,6 +588,29 @@ bool taosIsSpecialChar(char c) {
|
|||
}
|
||||
}
|
||||
|
||||
// check if the string is a complex string, a complex string contains
|
||||
// at least 3 types of characters: upper, lower, digit, special
|
||||
bool taosIsComplexString(const char* str) {
|
||||
int hasUpper = 0, hasLower = 0, hasDigit = 0, hasSpecial = 0;
|
||||
|
||||
for (char c = *str; c != 0; c = *(++str)) {
|
||||
if (taosIsBigChar(c)) {
|
||||
hasUpper = 1;
|
||||
} else if (taosIsSmallChar(c)) {
|
||||
hasLower = 1;
|
||||
} else if (taosIsNumberChar(c)) {
|
||||
hasDigit = 1;
|
||||
} else if (taosIsSpecialChar(c)) {
|
||||
hasSpecial = 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (hasUpper + hasLower + hasDigit + hasSpecial) >= 3;
|
||||
}
|
||||
|
||||
|
||||
void tTrimMountPrefix(char *fullName) {
|
||||
if (fullName == NULL) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ TSDB_CODE_TSC_ENCODE_PARAM_NULL = 0x80000232
|
|||
TSDB_CODE_TSC_COMPRESS_PARAM_ERROR = 0x80000233
|
||||
TSDB_CODE_TSC_COMPRESS_LEVEL_ERROR = 0x80000234
|
||||
TSDB_CODE_TSC_STMT_BIND_NUMBER_ERROR = 0x80000236
|
||||
TSDB_CODE_TSC_INVALID_TOTP_CODE = 0x80000238
|
||||
TSDB_CODE_TSC_INTERNAL_ERROR = 0x800002FF
|
||||
TSDB_CODE_MND_REQ_REJECTED = 0x80000300
|
||||
TSDB_CODE_MND_NO_RIGHTS = 0x80000303
|
||||
|
|
@ -145,6 +146,7 @@ TSDB_CODE_MND_INVALID_ALTER_OPER = 0x80000356
|
|||
TSDB_CODE_MND_AUTH_FAILURE = 0x80000357
|
||||
TSDB_CODE_MND_PRIVILEDGE_EXIST = 0x80000359
|
||||
TSDB_CODE_MND_USER_PASSWORD_REUSE = 0x8000035A
|
||||
TSDB_CODE_MND_WRONG_TOTP_CODE = 0x8000035B
|
||||
TSDB_CODE_MND_TOO_MANY_USER_IP_RANGE = 0x8000035C
|
||||
TSDB_CODE_MND_TOO_MANY_USER_TIME_RANGE = 0x8000035D
|
||||
TSDB_CODE_MND_STB_ALREADY_EXIST = 0x80000360
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ ignoreCodes = [
|
|||
'0x80000129', '0x8000012C', '0x8000012D', '0x8000012E', '0x8000012F', '0x80000136', '0x80000137', '0x80000138', '0x80000139', '0x8000013A',
|
||||
'0x8000013B', '0x80000200', '0x80000201', '0x80000202', '0x80000203', '0x80000204', '0x80000205', '0x80000206', '0x8000020B', '0x8000020E',
|
||||
'0x80000210', '0x80000212', '0x80000214', '0x80000215', '0x80000217', '0x8000021B', '0x8000021C', '0x8000021D', '0x8000021E', '0x80000220',
|
||||
'0x80000221', '0x8000022C', '0x80000232', '0x80000233', '0x80000234', '0x80000235', '0x80000236', '0x800002FF', '0x80000300', '0x80000316',
|
||||
'0x80000221', '0x8000022C', '0x80000231', '0x80000232', '0x80000233', '0x80000234', '0x80000235', '0x80000236', '0x800002FF', '0x80000300', '0x80000316',
|
||||
'0x80000317', '0x80000338', '0x80000339', '0x8000033F', '0x80000343', '0x80000345', '0x80000356', '0x80000359', '0x8000035A', '0x8000035B',
|
||||
'0x8000035C', '0x8000035D', '0x80000362', '0x8000038C', '0x8000038E', '0x8000039B', '0x8000039C', '0x8000039D', '0x8000039E', '0x800003A6',
|
||||
'0x800003A7', '0x800003AA', '0x800003AB', '0x800003AC', '0x800003AD', '0x800003B0', '0x800003B2', '0x800003B4', '0x800003B5', '0x800003BA',
|
||||
|
|
|
|||
Loading…
Reference in a new issue