diff --git a/deps/oblib/src/lib/charset/ob_charset.h b/deps/oblib/src/lib/charset/ob_charset.h index 4e9acbafe507ba4617607dce44e2c26f0597e1ae..dd3062b851f7f2fcdb19dcb0175d86028bfdbd7a 100644 --- a/deps/oblib/src/lib/charset/ob_charset.h +++ b/deps/oblib/src/lib/charset/ob_charset.h @@ -277,7 +277,7 @@ public: return result; } template - static int foreach_char(common::ObString& str, common::ObCollationType collation_type, foreach_char_func& func) + static int foreach_char(const common::ObString &str, common::ObCollationType collation_type, foreach_char_func &func) { int ret = common::OB_SUCCESS; int32_t wchar = 0; diff --git a/deps/oblib/src/lib/ob_name_def.h b/deps/oblib/src/lib/ob_name_def.h index 98ea1066360818cf0a768fdf85a64dff2831c73a..5f4773454ff10ec6ce6ece82439aae132c3d0bf5 100644 --- a/deps/oblib/src/lib/ob_name_def.h +++ b/deps/oblib/src/lib/ob_name_def.h @@ -336,6 +336,7 @@ #define N_OPERATOR_MONITOR_INFO "op_info" #define N_PLAN_MONITOR_INFO "plan_info" #define N_ANY_VAL "any_value" +#define N_VALIDATE_PASSWORD_STRENGTH "validate_password_strength" // common comparison operator #define N_LESS_THAN "<" #define N_GREATER_THAN ">" diff --git a/src/sql/CMakeLists.txt b/src/sql/CMakeLists.txt index 4f037d0408761a44309ba217eb81e495ac1c14f1..9255a4c089c7aa04100bce348336dfef5d75a40d 100644 --- a/src/sql/CMakeLists.txt +++ b/src/sql/CMakeLists.txt @@ -464,6 +464,8 @@ ob_set_subtarget(ob_sql engine engine/expr/ob_expr_calc_urowid.cpp engine/expr/ob_expr_cardinality.cpp engine/expr/ob_expr_coll_pred.cpp + engine/expr/ob_expr_validate_password_strength.cpp + engine/expr/ob_expr_validate_password_strength.h engine/expr/ob_expr_benchmark.cpp engine/expr/ob_expr_benchmark.h engine/join/ob_basic_nested_loop_join.cpp diff --git a/src/sql/engine/expr/ob_expr_eval_functions.cpp b/src/sql/engine/expr/ob_expr_eval_functions.cpp index fa411a003ea479d568aa6e3c7fa86914b494111d..aae88e129fee1f43bc14fb4c080e7852d1988a08 100644 --- a/src/sql/engine/expr/ob_expr_eval_functions.cpp +++ b/src/sql/engine/expr/ob_expr_eval_functions.cpp @@ -187,6 +187,7 @@ #include "ob_expr_degrees.h" #include "ob_expr_weight_string.h" #include "ob_expr_any_value.h" +#include "ob_expr_validate_password_strength.h" #include "ob_expr_benchmark.h" namespace oceanbase { @@ -704,7 +705,7 @@ static ObExpr::EvalFunc g_expr_eval_functions[] = { NULL, /* 440 */ ObExprTimestamp::calc_timestamp1, /* 441 */ ObExprTimestamp::calc_timestamp2, /* 442 */ - NULL, /* 443 */ + ObExprValidatePasswordStrength::eval_password_strength, /* 443 */ NULL, /* 444 */ NULL, /* 445 */ NULL, /* 446 */ diff --git a/src/sql/engine/expr/ob_expr_operator_factory.cpp b/src/sql/engine/expr/ob_expr_operator_factory.cpp index 67d3fec59007b7140ec4186db5770aacaec47241..3087eb8c2e04c12397a4fbb66b40fa7543b6b160 100644 --- a/src/sql/engine/expr/ob_expr_operator_factory.cpp +++ b/src/sql/engine/expr/ob_expr_operator_factory.cpp @@ -271,6 +271,7 @@ #include "sql/engine/expr/ob_expr_degrees.h" #include "sql/engine/expr/ob_expr_weight_string.h" #include "sql/engine/expr/ob_expr_any_value.h" +#include "sql/engine/expr/ob_expr_validate_password_strength.h" #include "sql/engine/expr/ob_expr_benchmark.h" using namespace oceanbase::common; @@ -683,6 +684,7 @@ void ObExprOperatorFactory::register_expr_operators() REG_OP(ObExprTimeFormat); REG_OP(ObExprTimestamp); REG_OP(ObExprDegrees); + REG_OP(ObExprValidatePasswordStrength); REG_OP(ObExprWeightString); REG_OP(ObExprBenchmark); REG_OP(ObExprDay); diff --git a/src/sql/engine/expr/ob_expr_validate_password_strength.cpp b/src/sql/engine/expr/ob_expr_validate_password_strength.cpp new file mode 100644 index 0000000000000000000000000000000000000000..19f8f9e84a2455fd3e05a234860030a6df344f2d --- /dev/null +++ b/src/sql/engine/expr/ob_expr_validate_password_strength.cpp @@ -0,0 +1,211 @@ +// Copyright (c) 2021 Alibaba Inc. All Rights Reserved. +// Author: +// renqing + +#define USING_LOG_PREFIX SQL_ENG + +#include "ob_expr_validate_password_strength.h" + +#include "sql/session/ob_sql_session_info.h" +#include "sql/engine/ob_exec_context.h" + +using namespace oceanbase::common; +using namespace oceanbase::sql; +namespace oceanbase { +namespace sql { + +const ObValidatePasswordFunc ObExprValidatePasswordStrength::validate_funcs_[STRENGTH_MAX] = { + NULL, // STRENGTH_WEAK is default strength, need no validation + ObExprValidatePasswordStrength::validate_password_lessweak, + ObExprValidatePasswordStrength::validate_password_low, + ObExprValidatePasswordStrength::validate_password_medium, + ObExprValidatePasswordStrength::validate_password_strong, +}; + +ObExprValidatePasswordStrength::ObExprValidatePasswordStrength(ObIAllocator &alloc) + : ObFuncExprOperator( + alloc, T_FUN_SYS_VALIDATE_PASSWORD_STRENGTH, N_VALIDATE_PASSWORD_STRENGTH, 1, NOT_ROW_DIMENSION) +{} + +ObExprValidatePasswordStrength::~ObExprValidatePasswordStrength() +{} + +int ObExprValidatePasswordStrength::calc_result_type1( + ObExprResType &type, ObExprResType &type1, ObExprTypeCtx &type_ctx) const +{ + int ret = OB_SUCCESS; + UNUSED(type_ctx); + type.set_int(); + type.set_scale(common::ObAccuracy::DDL_DEFAULT_ACCURACY[common::ObIntType].scale_); + type.set_precision(common::ObAccuracy::DDL_DEFAULT_ACCURACY[common::ObIntType].precision_); + type1.set_calc_type(common::ObVarcharType); + ObExprOperator::calc_result_flag1(type, type1); + return ret; +} + +int ObExprValidatePasswordStrength::calc_result1(ObObj &result, const ObObj &obj1, ObExprCtx &expr_ctx) const +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(expr_ctx.my_session_) || OB_ISNULL(expr_ctx.calc_buf_)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("Invalid argument", KP(expr_ctx.my_session_), KP(expr_ctx.calc_buf_), K(ret)); + } else if (obj1.is_null()) { + result.set_null(); + } else { + ObString password; + int strength = 0; + if (OB_FAIL(ObCharset::charset_convert(*expr_ctx.calc_buf_, + obj1.get_string(), + obj1.get_collation_type(), + ObCharset::get_system_collation(), + password))) { + LOG_WARN("fail to convert password to sys collation", K(obj1), K(ret)); + } else if (OB_FAIL(calc_password_strength(password, *expr_ctx.my_session_, strength))) { + LOG_WARN("fail to calc password strength", K(password), K(ret)); + } else { + result.set_int(strength); + } + } + return ret; +} + +int ObExprValidatePasswordStrength::cg_expr(ObExprCGCtx &op_cg_ctx, const ObRawExpr &raw_expr, ObExpr &rt_expr) const +{ + int ret = OB_SUCCESS; + UNUSED(op_cg_ctx); + UNUSED(raw_expr); + rt_expr.eval_func_ = eval_password_strength; + return ret; +} + +int ObExprValidatePasswordStrength::eval_password_strength(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res_datum) +{ + int ret = OB_SUCCESS; + ObDatum *pwd_param = NULL; + ObCollationType c_src = ObCollationType::CS_TYPE_INVALID; + ObString password_orig; + ObString password; + int strength = 0; + + const ObBasicSessionInfo *session = ctx.exec_ctx_.get_my_session(); + if (OB_ISNULL(session)) { + LOG_WARN("session is null", K(ret)); + } else if (OB_FAIL(expr.args_[0]->eval(ctx, pwd_param))) { + LOG_WARN("eval arg failed", K(ret)); + } else if (pwd_param->is_null()) { + res_datum.set_null(); + } else if (FALSE_IT(password_orig = pwd_param->get_string())) { + } else if (FALSE_IT(c_src = expr.args_[0]->obj_meta_.get_collation_type())) { + } else if (OB_FAIL(ObCharset::charset_convert( + ctx.exec_ctx_.get_allocator(), password_orig, c_src, ObCharset::get_system_collation(), password))) { + LOG_WARN("fail to convert password to sys collation", K(password_orig), K(c_src), K(ret)); + } else if (OB_FAIL(calc_password_strength(password, *session, strength))) { + LOG_WARN("fail to calc password strength", K(password), K(ret)); + } else { + res_datum.set_int(strength); + } + + return ret; +} + +int ObExprValidatePasswordStrength::calc_password_strength( + const ObString &password, const ObBasicSessionInfo &session, int &strength) +{ + int ret = OB_SUCCESS; + bool passed = true; + strength = 0; + for (int i = STRENGTH_LESSWEAK; OB_SUCC(ret) && passed && i < STRENGTH_MAX; ++i) { + if (OB_FAIL(validate_funcs_[i](password, session, passed))) { + LOG_WARN("failed to validate", K(password), K(i), K(ret)); + } else if (passed) { + strength = i * PASSWORD_STRENGTH_MULTIPLIER; + } + } + return ret; +} + +int ObExprValidatePasswordStrength::validate_password_lessweak( + const ObString &password, const ObBasicSessionInfo &session, bool &passed) +{ + int ret = OB_SUCCESS; + int64_t check_user_name = 0; + passed = false; + if (OB_FAIL(session.get_sys_variable(share::SYS_VAR_VALIDATE_PASSWORD_CHECK_USER_NAME, check_user_name))) { + LOG_WARN("fail to get validate_password_length", K(ret)); + } else { + passed = password.length() >= VALID_PASSWORD_LENGTH_MIN && + (!check_user_name && !ObCharset::case_insensitive_equal(password, session.get_user_name())); + } + return ret; +} + +int ObExprValidatePasswordStrength::validate_password_low( + const ObString &password, const ObBasicSessionInfo &session, bool &passed) +{ + int ret = OB_SUCCESS; + uint64_t valid_pw_len = 0; + passed = false; + if (OB_FAIL(session.get_sys_variable(share::SYS_VAR_VALIDATE_PASSWORD_LENGTH, valid_pw_len))) { + LOG_WARN("fail to get validate_password_length", K(ret)); + } else { + passed = password.length() >= valid_pw_len; + } + return ret; +} + +int ObExprValidatePasswordStrength::validate_password_medium( + const ObString &password, const ObBasicSessionInfo &session, bool &passed) +{ + int ret = OB_SUCCESS; + uint64_t valid_mix_case_count = 0; + uint64_t valid_number_count = 0; + uint64_t valid_special_count = 0; + uint64_t lower_count = 0; + uint64_t upper_count = 0; + uint64_t digit_count = 0; + uint64_t special_count = 0; + passed = false; + if (OB_FAIL(session.get_sys_variable(share::SYS_VAR_VALIDATE_PASSWORD_MIXED_CASE_COUNT, valid_mix_case_count))) { + LOG_WARN("fail to get validate_password_mixed_case_count", K(ret)); + } else if (OB_FAIL(session.get_sys_variable(share::SYS_VAR_VALIDATE_PASSWORD_NUMBER_COUNT, valid_number_count))) { + LOG_WARN("fail to get validate_password_number_count", K(ret)); + } else if (OB_FAIL( + session.get_sys_variable(share::SYS_VAR_VALIDATE_PASSWORD_SPECIAL_CHAR_COUNT, valid_special_count))) { + LOG_WARN("fail to get validate_password_special_char_count", K(ret)); + } else { + auto handle_char_func = [&lower_count, &upper_count, &digit_count, &special_count](ObString, int wchar) -> int { + int ret = OB_SUCCESS; + if (islower(wchar)) { + lower_count++; + } else if (isupper(wchar)) { + upper_count++; + } else if (isdigit(wchar)) { + digit_count++; + } else { + special_count++; + } + return ret; + }; + OZ(ObCharsetUtils::foreach_char(password, ObCharset::get_system_collation(), handle_char_func)); + } + if (OB_SUCC(ret)) { + passed = lower_count >= valid_mix_case_count && upper_count >= valid_mix_case_count && + digit_count >= valid_number_count && special_count >= valid_special_count; + } + return ret; +} + +int ObExprValidatePasswordStrength::validate_password_strong( + const ObString &password, const ObBasicSessionInfo &session, bool &passed) +{ + int ret = OB_SUCCESS; + UNUSED(password); + UNUSED(session); + // Check if the password matches the words in the dictionary file. The dictionary file is not + // supported in OB yet, so this validation always passed + passed = true; + return ret; +} + +} // namespace sql +} // namespace oceanbase diff --git a/src/sql/engine/expr/ob_expr_validate_password_strength.h b/src/sql/engine/expr/ob_expr_validate_password_strength.h new file mode 100644 index 0000000000000000000000000000000000000000..07c358e523d41781bbaaa82ba8664ee9194e0e45 --- /dev/null +++ b/src/sql/engine/expr/ob_expr_validate_password_strength.h @@ -0,0 +1,50 @@ +// Copyright (c) 2021 Alibaba Inc. All Rights Reserved. +// Author: +// renqing + +#ifndef SRC_SQL_ENGINE_EXPR_OB_EXPR_VALIDATE_PASSWORD_STRENGTH_H_ +#define SRC_SQL_ENGINE_EXPR_OB_EXPR_VALIDATE_PASSWORD_STRENGTH_H_ + +#include "sql/engine/expr/ob_expr_operator.h" + +namespace oceanbase { +namespace sql { + +typedef int (*ObValidatePasswordFunc)( + const common::ObString &password, const ObBasicSessionInfo &session, bool &passed); +class ObExprValidatePasswordStrength : public ObFuncExprOperator { +public: + enum PasswordStrength { + STRENGTH_WEAK = 0, + STRENGTH_LESSWEAK, + STRENGTH_LOW, + STRENGTH_MEDIUM, + STRENGTH_STRONG, + STRENGTH_MAX, + }; + explicit ObExprValidatePasswordStrength(common::ObIAllocator &alloc); + virtual ~ObExprValidatePasswordStrength(); + virtual int calc_result_type1(ObExprResType &type, ObExprResType &type1, common::ObExprTypeCtx &type_ctx) const; + virtual int calc_result1(common::ObObj &result, const common::ObObj &obj1, common::ObExprCtx &expr_ctx) const; + virtual int cg_expr(ObExprCGCtx &op_cg_ctx, const ObRawExpr &raw_expr, ObExpr &rt_expr) const override; + static int eval_password_strength(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res_datum); + +private: + static int calc_password_strength(const common::ObString &password, const ObBasicSessionInfo &session, int &strength); + static int validate_password_lessweak( + const common::ObString &password, const ObBasicSessionInfo &session, bool &passed); + static int validate_password_low(const common::ObString &password, const ObBasicSessionInfo &session, bool &passed); + static int validate_password_medium( + const common::ObString &password, const ObBasicSessionInfo &session, bool &passed); + static int validate_password_strong( + const common::ObString &password, const ObBasicSessionInfo &session, bool &passed); + static const int64_t VALID_PASSWORD_LENGTH_MIN = 4; + static const int64_t PASSWORD_STRENGTH_MULTIPLIER = 25; + static const ObValidatePasswordFunc validate_funcs_[STRENGTH_MAX]; + // disallow copy + DISALLOW_COPY_AND_ASSIGN(ObExprValidatePasswordStrength); +}; +} // namespace sql +} // namespace oceanbase + +#endif /* SRC_SQL_ENGINE_EXPR_OB_EXPR_VALIDATE_PASSWORD_STRENGTH_H_ */ diff --git a/src/sql/parser/ob_item_type.h b/src/sql/parser/ob_item_type.h index f41a41437516e9262e89718e1b9645638f09c445..567c6f9423bf1e4c5880585d29f7bec58bf069b3 100644 --- a/src/sql/parser/ob_item_type.h +++ b/src/sql/parser/ob_item_type.h @@ -428,6 +428,7 @@ typedef enum ObItemType { T_FUN_SYS_PI = 713, T_FUN_SYS_ANY_VALUE = 714, T_FUN_SYS_DEGREES = 715, + T_FUN_SYS_VALIDATE_PASSWORD_STRENGTH = 716, T_FUN_SYS_BENCHMARK = 720, T_FUN_SYS_EXPORT_SET = 721,