You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by GitBox <gi...@apache.org> on 2022/03/04 08:05:18 UTC

[GitHub] [apisix] Dog-Lee opened a new pull request #6512: feat: support google reCAPTCHAP

Dog-Lee opened a new pull request #6512:
URL: https://github.com/apache/apisix/pull/6512


   ### What this PR does / why we need it:
   <!--- Why is this change required? What problem does it solve? -->
   Google reCAPTCHA is a popular human-identify service in the world. It protects API from spam and abuse.
   
   
   It will be great if it's integrated into official plugins. And This helps people who love APISIX use reCAPTCHA out of the box without any coding.
   
   ### Pre-submission checklist:
   
   <!--
   Please follow the PR manners:
   1. Use Draft if the PR is not ready to be reviewed
   2. Test is required for the feat/fix PR, unless you have a good reason
   3. Doc is required for the feat PR
   4. Use a new commit to resolve review instead of `push -f`
   5. If you need to resolve merge conflicts after the PR is reviewed, please merge master but do not rebase
   6. Use "request review" to notify the reviewer once you have resolved the review
   7. Only reviewer can click "Resolve conversation" to mark the reviewer's review resolved
   -->
   
   * [ ] Did you explain what problem does this PR solve? Or what new features have been added?
   * [ ] Have you added corresponding test cases?
   * [ ] Have you modified the corresponding document?
   * [ ] Is this PR backward compatible? **If it is not backward compatible, please discuss on the [mailing list](https://github.com/apache/apisix/tree/master#community) first**
   


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1059140517


   > missing test cases
   
   @membphis Have some trouble for write test cases. Google reCAPTCHA is aimed at protecting API from bots, so it's almost impossible to obtain a valid recaptcha token from google by programing. It's kind of a paradox. What can I do is only writing the failure test cases


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r823289828



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false

Review comment:
       https://github.com/apache/apisix/pull/6512#discussion_r823289710




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822252628



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },

Review comment:
       Sure.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r826847315



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {

Review comment:
       https://developers.google.com/recaptcha/docs/faq?hl=en#can-i-use-recaptcha-globally
   > please use "www.recaptcha.net" in your code in circumstances when "www.google.com" is not accessible.
   
   Due to the GWF, the Chinese server might not be able to connect with www.google.com(and it reproduces in my local env). I think this might be the reason why it has www.recaptcha.net domain.
   
   @nic-6443 I would say it's not a must-have feature in the current stage, and I can add it in the next version. Can we? : )




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] vm-001 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
vm-001 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r831702939



##########
File path: docs/zh/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,112 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## 描述
+
+通过向 Google reCAPTCHA 服务校验客户端传递的验证码来限制对上游服务的访问。插件支持自定义无效校验码的响应体。
+
+> 注意,此插件只支持 Google reCAPTCHA v2 版本。
+
+## 属性
+
+| Name      | Type          | Requirement | Default    | Valid                                                                    | Description                         |
+| --------- | ------------- | ----------- | ---------- | ------------------------------------------------------------------------ |-------------------------------------|
+| secret_key | string        | 必须    |            |  | Google reCAPTCHA v2 的 secret key    |
+| parameter_source | string | 可选 | header | | 验证码参数的来源枚举值。当前仅支持 `header`, `query` |
+| parameter_name | string | 可选 | captcha | | 验证码参数的名称                            |
+| response | object | 可选    | content_type  = `application/json; charset=utf-8`<br />status_code = `400`<br />body = `{"message":"invalid captcha"}` |  | 无效验证码的 HTTP 响应体                     |

Review comment:
       Sure.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830435345



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)
+        if recaptcha_result.success == true then
+            invalid_captcha = false
+        end

Review comment:
       I prefer to handle all of the invalid captcha cases in the last.
   ```
    if invalid_captcha then
         core.response.set_header("Content-Type", conf.response.content_type)
         return conf.response.status_code, core.utils.resolve_var(conf.response.body, ctx.var)
     end
   ```




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] vm-001 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
vm-001 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830707336



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)
+        if recaptcha_result.success == true then
+            invalid_captcha = false
+        end

Review comment:
       ```lua
   function _M.access(conf, ctx)
       local invalid_captcha = true
       local captcha = retrieve_captcha(ctx, conf)
       if captcha == nil or captcha == "" then
           -- duplicated !
           core.response.set_header("Content-Type", conf.response.content_type)
           return conf.response.status_code, core.utils.resolve_var(conf.response.body, ctx.var)
       end
   
       local httpc = http.new()
       local secret = conf.secret_key
       local remote_ip = core.request.get_remote_client_ip(ctx)
       local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
           method = "POST",
           body = ngx.encode_args({ secret = secret, response = captcha, remoteip = remote_ip }),
           headers = {
               ["Content-Type"] = "application/x-www-form-urlencoded",
           },
           ssl_verify = conf.ssl_verify
       })
       if not res then
           core.log.error("request failed: ", err)
           return 503
       end
       core.log.debug("recaptcha verify result: ", res.body)
       local recaptcha_result, err = core.json.decode(res.body)
       if err then
           core.log.error("faield to decode the recaptcha response json: ", err)
       end
       if recaptcha_result.success ~= true then
           -- duplicated !
           core.response.set_header("Content-Type", conf.response.content_type)
           return conf.response.status_code, core.utils.resolve_var(conf.response.body, ctx.var)
       end
   
       return
   end
   ```
   
   Hi @tzssangglass, two duplicated return codes. 




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] bzp2010 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
bzp2010 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r819439267



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,122 @@
+local radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = { type = "string", default = "header", enum = { "header", "query" } },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end

Review comment:
       Why would you want to manage the api manually through your plugin? With all due respect, this does not match the design of APISIX.
   
   `Route` should be set up and plugins enabled for them first, rather than having your global plugins manage the routes.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r819520558



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,122 @@
+local radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = { type = "string", default = "header", enum = { "header", "query" } },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end

Review comment:
       Good question. 
   This is designed on purpose. In enterprise architecture, you may have hundreds of microservice in the backend. the api gateway dispatches the requests to those services based on the prefix of URL.
   
   For example
   ```
   /user/* -> user service
   /book/* -> book service
   ...
   ```
   Let's say if every service has one or two API need to be verified by recaptcha. If we try to add recaptcha plugin in every service/route, I think it will end up a mess, and it's not good for maintaining the plugin(or secret key in the plugin) in one place. 
   
   Perhaps we can add an option property to verify every request once the plugin is executed. just like the other plugins(I don't think users usually use this). 
   




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1059644370


   > > missing test cases
   > 
   > @membphis Have some trouble for write test cases. Google reCAPTCHA is aimed at protecting API from bots, so it's almost impossible to obtain a valid recaptcha token from google by programing. It's kind of a paradox. What can I do is only writing the failure test cases
   
   FYI: https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do
   We can save some credential information in the secret of Github action, so that it is private to CI.
   And how to test for failure scenarios can be found here: https://stackoverflow.com/questions/43397678/is-it-possible-to-force-fail-a-recaptcha-v2-for-testing-purposes-i-e-pretend


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822340101



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end
+
+local function retrieve_captcha(ctx, api)
+    local captcha
+    if api.param_from == "header" then
+        captcha = core.request.header(ctx, api.param_name)
+    elseif api.param_from == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[api.param_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local api = find_api({ path = path, method = method }, conf.apis)
+    if not api then
+        return
+    end
+
+    core.log.debug("api found: ", core.json.encode(api))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, api)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.recaptcha_secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 500

Review comment:
       > IMO, I afraid it's not good practice for this scenario, user should remove the reCAPTCHA plugin instead of tolerate the failure.
   
   To make sure we are on the same page, something needs to be clarified. Actually, the behavior of manually removing reCAPTCHA plugin or automatically failure tolerated is just the same. And we are using HTTP to the verify captcha token. Since the network instability, sometimes it fails. the users might not be able to remove this plugin in time. Just like fault tolerant in the rate-limiting scene.
   




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] membphis commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
membphis commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r823251953



##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,241 @@
+#
+# 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';
+
+log_level('debug');
+repeat_each(1);
+no_long_string();
+no_root_location();
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                }
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request
+GET /t
+--- response_body
+done
+--- no_error_log
+[error]
+
+== TEST 2: invalid secret_key

Review comment:
       need 3 blank lines between different test cases

