spacewander commented on a change in pull request #6382:
URL: https://github.com/apache/apisix/pull/6382#discussion_r825441354



##########
File path: apisix/plugins/auth-casdoor.lua
##########
@@ -0,0 +1,121 @@
+--
+-- 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 http = require("resty.http")
+local session = require("resty.session")
+local ngx = ngx
+
+local plugin_name = "auth-casdoor"
+local schema = {
+    type = "object",
+    properties = {
+        --Note: endpoint_addr and callback_url should not end with '/'
+        endpoint_addr = {type = "string", pattern = "^[^%?]+[^/]$"},
+        client_id = {type = "string"},
+        client_secret = {type = "string"},
+        callback_url = {type = "string", pattern = "^[^%?]+[^/]$"},
+    },
+    required = {
+        "callback_url", "endpoint_addr", "client_id", "client_secret"
+    }
+}
+
+local _M = {
+    version = 0.1,
+    priority = 2559,
+    name = plugin_name,
+    schema = schema
+}
+
+local function fetch_access_token(ctx, conf)
+    local args = core.request.get_uri_args(ctx)
+    if not args or not args.code or not args.state then
+        return nil, "failed when accessing token. Invalid code or state"
+    end
+    local client = http.new()
+    local url = conf.endpoint_addr .. "/api/login/oauth/access_token"
+
+    local res, err = client:request_uri(url, {
+        method = "POST",
+        query = {
+            code = args.code,
+            grant_type = "authorization_code",
+            client_id = conf.client_id,
+            client_secret = conf.client_secret
+        }
+    })
+    if not res then return nil, err end
+    local data, err = core.cjson.decode(res.body)
+
+    if err or not data then
+        err = "failed to parse casdoor response data: " .. err
+        return nil, err
+    end
+
+    if not data.access_token then
+        return nil, "failed when accessing token: no access_token contained"
+    end
+
+    return data.access_token, nil
+end
+
+function _M.check_schema(conf) return core.schema.check(schema, conf) end
+
+function _M.access(conf, ctx)
+    -- log.info("hit auth-casdoor access")
+    local current_uri = ctx.var.uri
+    local session_obj_read, session_present = session.open()
+
+    -- step 1: check whether hits the callback
+    local real_callback_url=ngx.re.match(conf.callback_url, ".-//[^/]+(/.*)")

Review comment:
       ```suggestion
       local m, err = ngx.re.match(conf.callback_url, ".-//[^/]+(/.*)", "jo")
   ```
   
   Please refer to 
https://github.com/openresty/lua-nginx-module/tree/master#ngxrematch for how to 
use it.
   Now real_callback_url is either nil or array.

