Skip to content

Commit

Permalink
feature: new plugin request-validator (#1709)
Browse files Browse the repository at this point in the history
  • Loading branch information
sshniro authored Jul 21, 2020
1 parent a5fc25c commit a617999
Show file tree
Hide file tree
Showing 6 changed files with 640 additions and 1 deletion.
96 changes: 96 additions & 0 deletions apisix/plugins/request-validation.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
--
-- 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 plugin_name = "request-validation"
local ngx = ngx
local io = io

local schema = {
type = "object",
properties = {
body_schema = {type = "object"},
header_schema = {type = "object"}
},
anyOf = {
{required = {"body_schema"}},
{required = {"header_schema"}}
}
}


local _M = {
version = 0.1,
priority = 2800,
type = 'validation',
name = plugin_name,
schema = schema,
}


function _M.check_schema(conf)
return core.schema.check(schema, conf)
end


function _M.rewrite(conf)
local headers = ngx.req.get_headers()

if conf.header_schema then
local ok, err = core.schema.check(conf.header_schema, headers)
if not ok then
core.log.error("req schema validation failed", err)
core.response.exit(400, err)
end
end

if conf.body_schema then
ngx.req.read_body()
local req_body, error
local body = ngx.req.get_body_data()

if not body then
local filename = ngx.req.get_body_file()
if not filename then
return core.response.exit(500)
end
local fd = io.open(filename, 'rb')
if not fd then
return core.response.exit(500)
end
body = fd:read('*a')
end

if headers["content-type"] == "application/x-www-form-urlencoded" then
req_body, error = ngx.decode_args(body)
else -- JSON as default
req_body, error = core.json.decode(body)
end

if not req_body then
core.log.error('failed to decode the req body', error)
return core.response.exit(400, error)
end

local ok, err = core.schema.check(conf.body_schema, req_body)
if not ok then
core.log.error("req schema validation failed", err)
return core.response.exit(400, err)
end
end
end

return _M
1 change: 1 addition & 0 deletions conf/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ plugins: # plugin list
- echo
- authz-keycloak
- uri-blocker
- request-validation

stream_plugins:
- mqtt-proxy
151 changes: 151 additions & 0 deletions doc/plugins/request-validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<!--
#
# 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.
#
-->

[Chinese](request-validation-cn.md)

# Summary
- [**Name**](#name)
- [**Attributes**](#attributes)
- [**How To Enable**](#how-to-enable)
- [**Test Plugin**](#test-plugin)
- [**Disable Plugin**](#disable-plugin)
- [**Examples**](#examples)


## Name

`request-validation` plugin validates the requests before forwarding to an upstream service. The validation plugin uses
json-schema to validate the schema. The plugin can be used to validate the headers and body data.

For more information on schema, refer to [JSON schema](https://github.com/api7/jsonschema) for more information.

## Attributes

|Name |Requirement |Description|
|--------- |-------- |-----------|
| header_schema |optional |schema for the header data|
| body_schema |optional |schema for the body data|

## How To Enable

Create a route and enable the request-validation plugin on the route:

```shell
curl http://127.0.0.1:9080/apisix/admin/routes/5 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/get",
"plugins": {
"request-validation": {
"body_schema": {
"type": "object",
"required": ["required_payload"],
"properties": {
"required_payload": {"type": "string"},
"boolean_payload": {"type": "boolean"}
}
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:8080": 1
}
}
}
```
## Test Plugin
```shell
curl --header "Content-Type: application/json" \
--request POST \
--data '{"boolean-payload":true,"required_payload":"hello"}' \
http://127.0.0.1:9080/get
```
If the schema is violated the plugin will yield a `400` bad request.
## Disable Plugin
Remove the corresponding json configuration in the plugin configuration to disable the `request-validation`.
APISIX plugins are hot-reloaded, therefore no need to restart APISIX.
```shell
curl http://127.0.0.1:9080/apisix/admin/routes/5 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/get",
"plugins": {
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:8080": 1
}
}
}
```


## Examples:

**Using ENUMS:**

```shell
"body_schema": {
"type": "object",
"required": ["required_payload"],
"properties": {
"emum_payload": {
"type": "string",
enum: ["enum_string_1", "enum_string_2"]
default = "enum_string_1"
}
}
}
```


**JSON with multiple levels:**

```shell
"body_schema": {
"type": "object",
"required": ["required_payload"],
"properties": {
"boolean_payload": {"type": "boolean"},
"child_element_name": {
"type": "object",
"properties": {
"http_statuses": {
"type": "array",
"minItems": 1,
"items": {
"type": "integer",
"minimum": 200,
"maximum": 599
},
"uniqueItems": true,
"default": [200, 201, 202, 203]
}
}
}
}
}
```
2 changes: 1 addition & 1 deletion t/admin/plugins.t
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ __DATA__
--- request
GET /apisix/admin/plugins/list
--- response_body_like eval
qr/\["fault-injection","serverless-pre-function","batch-requests","cors","ip-restriction","uri-blocker","openid-connect","wolf-rbac","basic-auth","jwt-auth","key-auth","consumer-restriction","authz-keycloak","proxy-mirror","proxy-cache","proxy-rewrite","limit-conn","limit-count","limit-req","node-status","redirect","response-rewrite","grpc-transcode","prometheus","echo","http-logger","tcp-logger","kafka-logger","syslog","udp-logger","zipkin","skywalking","serverless-post-function"\]/
qr/\["fault-injection","serverless-pre-function","batch-requests","cors","ip-restriction","uri-blocker","request-validation","openid-connect","wolf-rbac","basic-auth","jwt-auth","key-auth","consumer-restriction","authz-keycloak","proxy-mirror","proxy-cache","proxy-rewrite","limit-conn","limit-count","limit-req","node-status","redirect","response-rewrite","grpc-transcode","prometheus","echo","http-logger","tcp-logger","kafka-logger","syslog","udp-logger","zipkin","skywalking","serverless-post-function"\]/
--- no_error_log
[error]
Expand Down
1 change: 1 addition & 0 deletions t/debug/debug-mode.t
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ loaded plugin and sort by priority: 4010 name: batch-requests
loaded plugin and sort by priority: 4000 name: cors
loaded plugin and sort by priority: 3000 name: ip-restriction
loaded plugin and sort by priority: 2900 name: uri-blocker
loaded plugin and sort by priority: 2800 name: request-validation
loaded plugin and sort by priority: 2599 name: openid-connect
loaded plugin and sort by priority: 2555 name: wolf-rbac
loaded plugin and sort by priority: 2520 name: basic-auth
Expand Down
Loading

0 comments on commit a617999

Please sign in to comment.