##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,241 @@
+#
+# 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';
+
+log_level('debug');
+repeat_each(1);
+no_long_string();
+no_root_location();
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                }
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request
+GET /t
+--- response_body
+done
+--- no_error_log
+[error]
+
+== TEST 2: invalid secret_key
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                secret_key = nil,
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request
+GET /t
+--- response_body
+property "secret_key" is required
+done
+--- no_error_log
+[error]
+
+
+
+=== TEST 2: add plugin
+--- 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": {
+                          "recaptcha": {
+                              "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                              "parameter_source": "header",
+                              "parameter_name": "captcha",
+                              "response": {
+                                "content_type": "application/json; charset=utf-8",
+                                "status_code": 400,
+                                "body": "{\"message\":\"invalid captcha\"}\n"
+                              }
+                          }
+                       },
+                       "upstream": {
+                           "nodes": {
+                               "127.0.0.1:1980": 1
+                           },
+                           "type": "roundrobin"
+                       },
+                       "uri": "/index"
+                  }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 3: add fault-injection plugin for mocking upstream api response
+--- 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": {
+                              "recaptcha": {
+                                  "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                                  "parameter_source": "header",
+                                  "parameter_name": "captcha",
+                                  "response": {
+                                    "content_type": "application/json; charset=utf-8",
+                                    "status_code": 400,
+                                    "body": "{\"message\":\"invalid captcha\"}\n"
+                                  }
+                              }
+                           },
+                           "upstream": {
+                               "nodes": {
+                                   "127.0.0.1:1980": 1
+                               },
+                               "type": "roundrobin"
+                           },
+                           "uri": "/login"
+                   }]=]
+                   )
+               if code >= 300 then
+                   ngx.status = code
+                   ngx.say(body)
+               end
+
+                code, body = t('/apisix/admin/routes/2',

Review comment:
       bad indentation

##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,241 @@
+#
+# 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';
+
+log_level('debug');
+repeat_each(1);
+no_long_string();
+no_root_location();
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                }
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request
+GET /t
+--- response_body
+done
+--- no_error_log
+[error]
+
+== TEST 2: invalid secret_key
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                secret_key = nil,
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request
+GET /t
+--- response_body
+property "secret_key" is required
+done
+--- no_error_log
+[error]
+
+
+
+=== TEST 2: add plugin
+--- 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": {
+                          "recaptcha": {
+                              "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                              "parameter_source": "header",
+                              "parameter_name": "captcha",
+                              "response": {
+                                "content_type": "application/json; charset=utf-8",
+                                "status_code": 400,
+                                "body": "{\"message\":\"invalid captcha\"}\n"
+                              }
+                          }
+                       },
+                       "upstream": {
+                           "nodes": {
+                               "127.0.0.1:1980": 1
+                           },
+                           "type": "roundrobin"
+                       },
+                       "uri": "/index"
+                  }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 3: add fault-injection plugin for mocking upstream api response
+--- 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": {
+                              "recaptcha": {
+                                  "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                                  "parameter_source": "header",
+                                  "parameter_name": "captcha",
+                                  "response": {
+                                    "content_type": "application/json; charset=utf-8",
+                                    "status_code": 400,
+                                    "body": "{\"message\":\"invalid captcha\"}\n"
+                                  }
+                              }
+                           },
+                           "upstream": {
+                               "nodes": {
+                                   "127.0.0.1:1980": 1
+                               },
+                               "type": "roundrobin"
+                           },
+                           "uri": "/login"
+                   }]=]
+                   )
+               if code >= 300 then
+                   ngx.status = code
+                   ngx.say(body)
+               end
+
+                code, body = t('/apisix/admin/routes/2',
+                    ngx.HTTP_PUT,
+                    [=[{
+                           "plugins": {
+                              "recaptcha": {
+                                  "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                                  "parameter_source": "query",
+                                  "parameter_name": "captcha",
+                                  "response": {
+                                    "content_type": "application/json; charset=utf-8",
+                                    "status_code": 400,
+                                    "body": "{\"message\":\"invalid captcha\"}\n"
+                                  }
+                              }
+                           },
+                           "upstream": {
+                               "nodes": {
+                                   "127.0.0.1:1980": 1
+                               },
+                               "type": "roundrobin"
+                           },
+                           "uri": "/active"
+                   }]=]
+                   )
+               if code >= 300 then
+                   ngx.status = code
+               end
+
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 4: request is terminated by recaptcha plugin
+--- request
+POST /login
+--- error_code: 400
+--- response_headers
+Content-Type: application/json; charset=utf-8
+--- response_body
+{"message":"invalid captcha"}
+--- no_error_log
+[error]
+

Review comment:
       remove this useless line




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r827570175



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false

Review comment:
       @nic-6443 @spacewander I have just pushed a commit to support customize `ssl_verify` and default to `true`.  Thanks for pointing out your concern.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r827590795



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false

Review comment:
       > @nic-6443 Why do you approve this PR even the author doesn't resolve your comment?
   
   Sorry, my bad.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r826847315



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {

Review comment:
       https://developers.google.com/recaptcha/docs/faq?hl=en#can-i-use-recaptcha-globally
   > please use "www.recaptcha.net" in your code in circumstances when "www.google.com" is not accessible.
   
   Due to the GWF, the Chinese server might not be able to connect with `www.google.com` (it reproduces in my local env). I think this might be the reason why it has `www.recaptcha.net` domain.
   
   @nic-6443 I would say it's not a must-have feature in the current stage, and I can add it in the next version. Can we? : )




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r832747978



##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,241 @@
+#
+# 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';
+
+log_level('info');
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (! $block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if (! $block->no_error_log && ! $block->error_log) {
+        $block->set_value("no_error_log", "[error]\n[alert]");
+    }
+});
+
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                },
+                ssl_verify = false,
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- response_body
+done
+
+
+
+== TEST 2: invalid secret_key
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                secret_key = nil,
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- response_body
+property "secret_key" is required
+done
+
+
+
+=== TEST 2: add plugin
+--- 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": {
+                          "recaptcha": {
+                              "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                              "parameter_source": "header",
+                              "parameter_name": "captcha",
+                              "response": {
+                                "content_type": "application/json; charset=utf-8",
+                                "status_code": 400,
+                                "body": "{\"message\":\"invalid captcha\"}\n"
+                              },
+                              "ssl_verify": false
+                          }
+                       },
+                       "upstream": {
+                           "nodes": {
+                               "127.0.0.1:1980": 1
+                           },
+                           "type": "roundrobin"
+                       },
+                       "uri": "/index"
+                  }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 3: add plugin on routes
+--- 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": {
+                              "recaptcha": {
+                                  "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                                  "parameter_source": "header",
+                                  "parameter_name": "captcha",
+                                  "response": {
+                                    "content_type": "application/json; charset=utf-8",
+                                    "status_code": 400,
+                                    "body": "{\"message\":\"invalid captcha\"}\n"
+                                  },
+                                  "ssl_verify": false
+                              }
+                           },
+                           "upstream": {
+                               "nodes": {
+                                   "127.0.0.1:1980": 1
+                               },
+                               "type": "roundrobin"
+                           },
+                           "uri": "/login"
+                   }]=]
+                   )
+               if code >= 300 then
+                   ngx.status = code
+                   ngx.say(body)
+               end
+
+               code, body = t('/apisix/admin/routes/2',
+                   ngx.HTTP_PUT,
+                   [=[{
+                          "plugins": {
+                             "recaptcha": {
+                                 "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                                 "parameter_source": "query",
+                                 "parameter_name": "captcha",
+                                 "response": {
+                                   "content_type": "application/json; charset=utf-8",
+                                   "status_code": 400,
+                                   "body": "{\"message\":\"invalid captcha\"}\n"
+                                 },
+                                 "ssl_verify": false
+                             }
+                          },
+                          "upstream": {
+                              "nodes": {
+                                  "127.0.0.1:1980": 1
+                              },
+                              "type": "roundrobin"
+                          },
+                          "uri": "/active"
+                  }]=]
+                  )
+               if code >= 300 then
+                   ngx.status = code
+               end
+
+               ngx.say(body)
+           }
+       }
+--- response_body
+passed
+
+
+
+=== TEST 4: request is terminated by recaptcha plugin 1
+--- request
+POST /login
+--- error_code: 400
+--- response_headers
+Content-Type: application/json; charset=utf-8
+--- response_body
+{"message":"invalid captcha"}
+
+
+
+=== TEST 5: request is terminated by recaptcha plugin 2
+--- request
+POST /active
+--- error_code: 400
+--- response_headers
+Content-Type: application/json; charset=utf-8
+--- response_body
+{"message":"invalid captcha"}
+
+
+
+=== TEST 6: recaptcha valid
+--- request
+POST /login
+--- more_headers
+captcha: test
+--- error_code: 404
+
+
+
+=== TEST 7: recaptcha valid
+--- request
+POST /active?captcha=test
+--- error_code: 404
+--- no_error_log

Review comment:
       Yes, I just mean the `--- no_error_log`.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] tzssangglass commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
tzssangglass commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r829710031



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))

Review comment:
       ```suggestion
       core.log.debug("path: ", path, ", method: ", method,
                      ", conf: ", core.json.delay_encode(conf))
   ```

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then

Review comment:
       before we check `err`, we need to check if `res` is nil or not

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)
+        if recaptcha_result.success == true then
+            invalid_captcha = false
+        end

