diff --git a/apisix/plugins/basic-auth.lua b/apisix/plugins/basic-auth.lua index 752ea81337d2..a6a90d98c946 100644 --- a/apisix/plugins/basic-auth.lua +++ b/apisix/plugins/basic-auth.lua @@ -32,7 +32,8 @@ local schema = { hide_credentials = { type = "boolean", default = false, - } + }, + realm = schema_def.get_realm_schema("basic"), }, anonymous_consumer = schema_def.anonymous_consumer_schema, } @@ -124,7 +125,6 @@ end local function find_consumer(ctx) local auth_header = core.request.header(ctx, "Authorization") if not auth_header then - core.response.set_header("WWW-Authenticate", "Basic realm='.'") return nil, nil, "Missing authorization in request" end @@ -157,15 +157,17 @@ end function _M.rewrite(conf, ctx) - local cur_consumer, consumer_conf, err = find_consumer(ctx) + local cur_consumer, consumer_conf, err = find_consumer(ctx, conf) if not cur_consumer then if not conf.anonymous_consumer then + core.response.set_header("WWW-Authenticate", "Basic realm=\"" .. conf.realm .. "\"") return 401, { message = err } end cur_consumer, consumer_conf, err = consumer.get_anonymous_consumer(conf.anonymous_consumer) if not cur_consumer then err = "basic-auth failed to authenticate the request, code: 401. error: " .. err core.log.error(err) + core.response.set_header("WWW-Authenticate", "Basic realm=\"" .. conf.realm .. "\"") return 401, { message = "Invalid user authorization" } end end diff --git a/apisix/plugins/hmac-auth.lua b/apisix/plugins/hmac-auth.lua index 60eca605e044..97dda11fe25e 100644 --- a/apisix/plugins/hmac-auth.lua +++ b/apisix/plugins/hmac-auth.lua @@ -63,6 +63,7 @@ local schema = { default = false, }, hide_credentials = {type = "boolean", default = false}, + realm = schema_def.get_realm_schema("hmac"), anonymous_consumer = schema_def.anonymous_consumer_schema, }, } @@ -346,14 +347,17 @@ function _M.rewrite(conf, ctx) local cur_consumer, consumers_conf, err = find_consumer(conf, ctx) if not cur_consumer then if not conf.anonymous_consumer then + core.response.set_header("WWW-Authenticate", "hmac realm=\"" .. conf.realm .. "\"") return 401, { message = err } end cur_consumer, consumers_conf, err = consumer.get_anonymous_consumer(conf.anonymous_consumer) if not cur_consumer then if auth_utils.is_running_under_multi_auth(ctx) then + core.response.set_header("WWW-Authenticate", "hmac realm=\"" .. conf.realm .. "\"") return 401, err end core.log.error(err) + core.response.set_header("WWW-Authenticate", "hmac realm=\"" .. conf.realm .. "\"") return 401, { message = "Invalid user authorization" } end end diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua index 197efc5facc8..4c32609bff35 100644 --- a/apisix/plugins/jwt-auth.lua +++ b/apisix/plugins/jwt-auth.lua @@ -58,6 +58,7 @@ local schema = { type = "boolean", default = false }, + realm = schema_def.get_realm_schema("jwt"), anonymous_consumer = schema_def.anonymous_consumer_schema, }, } @@ -307,12 +308,14 @@ function _M.rewrite(conf, ctx) local consumer, consumer_conf, err = find_consumer(conf, ctx) if not consumer then if not conf.anonymous_consumer then + core.response.set_header("WWW-Authenticate", "Bearer realm=\"" .. conf.realm .. "\"") return 401, { message = err } end consumer, consumer_conf, err = consumer_mod.get_anonymous_consumer(conf.anonymous_consumer) if not consumer then err = "jwt-auth failed to authenticate the request, code: 401. error: " .. err core.log.error(err) + core.response.set_header("WWW-Authenticate", "Bearer realm=\"" .. conf.realm .. "\"") return 401, { message = "Invalid user authorization"} end end diff --git a/apisix/plugins/key-auth.lua b/apisix/plugins/key-auth.lua index 7bef9f83c32a..020326dce9fd 100644 --- a/apisix/plugins/key-auth.lua +++ b/apisix/plugins/key-auth.lua @@ -30,6 +30,7 @@ local schema = { type = "string", default = "apikey", }, + realm = schema_def.get_realm_schema("key"), hide_credentials = { type = "boolean", default = false, @@ -104,12 +105,14 @@ function _M.rewrite(conf, ctx) local consumer, consumer_conf, err = find_consumer(ctx, conf) if not consumer then if not conf.anonymous_consumer then + core.response.set_header("WWW-Authenticate", "apikey realm=\"" .. conf.realm .. "\"") return 401, { message = err} end consumer, consumer_conf, err = consumer_mod.get_anonymous_consumer(conf.anonymous_consumer) if not consumer then err = "key-auth failed to authenticate the request, code: 401. error: " .. err core.log.error(err) + core.response.set_header("WWW-Authenticate", "apikey realm=\"" .. conf.realm .. "\"") return 401, { message = "Invalid user authorization"} end end diff --git a/apisix/plugins/ldap-auth.lua b/apisix/plugins/ldap-auth.lua index 1d89efef8ac9..30c836742dae 100644 --- a/apisix/plugins/ldap-auth.lua +++ b/apisix/plugins/ldap-auth.lua @@ -19,6 +19,8 @@ local ngx = ngx local ngx_re = require("ngx.re") local consumer_mod = require("apisix.consumer") local ldap = require("resty.ldap") +local schema_def = require("apisix.schema_def") + local schema = { type = "object", @@ -28,7 +30,8 @@ local schema = { ldap_uri = { type = "string" }, use_tls = { type = "boolean", default = false }, tls_verify = { type = "boolean", default = false }, - uid = { type = "string", default = "cn" } + uid = { type = "string", default = "cn" }, + realm = schema_def.get_realm_schema("ldap"), }, required = {"base_dn","ldap_uri"}, } @@ -106,7 +109,7 @@ function _M.rewrite(conf, ctx) -- 1. extract authorization from header local auth_header = core.request.header(ctx, "Authorization") if not auth_header then - core.response.set_header("WWW-Authenticate", "Basic realm='.'") + core.response.set_header("WWW-Authenticate", "Basic realm=\"" .. conf.realm .. "\"") return 401, { message = "Missing authorization in request" } end @@ -117,6 +120,7 @@ function _M.rewrite(conf, ctx) else core.log.warn("nil user") end + core.response.set_header("WWW-Authenticate", "Basic realm=\"" .. conf.realm .. "\"") return 401, { message = "Invalid authorization in request" } end @@ -136,6 +140,7 @@ function _M.rewrite(conf, ctx) local res, err = ldap.ldap_authenticate(user.username, user.password, ldapconf) if not res then core.log.warn("ldap-auth failed: ", err) + core.response.set_header("WWW-Authenticate", "Basic realm=\"" .. conf.realm .. "\"") return 401, { message = "Invalid user authorization" } end @@ -144,12 +149,14 @@ function _M.rewrite(conf, ctx) -- 3. Retrieve consumer for authorization plugin local consumer_conf = consumer_mod.plugin(plugin_name) if not consumer_conf then + core.response.set_header("WWW-Authenticate", "Basic realm=\"" .. conf.realm .. "\"") return 401, { message = "Missing related consumer" } end local consumers = consumer_mod.consumers_kv(plugin_name, consumer_conf, "user_dn") local consumer = consumers[user_dn] if not consumer then + core.response.set_header("WWW-Authenticate", "Basic realm=\"" .. conf.realm .. "\"") return 401, {message = "Invalid user authorization"} end consumer_mod.attach_consumer(ctx, consumer, consumer_conf) diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua index 53b5476db7da..491265a58a18 100644 --- a/apisix/schema_def.lua +++ b/apisix/schema_def.lua @@ -29,6 +29,21 @@ _M.anonymous_consumer_schema = { minLength = "1" } +function _M.get_realm_schema(default_val) + return { + type = "string", + -- Pattern: Only allow printable ASCII, but EXCLUDE " and \ + -- \x20-\x21 (Space and !) + -- \x23-\x5B (# through [) + -- \x5D-\x7E (] through ~) + -- Escaped closing bracket (\x5D) assertion for PCRE compatibility + pattern = "^[\x20-\x21\x23-\x5B\\]-\x7E]+$", + default = default_val, + minLength = 1, + maxLength = 128, + } +end + local id_schema = { anyOf = { { diff --git a/docs/en/latest/plugins/basic-auth.md b/docs/en/latest/plugins/basic-auth.md index 37e38593c1d7..a82424c3f72b 100644 --- a/docs/en/latest/plugins/basic-auth.md +++ b/docs/en/latest/plugins/basic-auth.md @@ -55,6 +55,7 @@ For Route: |------------------|---------|----------|---------|------------------------------------------------------------------------| | hide_credentials | boolean | False | false | If true, do not pass the authorization request header to Upstream services. | | anonymous_consumer | boolean | False | false | Anonymous Consumer name. If configured, allow anonymous users to bypass the authentication. | +| realm | string | False | basic | The realm to include in the `WWW-Authenticate` header when authentication fails. | ## Examples diff --git a/docs/en/latest/plugins/hmac-auth.md b/docs/en/latest/plugins/hmac-auth.md index 354af2a15764..24f741f250d2 100644 --- a/docs/en/latest/plugins/hmac-auth.md +++ b/docs/en/latest/plugins/hmac-auth.md @@ -54,9 +54,10 @@ The following attributes are available for configurations on Routes or Services. | allowed_algorithms | array[string] | False | ["hmac-sha1","hmac-sha256","hmac-sha512"] | combination of "hmac-sha1","hmac-sha256",and "hmac-sha512" | The list of HMAC algorithms allowed. | | clock_skew | integer | False | 300 | >=1 | Maximum allowable time difference in seconds between the client request's timestamp and APISIX server's current time. This helps account for discrepancies in time synchronization between the client’s and server’s clocks and protect against replay attacks. The timestamp in the Date header (must be in GMT format) will be used for the calculation. | | signed_headers | array[string] | False | | | The list of HMAC-signed headers that should be included in the client request's HMAC signature. | -| validate_request_body | boolean | False | false | | If true, validate the integrity of the request body to ensure it has not been tampered with during transmission. Specifically, the Plugin creates a SHA-256 base64-encoded digest and compare it to the `Digest` header. If the Digest` header is missing or if the digests do not match, the validation fails. | +| validate_request_body | boolean | False | false | | If true, validate the integrity of the request body to ensure it has not been tampered with during transmission. Specifically, the Plugin creates a SHA-256 base64-encoded digest and compare it to the `Digest` header. If the `Digest` header is missing or if the digests do not match, the validation fails. | | hide_credentials | boolean | False | false | | If true, do not pass the authorization request header to Upstream services. | | anonymous_consumer | string | False | | | Anonymous Consumer name. If configured, allow anonymous users to bypass the authentication. | +| realm | string | False | hmac | | The realm to include in the `WWW-Authenticate` header when authentication fails. | NOTE: `encrypt_fields = {"secret_key"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields). diff --git a/docs/en/latest/plugins/jwt-auth.md b/docs/en/latest/plugins/jwt-auth.md index a22b6fbb8034..a85d84429365 100644 --- a/docs/en/latest/plugins/jwt-auth.md +++ b/docs/en/latest/plugins/jwt-auth.md @@ -68,6 +68,7 @@ For Routes or Services: | key_claim_name | string | False | key | The name of the JWT claim that contains the user key (corresponds to Consumer's key attribute). | | anonymous_consumer | string | False | false | Anonymous Consumer name. If configured, allow anonymous users to bypass the authentication. | | store_in_ctx | boolean | False | false | Set to true will store the JWT payload in the request context (`ctx.jwt_auth_payload`). This allows lower-priority plugins that run afterwards on the same request to retrieve and use the JWT token. | +| realm | string | False | jwt | The realm to include in the `WWW-Authenticate` header when authentication fails. | You can implement `jwt-auth` with [HashiCorp Vault](https://www.vaultproject.io/) to store and fetch secrets and RSA keys pairs from its [encrypted KV engine](https://developer.hashicorp.com/vault/docs/secrets/kv) using the [APISIX Secret](../terminology/secret.md) resource. diff --git a/docs/en/latest/plugins/key-auth.md b/docs/en/latest/plugins/key-auth.md index 655eb3e313bd..8a7d7aa1a7bf 100644 --- a/docs/en/latest/plugins/key-auth.md +++ b/docs/en/latest/plugins/key-auth.md @@ -58,6 +58,7 @@ For Route: | query | string | False | apikey | The query string to get the key from. Lower priority than header. | | hide_credentials | boolean | False | false | If true, do not pass the header or query string with key to Upstream services. | | anonymous_consumer | string | False | false | Anonymous Consumer name. If configured, allow anonymous users to bypass the authentication. | +| realm | string | False | key | The realm to include in the `WWW-Authenticate` header when authentication fails. | ## Examples diff --git a/docs/en/latest/plugins/ldap-auth.md b/docs/en/latest/plugins/ldap-auth.md index 7e33fc977f6c..95b6884bdb8a 100644 --- a/docs/en/latest/plugins/ldap-auth.md +++ b/docs/en/latest/plugins/ldap-auth.md @@ -53,6 +53,7 @@ For Route: | use_tls | boolean | False | `false` | If set to `true` uses TLS. | | tls_verify| boolean | False | `false` | Whether to verify the server certificate when `use_tls` is enabled; If set to `true`, you must set `ssl_trusted_certificate` in `config.yaml`, and make sure the host of `ldap_uri` matches the host in server certificate. | | uid | string | False | `cn` | uid attribute. | +| realm | string | False | ldap | The realm to include in the `WWW-Authenticate` header when authentication fails. | ## Enable plugin diff --git a/t/plugin/basic-auth-realm.t b/t/plugin/basic-auth-realm.t new file mode 100644 index 000000000000..ff9ad3968d52 --- /dev/null +++ b/t/plugin/basic-auth-realm.t @@ -0,0 +1,159 @@ +# +# 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. +# + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); + +run_tests; + +__DATA__ + +=== TEST 1: sanity, default realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "basic-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 2: verify default realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: Basic realm="basic" + + + +=== TEST 3: set custom realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "basic-auth": { + "realm": "secure-zone" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: verify custom realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: Basic realm="secure-zone" + + + +=== TEST 5: set anonymous consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "basic-auth": { + "anonymous_consumer": "missing-consumer" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 6: verify anonymous consumer missing returns realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: Basic realm="basic" +--- error_log +failed to get anonymous consumer diff --git a/t/plugin/hmac-auth-realm.t b/t/plugin/hmac-auth-realm.t new file mode 100644 index 000000000000..34dc822d7cf9 --- /dev/null +++ b/t/plugin/hmac-auth-realm.t @@ -0,0 +1,159 @@ +# +# 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. +# + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); + +run_tests; + +__DATA__ + +=== TEST 1: sanity, default realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "hmac-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 2: verify default realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: hmac realm="hmac" + + + +=== TEST 3: set custom realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "hmac-auth": { + "realm": "my-hmac-realm" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: verify custom realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: hmac realm="my-hmac-realm" + + + +=== TEST 5: set anonymous consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "hmac-auth": { + "anonymous_consumer": "missing-consumer" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 6: verify anonymous consumer missing returns realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: hmac realm="hmac" +--- error_log +failed to get anonymous consumer diff --git a/t/plugin/jwt-auth-realm.t b/t/plugin/jwt-auth-realm.t new file mode 100644 index 000000000000..1bb4c921ab39 --- /dev/null +++ b/t/plugin/jwt-auth-realm.t @@ -0,0 +1,159 @@ +# +# 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. +# + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); + +run_tests; + +__DATA__ + +=== TEST 1: sanity, default realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "jwt-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 2: verify default realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: Bearer realm="jwt" + + + +=== TEST 3: set custom realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "jwt-auth": { + "realm": "my-jwt-realm" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: verify custom realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: Bearer realm="my-jwt-realm" + + + +=== TEST 5: set anonymous consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "jwt-auth": { + "anonymous_consumer": "missing-consumer" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 6: verify anonymous consumer missing returns realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: Bearer realm="jwt" +--- error_log +failed to get anonymous consumer diff --git a/t/plugin/key-auth-realm.t b/t/plugin/key-auth-realm.t new file mode 100644 index 000000000000..ce3b83d8b03d --- /dev/null +++ b/t/plugin/key-auth-realm.t @@ -0,0 +1,159 @@ +# +# 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. +# + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); + +run_tests; + +__DATA__ + +=== TEST 1: sanity, default realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 2: verify default realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: apikey realm="key" + + + +=== TEST 3: set custom realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": { + "realm": "my-custom-realm" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: verify custom realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: apikey realm="my-custom-realm" + + + +=== TEST 5: set anonymous consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": { + "anonymous_consumer": "missing-consumer" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 6: verify anonymous consumer missing returns realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: apikey realm="key" +--- error_log +failed to get anonymous consumer diff --git a/t/plugin/ldap-auth-realm.t b/t/plugin/ldap-auth-realm.t new file mode 100644 index 000000000000..63dd698b22bb --- /dev/null +++ b/t/plugin/ldap-auth-realm.t @@ -0,0 +1,177 @@ +# +# 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. +# + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); + +run_tests; + +__DATA__ + +=== TEST 1: sanity, default realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "ldap-auth": { + "base_dn": "dc=example,dc=com", + "ldap_uri": "ldap://127.0.0.1:1389" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 2: verify default realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: Basic realm="ldap" + + + +=== TEST 3: set custom realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "ldap-auth": { + "base_dn": "dc=example,dc=com", + "ldap_uri": "ldap://127.0.0.1:1389", + "realm": "my-ldap-realm" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: verify custom realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: Basic realm="my-ldap-realm" + + + +=== TEST 5: ldap auth failure (missing header) returns realm +--- request +GET /hello +--- error_code: 401 +--- response_headers +WWW-Authenticate: Basic realm="my-ldap-realm" + + + +=== TEST 6: ldap auth failure (invalid credentials) returns realm +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "ldap-auth": { + "base_dn": "dc=example,dc=com", + "ldap_uri": "ldap://127.0.0.1:1389", + "realm": "my-ldap-realm" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 7: verify ldap invalid credentials returns realm +--- request +GET /hello +--- more_headers +Authorization: Basic dXNlcjp3cm9uZw== +--- error_code: 401 +--- response_headers +WWW-Authenticate: Basic realm="my-ldap-realm" +--- error_log +ldap-auth failed