##########
File path: t/plugin/auth-casdoor.t
##########
@@ -0,0 +1,280 @@
+#
+# 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();
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
+    }
+
+    if (!defined $block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    my $http_config = $block->http_config // <<_EOC_;
+    server {
+        listen 10420;
+        location /api/login/oauth/access_token {
+            content_by_lua_block {
+                local json_encode = require("toolkit.json").encode
+                ngx.status = 200
+                ngx.say(json_encode({ access_token = "aaaaaaaaaaaaaaaa" }))
+            }
+        }
+    }
+_EOC_
+
+    $block->set_value("http_config", $http_config);
+});
+run_tests();
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.auth-casdoor")
+            local fake_uri = "http://127.0.0.1:"; .. ngx.var.server_port
+            local callback_url = "http://127.0.0.1:"; .. ngx.var.server_port ..
+                                    "/anything/callback"
+            local conf = {
+                callback_url = callback_url,
+                endpoint_addr = fake_uri,
+                client_id = "7ceb9b7fda4a9061ec1c",
+                client_secret = "3416238e1edf915eac08b8fe345b2b95cdba7e04"
+            }
+            local ok, err = plugin.check_schema(conf)
+            if not ok then ngx.say(err) end
+
+            local conf2 = {
+                callback_url = callback_url .. "/?code=aaa",
+                endpoint_addr = fake_uri,
+                client_id = "7ceb9b7fda4a9061ec1c",
+                client_secret = "3416238e1edf915eac08b8fe345b2b95cdba7e04"
+            }
+            local ok2, err2 = plugin.check_schema(conf2)
+            if ok2 then ngx.say(err) end
+
+            local conf3 = {
+                callback_url = callback_url,
+                endpoint_addr = fake_uri .. "/",
+                client_id = "7ceb9b7fda4a9061ec1c",
+                client_secret = "3416238e1edf915eac08b8fe345b2b95cdba7e04"
+            }
+            local ok3, err3 = plugin.check_schema(conf3)
+            if ok3 then ngx.say(err) end
+
+            ngx.say("done")
+
+        }
+    }
+--- response_body
+done
+
+
+
+=== TEST 2: enable plugin test redirect
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.auth-casdoor")
+            local core = require("apisix.core")
+            local log = core.log
+            local t = require("lib.test_admin").test
+
+            local fake_uri = "http://127.0.0.1:10420";
+            local callback_url = "http://127.0.0.1:"; .. ngx.var.server_port ..
+                                    "/anything/callback"
+            local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{
+                                "methods": ["GET"],
+                                "uri": "/anything/*",
+                                "plugins": {
+                                    "auth-casdoor": {
+                                        "callback_url":"]] .. callback_url .. 
[[",
+                                        "endpoint_addr":"]] .. fake_uri .. [[",
+                                        "client_id":"7ceb9b7fda4a9061ec1c",
+                                        
"client_secret":"3416238e1edf915eac08b8fe345b2b95cdba7e04"
+                                    }
+                                },
+                                "upstream": {
+                                    "type": "roundrobin",
+                                    "nodes": {
+                                    "httpbin.org:80": 1
+                                    }
+                                }
+                            }]])
+            if not code == 200 then ngx.say("failed to set up routing rule") 
end
+            ngx.say("done")
+
+        }
+    }
+--- response_body
+done
+
+
+
+=== TEST 3: test redirect
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.auth-casdoor")
+            local core = require("apisix.core")
+            local log = core.log
+            local t = require("lib.test_admin").test
+
+            local code, body = t('/anything/d?param1=foo&param2=bar', 
ngx.HTTP_GET, [[]])
+            if not code == 302 then ngx.say("should have redirected") end
+
+            ngx.say("done")
+
+        }
+    }
+--- response_body
+done
+
+
+
+=== TEST 4: enable fake casdoor
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/2',
+                ngx.HTTP_PUT,
+                [[{
+
+                        "uri": "/api/login/oauth/access_token",
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        }
+                }]]
+                )
+
+            if not code == 200 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 5: test fake casdoor
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.auth-casdoor")
+            local core = require("apisix.core")
+            local log = core.log
+            local t = require("lib.test_admin").test
+            local httpc = require("resty.http").new()
+            local cjson = require("cjson")
+            local fake_uri = 
"http://127.0.0.1:10420/api/login/oauth/access_token";
+
+            local res, err = httpc:request_uri(fake_uri, {method = "GET"})
+            if not res then ngx.say(err) end
+
+            local data = cjson.decode(res.body)
+            if not data then ngx.say("invalid res.body") end
+
+            if not data.access_token == "aaaaaaaaaaaaaaaa" then 
ngx.say("invalid token") end
+            ngx.say("done")
+
+        }
+    }
+--- response_body
+done
+
+
+
+=== TEST 6: test code handling
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.auth-casdoor")
+            local core = require("apisix.core")
+            local log = core.log
+            local t = require("lib.test_admin").test
+            local cjson = require("cjson")
+            local fake_uri = "http://127.0.0.1:"; .. ngx.var.server_port ..
+                                "/anything/d?param1=foo&param2=bar"
+            local callback_url = "http://127.0.0.1:"; .. ngx.var.server_port ..
+                                    "/anything/callback?code=aaa&state=bbb"
+
+            local httpc = require("resty.http").new()
+            local res1, err1 = httpc:request_uri(fake_uri, {method = "GET"})
+            if not res1 then ngx.say(err1) end
+
+            local cookie = res1.headers["Set-Cookie"]
+
+            local res2, err2 = httpc:request_uri(callback_url, {
+                method = "GET",
+                headers = {Cookie = cookie}
+            })
+            if not res2 then ngx.say(err) end
+            if not res2.code == 302 then log.error(res2.code) end
+
+            local cookie2 = res1.headers["Set-Cookie"]
+            local res3, err3 = httpc:request_uri(fake_uri, {
+                method = "GET",
+                headers = {Cookie = cookie2}
+
+            })
+            if not res3 then ngx.say(err) end
+            if not res3.status == 200 then log.error(res3.status) end
+            ngx.say("done")
+
+        }
+    }
+--- response_body
+done
+
+
+
+=== TEST 7: incorrect test code handling
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.auth-casdoor")
+            local core = require("apisix.core")
+            local log = core.log
+            local t = require("lib.test_admin").test
+            local cjson = require("cjson")
+
+            local callback_url = "http://127.0.0.1:"; .. ngx.var.server_port ..
+                                    "/anything/callback?code=aaa&state=bbb"
+
+            local httpc = require("resty.http").new()
+            local res1, err1 = httpc:request_uri(callback_url, {method = 
"GET"})