Review comment:
       ```suggestion
           if recaptcha_result.success ~= true then
               core.response.set_header("Content-Type", conf.response.content_type)
               return conf.response.status_code, core.utils.resolve_var(conf.response.body, ctx.var)
           end
   ```
   
   and rm `if invalid_captcha then ......` is better?

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)

Review comment:
       `schema_type` is unuesd?
   

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end

Review comment:
       ```suggestion
   local function retrieve_captcha(ctx, conf)
       if conf.parameter_source == "header" then
           return core.request.header(ctx, conf.parameter_name)
       elseif conf.parameter_source == "query" then
           local uri_args = core.request.get_uri_args(ctx) or {}
           return uri_args[conf.parameter_name]
       end
   end
   ```
   
   is better?

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)

Review comment:
       `veirfy` -> `verify`




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] tzssangglass commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
tzssangglass commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830565843



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then

Review comment:
       `res` does not always have a value, and may be nil




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1060364338


   Hey guys. I've submitted `recaptcha.t` file. Can somebody help to review it?


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] membphis commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
membphis commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1060607058


   > Hey guys. I've submitted `recaptcha.t` file. Can somebody help to review it?
   
   You can look at the output of CI, many fail. You need to fix them at first, if you need help, pls let us know ^_^


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 edited a comment on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 edited a comment on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1059644370


   > > missing test cases
   > 
   > @membphis Have some trouble for write test cases. Google reCAPTCHA is aimed at protecting API from bots, so it's almost impossible to obtain a valid recaptcha token from google by programing. It's kind of a paradox. What can I do is only writing the failure test cases
   
   FYI: https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do
   We can save some credential information in the secret of GitHub action, so that it is private for CI.
   And how to test for failure scenarios can be found here: https://stackoverflow.com/questions/43397678/is-it-possible-to-force-fail-a-recaptcha-v2-for-testing-purposes-i-e-pretend


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r824545133



##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,242 @@
+#
+# 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';
+
+log_level('debug');
+repeat_each(1);
+no_long_string();
+no_root_location();
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                }
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request

Review comment:
       What he means is that there are duplicate fields in these test cases, e.g.
   ```
   --- request
   GET /t
   --- no_error_log
   [error]
   ```
   You can use the script provided above to remove these duplicate fields, and then use `add_block_preprocessor` to take care of them.
   
   See:
   https://github.com/apache/apisix/blob/0c2ba4bca6192fc200a34ff7b7bebf44a079f0df/docs/en/latest/internal/testing-framework.md?plain=1#L47-L63




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] shuaijinchao commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
shuaijinchao commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822673629



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end
+
+local function retrieve_captcha(ctx, api)
+    local captcha
+    if api.param_from == "header" then
+        captcha = core.request.header(ctx, api.param_name)
+    elseif api.param_from == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[api.param_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local api = find_api({ path = path, method = method }, conf.apis)
+    if not api then
+        return
+    end
+
+    core.log.debug("api found: ", core.json.encode(api))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, api)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.recaptcha_secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 500

Review comment:
       > I think here is more like an Internal server error(500) rather than Service Unavailable(503). The recaptcha layer is part of the request, and 500 seems will be more reasonable for the user.
   > 
   hi @Dog-Lee When responding with a 500 error, it is intercepted by Nginx's default error_page, so a 503 response would be more appropriate.
   Check it out here https://github.com/apache/apisix/blob/c84158c40f1bc3eeb0683310a5ae5e04cce275a9/apisix/cli/ngx_tpl.lua#L315




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822239004



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,122 @@
+local radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = { type = "string", default = "header", enum = { "header", "query" } },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end

Review comment:
       Actually, it isn't a problem as we can configure the basic plugins in service and configure reCAPTCHA in route.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] membphis commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
membphis commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r829725748



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))

Review comment:
       nice catch

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end

Review comment:
       another style and cleaer:
   
   ```lua
   local function retrieve_captcha(ctx, conf)
       if conf.parameter_source == "header" then
           return core.request.header(ctx, conf.parameter_name)
       end
   
       if conf.parameter_source == "query" then
           local uri_args = core.request.get_uri_args(ctx)
           return uri_args and uri_args[conf.parameter_name]
       end
   end
   ```

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end

Review comment:
       another style and clearer:
   
   ```lua
   local function retrieve_captcha(ctx, conf)
       if conf.parameter_source == "header" then
           return core.request.header(ctx, conf.parameter_name)
       end
   
       if conf.parameter_source == "query" then
           local uri_args = core.request.get_uri_args(ctx)
           return uri_args and uri_args[conf.parameter_name]
       end
   end
   ```




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830432161



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)

Review comment:
       Please check this out. https://apisix.apache.org/docs/apisix/plugin-develop
   
   At the same time, we need to implement the check_schema(conf) method to complete the specification verification.
   ```
   function _M.check_schema(conf, schema_type)
       return core.schema.check(schema, conf)
   end
   ```
   Note: the project has provided the public method "core.schema.check", which can be used directly to complete JSON verification.
   
   




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] vm-001 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
vm-001 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r831944763



##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,241 @@
+#
+# 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';
+
+log_level('info');
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (! $block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if (! $block->no_error_log && ! $block->error_log) {
+        $block->set_value("no_error_log", "[error]\n[alert]");
+    }
+});
+
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                },
+                ssl_verify = false,
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- response_body
+done
+
+
+
+== TEST 2: invalid secret_key
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                secret_key = nil,
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- response_body
+property "secret_key" is required
+done
+
+
+
+=== TEST 2: add plugin
+--- 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": {
+                          "recaptcha": {
+                              "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                              "parameter_source": "header",
+                              "parameter_name": "captcha",
+                              "response": {
+                                "content_type": "application/json; charset=utf-8",
+                                "status_code": 400,
+                                "body": "{\"message\":\"invalid captcha\"}\n"
+                              },
+                              "ssl_verify": false
+                          }
+                       },
+                       "upstream": {
+                           "nodes": {
+                               "127.0.0.1:1980": 1
+                           },
+                           "type": "roundrobin"
+                       },
+                       "uri": "/index"
+                  }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 3: add plugin on routes
+--- 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": {
+                              "recaptcha": {
+                                  "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                                  "parameter_source": "header",
+                                  "parameter_name": "captcha",
+                                  "response": {
+                                    "content_type": "application/json; charset=utf-8",
+                                    "status_code": 400,
+                                    "body": "{\"message\":\"invalid captcha\"}\n"
+                                  },
+                                  "ssl_verify": false
+                              }
+                           },
+                           "upstream": {
+                               "nodes": {
+                                   "127.0.0.1:1980": 1
+                               },
+                               "type": "roundrobin"
+                           },
+                           "uri": "/login"
+                   }]=]
+                   )
+               if code >= 300 then
+                   ngx.status = code
+                   ngx.say(body)
+               end
+
+               code, body = t('/apisix/admin/routes/2',
+                   ngx.HTTP_PUT,
+                   [=[{
+                          "plugins": {
+                             "recaptcha": {
+                                 "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                                 "parameter_source": "query",
+                                 "parameter_name": "captcha",
+                                 "response": {
+                                   "content_type": "application/json; charset=utf-8",
+                                   "status_code": 400,
+                                   "body": "{\"message\":\"invalid captcha\"}\n"
+                                 },
+                                 "ssl_verify": false
+                             }
+                          },
+                          "upstream": {
+                              "nodes": {
+                                  "127.0.0.1:1980": 1
+                              },
+                              "type": "roundrobin"
+                          },
+                          "uri": "/active"
+                  }]=]
+                  )
+               if code >= 300 then
+                   ngx.status = code
+               end
+
+               ngx.say(body)
+           }
+       }
+--- response_body
+passed
+
+
+
+=== TEST 4: request is terminated by recaptcha plugin 1
+--- request
+POST /login
+--- error_code: 400
+--- response_headers
+Content-Type: application/json; charset=utf-8
+--- response_body
+{"message":"invalid captcha"}
+
+
+
+=== TEST 5: request is terminated by recaptcha plugin 2
+--- request
+POST /active
+--- error_code: 400
+--- response_headers
+Content-Type: application/json; charset=utf-8
+--- response_body
+{"message":"invalid captcha"}
+
+
+
+=== TEST 6: recaptcha valid
+--- request
+POST /login
+--- more_headers
+captcha: test
+--- error_code: 404
+
+
+
+=== TEST 7: recaptcha valid
+--- request
+POST /active?captcha=test
+--- error_code: 404
+--- no_error_log

