This is an automated email from the ASF dual-hosted git repository.

bzp2010 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new 2fcfbd83e feat: support gcp secret manager (#11436)
2fcfbd83e is described below

commit 2fcfbd83e22301aea4f027738d628f19c262a458
Author: HuanXin-Chen <[email protected]>
AuthorDate: Sun Sep 22 11:53:48 2024 +0800

    feat: support gcp secret manager (#11436)
---
 apisix/secret/gcp.lua                | 202 ++++++++++
 apisix/utils/google-cloud-oauth.lua  | 130 ++++++
 docs/en/latest/terminology/secret.md |  54 +++
 docs/zh/latest/terminology/secret.md |  56 ++-
 t/lib/server.lua                     | 186 +++++++++
 t/secret/conf/error.json             |   9 +
 t/secret/conf/success.json           |  10 +
 t/secret/gcp.t                       | 737 +++++++++++++++++++++++++++++++++++
 8 files changed, 1383 insertions(+), 1 deletion(-)

diff --git a/apisix/secret/gcp.lua b/apisix/secret/gcp.lua
new file mode 100644
index 000000000..6b6e661c4
--- /dev/null
+++ b/apisix/secret/gcp.lua
@@ -0,0 +1,202 @@
+--
+-- 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.
+--
+
+--- GCP Tools.
+local core         = require("apisix.core")
+local http         = require("resty.http")
+local google_oauth = require("apisix.utils.google-cloud-oauth")
+
+local str_sub       = core.string.sub
+local str_find      = core.string.find
+local decode_base64 = ngx.decode_base64
+
+local lrucache = core.lrucache.new({ ttl = 300, count = 8 })
+
+local schema = {
+    type = "object",
+    properties = {
+        auth_config = {
+            type = "object",
+            properties = {
+                client_email = { type = "string" },
+                private_key = { type = "string" },
+                project_id = { type = "string" },
+                token_uri = {
+                    type = "string",
+                    default = "https://oauth2.googleapis.com/token";
+                },
+                scope = {
+                    type = "array",
+                    items = {
+                        type = "string"
+                    },
+                    default = {
+                        "https://www.googleapis.com/auth/cloud-platform";
+                    }
+                },
+                entries_uri = {
+                    type = "string",
+                    default = "https://secretmanager.googleapis.com/v1";
+                },
+            },
+            required = { "client_email", "private_key", "project_id" }
+        },
+        ssl_verify = {
+            type = "boolean",
+            default = true
+        },
+        auth_file = { type = "string" },
+    },
+    oneOf = {
+        { required = { "auth_config" } },
+        { required = { "auth_file" } },
+    },
+}
+
+local _M = {
+    schema = schema
+}
+
+local function fetch_oauth_conf(conf)
+    if conf.auth_config then
+        return conf.auth_config
+    end
+
+    local file_content, err = core.io.get_file(conf.auth_file)
+    if not file_content then
+        return nil, "failed to read configuration, file: " .. conf.auth_file 
.. ", err: " .. err
+    end
+
+    local config_tab, err = core.json.decode(file_content)
+    if not config_tab then
+        return nil, "config parse failure, data: " .. file_content .. ", err: 
" .. err
+    end
+
+    local config = {
+        auth_config = {
+            client_email = config_tab.client_email,
+            private_key = config_tab.private_key,
+            project_id = config_tab.project_id
+        }
+    }
+
+    local ok, err = core.schema.check(schema, config)
+    if not ok then
+        return nil, "config parse failure, file: " .. conf.auth_file .. ", 
err: " .. err
+    end
+
+    return config_tab
+end
+
+
+local function get_secret(oauth, secrets_id)
+    local httpc = http.new()
+
+    local access_token = oauth:generate_access_token()
+    if not access_token then
+        return nil, "failed to get google oauth token"
+    end
+
+    local entries_uri = oauth.entries_uri .. "/projects/" .. oauth.project_id
+                            .. "/secrets/" .. secrets_id .. 
"/versions/latest:access"
+
+    local res, err = httpc:request_uri(entries_uri, {
+        ssl_verify = oauth.ssl_verify,
+        method = "GET",
+        headers = {
+            ["Content-Type"] = "application/json",
+            ["Authorization"] = (oauth.access_token_type or "Bearer") .. " " 
.. access_token,
+        },
+    })
+
+    if not res then
+        return nil, err
+    end
+
+    if res.status ~= 200 then
+        return nil, res.body
+    end
+
+    local body, err = core.json.decode(res.body)
+    if not body then
+        return nil, "failed to parse response data, " .. err
+    end
+
+    local payload = body.payload
+    if not payload then
+        return nil, "invalid payload"
+    end
+
+    return decode_base64(payload.data)
+end
+
+
+local function make_request_to_gcp(conf, secrets_id)
+    local auth_config, err = fetch_oauth_conf(conf)
+    if not auth_config then
+        return nil, err
+    end
+
+    local lru_key =  auth_config.client_email .. "#" .. auth_config.project_id
+
+    local oauth, err = lrucache(lru_key, "gcp", google_oauth.new, auth_config, 
conf.ssl_verify)
+    if not oauth then
+        return nil, "failed to create oauth object, " .. err
+    end
+
+    local secret, err = get_secret(oauth, secrets_id)
+    if not secret then
+        return nil, err
+    end
+
+    return secret
+end
+
+
+function _M.get(conf, key)
+    core.log.info("fetching data from gcp for key: ", key)
+
+    local idx = str_find(key, '/')
+
+    local main_key = idx and str_sub(key, 1, idx - 1) or key
+    if main_key == "" then
+        return nil, "can't find main key, key: " .. key
+    end
+
+    local sub_key = idx and str_sub(key, idx + 1)
+
+    core.log.info("main: ", main_key, sub_key and ", sub: " .. sub_key or "")
+
+    local res, err = make_request_to_gcp(conf, main_key)
+    if not res then
+        return nil, "failed to retrtive data from gcp secret manager: " .. err
+    end
+
+    if not sub_key then
+        return res
+    end
+
+    local data, err = core.json.decode(res)
+    if not data then
+        return nil, "failed to decode result, err: " .. err
+    end
+
+    return data[sub_key]
+end
+
+
+return _M
diff --git a/apisix/utils/google-cloud-oauth.lua 
b/apisix/utils/google-cloud-oauth.lua
new file mode 100644
index 000000000..6cb352848
--- /dev/null
+++ b/apisix/utils/google-cloud-oauth.lua
@@ -0,0 +1,130 @@
+--
+-- 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 type = type
+local setmetatable = setmetatable
+
+local ngx_update_time = ngx.update_time
+local ngx_time = ngx.time
+local ngx_encode_args = ngx.encode_args
+
+local http = require("resty.http")
+local jwt = require("resty.jwt")
+
+
+local function get_timestamp()
+    ngx_update_time()
+    return ngx_time()
+end
+
+
+local _M = {}
+
+
+function _M.generate_access_token(self)
+    if not self.access_token or get_timestamp() > 
self.access_token_expire_time - 60 then
+        self:refresh_access_token()
+    end
+    return self.access_token
+end
+
+
+function _M.refresh_access_token(self)
+    local http_new = http.new()
+    local res, err = http_new:request_uri(self.token_uri, {
+        ssl_verify = self.ssl_verify,
+        method = "POST",
+        body = ngx_encode_args({
+            grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer",
+            assertion = self:generate_jwt_token()
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded",
+        },
+    })
+
+    if not res then
+        core.log.error("failed to refresh google oauth access token, ", err)
+        return
+    end
+
+    if res.status ~= 200 then
+        core.log.error("failed to refresh google oauth access token: ", 
res.body)
+        return
+    end
+
+    res, err = core.json.decode(res.body)
+    if not res then
+        core.log.error("failed to parse google oauth response data: ", err)
+        return
+    end
+
+    self.access_token = res.access_token
+    self.access_token_type = res.token_type
+    self.access_token_expire_time = get_timestamp() + res.expires_in
+end
+
+
+function _M.generate_jwt_token(self)
+    local payload = core.json.encode({
+        iss = self.client_email,
+        aud = self.token_uri,
+        scope = self.scope,
+        iat = get_timestamp(),
+        exp = get_timestamp() + (60 * 60)
+    })
+
+    local jwt_token = jwt:sign(self.private_key, {
+        header = { alg = "RS256", typ = "JWT" },
+        payload = payload,
+    })
+
+    return jwt_token
+end
+
+
+function _M.new(config, ssl_verify)
+    local oauth = {
+        client_email = config.client_email,
+        private_key = config.private_key,
+        project_id = config.project_id,
+        token_uri = config.token_uri or "https://oauth2.googleapis.com/token";,
+        auth_uri = config.auth_uri or 
"https://accounts.google.com/o/oauth2/auth";,
+        entries_uri = config.entries_uri,
+        access_token = nil,
+        access_token_type = nil,
+        access_token_expire_time = 0,
+    }
+
+    oauth.ssl_verify = ssl_verify
+
+    if config.scope then
+        if type(config.scope) == "string" then
+            oauth.scope = config.scope
+        end
+
+        if type(config.scope) == "table" then
+            oauth.scope = core.table.concat(config.scope, " ")
+        end
+    end
+
+    return setmetatable(oauth, { __index = _M })
+end
+
+
+return _M
diff --git a/docs/en/latest/terminology/secret.md 
b/docs/en/latest/terminology/secret.md
index e27ee79fc..94c1b8843 100644
--- a/docs/en/latest/terminology/secret.md
+++ b/docs/en/latest/terminology/secret.md
@@ -40,6 +40,7 @@ APISIX currently supports storing secrets in the following 
ways:
 - [Environment Variables](#use-environment-variables-to-manage-secrets)
 - [HashiCorp Vault](#use-hashicorp-vault-to-manage-secrets)
 - [AWS Secrets Manager](#use-aws-secrets-manager-to-manage-secrets)
+- [GCP Secrets Manager](#use-gcp-secrets-manager-to-manage-secrets)
 
 You can use APISIX Secret functions by specifying format variables in the 
consumer configuration of the following plugins, such as `key-auth`.
 
@@ -293,3 +294,56 @@ curl -i http://127.0.0.1:9080/your_route -H 'apikey: value'
 ```
 
 This will verify whether the `key-auth` plugin is correctly using the key from 
AWS Secrets Manager.
+
+## Use GCP Secrets Manager to manage secrets
+
+Using the GCP Secrets Manager to manage secrets means you can store the secret 
information in the GCP service, and reference it using a specific format of 
variables when configuring plugins. APISIX currently supports integration with 
the GCP Secrets Manager, and the supported authentication method is [OAuth 
2.0](https://developers.google.com/identity/protocols/oauth2).
+
+### Reference Format
+
+```
+$secret://$manager/$id/$secret_name/$key
+```
+
+The reference format is the same as before:
+
+- manager: secrets management service, could be the HashiCorp Vault, AWS, GCP 
etc.
+- id: APISIX Secrets resource ID, which needs to be consistent with the one 
specified when adding the APISIX Secrets resource
+- secret_name: the secret name in the secrets management service
+- key: get the value of a property when the value of the secret is a JSON 
string
+
+### Required Parameters
+
+| Name                    | Required | Default                                 
                                                                                
                                                                             | 
Description                                                                     
                                                                                
   |
+|-------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| auth_config             | True     |                                         
                                                                                
                                                                             | 
Either `auth_config` or `auth_file` must be provided.                           
                                                                                
   |
+| auth_config.client_email | True     |                                        
                                                                                
                                                                            | 
Email address of the Google Cloud service account.                              
                                                                                
     |
+| auth_config.private_key | True     |                                         
                                                                                
                                                                             | 
Private key of the Google Cloud service account.                                
                                                                                
   |
+| auth_config.project_id  | True     |                                         
                                                                                
                                                                             | 
Project ID in the Google Cloud service account.                                 
                                                                                
   |
+| auth_config.token_uri   | False    | https://oauth2.googleapis.com/token     
                                                                                
                                                                               
| Token URI of the Google Cloud service account.                                
                                                                                
     |
+| auth_config.entries_uri | False    | https://secretmanager.googleapis.com/v1 
                                                                                
                                                                     |  The API 
access endpoint for the Google Secrets Manager.                                 
                                                                                
               |
+| auth_config.scope      | False    | 
https://www.googleapis.com/auth/cloud-platform | Access scopes of the Google 
Cloud service account. See [OAuth 2.0 Scopes for Google 
APIs](https://developers.google.com/identity/protocols/oauth2/scopes) |
+| auth_file               | True     |                                         
                                                                                
                                                                             | 
Path to the Google Cloud service account authentication JSON file. Either 
`auth_config` or `auth_file` must be provided.                                  
         |
+| ssl_verify              | False    | true                                    
                                                                                
                                                                             | 
When set to `true`, enables SSL verification as mentioned in [OpenResty 
docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake).       
           |
+
+You need to configure the corresponding authentication parameters, or specify 
the authentication file through auth_file, where the content of auth_file is in 
JSON format.
+
+### Example
+
+Here is a correct configuration example:
+
+```
+curl http://127.0.0.1:9180/apisix/admin/secrets/gcp/1 \
+-H "X-API-KEY: $admin_key" -X PUT -d '
+{
+    "auth_config" : {
+        "client_email": "[email protected]",
+        "private_key": "private_key",
+        "project_id": "apisix-project",
+        "token_uri": "https://oauth2.googleapis.com/token";,
+        "entries_uri": "https://secretmanager.googleapis.com/v1";,
+        "scope": ["https://www.googleapis.com/auth/cloud-platform";]
+    }
+}'
+
+```
diff --git a/docs/zh/latest/terminology/secret.md 
b/docs/zh/latest/terminology/secret.md
index 03dbf0c1b..810abb7dd 100644
--- a/docs/zh/latest/terminology/secret.md
+++ b/docs/zh/latest/terminology/secret.md
@@ -40,6 +40,7 @@ APISIX 目前支持通过以下方式存储密钥:
 - [环境变量](#使用环境变量管理密钥)
 - [HashiCorp Vault](#使用-vault-管理密钥)
 - [AWS Secrets Manager](#使用-aws-secrets-manager-管理密钥)
+- [GCP Secrets Manager](#使用-gcp-secrets-manager-管理密钥)
 
 你可以在以下插件的 consumer 配置中通过指定格式的变量来使用 APISIX Secret 功能,比如 `key-auth` 插件。
 
@@ -135,7 +136,7 @@ curl http://127.0.0.1:9180/apisix/admin/consumers \
 $secret://$manager/$id/$secret_name/$key
 ```
 
-- manager: 密钥管理服务,可以是 Vault、AWS 等
+- manager: 密钥管理服务,可以是 Vault、AWS、GCP 等
 - APISIX Secret 资源 ID,需要与添加 APISIX Secret 资源时指定的 ID 保持一致
 - secret_name: 密钥管理服务中的密钥名称
 - key:密钥管理服务中密钥对应的 key
@@ -295,3 +296,56 @@ curl -i http://127.0.0.1:9080/your_route -H 'apikey: value'
 ```
 
 这将验证 key-auth 插件是否正确地使用 AWS Secret Manager 中的密钥。
+
+## 使用 GCP Secrets Manager 管理密钥
+
+使用 GCP Secret Manager 来管理密钥意味着你可以将密钥信息保存在 GCP 服务中,在配置插件时通过特定格式的变量来引用。APISIX 
目前支持对接 GCP Secret Manager, 所支持的验证方式是[OAuth 
2.0](https://developers.google.com/identity/protocols/oauth2?hl=zh-cn)。
+
+### 引用方式
+
+```
+$secret://$manager/$id/$secret_name/$key
+```
+
+引用方式和之前保持一致:
+
+- manager: 密钥管理服务,可以是 Vault、AWS\GCP 等
+- APISIX Secret 资源 ID,需要与添加 APISIX Secret 资源时指定的 ID 保持一致
+- secret_name: 密钥管理服务中的密钥名称
+- key:当密钥的值是 JSON 字符串时,获取某个属性的值
+
+### 必要参数
+
+| 名称                     | 必选项   | 默认值                                         
  | 描述                                                                          
                                                   |
+| ----------------------- | -------- | 
------------------------------------------------ | 
-------------------------------------------------------------------------------------------------------------------------------
  |
+| auth_config             | 是       |                                          
        | `auth_config` 和 `auth_file` 必须配置一个。                                   
                                                  |
+| auth_config.client_email | 是       |                                         
         | 谷歌服务帐号的 email 参数。                                                    
                                                       |
+| auth_config.private_key | 是       |                                          
        | 谷歌服务帐号的私钥参数。                                                          
                                                 |
+| auth_config.project_id  | 是       |                                          
        | 谷歌服务帐号的项目 ID。                                                         
                                                   |
+| auth_config.token_uri   | 否       | https://oauth2.googleapis.com/token      
        | 请求谷歌服务帐户的令牌的 URI。                                                     
                                                   |
+| auth_config.entries_uri | 否       |   
https://secretmanager.googleapis.com/v1     | 谷歌密钥服务访问端点 API。                   
                                                                                
|
+| auth_config.scope      | 否       |   
https://www.googleapis.com/auth/cloud-platform                                  
             | 谷歌服务账号的访问范围,可参考 [OAuth 2.0 Scopes for Google 
APIs](https://developers.google.com/identity/protocols/oauth2/scopes)|
+| auth_file               | 是       |                                          
        | `auth_config` 和 `auth_file` 必须配置一个。          |
+| ssl_verify              | 否       | true                                     
        | 当设置为 `true` 时,启用 `SSL` 验证。                 |
+
+你需要配置相应的认证参数,或者通过 auth_file 来指定认证文件,其中 auth_file 的内容为认证参数的 json 格式。
+
+### 示例
+
+以下一种正确的配置实例:
+
+```
+curl http://127.0.0.1:9180/apisix/admin/secrets/gcp/1 \
+-H "X-API-KEY: $admin_key" -X PUT -d '
+{
+    "auth_config" : {
+        "client_email": "[email protected]",
+        "private_key": "private_key",
+        "project_id": "apisix-project",
+        "token_uri": "https://oauth2.googleapis.com/token";,
+        "entries_uri": "https://secretmanager.googleapis.com/v1";,
+        "scope": ["https://www.googleapis.com/auth/cloud-platform";]
+    }
+}'
+
+```
diff --git a/t/lib/server.lua b/t/lib/server.lua
index 7cc8101a3..309873636 100644
--- a/t/lib/server.lua
+++ b/t/lib/server.lua
@@ -558,6 +558,192 @@ function _M.google_logging_entries()
     ngx.say(data)
 end
 
+function _M.google_secret_token()
+    local args = ngx.req.get_uri_args()
+    local args_token_type = args.token_type or "Bearer"
+    ngx.req.read_body()
+    local data = ngx.decode_args(ngx.req.get_body_data())
+    local jwt = require("resty.jwt")
+    local access_scopes = "https://www.googleapis.com/auth/cloud";
+    local verify = jwt:verify(rsa_public_key, data["assertion"])
+    if not verify.verified then
+        ngx.status = 401
+        ngx.say(json_encode({ error = "identity authentication failed" }))
+        return
+    end
+
+    local scopes_valid = type(verify.payload.scope) == "string" and
+            verify.payload.scope:find(access_scopes)
+    if not scopes_valid then
+        ngx.status = 403
+        ngx.say(json_encode({ error = "no access to this scope" }))
+        return
+    end
+
+    local expire_time = (verify.payload.exp or ngx.time()) - ngx.time()
+    if expire_time <= 0 then
+        expire_time = 0
+    end
+
+    local jwt_token = jwt:sign(rsa_private_key, {
+        header = { typ = "JWT", alg = "RS256" },
+        payload = { exp = verify.payload.exp, scope = access_scopes }
+    })
+
+    ngx.say(json_encode({
+        access_token = jwt_token,
+        expires_in = expire_time,
+        token_type = args_token_type
+    }))
+end
+
+function _M.google_secret_apisix_jack()
+    local args = ngx.req.get_uri_args()
+    local args_token_type = args.token_type or "Bearer"
+    local jwt = require("resty.jwt")
+    local access_scopes = "https://www.googleapis.com/auth/cloud";
+
+    local headers = ngx.req.get_headers()
+    local token = headers["Authorization"]
+    if not token then
+        ngx.status = 401
+        ngx.say(json_encode({ error = "authentication header not exists" }))
+        return
+    end
+
+    token = string.sub(token, #args_token_type + 2)
+    local verify = jwt:verify(rsa_public_key, token)
+    if not verify.verified then
+        ngx.status = 401
+        ngx.say(json_encode({ error = "identity authentication failed" }))
+        return
+    end
+
+    local scopes_valid = type(verify.payload.scope) == "string" and
+            verify.payload.scope:find(access_scopes)
+    if not scopes_valid then
+        ngx.status = 403
+        ngx.say(json_encode({ error = "no access to this scope" }))
+        return
+    end
+
+    local expire_time = (verify.payload.exp or ngx.time()) - ngx.time()
+    if expire_time <= 0 then
+        ngx.status = 403
+        ngx.say(json_encode({ error = "token has expired" }))
+        return
+    end
+
+    local response = {
+        name = "projects/647037004838/secrets/apisix/versions/1",
+        payload = {
+          data = "eyJrZXkiOiJ2YWx1ZSJ9",
+          dataCrc32c = "2296192492"
+        }
+      }
+
+    ngx.status = 200
+    ngx.say(json_encode(response))
+end
+
+function _M.google_secret_apisix_error_jack()
+    local args = ngx.req.get_uri_args()
+    local args_token_type = args.token_type or "Bearer"
+    local jwt = require("resty.jwt")
+    local access_scopes = "https://www.googleapis.com/auth/root/cloud";
+
+    local headers = ngx.req.get_headers()
+    local token = headers["Authorization"]
+    if not token then
+        ngx.status = 401
+        ngx.say(json_encode({ error = "authentication header not exists" }))
+        return
+    end
+
+    token = string.sub(token, #args_token_type + 2)
+    local verify = jwt:verify(rsa_public_key, token)
+    if not verify.verified then
+        ngx.status = 401
+        ngx.say(json_encode({ error = "identity authentication failed" }))
+        return
+    end
+
+    local scopes_valid = type(verify.payload.scope) == "string" and
+            verify.payload.scope:find(access_scopes)
+    if not scopes_valid then
+        ngx.status = 403
+        ngx.say(json_encode({ error = "no access to this scope" }))
+        return
+    end
+
+    local expire_time = (verify.payload.exp or ngx.time()) - ngx.time()
+    if expire_time <= 0 then
+        ngx.status = 403
+        ngx.say(json_encode({ error = "token has expired" }))
+        return
+    end
+
+    local response = {
+        name = "projects/647037004838/secrets/apisix_error/versions/1",
+        payload = {
+          data = "eyJrZXkiOiJ2YWx1ZSJ9",
+          dataCrc32c = "2296192492"
+        }
+      }
+
+    ngx.status = 200
+    ngx.say(json_encode(response))
+end
+
+function _M.google_secret_apisix_mysql()
+    local args = ngx.req.get_uri_args()
+    local args_token_type = args.token_type or "Bearer"
+    local jwt = require("resty.jwt")
+    local access_scopes = "https://www.googleapis.com/auth/cloud";
+
+    local headers = ngx.req.get_headers()
+    local token = headers["Authorization"]
+    if not token then
+        ngx.status = 401
+        ngx.say(json_encode({ error = "authentication header not exists" }))
+        return
+    end
+
+    token = string.sub(token, #args_token_type + 2)
+    local verify = jwt:verify(rsa_public_key, token)
+    if not verify.verified then
+        ngx.status = 401
+        ngx.say(json_encode({ error = "identity authentication failed" }))
+        return
+    end
+
+    local scopes_valid = type(verify.payload.scope) == "string" and
+            verify.payload.scope:find(access_scopes)
+    if not scopes_valid then
+        ngx.status = 403
+        ngx.say(json_encode({ error = "no access to this scope" }))
+        return
+    end
+
+    local expire_time = (verify.payload.exp or ngx.time()) - ngx.time()
+    if expire_time <= 0 then
+        ngx.status = 403
+        ngx.say(json_encode({ error = "token has expired" }))
+        return
+    end
+
+    local response = {
+        name = "projects/647037004838/secrets/apisix/versions/1",
+        payload = {
+          data = "c2VjcmV0",
+          dataCrc32c = "0xB03C4D4D"
+        }
+      }
+
+    ngx.status = 200
+    ngx.say(json_encode(response))
+end
+
 function _M.plugin_proxy_rewrite_resp_header()
     ngx.req.read_body()
     local s = "plugin_proxy_rewrite_resp_header"
diff --git a/t/secret/conf/error.json b/t/secret/conf/error.json
new file mode 100644
index 000000000..3d0bb6295
--- /dev/null
+++ b/t/secret/conf/error.json
@@ -0,0 +1,9 @@
+{
+  "private_key": "-----BEGIN PRIVATE 
KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR\naeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC\nUuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF\n2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4\nv5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep\nAB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw\nIu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4p
 [...]
+  "project_id": "apisix",
+  "token_uri": "http://127.0.0.1:1980/google/logging/token";,
+  "scope": [
+    "https://apisix.apache.org/logs:admin";
+  ],
+  "entries_uri": "http://127.0.0.1:1980/google/logging/entries";
+}
diff --git a/t/secret/conf/success.json b/t/secret/conf/success.json
new file mode 100644
index 000000000..d9cfbc3c8
--- /dev/null
+++ b/t/secret/conf/success.json
@@ -0,0 +1,10 @@
+{
+  "private_key": "-----BEGIN PRIVATE 
KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR\naeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC\nUuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF\n2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4\nv5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep\nAB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw\nIu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4p
 [...]
+  "project_id": "apisix",
+  "token_uri": "http://127.0.0.1:1980/google/secret/token";,
+  "scope": [
+    "https://www.googleapis.com/auth/cloud";
+  ],
+  "entries_uri": "http://127.0.0.1:1984";,
+  "client_email": "[email protected]"
+}
diff --git a/t/secret/gcp.t b/t/secret/gcp.t
new file mode 100644
index 000000000..b7fc5331c
--- /dev/null
+++ b/t/secret/gcp.t
@@ -0,0 +1,737 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+log_level("info");
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: validate different schema situation
+--- config
+    location /t {
+        content_by_lua_block {
+            local test_case = {
+                {},
+                {auth_file = "123"},
+                {auth_file = 123},
+                {auth_config = {client_email = "client", private_key = 
"private_key"}},
+                {auth_config = {private_key = "private_key", project_id = 
"project_id"}},
+                {auth_config = {client_email = "client", project_id = 
"project_id"}},
+                {auth_config = {client_email = "client", private_key = 
"private_key", project_id = "project_id"}},
+                {auth_config = {client_email = 1234, private_key = 
"private_key", project_id = "project_id"}},
+                {auth_config = {client_email = "client", private_key = 1234, 
project_id = "project_id"}},
+                {auth_config = {client_email = "client", private_key = 
"private_key", project_id = 1234}},
+                {auth_config = {client_email = "client", private_key = 
"private_key", project_id = "project_id"}, ssl_verify = 1234},
+                {auth_config = {client_email = "client", private_key = 
"private_key", project_id = "project_id", token_uri = 1234}},
+                {auth_config = {client_email = "client", private_key = 
"private_key", project_id = "project_id", scope = 1234}},
+                {auth_config = {client_email = "client", private_key = 
"private_key", project_id = "project_id", entries_uri = 1234}},
+                {auth_config = {client_email = "client", private_key = 
"private_key", project_id = "project_id", token_uri = "token_uri",
+                    scope = {"scope"}, entries_uri = "entries_uri"}, 
ssl_verify = true},
+            }
+            local gcp = require("apisix.secret.gcp")
+            local core = require("apisix.core")
+            local metadata_schema = gcp.schema
+
+            for _, conf in ipairs(test_case) do
+                local ok, err = core.schema.check(metadata_schema, conf)
+                ngx.say(ok and "done" or err)
+            end
+        }
+    }
+--- request
+GET /t
+--- response_body
+value should match only one schema, but matches none
+done
+property "auth_file" validation failed: wrong type: expected string, got number
+property "auth_config" validation failed: property "project_id" is required
+property "auth_config" validation failed: property "client_email" is required
+property "auth_config" validation failed: property "private_key" is required
+done
+property "auth_config" validation failed: property "client_email" validation 
failed: wrong type: expected string, got number
+property "auth_config" validation failed: property "private_key" validation 
failed: wrong type: expected string, got number
+property "auth_config" validation failed: property "project_id" validation 
failed: wrong type: expected string, got number
+property "ssl_verify" validation failed: wrong type: expected boolean, got 
number
+property "auth_config" validation failed: property "token_uri" validation 
failed: wrong type: expected string, got number
+property "auth_config" validation failed: property "scope" validation failed: 
wrong type: expected array, got number
+property "auth_config" validation failed: property "entries_uri" validation 
failed: wrong type: expected string, got number
+done
+
+
+
+=== TEST 2: check key: no main key
+--- config
+    location /t {
+        content_by_lua_block {
+            local gcp = require("apisix.secret.gcp")
+            local conf = {
+                auth_config = {
+                    client_email = "[email protected]",
+                    private_key = [[
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR
+aeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC
+UuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF
+2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4
+v5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep
+AB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw
+Iu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P
+PR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic
+DcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49
+sxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC
+afOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC
+l85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz
+lw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC
+rCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g
+tdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16
+UyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1
+Ujqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI
+1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh
+GfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46
+xn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4
+upppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF
+FzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo
+y4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W
+vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK
+Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S
+kEJQcmfVew5mFXyxuEn3zA==
+-----END PRIVATE KEY-----]],
+                    project_id = "apisix",
+                    token_uri = "http://127.0.0.1:1980/token";,
+                    scope = {
+                        "https://www.googleapis.com/auth/cloud-platform";
+                    },
+                },
+            }
+            local data, err = gcp.get(conf, "/apisix")
+            if err then
+                return ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- request
+GET /t
+--- response_body
+can't find main key, key: /apisix
+
+
+
+=== TEST 3: add secret  && consumer && check
+--- request
+GET /t
+--- config
+    location /t {
+        content_by_lua_block {
+            local conf = {
+                auth_config = {
+                    client_email = "[email protected]",
+                    private_key = [[
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR
+aeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC
+UuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF
+2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4
+v5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep
+AB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw
+Iu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P
+PR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic
+DcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49
+sxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC
+afOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC
+l85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz
+lw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC
+rCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g
+tdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16
+UyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1
+Ujqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI
+1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh
+GfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46
+xn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4
+upppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF
+FzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo
+y4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W
+vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK
+Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S
+kEJQcmfVew5mFXyxuEn3zA==
+-----END PRIVATE KEY-----]],
+                    project_id = "apisix",
+                    token_uri = "http://127.0.0.1:1980/google/secret/token";,
+                    scope = {
+                        "https://www.googleapis.com/auth/cloud-platform";
+                    },
+                    entries_uri = "http://127.0.0.1:1984";
+                },
+            }
+
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/secrets/gcp/mysecret', 
ngx.HTTP_PUT, conf)
+
+            if code >= 300 then
+                ngx.status = code
+                return ngx.say(body)
+            end
+
+            -- change consumer with secrets ref: gcp
+            code, body = t('/apisix/admin/consumers',
+                ngx.HTTP_PUT,
+                [[{
+                    "username": "jack",
+                    "plugins": {
+                          "key-auth": {
+                            "key": "$secret://gcp/mysecret/jack/key"
+                        }
+                    }
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+                return ngx.say(body)
+            end
+
+
+            local secret = require("apisix.secret")
+            local value = 
secret.fetch_by_uri("$secret://gcp/mysecret/jack/key")
+
+
+            local code, body = t('/apisix/admin/secrets/gcp/mysecret', 
ngx.HTTP_DELETE)
+            if code >= 300 then
+                ngx.status = code
+                return ngx.say(body)
+            end
+
+            code, body = t('/apisix/admin/consumers',
+                ngx.HTTP_PUT,
+                [[{
+                    "username": "jack",
+                    "plugins": {
+                          "key-auth": {
+                            "key": "$secret://gcp/mysecret/jack/key"
+                        }
+                    }
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+                return ngx.say(body)
+            end
+
+            local secret = require("apisix.secret")
+            local value = 
secret.fetch_by_uri("$secret://gcp/mysecret/jack/key")
+            if value then
+                ngx.say("secret value: ", value)
+            end
+            ngx.say("all done")
+        }
+    }
+--- response_body
+all done
+
+
+
+=== TEST 4: setup route (/projects/apisix/secrets/jack/versions/latest:access)
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions": [
+                                "return function(conf, ctx)
+                                    
require('lib.server').google_secret_apisix_jack()
+                                end"
+                            ]
+                        }
+                    },
+                    "uri": 
"/projects/apisix/secrets/jack/versions/latest:access",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 5: setup route 
(/projects/apisix_error/secrets/jack/versions/latest:access)
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/2',
+                ngx.HTTP_PUT,
+                [[{
+                    "plugins": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions": [
+                                "return function(conf, ctx)
+                                    
require('lib.server').google_secret_apisix_error_jack()
+                                end"
+                            ]
+                        }
+                    },
+                    "uri": 
"/projects/apisix_error/secrets/jack/versions/latest:access",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 6: setup route (/projects/apisix/secrets/mysql/versions/latest:access)
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/3',
+                ngx.HTTP_PUT,
+                [[{
+                    "plugins": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions": [
+                                "return function(conf, ctx)
+                                    
require('lib.server').google_secret_apisix_mysql()
+                                end"
+                            ]
+                        }
+                    },
+                    "uri": 
"/projects/apisix/secrets/mysql/versions/latest:access",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 7: get value from gcp by auth_file(fetch_oatuh_conf failed, read 
failed)
+--- config
+    location /t {
+        content_by_lua_block {
+            local conf = {
+                auth_file = "t/secret/conf/nofind.json",
+            }
+            local gcp = require("apisix.secret.gcp")
+            local value, err = gcp.get(conf, "jack/key")
+            if not value then
+                return ngx.say(err)
+            end
+            ngx.say(value)
+        }
+    }
+--- request
+GET /t
+--- response_body
+failed to retrtive data from gcp secret manager: failed to read configuration, 
file: t/secret/conf/nofind.json, err: t/secret/conf/nofind.json: No such file 
or directory
+
+
+
+=== TEST 8: get value from gcp by auth_file(fetch_oatuh_conf success)
+--- config
+    location /t {
+        content_by_lua_block {
+            local conf = {
+                auth_file = "t/secret/conf/success.json",
+            }
+            local gcp = require("apisix.secret.gcp")
+            local value, err = gcp.get(conf, "jack/key")
+            if not value then
+                return ngx.say(err)
+            end
+            ngx.say(value)
+        }
+    }
+--- request
+GET /t
+--- response_body
+value
+
+
+
+=== TEST 9: get value from gcp by auth_file(fetch_oatuh_conf failed, undefined)
+--- config
+    location /t {
+        content_by_lua_block {
+            local conf = {
+                auth_file = "t/secret/conf/error.json",
+            }
+            local gcp = require("apisix.secret.gcp")
+            local value, err = gcp.get(conf, "jack/key")
+            if not value then
+                return ngx.say(err)
+            end
+            ngx.say(value)
+        }
+    }
+--- request
+GET /t
+--- response_body
+failed to retrtive data from gcp secret manager: config parse failure, file: 
t/secret/conf/error.json, err: property "auth_config" validation failed: 
property "client_email" is required
+
+
+
+=== TEST 10: get json value from gcp
+--- config
+    location /t {
+        content_by_lua_block {
+            local conf = {
+                auth_config = {
+                    client_email = "[email protected]",
+                    private_key = [[
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR
+aeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC
+UuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF
+2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4
+v5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep
+AB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw
+Iu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P
+PR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic
+DcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49
+sxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC
+afOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC
+l85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz
+lw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC
+rCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g
+tdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16
+UyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1
+Ujqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI
+1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh
+GfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46
+xn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4
+upppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF
+FzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo
+y4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W
+vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK
+Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S
+kEJQcmfVew5mFXyxuEn3zA==
+-----END PRIVATE KEY-----]],
+                    project_id = "apisix",
+                    token_uri = "http://127.0.0.1:1980/google/secret/token";,
+                    scope = {
+                        "https://www.googleapis.com/auth/cloud-platform";
+                    },
+                    entries_uri = "http://127.0.0.1:1984";
+                },
+            }
+            local gcp = require("apisix.secret.gcp")
+            local value, err = gcp.get(conf, "jack/key")
+            if not value then
+                return ngx.say(err)
+            end
+            ngx.say(value)
+        }
+    }
+--- request
+GET /t
+--- response_body
+value
+
+
+
+=== TEST 11: get string value from gcp
+--- config
+    location /t {
+        content_by_lua_block {
+            local conf = {
+                auth_config = {
+                    client_email = "[email protected]",
+                    private_key = [[
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR
+aeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC
+UuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF
+2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4
+v5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep
+AB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw
+Iu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P
+PR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic
+DcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49
+sxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC
+afOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC
+l85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz
+lw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC
+rCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g
+tdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16
+UyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1
+Ujqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI
+1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh
+GfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46
+xn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4
+upppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF
+FzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo
+y4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W
+vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK
+Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S
+kEJQcmfVew5mFXyxuEn3zA==
+-----END PRIVATE KEY-----]],
+                    project_id = "apisix",
+                    token_uri = "http://127.0.0.1:1980/google/secret/token";,
+                    scope = {
+                        "https://www.googleapis.com/auth/cloud-platform";
+                    },
+                    entries_uri = "http://127.0.0.1:1984";
+                },
+            }
+            local gcp = require("apisix.secret.gcp")
+            local value, err = gcp.get(conf, "mysql")
+            if not value then
+                return ngx.say(err)
+            end
+            ngx.say(value)
+        }
+    }
+--- request
+GET /t
+--- response_body
+secret
+
+
+
+=== TEST 12: get value from gcp(failed to get google oauth token)
+--- config
+    location /t {
+        content_by_lua_block {
+            local conf = {
+                auth_config = {
+                    client_email = "[email protected]",
+                    private_key = [[
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR
+aeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC
+UuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF
+2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4
+v5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep
+AB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw
+Iu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P
+PR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic
+DcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49
+sxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC
+afOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC
+l85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz
+lw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC
+rCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g
+tdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16
+UyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1
+Ujqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI
+1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh
+GfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46
+xn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4
+upppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF
+FzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo
+y4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W
+vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK
+Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S
+kEJQcmfVew5mFXyxuEn3zA==
+-----END PRIVATE KEY-----]],
+                    project_id = "apisix",
+                    token_uri = "http://127.0.0.1:1980/google/secret/token";,
+                    scope = {
+                        "https://www.googleapis.com/auth/root/cloud-platform";
+                    },
+                    entries_uri = "http://127.0.0.1:1984";
+                },
+            }
+            local gcp = require("apisix.secret.gcp")
+            local value, err = gcp.get(conf, "jack/key")
+            if not value then
+                return ngx.say(err)
+            end
+            ngx.say(value)
+        }
+    }
+--- request
+GET /t
+--- response_body
+failed to retrtive data from gcp secret manager: failed to get google oauth 
token
+--- grep_error_log eval
+qr/\{\"error\"\:\"[\w+\s+]*\"\}/
+--- grep_error_log_out
+{"error":"no access to this scope"}
+
+
+
+=== TEST 13: get value from gcp (not res)
+--- config
+    location /t {
+        content_by_lua_block {
+            local conf = {
+                auth_config = {
+                    client_email = "[email protected]",
+                    private_key = [[
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR
+aeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC
+UuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF
+2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4
+v5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep
+AB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw
+Iu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P
+PR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic
+DcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49
+sxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC
+afOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC
+l85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz
+lw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC
+rCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g
+tdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16
+UyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1
+Ujqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI
+1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh
+GfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46
+xn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4
+upppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF
+FzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo
+y4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W
+vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK
+Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S
+kEJQcmfVew5mFXyxuEn3zA==
+-----END PRIVATE KEY-----]],
+                    project_id = "apisix_error",
+                    token_uri = "http://127.0.0.1:1980/google/secret/token";,
+                    scope = {
+                        "https://www.googleapis.com/auth/cloud-platform";
+                    },
+                    entries_uri = "http://127.0.0.1:1984";
+                },
+            }
+            local gcp = require("apisix.secret.gcp")
+            local value, err = gcp.get(conf, "jack/key")
+            if not value then
+                return ngx.say("err")
+            end
+            ngx.say(value)
+        }
+    }
+--- request
+GET /t
+--- response_body
+err
+
+
+
+=== TEST 14: get value from gcp (res status ~= 200)
+--- config
+    location /t {
+        content_by_lua_block {
+            local conf = {
+                auth_config = {
+                    client_email = "[email protected]",
+                    private_key = [[
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR
+aeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC
+UuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF
+2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4
+v5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep
+AB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw
+Iu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P
+PR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic
+DcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49
+sxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC
+afOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC
+l85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz
+lw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC
+rCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g
+tdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16
+UyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1
+Ujqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI
+1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh
+GfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46
+xn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4
+upppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF
+FzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo
+y4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W
+vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK
+Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S
+kEJQcmfVew5mFXyxuEn3zA==
+-----END PRIVATE KEY-----]],
+                    project_id = "apisix_error",
+                    token_uri = "http://127.0.0.1:1980/google/secret/token";,
+                    scope = {
+                        "https://www.googleapis.com/auth/cloud-platform";
+                    },
+                    entries_uri = "http://127.0.0.1:1984";
+                },
+            }
+            local gcp = require("apisix.secret.gcp")
+            local value, err = gcp.get(conf, "jack/key")
+            if not value then
+                return ngx.say("err")
+            end
+            ngx.say(value)
+        }
+    }
+--- request
+GET /t
+--- response_body
+err

Reply via email to