diff --git a/apisix/plugins/cas-auth.lua b/apisix/plugins/cas-auth.lua index db7b7860e7ea..08dfc0d72d4f 100644 --- a/apisix/plugins/cas-auth.lua +++ b/apisix/plugins/cas-auth.lua @@ -395,6 +395,8 @@ function _M.access(conf, ctx) local _, user = unpack_entry(entry) core.log.info("cas-auth: SLO session deleted for user=", user or "") end + -- SLO callback ends here; never proxy the IdP's logout POST upstream + return ngx.HTTP_OK else local opts = session_opts(conf) local session_id = get_cookie(ctx, opts.cookie_name) diff --git a/docs/en/latest/plugins/cas-auth.md b/docs/en/latest/plugins/cas-auth.md index 0078e5eab3d8..3d64e226cf8d 100644 --- a/docs/en/latest/plugins/cas-auth.md +++ b/docs/en/latest/plugins/cas-auth.md @@ -98,6 +98,8 @@ Once this is done, the user is redirected to the original URL they wanted to vis Later, the user could visit `logout_uri` to start logout process. The user would be redirected to `idp_uri` to do logout. +The IdP may also send a single logout (SLO) `POST` request to `cas_callback_uri`. The Plugin handles such requests itself (invalidating the matching session) and never forwards them to the upstream. + Note that, `cas_callback_uri` and `logout_uri` should be either full qualified address (e.g. `http://127.0.0.1:9080/anything/logout`), or path only (e.g. `/anything/logout`), but it is recommended to be path only to keep consistent. diff --git a/t/plugin/cas-auth.t b/t/plugin/cas-auth.t index de895c463c3d..8e65f7da5501 100644 --- a/t/plugin/cas-auth.t +++ b/t/plugin/cas-auth.t @@ -878,3 +878,74 @@ passed } --- response_body passed + + + +=== TEST 21: add route whose upstream is a closed port for the SLO fall-through test +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + local code, body = t('/apisix/admin/routes/cas-slo-noproxy', + ngx.HTTP_PUT, + [[{ + "methods": ["GET", "POST"], + "host": "127.0.0.21", + "priority": 10, + "plugins": { + "cas-auth": { + "idp_uri": "http://127.0.0.1:8080/realms/test/protocol/cas", + "cas_callback_uri": "/cas_callback", + "logout_uri": "/logout", + "cookie": { + "secret": "0123456789abcdef0123456789abcdef", + "secure": false + } + } + }, + "upstream": { + "nodes": {"127.0.0.1:1": 1}, + "type": "roundrobin" + }, + "uri": "/*" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 22: well-formed SLO POST stops at the plugin and is never proxied upstream +--- config + location /t { + content_by_lua_block { + local http = require "resty.http" + local httpc = http.new() + local base = "http://127.0.0.1:" .. ngx.var.server_port + + -- A POST carrying a valid SessionIndex must be terminated by the + -- plugin (200), not fall through to the upstream. The upstream is a + -- closed port, so any fall-through would surface as a 502. + local res, err = httpc:request_uri(base .. "/cas_callback", { + method = "POST", + headers = { ["Host"] = "127.0.0.21" }, + body = "ST-no-such-session", + }) + assert(res, "request failed: " .. tostring(err)) + assert(res.status == 200, + "expected 200 from plugin, got " .. res.status .. + " (non-200 means the SLO POST was proxied upstream)") + + ngx.say("passed") + } + } +--- response_body +passed