Review comment:
       If you are talking about 
   ```
   --- no_error_log
   [error]
   ```
   then yes.
   
   If you are talking `TEST 6` and `TEST 7`. We need these cases for test passed cases. (because the test data had been added on `TEST4`, so I just omit it.)




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r826852033



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {

Review comment:
       ok




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r828719017



##########
File path: docs/zh/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,110 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## 简介
+
+通过向 Google reCAPTCHA 服务校验客户端传递的验证码来限制对上游服务的访问。插件支持自定义无效校验码的响应体。注意,此插件只支持 Google reCAPTCHA v2 版本。

Review comment:
       How about this?  
   
   通过向 Google reCAPTCHA 服务校验客户端传递的验证码来限制对上游服务的访问。插件支持自定义无效校验码的响应体。
   
   >注意,此插件只支持 Google reCAPTCHA v2 版本。




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] membphis commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
membphis commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r829725748



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))

Review comment:
       nice catch




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] leslie-tsang commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
leslie-tsang commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r828731082



##########
File path: docs/zh/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,110 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## 简介
+
+通过向 Google reCAPTCHA 服务校验客户端传递的验证码来限制对上游服务的访问。插件支持自定义无效校验码的响应体。注意,此插件只支持 Google reCAPTCHA v2 版本。

Review comment:
       LGTM




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822338195



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end
+
+local function retrieve_captcha(ctx, api)
+    local captcha
+    if api.param_from == "header" then
+        captcha = core.request.header(ctx, api.param_name)
+    elseif api.param_from == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[api.param_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local api = find_api({ path = path, method = method }, conf.apis)
+    if not api then
+        return
+    end
+
+    core.log.debug("api found: ", core.json.encode(api))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, api)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.recaptcha_secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 500

Review comment:
       To make sure we are on the same page, something needs to be clarified. Actually, the behavior of manually removing reCAPTCHA plugin or automatically failure tolerated is just the same. And we are using HTTP to the verify captcha token. Since the network instability, sometimes it fails. the users might won't be able to remove this plugin in time. 
   
   




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822252628



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },

Review comment:
       Sure.

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },

Review comment:
       Done.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r819520558



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,122 @@
+local radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = { type = "string", default = "header", enum = { "header", "query" } },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end

Review comment:
       Good question. 
   This is designed on purpose. In enterprise architecture, you may have hundreds of microservice in the backend. the api gateway dispatches the requests to those services based on the prefix of URL.
   
   For example
   ```
   /user/* -> user service
   /book/* -> book service
   ...
   ```
   Let's say if every service has one or two API need to be verified by recaptcha. If we try to add recaptcha plugin in every service/route, I think it will end up a mess, and it's not good for maintaining the plugin(or secret key in the plugin) in one place. 
   
   Perhaps we can add an option property to verify every request once the plugin is executed. Just like the other plugins(I don't think users usually use this way). 
   




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
spacewander commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1064687036


   @nic-6443 
   Would you like to take a look? Thanks!


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] shuaijinchao commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
shuaijinchao commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r824513482



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {

Review comment:
       @spacewander What do you think about this question?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] bzp2010 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
bzp2010 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r820261205



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,122 @@
+local radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = { type = "string", default = "header", enum = { "header", "query" } },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end

Review comment:
       I think this is creating a model that is rarely used and doesn't fit the existing concept. Needs @membphis @spacewander to be looked at.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1060070158


   > > We can save some credential information in the secret of GitHub action, so that it is private for CI.
   > 
   > Is it safe if we need to run the CI for pull requests? Attackers might update the test script and send the credential to their servers.
   
   Makes sense, this may help us deal with this security risk: https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-codeowners-to-monitor-changes


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] moonming commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
moonming commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1059121821


   Can this plugin be used with the rate limit plugin?
   I feel like this would be a good example of plugin orchestration


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r823289710



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {

Review comment:
       In my experience, recaptcha is a very stable service. If we provide customize URL, I think it might add a potential security breach that allows the bad guy to change the URL and mock response, and the customer might lose their profit eventually.
   
   Let's move it into the next phase. : )




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822261123



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,122 @@
+local radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = { type = "string", default = "header", enum = { "header", "query" } },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end

Review comment:
       @spacewander I just dig into lua-resty-expr document, and found that it seems only support arg_xxx now(please correct me if I'm wrong). Usually, the recaptcha API depends on the path rather than arg.
   
   And yes, this is an anti-pattern. I have discussed this with @shuaijinchao before. To match APISIX pattern, I can add a match_all(boolean, default true) property to verify every request once the plugin is executed. Just like the other plugins.
   
   What do you think?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r826836292



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {

Review comment:
       @Dog-LeeWant to reconsider this review? I think it makes sense to make the server-side address configurable.

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {

Review comment:
       @Dog-Lee Want to reconsider this review? I think it makes sense to make the server-side address configurable.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r824407950



##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,242 @@
+#
+# 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';
+
+log_level('debug');
+repeat_each(1);
+no_long_string();
+no_root_location();
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                }
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request

Review comment:
       Sorry. Can you explain more? Anything I need to update?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1064915535


   > @nic-6443 Would you like to take a look? Thanks!
   
   OK.


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r824748876



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {

Review comment:
       I don't think there's any security risk in supporting the configuration of `recaptcha_url`, because if this "bad guy" can already maliciously modify your gateway configuration, he could do something even worse than mock recaptcha. 
   
   APISIX has a lot of plugins that need to interact to external services, and it is normal to configure the address of external services. I looked at Google's documentation, the official has provided at least two connection addresses for recaptcha: "www.google.com" and "www.recaptcha.net", it is not clear whether the enterprise version will have other connection addresses. So I think it is necessary to have configurable connection addresses.
   




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r823238993



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end
+
+local function retrieve_captcha(ctx, api)
+    local captcha
+    if api.param_from == "header" then
+        captcha = core.request.header(ctx, api.param_name)
+    elseif api.param_from == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[api.param_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local api = find_api({ path = path, method = method }, conf.apis)
+    if not api then
+        return
+    end
+
+    core.log.debug("api found: ", core.json.encode(api))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, api)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.recaptcha_secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 500

Review comment:
       @shuaijinchao Thanks. Then I have no concern about this now.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r827563277



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false

Review comment:
       @nic-6443 
   Why do you approve this PR even the author doesn't resolve your comment?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r828714024



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)

Review comment:
       will you prefer to return 500 or 503 if JSON decode failed?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] leslie-tsang commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
leslie-tsang commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830708007



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 ngx = ngx
+
+local schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)

Review comment:
       ```suggestion
   
   local function retrieve_captcha(ctx, conf)
   ```
   Please follow the [CODE_STYLE.md](https://github.com/apache/apisix/blob/master/CODE_STYLE.md#blank-line).
   
   > The functions needs to be separated by two blank lines:

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 ngx = ngx
+
+local schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    if conf.parameter_source == "header" then
+        return core.request.header(ctx, conf.parameter_name)
+    end
+
+    if conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        return uri_args[conf.parameter_name]
+    end
+end
+
+function _M.access(conf, ctx)

Review comment:
       ```suggestion
   
   function _M.access(conf, ctx)
   ```
   Please follow the [CODE_STYLE.md](https://github.com/apache/apisix/blob/master/CODE_STYLE.md#blank-line).
   
   > The functions needs to be separated by two blank lines:

##########
File path: docs/zh/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,112 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## 描述
+
+通过向 Google reCAPTCHA 服务校验客户端传递的验证码来限制对上游服务的访问。插件支持自定义无效校验码的响应体。
+
+> 注意,此插件只支持 Google reCAPTCHA v2 版本。
+
+## 属性
+
+| Name      | Type          | Requirement | Default    | Valid                                                                    | Description                         |
+| --------- | ------------- | ----------- | ---------- | ------------------------------------------------------------------------ |-------------------------------------|
+| secret_key | string        | 必须    |            |  | Google reCAPTCHA v2 的 secret key    |
+| parameter_source | string | 可选 | header | | 验证码参数的来源枚举值。当前仅支持 `header`, `query` |
+| parameter_name | string | 可选 | captcha | | 验证码参数的名称                            |
+| response | object | 可选    | content_type  = `application/json; charset=utf-8`<br />status_code = `400`<br />body = `{"message":"invalid captcha"}` |  | 无效验证码的 HTTP 响应体                     |

Review comment:
       Why `<br>` here ?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] vm-001 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
vm-001 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830712828



##########
File path: docs/zh/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,112 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## 描述
+
+通过向 Google reCAPTCHA 服务校验客户端传递的验证码来限制对上游服务的访问。插件支持自定义无效校验码的响应体。
+
+> 注意,此插件只支持 Google reCAPTCHA v2 版本。
+
+## 属性
+
+| Name      | Type          | Requirement | Default    | Valid                                                                    | Description                         |
+| --------- | ------------- | ----------- | ---------- | ------------------------------------------------------------------------ |-------------------------------------|
+| secret_key | string        | 必须    |            |  | Google reCAPTCHA v2 的 secret key    |
+| parameter_source | string | 可选 | header | | 验证码参数的来源枚举值。当前仅支持 `header`, `query` |
+| parameter_name | string | 可选 | captcha | | 验证码参数的名称                            |
+| response | object | 可选    | content_type  = `application/json; charset=utf-8`<br />status_code = `400`<br />body = `{"message":"invalid captcha"}` |  | 无效验证码的 HTTP 响应体                     |

Review comment:
       The response is an object type and it has three properties. And I just want to make sure it looks good. Has APISIX had any guidance on this? Or any suggestions from you? 




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] tzssangglass commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
tzssangglass commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830566046



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)
+        if recaptcha_result.success == true then
+            invalid_captcha = false
+        end

