shreemaan-abhishek commented on code in PR #10252: URL: https://github.com/apache/apisix/pull/10252#discussion_r1363405717
########## apisix/plugins/jwe-decrypt.lua: ########## @@ -0,0 +1,234 @@ +-- +-- 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 consumer_mod = require("apisix.consumer") +local base64 = require("ngx.base64") +local ngx = ngx +local sub_str = string.sub +local plugin_name = "jwe-decrypt" +local cipher = require("resty.openssl.cipher").new("aes-256-gcm") + +local schema = { + type = "object", + properties = { + header = { + type = "string", + default = "Authorization" + }, + forward_header = { + type = "string", + default = "Authorization" + }, + strict = { + type = "boolean", + default = true + } + }, +} + +local consumer_schema = { Review Comment: why not use existing schema for consumer?https://github.com/apache/apisix/blob/master/apisix/schema_def.lua ########## t/plugin/jwe-decrypt.t: ########## Review Comment: please add a test case that demonstrates that this plugin works correctly with secrets defined using the APISIX secret resource. ########## apisix/plugins/jwe-decrypt.lua: ########## @@ -0,0 +1,234 @@ +-- +-- 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 consumer_mod = require("apisix.consumer") +local base64 = require("ngx.base64") +local ngx = ngx +local sub_str = string.sub +local plugin_name = "jwe-decrypt" +local cipher = require("resty.openssl.cipher").new("aes-256-gcm") + +local schema = { + type = "object", + properties = { + header = { + type = "string", + default = "Authorization" + }, + forward_header = { + type = "string", + default = "Authorization" + }, + strict = { + type = "boolean", + default = true + } + }, +} + +local consumer_schema = { + type = "object", + -- can't use additionalProperties with dependencies + properties = { + key = { type = "string" }, + secret = { type = "string", minLength = 32 }, + base64_secret = { type = "boolean" }, + }, + required = { "key", "secret" }, +} + + +local _M = { + version = 0.1, + priority = 2509, + type = 'auth', + name = plugin_name, + schema = schema, + consumer_schema = consumer_schema +} + + +function _M.check_schema(conf, schema_type) + core.log.info("input conf: ", core.json.delay_encode(conf)) + + local ok, err + if schema_type == core.schema.TYPE_CONSUMER then + ok, err = core.schema.check(consumer_schema, conf) + else + return core.schema.check(schema, conf) + end + + if not ok then + return false, err + end + + return true +end + +local function get_secret(conf) + local secret = conf.secret + + if conf.base64_secret then + return base64.decode_base64(secret) + end + + return secret +end + +local function load_jwe_token(jwe_token) + local o = { valid = false } + o.header, o.enckey, o.iv, o.ciphertext, o.tag = jwe_token:match("(.-)%.(.-)%.(.-)%.(.-)%.(.*)") + if not o.header then + return o + end + local he = base64.decode_base64url(o.header) + if not he then + return o + end + o.header_obj = core.json.decode(he) + if not o.header_obj then + return o + end + o.valid = true + return o +end + +local function jwe_decrypt_with_obj(o, consumer) + local secret = get_secret(consumer.auth_conf) + local dec = base64.decode_base64url + return cipher:decrypt(secret, dec(o.iv), dec(o.ciphertext), false, o.header, dec(o.tag)) +end + +local function jwe_encrypt(o, consumer) + local secret = get_secret(consumer.auth_conf) + local enc = base64.encode_base64url + o.ciphertext = cipher:encrypt(secret, o.iv, o.plaintext, false, o.header) + o.tag = cipher:get_aead_tag() + return o.header .. ".." .. enc(o.iv) .. "." .. enc(o.ciphertext) .. "." .. enc(o.tag) +end + + +local function get_consumer(key) + local consumer_conf = consumer_mod.plugin(plugin_name) + if not consumer_conf then + return nil + end + local consumers = consumer_mod.consumers_kv(plugin_name, consumer_conf, "key") + if not consumers then + return nil + end + core.log.info("consumers: ", core.json.delay_encode(consumers)) + return consumers[key] +end + +local function fetch_jwe_token(conf, ctx) + local token = core.request.header(ctx, conf.header) + if token then + local prefix = sub_str(token, 1, 7) + if prefix == 'Bearer ' or prefix == 'bearer ' then + return sub_str(token, 8) + end + + return token + end +end + +function _M.rewrite(conf, ctx) + -- fetch token and hide credentials if necessary + local jwe_token, err = fetch_jwe_token(conf, ctx) + if not jwe_token and conf.strict then + core.log.info("failed to fetch JWE token: ", err) + return 403, { message = "missing JWE token in request" } + end + + local jwe_obj = load_jwe_token(jwe_token) + + if not jwe_obj.valid then + return 400, { message = "JWE token invalid" } + end + + if not jwe_obj.header_obj.kid then + return 400, { message = "missing kid in JWE token" } + end + + local consumer = get_consumer(jwe_obj.header_obj.kid) + if not consumer then + return 400, { message = "invalid kid in JWE token" } + end + + local plaintext, err = jwe_decrypt_with_obj(jwe_obj, consumer) + if err ~= nil then + return 400, { message = "failed to decrypt JWE token" } + end + core.request.set_header(ctx, conf.forward_header, plaintext) +end + +local function gen_token() + local args = core.request.get_uri_args() + if not args or not args.key then + return core.response.exit(400) + end + + local key = args.key + local payload = args.payload + if payload then + payload = ngx.unescape_uri(payload) + end + + local consumer = get_consumer(key) + if not consumer then + return core.response.exit(404) + end + + core.log.info("consumer: ", core.json.delay_encode(consumer)) + + local iv = args.iv Review Comment: what does `iv` mean? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
