janiussyafiq commented on code in PR #13762: URL: https://github.com/apache/apisix/pull/13762#discussion_r3688055938
########## apisix/plugins/ldap-auth-advanced.lua: ########## @@ -0,0 +1,382 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +local core = require("apisix.core") +local schema_def = require("apisix.schema_def") +local auth_utils = require("apisix.utils.auth") +local consumer_mod = require("apisix.consumer") +local ldap_client = require("resty.ldap.client") +local ldap_protocol = require("resty.ldap.protocol") +local ldap_filter = require("resty.ldap.filter") +local ngx = ngx +local ipairs = ipairs +local type = type +local ngx_decode_base64 = ngx.decode_base64 +local ngx_re_match = ngx.re.match +local str_find = string.find +local str_sub = string.sub +local parse_addr = core.utils.parse_addr + +-- RFC 4512 attribute-description shape: a leading letter, then letters, +-- digits, semicolons (option separators) or hyphens. +local ATTR_PATTERN = "^[A-Za-z][A-Za-z0-9;-]*$" + +local schema = { + type = "object", + title = "work with route or service object", + properties = { + -- connection + ldap_uri = { type = "string" }, -- "host[:port]" + use_ldaps = { type = "boolean", default = false }, + use_starttls = { type = "boolean", default = false }, + ssl_verify = { type = "boolean", default = true }, + timeout = { type = "integer", minimum = 1, maximum = 60000, + default = 3000 }, -- milliseconds + + -- connection pool + keepalive = { type = "boolean", default = true }, + keepalive_timeout = { type = "integer", minimum = 1000, default = 60000 }, + keepalive_pool_size = { type = "integer", minimum = 1, default = 5 }, + keepalive_pool_name = { type = "string" }, + + -- user resolution (search-then-bind) + base_dn = { type = "string" }, -- search root + attribute = { type = "string", -- filter: (attribute=username) + default = "cn", pattern = ATTR_PATTERN }, + bind_dn = { type = "string" }, -- absent => anonymous search + ldap_password = { type = "string" }, + + -- search bounds + size_limit = { type = "integer", minimum = 2, default = 2 }, + time_limit = { type = "integer", minimum = 0, default = 5 }, -- seconds; 0 = server default + + + + -- consumer + consumer_required = { type = "boolean", default = true }, + + -- request handling + header_type = { type = "string", enum = {"ldap", "basic"}, default = "ldap" }, + realm = schema_def.get_realm_schema("ldap"), + + }, + encrypt_fields = {"ldap_password"}, + required = {"ldap_uri", "base_dn"}, +} + +local consumer_schema = { + type = "object", + title = "work with consumer object", + properties = { + user_dn = { type = "string" }, + }, + required = {"user_dn"}, +} + +local plugin_name = "ldap-auth-advanced" + + +local _M = { + version = 0.1, + priority = 2541, + type = 'auth', + name = plugin_name, + schema = schema, + consumer_schema = consumer_schema, +} + +function _M.check_schema(conf, schema_type) + if schema_type == core.schema.TYPE_CONSUMER then + return core.schema.check(consumer_schema, conf) + end + + local ok, err = core.schema.check(schema, conf) + if not ok then + return false, err + end + + if conf.use_ldaps and conf.use_starttls then + return false, "use_ldaps and use_starttls are mutually exclusive" + end + + if conf.bind_dn and not conf.ldap_password then + return false, "ldap_password is required when bind_dn is set" + end + + -- ldap_uri may omit ":port"; the effective port (636 with use_ldaps, + -- else 389) is resolved when the connection is opened. + + return true +end + + + +-- Shared 401 helper for the authentication-failure paths. +local function auth_failed(conf, ctx, reason) + + -- under multi-auth, decline quietly and let the wrapper render the 401 + if auth_utils.is_running_under_multi_auth(ctx) then + return 401 + end + + if reason then + core.log.warn(plugin_name, ": ", reason) + end + core.response.set_header("WWW-Authenticate", + conf.header_type .. " realm=\"" .. conf.realm .. "\"") + return 401, { message = "Authorization required" } +end + + + + +-- Extract username/password from the credential header: the scheme word is +-- conf.header_type ("ldap" or "basic", case-insensitive), the payload is +-- base64("username:password"). +local function extract_credentials(conf, ctx) + -- Proxy-Authorization is checked before Authorization + local header_name = "Proxy-Authorization" + local auth_header = core.request.header(ctx, header_name) + if not auth_header then Review Comment: fixed -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