Review comment:
       But you've only set `invalid_captcha == false` in one place, why not just handle it here?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830434863



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then

Review comment:
       Can you please explain why? 




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822252262



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end
+
+local function retrieve_captcha(ctx, api)
+    local captcha
+    if api.param_from == "header" then
+        captcha = core.request.header(ctx, api.param_name)
+    elseif api.param_from == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[api.param_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local api = find_api({ path = path, method = method }, conf.apis)
+    if not api then
+        return
+    end
+
+    core.log.debug("api found: ", core.json.encode(api))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, api)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.recaptcha_secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 500

Review comment:
       I think here is more like an Internal server error(500) rather than Service Unavailable(503). The recaptcha layer is part of the request, and 500 seems will be more reasonable for the user.
   
   What do you think? 
   
   BTW, Do you think it's a good practice to add a failure tolerance option to let the request continue even recaptcha API is unreachable?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822252430



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,

Review comment:
       OK




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] leslie-tsang commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
leslie-tsang commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822268094



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end
+
+local function retrieve_captcha(ctx, api)
+    local captcha
+    if api.param_from == "header" then
+        captcha = core.request.header(ctx, api.param_name)
+    elseif api.param_from == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[api.param_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local api = find_api({ path = path, method = method }, conf.apis)
+    if not api then
+        return
+    end
+
+    core.log.debug("api found: ", core.json.encode(api))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, api)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.recaptcha_secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 500

Review comment:
       > BTW, Do you think it's a good practice to add a failure tolerance option to let the request continue even recaptcha API is unreachable?
   
   IMO, I afraid it's not good practice for this scenario, user should remove the reCAPTCHA plugin instead of tolerate the failure.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822264011



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,

Review comment:
       Done.

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,

Review comment:
       OK




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r824318922



##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,242 @@
+#
+# 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';
+
+log_level('debug');

Review comment:
       Is it necessary to use debug log level?

##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,242 @@
+#
+# 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';
+
+log_level('debug');
+repeat_each(1);
+no_long_string();
+no_root_location();

Review comment:
       Let's add 
   ```
   add_block_preprocessor(sub {
       my ($block) = @_;
   
       if (!$block->request) {
           $block->set_value("request", "GET /t");
       }
   
       if (!$block->no_error_log && !$block->error_log) {
           $block->set_value("no_error_log", "[error]\n[alert]");
       }
   });
   ```
   to remove the duplicate fields.

##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,242 @@
+#
+# 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';
+
+log_level('debug');
+repeat_each(1);
+no_long_string();
+no_root_location();
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                }
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request

Review comment:
       Let's use this script to simplify the duplicate fields:
   ```
   #!/usr/bin/env python
   # coding: utf-8
   # Usage: ./simpify.py $test_filename
   import sys
   with open(sys.argv[1]) as f:
       lines = [l.rstrip() for l in f.readlines()]
   newlines = []
   for i, l in enumerate(lines):
       if ((l == "GET /t" and lines[i-1] == "--- request") or
           (l == "[error]" and lines[i-1] == "--- no_error_log")):
           newlines = newlines[:-1]
           continue
       newlines.append(l)
   res = "\n".join(newlines)
   # print(res)
   with open(sys.argv[1], "w") as f:
      f.write(res + '\n') 
   ```




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1067801773


   Hi guys. Any progress on this PR? When this PR can be merged?


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] membphis commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
membphis commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r828916784



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)

Review comment:
       I think we should use `400` here because the request body is invalid.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] tzssangglass commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
tzssangglass commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r829710031



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))

Review comment:
       ```suggestion
       core.log.debug("path: ", path, ", method: ", method,
                      ", conf: ", core.json.delay_encode(conf))
   ```

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then

Review comment:
       before we check `err`, we need to check if `res` is nil or not

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)
+        if recaptcha_result.success == true then
+            invalid_captcha = false
+        end

Review comment:
       ```suggestion
           if recaptcha_result.success ~= true then
               core.response.set_header("Content-Type", conf.response.content_type)
               return conf.response.status_code, core.utils.resolve_var(conf.response.body, ctx.var)
           end
   ```
   
   and rm `if invalid_captcha then ......` is better?

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)

Review comment:
       `schema_type` is unuesd?
   

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end

Review comment:
       ```suggestion
   local function retrieve_captcha(ctx, conf)
       if conf.parameter_source == "header" then
           return core.request.header(ctx, conf.parameter_name)
       elseif conf.parameter_source == "query" then
           local uri_args = core.request.get_uri_args(ctx) or {}
           return uri_args[conf.parameter_name]
       end
   end
   ```
   
   is better?

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)

Review comment:
       `veirfy` -> `verify`




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] bzp2010 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
bzp2010 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r820261205



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,122 @@
+local radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = { type = "string", default = "header", enum = { "header", "query" } },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end

Review comment:
       I think this is creating a model that is rarely used and doesn't fit the existing concept and needs @spacewander to be looked at.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] moonming commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
moonming commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1060439115


   > Hey guys. I've submitted `recaptcha.t` file. Can somebody help to review it?
   
   great work 👍


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822235568



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,122 @@
+local radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = { type = "string", default = "header", enum = { "header", "query" } },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end

Review comment:
       I prefer to add an expression from lua-resty-expr to filter out the requests that need to be reCAPTCHA, instead of using a mini router matcher (which is an anti-pattern).




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] membphis commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
membphis commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1063552515


   > Have been discussed with @membphis. In the current stages, we all agree with keeping this plugin as simple and easy as possible to be used. And we'll receive feedback from the community and then decide what features will be added in the next stage.
   
   nice ^_^


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] shuaijinchao commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
shuaijinchao commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r823280739



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {

Review comment:
       It should be better if the URI is set into the plugin configuration, if the authentication service API is updated, we don't need to modify the code to configure it.

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false

Review comment:
       It can be set into the plugin configuration as optional configuration.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] membphis commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
membphis commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r829730044



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end

Review comment:
       another style and clearer:
   
   ```lua
   local function retrieve_captcha(ctx, conf)
       if conf.parameter_source == "header" then
           return core.request.header(ctx, conf.parameter_name)
       end
   
       if conf.parameter_source == "query" then
           local uri_args = core.request.get_uri_args(ctx)
           return uri_args and uri_args[conf.parameter_name]
       end
   end
   ```




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r824749090



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false

Review comment:
       > [#6512 (comment)](https://github.com/apache/apisix/pull/6512#discussion_r823289710)
   
   Instead, `ssl_verify = false` is a real security risk, which could lead to man-in-the-middle attacks.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1066482862


   > I read through the reCAPTCHA documentation and there should be two versions of the API currently: v2(Verify requests with challenge) and v3(Verify requests with a score). What we are implementing here is supposed to be the v2 version, so would it be better to clarify this in the documentation?
   
   Hi, @nic-6443. You are right. This plugin is for v2, and I'd modified the document.  Please check it out and let me know if needs to be improved.


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] leslie-tsang commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
leslie-tsang commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830718799



##########
File path: docs/zh/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,112 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## 描述
+
+通过向 Google reCAPTCHA 服务校验客户端传递的验证码来限制对上游服务的访问。插件支持自定义无效校验码的响应体。
+
+> 注意,此插件只支持 Google reCAPTCHA v2 版本。
+
+## 属性
+
+| Name      | Type          | Requirement | Default    | Valid                                                                    | Description                         |
+| --------- | ------------- | ----------- | ---------- | ------------------------------------------------------------------------ |-------------------------------------|
+| secret_key | string        | 必须    |            |  | Google reCAPTCHA v2 的 secret key    |
+| parameter_source | string | 可选 | header | | 验证码参数的来源枚举值。当前仅支持 `header`, `query` |
+| parameter_name | string | 可选 | captcha | | 验证码参数的名称                            |
+| response | object | 可选    | content_type  = `application/json; charset=utf-8`<br />status_code = `400`<br />body = `{"message":"invalid captcha"}` |  | 无效验证码的 HTTP 响应体                     |