Review comment:
       Let's check the error log too

##########
File path: docs/en/latest/plugins/auth-casdoor.md
##########
@@ -0,0 +1,94 @@
+---
+title: auth-casdoor
+---
+
+<!--
+#
+# 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.
+#
+-->
+
+## Summary
+
+- [**Name**](#name)
+- [**Attributes**](#attributes)
+- [**Metadata**](#metadata)
+- [**How To Enable**](#how-to-enable)
+- [**Test Plugin**](#test-plugin)
+- [**Disable Plugin**](#disable-plugin)
+- [**Examples**](#examples)
+
+## Name
+
+`auth-casdoor` is an authorization plugin based on 
[Casdoor](https://casdoor.org/). Casdoor is a centralized authentication / 
Single-Sign-On (SSO) platform supporting OAuth 2.0, OIDC and SAML, integrated 
with Casbin RBAC and ABAC permission management.
+
+## Attributes
+
+| Name        | Type   | Requirement | Default | Valid | Description           
                                       |
+| ----------- | ------ | ----------- | ------- | ----- | 
------------------------------------------------------------ |
+| endpoint_addr  | string | required    |         |       | The url of 
casdoor.             |
+| client_id | string | required    |         |       | The client id in 
casdoor.                          |
+| client_secret       | string | required    |         |       | The client 
secret in casdoor.               |
+| callback_url      | string | required    |         |       | The callback 
url which is used to receive state and code.                            |
+
+*Note: endpoint_addr and callback_url should not end with '/'*
+
+## How To Enable
+
+You can enable the plugin on any route by giving out all four attributes 
mentioned above.

Review comment:
       ```suggestion
   You can enable the plugin on any route by giving out all attributes 
mentioned above.
   ```
   would be better

##########
File path: apisix/plugins/auth-casdoor.lua
##########
@@ -0,0 +1,122 @@
+--
+-- 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 cjson = require("cjson")
+local http = require("resty.http")
+local session = require("resty.session")
+local log = core.log
+
+local plugin_name = "auth-casdoor"
+local schema = {
+    type = "object",
+    properties = {
+        --Note: endpoint_addr and callback_url should not end with '/'
+        endpoint_addr = {type = "string", pattern = "^[^%?]+[^/]$"},
+        client_id = {type = "string"},
+        client_secret = {type = "string"},
+        callback_url = {type = "string", pattern = "^[^%?]+[^/]$"},
+    },
+    required = {
+        "callback_url", "endpoint_addr", "client_id", "client_secret"
+    }
+}
+
+local _M = {
+    version = 0.1,
+    priority = 2559,
+    name = plugin_name,
+    schema = schema
+}
+
+local function fetch_access_token(ctx, conf)
+    local args = core.request.get_uri_args(ctx)
+    if not args or not args.code or not args.state then
+        return nil, "failed when accessing token. Invalid cofde or state"
+    end
+    local client = http.new()
+    local url = conf.endpoint_addr .. "/api/login/oauth/access_token"
+
+    local res, err = client:request_uri(url, {
+        method = "POST",
+        query = {
+            code = args.code,
+            grant_type = "authorization_code",
+            client_id = conf.client_id,
+            client_secret = conf.client_secret
+        }
+    })
+    if not res then return nil, err end
+    local data, err = cjson.decode(res.body)
+
+    if err or not data then return nil, err end
+
+    if not data.access_token then
+        return nil, "failed when accessing token: no access_token contained"
+    end
+
+    return data.access_token, nil
+end
+
+function _M.check_schema(conf) return core.schema.check(schema, conf) end
+
+function _M.access(conf, ctx)
+    -- log.info("hit auth-casdoor access")
+    local current_uri = ctx.var.uri
+    local session_obj_read, session_present = session.open()
+
+    -- step 1: check whether hits the callback
+    if current_uri == conf.callback_url:match(".-//[^/]+(/.*)") then
+        local access_token, err = fetch_access_token(ctx, conf)
+        if access_token then
+            if not session_present then
+                return 503, "no session found"

Review comment:
       Please log down the err, like:
   ```
   local err = "no session found"
   core.log.error(err)
   return 503, err
   ```




-- 
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]


Reply via email to