From e6c77fc45f1b1ee3095fc809adbc0493517053c2 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov <ivadmi5@gmail.com> Date: Wed, 5 Jul 2023 16:30:20 +0300 Subject: [PATCH] feat: Add `auth_type` to box.schema.user.create() Now it's possible to specify the desired authentication method during user creation via `auth_type`, e.g. ```lua box.schema.user.create('mickey', { auth_type = 'chap-sha1', password = 'foobar' }) ``` Furthermore, authentication methods may now specify that they don't require password to create stored authentication info. This is used in LDAP authentication (`auth_type = 'ldap'`): ```lua box.schema.user.create('mickey', { auth_type = 'ldap' }) ``` NO_DOC=picodata internal patch NO_CHANGELOG=picodata internal patch NO_TEST=picodata internal patch --- src/box/auth_ldap.c | 2 +- src/box/authentication.h | 6 ++++++ src/box/lua/misc.cc | 23 +++++++++++++++++++++++ src/box/lua/schema.lua | 28 +++++++++++++++++++++------- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/box/auth_ldap.c b/src/box/auth_ldap.c index 40bedced6b..1eae02e3e1 100644 --- a/src/box/auth_ldap.c +++ b/src/box/auth_ldap.c @@ -294,7 +294,7 @@ auth_ldap_new(void) { struct auth_method *method = xmalloc(sizeof(*method)); method->name = AUTH_LDAP_NAME; - method->flags = 0; + method->flags = AUTH_METHOD_PASSWORDLESS_DATA_PREPARE; method->auth_method_delete = auth_ldap_delete; method->auth_data_prepare = auth_ldap_data_prepare; method->auth_request_prepare = auth_ldap_request_prepare; diff --git a/src/box/authentication.h b/src/box/authentication.h index 35fd4cfe21..a843a1b8cf 100644 --- a/src/box/authentication.h +++ b/src/box/authentication.h @@ -53,6 +53,12 @@ enum auth_method_flag { * communication channel is encrypted (e.g. with SSL/TLS). */ AUTH_METHOD_REQUIRES_ENCRYPTION = 1 << 0, + /** + * Set if the authentication method does not need + * password in auth_method::auth_data_prepare. + * This is opt-in because most methods require password. + */ + AUTH_METHOD_PASSWORDLESS_DATA_PREPARE = 1 << 1, }; /** diff --git a/src/box/lua/misc.cc b/src/box/lua/misc.cc index 1d0c517bb6..f0676873af 100644 --- a/src/box/lua/misc.cc +++ b/src/box/lua/misc.cc @@ -236,6 +236,28 @@ lbox_generate_space_id(lua_State *L) /** {{{ Helper that generates user auth data. **/ +/** + * Takes authentication method name (e.g. 'chap-sha1'). + * Returns true if password is needed to produce auth data, false otherwise. + * Raises Lua error if the specified authentication method doesn't exist. + */ +static int +lbox_prepare_auth_needs_password(lua_State *L) +{ + size_t method_name_len; + const char *method_name = luaL_checklstring(L, 1, &method_name_len); + const struct auth_method *method = auth_method_by_name(method_name, + method_name_len); + if (method == NULL) { + diag_set(ClientError, ER_UNKNOWN_AUTH_METHOD, + tt_cstr(method_name, method_name_len)); + return luaT_error(L); + } + int pwdless = method->flags & AUTH_METHOD_PASSWORDLESS_DATA_PREPARE; + lua_pushboolean(L, !pwdless); + return 1; +} + /** * Takes authentication method name (e.g. 'chap-sha1'), password and user name. * Returns authentication data that can be stored in the _user space. @@ -507,6 +529,7 @@ void box_lua_misc_init(struct lua_State *L) { static const struct luaL_Reg boxlib_internal[] = { + {"prepare_auth_needs_password", lbox_prepare_auth_needs_password}, {"prepare_auth", lbox_prepare_auth}, {"select", lbox_select}, {"new_tuple_format", lbox_tuple_format_new}, diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index ea072a4562..12a439ad3c 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -3205,10 +3205,14 @@ box.schema.user.password = function(password, name) return internal.prepare_auth(box.cfg.auth_type, password, name) end -local function prepare_auth_list(password, name) +local function prepare_auth_list_needs_password(auth_type) + return internal.prepare_auth_needs_password(auth_type) +end + +local function prepare_auth_list(auth_type, password, name) return { - [box.cfg.auth_type] = - internal.prepare_auth(box.cfg.auth_type, password, name) + [auth_type] = + internal.prepare_auth(auth_type, password, name) } end @@ -3228,9 +3232,11 @@ end local function chpasswd(uid, new_password, name) local _user = box.space[box.schema.USER_ID] + local auth_type = box.cfg.auth_type + local auth_list = prepare_auth_list(auth_type, new_password, name) local auth_history = prepare_auth_history(uid) check_password(new_password, auth_history) - _user:update({uid}, {{'=', 5, prepare_auth_list(new_password, name)}, + _user:update({uid}, {{'=', 5, auth_list}, {'=', 6, auth_history}, {'=', 7, math.floor(fiber.time())}}) end @@ -3257,17 +3263,25 @@ end box.schema.user.create = function(name, opts) local uid = user_or_role_resolve(name) opts = opts or {} - check_param_table(opts, { password = 'string', if_not_exists = 'boolean' }) + check_param_table(opts, {auth_type = 'string', + password = 'string', + if_not_exists = 'boolean'}) if uid then if not opts.if_not_exists then box.error(box.error.USER_EXISTS, name) end return end + local auth_type = opts.auth_type or box.cfg.auth_type local auth_list - if opts.password then + if not prepare_auth_list_needs_password(auth_type) then + if opts.password then + log.warn(auth_type .. " doesn't need password") + end + auth_list = prepare_auth_list(auth_type, '', name) + elseif opts.password then check_password(opts.password) - auth_list = prepare_auth_list(opts.password, name) + auth_list = prepare_auth_list(auth_type, opts.password, name) else auth_list = setmap({}) end -- GitLab