Review comment:
       > Has APISIX had any guidance on this ?
   
   Not yet.
   
   > Or any suggestions from you ?
   
   I'm worried that this might cause exceptions in other markdown renderers, shall we remove it ?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r819520558



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,122 @@
+local radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = { type = "string", default = "header", enum = { "header", "query" } },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end

Review comment:
       Good question. 
   This is designed on purpose. In enterprise architecture, you may have hundreds of microservice in the backend. the api gateway dispatches the requests to those services based on the prefix of URL.
   
   For example
   ```
   /user/* -> user service
   /book/* -> book service
   ...
   ```
   Let's say if every service has one or two API need to be verified by recaptcha. If we try to add recaptcha plugin in every service/route, I think it will end up a mess, and it's not good for maintaining the plugin(or secret key in the plugin) in one place. 
   
   perhaps we can add an option property to verify every request once the plugin is executed. just like the other plugins. 
   




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r828704807



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,

Review comment:
       Let's use `ngx.encode_args` to build the body

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)

Review comment:
       ```suggestion
           local recaptcha_result, err = core.json.decode(res.body)
   ```
   Need to check the result

##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,242 @@
+#
+# 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';
+
+log_level('debug');
+repeat_each(1);
+no_long_string();
+no_root_location();
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                }
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request

Review comment:
       It's easy to do it. Here is the script:
   ```
   #!/usr/bin/env python
   # coding: utf-8
   # Usage: ./simpify.py $test_filename
   import sys
   with open(sys.argv[1]) as f:
       lines = [l.rstrip() for l in f.readlines()]
   newlines = []
   for i, l in enumerate(lines):
       if ((l == "GET /t" and lines[i-1] == "--- request") or
           (l == "[error]" and lines[i-1] == "--- no_error_log")):
           newlines = newlines[:-1]
           continue
       newlines.append(l)
   res = "\n".join(newlines)
   # print(res)
   with open(sys.argv[1], "w") as f:
      f.write(res + '\n') 
   ```

##########
File path: docs/en/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,114 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## Name
+
+Restrict access to an upstream service by verifying request captcha token to the Google reCAPTCHA service. The Plugin supports customizing the invalid captcha response. Note that the plugin only supports reCAPTCHA v2(verify requests with a challenge).
+
+## Attributes
+
+| Name      | Type          | Requirement | Default    | Valid                                                                    | Description                                                                                                                                         |
+| --------- | ------------- | ----------- | ---------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
+| secret_key | string        | required    |            |  | The secret key of the Google reCAPTCHA v2 service. |
+| parameter_source | string | optional | header | | The enum of captcha parameter source. Only `header`, `query` are supported. |
+| parameter_name | string | optional | captcha | | The name of captcha parameter. |
+| response | object | optional    | content_type  = `application/json; charset=utf-8`<br />status_code = `400`<br />body = `{"message":"invalid captcha"}` |  | The response of invalid recaptcha token. |

Review comment:
       ```suggestion
   | response | object | optional    | content_type  = `application/json; charset=utf-8`<br />status_code = `400`<br />body = `{"message":"invalid captcha"}` |  | The response for invalid recaptcha token. |
   ```

##########
File path: docs/zh/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,110 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## 简介

Review comment:
       ```suggestion
   ## 描述
   ```

##########
File path: docs/en/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,114 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## Name

Review comment:
       ```suggestion
   ## Description
   ```




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] leslie-tsang commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
leslie-tsang commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r828714401



##########
File path: docs/zh/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,110 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## 简介
+
+通过向 Google reCAPTCHA 服务校验客户端传递的验证码来限制对上游服务的访问。插件支持自定义无效校验码的响应体。注意,此插件只支持 Google reCAPTCHA v2 版本。
+
+## 属性
+
+| Name      | Type          | Requirement | Default    | Valid                                                                    | Description                         |
+| --------- | ------------- | ----------- | ---------- | ------------------------------------------------------------------------ |-------------------------------------|
+| secret_key | string        | 必须    |            |  | Google reCAPTCHA v2 的 secret key    |
+| parameter_source | string | 可选 | header | | 验证码参数的来源枚举值。当前仅支持 `header`, `query` |
+| parameter_name | string | 可选 | captcha | | 验证码参数的名称                            |
+| response | object | 可选    | content_type  = `application/json; charset=utf-8`<br />status_code = `400`<br />body = `{"message":"invalid captcha"}` |  | 无效验证码的 HTTP 响应体                     |
+| ssl_verify | boolean | 可选 | true | | 验证 SSL 证书与主机名是否匹配 |
+
+插件的配置如下:
+
+```json
+{
+    "secret_key":"6LeIxAcTAAAAAGGXXXXXXXXXXXXXXXXXXX",
+    "parameter_source": "header",
+    "parameter_name": "captcha",
+    "response":{
+        "content_type":"application/json; charset=utf-8",
+        "body":"{\"message\":\"invalid captcha\"}\n",
+        "status_code":400
+    }
+}
+```
+
+## 如何启用
+
+下面是一个示例,在指定的 `route` 上开启了 `recaptcha` 插件:
+
+```shell
+curl -i http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+    "plugins": {
+        "recaptcha": {
+            "secret_key": "6LeIxAcTAAAAAGG-XXXXXXXXXXXXXX",
+            "parameter_source": "header",
+            "parameter_name": "captcha",
+            "response": {
+                "content_type": "application/json; charset=utf-8",
+                "status_code": 400,
+                "body": "{\"message\":\"invalid captcha\"}\n"
+            }
+        }
+    },
+    "upstream": {
+        "nodes": {
+            "127.0.0.1:1980": 1
+        },
+        "type": "roundrobin"
+    },
+    "uri": "/login"
+}'
+```
+
+## 测试插件
+
+使用 `curl` 访问:
+
+```shell
+curl -X POST 'http://127.0.0.1:9080/login'
+{"message":"invalid captcha"}
+
+curl -X POST 'http://127.0.0.1:9080/login' -H 'captcha: the_invalid_captcha'
+{"message":"invalid captcha"}

Review comment:
       ```suggestion
   $ curl -X POST 'http://127.0.0.1:9080/login'
   {"message":"invalid captcha"}
   
   $ curl -X POST 'http://127.0.0.1:9080/login' -H 'captcha: the_invalid_captcha'
   {"message":"invalid captcha"}
   ```
   Need to use `$` to distinguish between command and the output, :)

##########
File path: docs/en/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,114 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## Name
+
+Restrict access to an upstream service by verifying request captcha token to the Google reCAPTCHA service. The Plugin supports customizing the invalid captcha response. Note that the plugin only supports reCAPTCHA v2(verify requests with a challenge).
+
+## Attributes
+
+| Name      | Type          | Requirement | Default    | Valid                                                                    | Description                                                                                                                                         |
+| --------- | ------------- | ----------- | ---------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
+| secret_key | string        | required    |            |  | The secret key of the Google reCAPTCHA v2 service. |
+| parameter_source | string | optional | header | | The enum of captcha parameter source. Only `header`, `query` are supported. |
+| parameter_name | string | optional | captcha | | The name of captcha parameter. |
+| response | object | optional    | content_type  = `application/json; charset=utf-8`<br />status_code = `400`<br />body = `{"message":"invalid captcha"}` |  | The response of invalid recaptcha token. |
+| ssl_verify | boolean | optional | true | | verify if SSL cert matches hostname. |
+
+The example configuration of plugin is
+
+```json
+{
+    "secret_key":"6LeIxAcTAAAAAGGXXXXXXXXXXXXXXXXXXX",
+    "parameter_source": "header",
+    "parameter_name": "captcha",
+    "response":{
+        "content_type":"application/json; charset=utf-8",
+        "body":"{\"message\":\"invalid captcha\"}\n",
+        "status_code":400
+    }
+}
+```
+
+## How To Enable
+
+Here's an example, enable this plugin on the specified route:
+
+```shell
+curl -i http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+    "plugins": {
+        "recaptcha": {
+            "secret_key": "6LeIxAcTAAAAAGG-XXXXXXXXXXXXXX",
+            "parameter_source": "header",
+            "parameter_name": "captcha",
+            "response": {
+                "content_type": "application/json; charset=utf-8",
+                "status_code": 400,
+                "body": "{\"message\":\"invalid captcha\"}\n"
+            }
+        }
+    },
+    "upstream": {
+        "nodes": {
+            "127.0.0.1:1980": 1
+        },
+        "type": "roundrobin"
+    },
+    "uri": "/login"
+}'
+```
+
+## Test Plugin
+
+Use curl to request:
+
+```shell
+curl -X POST 'http://127.0.0.1:9080/login'
+{"message":"invalid captcha"}
+
+curl -X POST 'http://127.0.0.1:9080/login' -H 'captcha: the_invalid_captcha'
+{"message":"invalid captcha"}

Review comment:
       ```suggestion
   $ curl -X POST 'http://127.0.0.1:9080/login'
   {"message":"invalid captcha"}
   
   $ curl -X POST 'http://127.0.0.1:9080/login' -H 'captcha: the_invalid_captcha'
   {"message":"invalid captcha"}
   ```
   Need to use `$` to distinguish between command and the output, :)

##########
File path: docs/zh/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,110 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## 简介
+
+通过向 Google reCAPTCHA 服务校验客户端传递的验证码来限制对上游服务的访问。插件支持自定义无效校验码的响应体。注意,此插件只支持 Google reCAPTCHA v2 版本。

Review comment:
       ```suggestion
   > 注意:此插件只支持 Google reCAPTCHA v2 版本。
   
   通过向 Google reCAPTCHA 服务校验客户端传递的验证码来限制对上游服务的访问。插件支持自定义无效校验码的响应体。
   ```
   Would be better ?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] moonming commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
moonming commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1067832378


   > 
   
   CI has not passed, do you need help with this?


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] shuaijinchao commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
shuaijinchao commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r824511386



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,101 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false

Review comment:
       I think whether `ssl_verify` needs to be verified, the choice should be left to the user.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] membphis commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
membphis commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830577655



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)

Review comment:
       APISIX mainly follow REST API design. I think use `400` is clearer.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r831737089



##########
File path: docs/en/latest/plugins/recaptcha.md
##########
@@ -0,0 +1,116 @@
+---
+title: recaptcha
+---
+
+<!--
+#
+
+# 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.
+#
+-->
+
+## Description
+
+Restrict access to an upstream service by verifying request captcha token to the Google reCAPTCHA service. The Plugin supports customizing the invalid captcha response.
+
+> Note that the plugin only supports reCAPTCHA v2(verify requests with a challenge).
+
+## Attributes
+
+| Name      | Type          | Requirement | Default                                                                                                        | Valid                                                                    | Description                                                                                                                                         |
+| --------- | ------------- | ----------- |----------------------------------------------------------------------------------------------------------------| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
+| secret_key | string        | required    |                                                                                                                |  | The secret key of the Google reCAPTCHA v2 service. |
+| parameter_source | string | optional | header                                                                                                         | | The enum of captcha parameter source. Only `header`, `query` are supported. |
+| parameter_name | string | optional | captcha                                                                                                        | | The name of captcha parameter. |
+| response | object | optional    | content_type  = `application/json; charset=utf-8`; status_code = `400`; body = `{"message":"invalid captcha"}` |  | The response for invalid recaptcha token. |
+| ssl_verify | boolean | optional | true                                                                                                           | | verify if SSL cert matches hostname. |
+
+The example configuration of plugin is
+
+```json
+{
+    "secret_key":"6LeIxAcTAAAAAGGXXXXXXXXXXXXXXXXXXX",
+    "parameter_source": "header",
+    "parameter_name": "captcha",
+    "response":{
+        "content_type":"application/json; charset=utf-8",
+        "body":"{\"message\":\"invalid captcha\"}\n",
+        "status_code":400
+    }
+}
+```
+
+## How To Enable
+
+Here's an example, enable this plugin on the specified route:
+
+```shell
+$ curl -i http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+    "plugins": {
+        "recaptcha": {
+            "secret_key": "6LeIxAcTAAAAAGG-XXXXXXXXXXXXXX",
+            "parameter_source": "header",
+            "parameter_name": "captcha",
+            "response": {
+                "content_type": "application/json; charset=utf-8",
+                "status_code": 400,
+                "body": "{\"message\":\"invalid captcha\"}\n"
+            }
+        }
+    },
+    "upstream": {
+        "nodes": {
+            "127.0.0.1:1980": 1
+        },
+        "type": "roundrobin"
+    },
+    "uri": "/login"
+}'
+```
+
+## Test Plugin
+
+Use curl to request:
+
+```shell
+$ curl -X POST 'http://127.0.0.1:9080/login'
+{"message":"invalid captcha"}
+
+$ curl -X POST 'http://127.0.0.1:9080/login' -H 'captcha: the_invalid_captcha'
+{"message":"invalid captcha"}

Review comment:
       Need a successful case

##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,241 @@
+#
+# 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';
+
+log_level('info');
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (! $block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if (! $block->no_error_log && ! $block->error_log) {
+        $block->set_value("no_error_log", "[error]\n[alert]");
+    }
+});
+
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                },
+                ssl_verify = false,
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- response_body
+done
+
+
+
+== TEST 2: invalid secret_key
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                secret_key = nil,
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- response_body
+property "secret_key" is required
+done
+
+
+
+=== TEST 2: add plugin
+--- 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": {
+                          "recaptcha": {
+                              "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                              "parameter_source": "header",
+                              "parameter_name": "captcha",
+                              "response": {
+                                "content_type": "application/json; charset=utf-8",
+                                "status_code": 400,
+                                "body": "{\"message\":\"invalid captcha\"}\n"
+                              },
+                              "ssl_verify": false
+                          }
+                       },
+                       "upstream": {
+                           "nodes": {
+                               "127.0.0.1:1980": 1
+                           },
+                           "type": "roundrobin"
+                       },
+                       "uri": "/index"
+                  }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 3: add plugin on routes
+--- 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": {
+                              "recaptcha": {
+                                  "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                                  "parameter_source": "header",
+                                  "parameter_name": "captcha",
+                                  "response": {
+                                    "content_type": "application/json; charset=utf-8",
+                                    "status_code": 400,
+                                    "body": "{\"message\":\"invalid captcha\"}\n"
+                                  },
+                                  "ssl_verify": false
+                              }
+                           },
+                           "upstream": {
+                               "nodes": {
+                                   "127.0.0.1:1980": 1
+                               },
+                               "type": "roundrobin"
+                           },
+                           "uri": "/login"
+                   }]=]
+                   )
+               if code >= 300 then
+                   ngx.status = code
+                   ngx.say(body)
+               end
+
+               code, body = t('/apisix/admin/routes/2',
+                   ngx.HTTP_PUT,
+                   [=[{
+                          "plugins": {
+                             "recaptcha": {
+                                 "secret_key": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
+                                 "parameter_source": "query",
+                                 "parameter_name": "captcha",
+                                 "response": {
+                                   "content_type": "application/json; charset=utf-8",
+                                   "status_code": 400,
+                                   "body": "{\"message\":\"invalid captcha\"}\n"
+                                 },
+                                 "ssl_verify": false
+                             }
+                          },
+                          "upstream": {
+                              "nodes": {
+                                  "127.0.0.1:1980": 1
+                              },
+                              "type": "roundrobin"
+                          },
+                          "uri": "/active"
+                  }]=]
+                  )
+               if code >= 300 then
+                   ngx.status = code
+               end
+
+               ngx.say(body)
+           }
+       }
+--- response_body
+passed
+
+
+
+=== TEST 4: request is terminated by recaptcha plugin 1
+--- request
+POST /login
+--- error_code: 400
+--- response_headers
+Content-Type: application/json; charset=utf-8
+--- response_body
+{"message":"invalid captcha"}
+
+
+
+=== TEST 5: request is terminated by recaptcha plugin 2
+--- request
+POST /active
+--- error_code: 400
+--- response_headers
+Content-Type: application/json; charset=utf-8
+--- response_body
+{"message":"invalid captcha"}
+
+
+
+=== TEST 6: recaptcha valid
+--- request
+POST /login
+--- more_headers
+captcha: test
+--- error_code: 404
+
+
+
+=== TEST 7: recaptcha valid
+--- request
+POST /active?captcha=test
+--- error_code: 404
+--- no_error_log

Review comment:
       We don't need this?




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830435345



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)
+        if recaptcha_result.success == true then
+            invalid_captcha = false
+        end

Review comment:
       I prefer to handle all of the invalid captcha cases in the last.  So we can avoid duplicated code or define additional function.
   ```
    if invalid_captcha then
         core.response.set_header("Content-Type", conf.response.content_type)
         return conf.response.status_code, core.utils.resolve_var(conf.response.body, ctx.var)
     end
   ```




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
spacewander commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1059941790


   > We can save some credential information in the secret of GitHub action, so that it is private for CI.
   
   Is it safe if we need to run the CI for pull requests? Attackers might update the test script and send the credential to their servers.


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] membphis commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
membphis commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r829730044



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end

Review comment:
       another style and cleaer:
   
   ```lua
   local function retrieve_captcha(ctx, conf)
       if conf.parameter_source == "header" then
           return core.request.header(ctx, conf.parameter_name)
       end
   
       if conf.parameter_source == "query" then
           local uri_args = core.request.get_uri_args(ctx)
           return uri_args and uri_args[conf.parameter_name]
       end
   end
   ```




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r827592921



##########
File path: t/plugin/recaptcha.t
##########
@@ -0,0 +1,242 @@
+#
+# 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';
+
+log_level('debug');
+repeat_each(1);
+no_long_string();
+no_root_location();
+run_tests;
+
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.recaptcha")
+            local ok, err = plugin.check_schema({
+                # https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha
+                secret_key = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe", # Google automated-tests secret key
+                parameter_source = "header",
+                parameter_name = "captcha",
+                response = {
+                    content_type = "application/json; charset=utf-8",
+                    status_code = 400,
+                    body = "{\"message\":\"invalid captcha\"}\n"
+                }
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request

Review comment:
       We can optimize this later, do you think it's OK? @spacewander 




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1067842156


   ```
   Failed test 't/plugin/api-breaker.t TEST 19: hit route 20 times, confirm the breaker time - pattern "phase_func(): breaker_time: 2" should match a line in error.log (req 0)'
   ```
   
   The CI output is confusing. I don't think it's related to the recaptcha. Please correct me if I'm wrong.


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee edited a comment on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee edited a comment on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1067842156


   ```
   ++ ./t/grpc_server_example/grpc_server_example -grpc-address :50051 -grpcs-address :50052 -grpcs-mtls-address :50053 -crt ./t/certs/apisix.crt -key ./t/certs/apisix.key -ca ./t/certs/mtls_ca.crt
   nc: connect to 127.0.0.1 port 50051 (tcp) failed: Connection refused
   ...
   Failed test 't/plugin/api-breaker.t TEST 19: hit route 20 times, confirm the breaker time - pattern "phase_func(): breaker_time: 2" should match a line in error.log (req 0)'
   ```
   
   The CI output is confusing. I don't think it's related to the recaptcha. Please correct me if I'm wrong.


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1062765897


   Hey guys, this plugin has been refactored. 
   Have been discussed with @membphis. In the current stages, we all agree with keeping this plugin as simple and easy as possible to be used. And we'll receive feedback from the community and then decide what features will be added in the next stage.  


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] nic-6443 edited a comment on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
nic-6443 edited a comment on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1059644370


   > > missing test cases
   > 
   > @membphis Have some trouble for write test cases. Google reCAPTCHA is aimed at protecting API from bots, so it's almost impossible to obtain a valid recaptcha token from google by programing. It's kind of a paradox. What can I do is only writing the failure test cases
   
   FYI: https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do
   We can save some credential information in the secret of GitHub action, so that it is private to CI.
   And how to test for failure scenarios can be found here: https://stackoverflow.com/questions/43397678/is-it-possible-to-force-fail-a-recaptcha-v2-for-testing-purposes-i-e-pretend


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822236155



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,

Review comment:
       Need to remove this option.

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = {
+                        type = "string",
+                        default = "header",
+                        enum = { "header", "query" }
+                    },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end
+
+local function retrieve_captcha(ctx, api)
+    local captcha
+    if api.param_from == "header" then
+        captcha = core.request.header(ctx, api.param_name)
+    elseif api.param_from == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[api.param_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local api = find_api({ path = path, method = method }, conf.apis)
+    if not api then
+        return
+    end
+
+    core.log.debug("api found: ", core.json.encode(api))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, api)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.recaptcha_secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = false
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 500

Review comment:
       ```suggestion
               return 503
   ```

##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,147 @@
+--
+-- 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 radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local ipairs = ipairs
+local table = table
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },

Review comment:
       let's remove `recaptcha_` in the conf of recaptcha plugin.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r822266647



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,122 @@
+local radix = require("resty.radixtree")
+local core = require("apisix.core")
+local http = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        recaptcha_secret_key = { type = "string" },
+        apis = {
+            type = "array",
+            items = {
+                type = "object",
+                properties = {
+                    path = { type = "string" },
+                    methods = { type = "array", items = { type = "string" }, minItems = 1 },
+                    param_from = { type = "string", default = "header", enum = { "header", "query" } },
+                    param_name = { type = "string", default = "captcha" },
+                }
+            },
+            minItems = 1
+        },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+
+    },
+    additionalProperties = false,
+    required = { "recaptcha_secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function build_radixtree(apis)
+    local items = {}
+    for _, api in ipairs(apis) do
+        local item = {
+            paths = { api.path },
+            methods = api.methods,
+            metadata = api,
+        }
+        table.insert(items, item)
+    end
+    return radix.new(items)
+end
+
+local function find_api(request, apis)
+    local rx = build_radixtree(apis)
+    return rx:match(request.path, { method = request.method })
+end

Review comment:
       > Actually, it isn't a problem as we can configure the basic plugins in service and configure reCAPTCHA in route.
   
   Sorry, any example of this? 




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] tzssangglass commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
tzssangglass commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830716082



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)
+        if recaptcha_result.success == true then
+            invalid_captcha = false
+        end

Review comment:
       `local invalid_captcha = true` is where you declare invalid_captcha, I mean `invalid_captcha == false` only one place.
   
   




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830434465



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)

Review comment:
       ```
   $ curl --location --request POST 'https://www.recaptcha.net/recaptcha/api/siteverify'
   {
       "success": false,
       "error-codes": [
           "missing-input-secret"
       ]
   }
   ```
   I think recaptcha API always returns JSON response even the request is invalid. So I suggest logging the `err` and then taking this case as invalid_captcha since the request is not qualify the recaptcha API. Do you guys agree?
   




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830435543



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))

Review comment:
       These are legacy code in the first version. already removed.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] vm-001 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
vm-001 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r830707616



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then

Review comment:
       ```
   if not res then
       core.log.error("request failed: ", err)
       return 503
   end
   ```
   
   I see. 




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] vm-001 commented on a change in pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
vm-001 commented on a change in pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#discussion_r831704775



##########
File path: apisix/plugins/recaptcha.lua
##########
@@ -0,0 +1,102 @@
+--
+-- 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 schema = {
+    type = "object",
+    properties = {
+        secret_key = { type = "string" },
+        parameter_source = { type = "string", default = "header", enum = { "header", "query" } },
+        parameter_name = { type = "string", default = "captcha" },
+        ssl_verify = { type = "boolean", default = true },
+        response = {
+            type = "object",
+            properties = {
+                content_type = { type = "string", default = "application/json; charset=utf-8" },
+                status_code = { type = "number", default = 400 },
+                body = { type = "string", default = '{"message": "invalid captcha"}' }
+            }
+        },
+    },
+    required = { "secret_key" },
+}
+
+local recaptcha_url = "https://www.recaptcha.net"
+
+local _M = {
+    version = 0.1,
+    priority = 700,
+    name = "recaptcha",
+    schema = schema,
+}
+
+function _M.check_schema(conf, schema_type)
+    return core.schema.check(schema, conf)
+end
+
+local function retrieve_captcha(ctx, conf)
+    local captcha
+    if conf.parameter_source == "header" then
+        captcha = core.request.header(ctx, conf.parameter_name)
+    elseif conf.parameter_source == "query" then
+        local uri_args = core.request.get_uri_args(ctx) or {}
+        captcha = uri_args[conf.parameter_name]
+    end
+    return captcha
+end
+
+function _M.access(conf, ctx)
+    local path = ctx.var.uri
+    local method = core.request.get_method()
+
+    core.log.debug("path: ", path, ", method: ", method, ", conf: ", core.json.encode(conf))
+
+    local invalid_captcha = true
+    local captcha = retrieve_captcha(ctx, conf)
+    if captcha ~= nil and captcha ~= "" then
+        local httpc = http.new()
+        local secret = conf.secret_key
+        local remote_ip = core.request.get_remote_client_ip(ctx)
+        local res, err = httpc:request_uri(recaptcha_url .. "/recaptcha/api/siteverify", {
+            method = "POST",
+            body = "secret=" .. secret .. "&response=" .. captcha .. "&remoteip=" .. remote_ip,
+            headers = {
+                ["Content-Type"] = "application/x-www-form-urlencoded",
+            },
+            ssl_verify = conf.ssl_verify
+        })
+        if err then
+            core.log.error("request failed: ", err)
+            return 503
+        end
+        core.log.debug("recaptcha veirfy result: ", res.body)
+        local recaptcha_result = core.json.decode(res.body)
+        if recaptcha_result.success == true then
+            invalid_captcha = false
+        end

Review comment:
       No. if request didn't pass the captcha parameter in the header(by default) also cause invalid captcha. and it will be handled in the last in function.




-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] Dog-Lee commented on pull request #6512: feat: support google reCAPTCHA

Posted by GitBox <gi...@apache.org>.
Dog-Lee commented on pull request #6512:
URL: https://github.com/apache/apisix/pull/6512#issuecomment-1059130999


   > Can this plugin be used with the rate limit plugin? I feel like this would be a good example of plugin orchestration
   
   @moonming The priority of limit-* is 1001-1003 and the recaptcha is 700(I'm not sure if it's appropriate). I think recaptcha plugin will be executed after rate limit plugins. So every request in recaptcha should be qualified for the quota of rete limiting.


-- 
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: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org