API 验证¶
https://blueprints.launchpad.net/manila/+spec/json-schema-validation
目前,Manila 在验证请求体方面有不同的实现方式。此蓝图的目的是跟踪验证发送到 Manila 服务器的请求体的进度,接受符合资源模式的请求,并拒绝不符合模式的请求。根据请求体的具体内容,请求应该被一致地接受或拒绝。
问题描述¶
目前 Manila 没有一致的请求验证层。有些资源在资源控制器处验证输入,有些则在后端失败。理想情况下,Manila 应该有一些验证机制,以捕获不允许的参数,并向用户返回验证错误。
最终用户将受益于一致且有用的反馈,无论他们正在与哪个资源交互。
用例¶
作为用户或开发人员,我希望观察到一致的 API 验证以及传递到 Manila API 服务器的值。
提议的变更¶
验证 Manila API 的一种可能方法是使用 jsonschema,类似于 Nova、Cinder、Keystone 和 Glance (https://pypi.python.org/project/jsonschema)。可以使用 jsonschema 验证器对象来检查每个资源是否符合该资源的适当模式。如果验证通过,则请求可以遵循现有的控制流,通过资源管理器到达后端。如果请求体参数未通过资源模式指定的验证,则服务器将返回一个包装在 HTTPBadRequest 中的验证错误。
示例:“字段‘name’的输入无效。该值为‘some invalid name value’。
每个 API 定义都应以以下方式添加
在 ./manila/api/schemas/ 下创建定义文件。
每个定义应使用 JSON Schema 描述。
每个定义中的参数(type、minLength 等)可以从当前验证代码、DB 模式、单元测试、Tempest 代码等定义。
关于执行此实现的一些说明
可以在所有 Manila 资源中利用常见的参数类型。例如,如下所示
# share create schema manila/api/schema/shares.py: from manila.api.validation import parameter_types create_v231 = { 'type': 'object', 'properties': { 'share': { 'type': 'object', 'properties': { 'description': parameter_types.description, 'share_type': { 'format': 'uuid' }, 'share_proto': parameter_types.proto 'share_network_id': { 'format': 'uuid' }, 'share_group_id': { 'format': 'uuid' }, 'name': parameter_types.name, 'snapshot_id': { 'format': 'uuid' }, 'size': parameter_types.positive_integer, 'metadata': { 'type': 'object' }, }, 'required': ['size'], 'additionalProperties': False, } 'required': ['share'], 'additionalProperties': False, } } manila/api/validation/parameter_types.py: description = { 'type': 'string', 'minLength': 0, 'maxLength': 255, } name = description # This registers a FormatChecker on the jsonschema module. # It might appear that nothing is using the decorated method but it gets # used in JSON schema validations to check uuid formatted strings. from oslo_utils import uuidutils @jsonschema.FormatChecker.cls_checks('uuid') def _validate_uuid_format(instance): return uuidutils.is_uuid_like(instance) positive_integer = { 'type': ['integer', 'string'], 'pattern': '^[0-9]*$', 'minimum': 1, 'minLength': 1 } proto = { 'type': ['string'], 'enum': ['NFS', 'CIFS', 'GlusterFS', 'HDFS', 'CephFS'] }
可以使用以下装饰器在控制器层进行验证
manila/api/v2/shares.py: from manila.api.schemas import share @wsgi.Controller.api_version("2.31") @validation.schema(shares.create_v231) def create(self, req, body): """creates a share."""
初始工作将包括为现有资源捕获共享文件 API 规范,并将其转换为模式。这应该为 API 的每个主要版本执行一次。这将应用于共享文件 V2 API。
在向 Manila 添加新的扩展时,必须提出新的扩展及其相应的模式。
备选方案¶
在 API 验证框架之前,我们需要将验证代码添加到每个 API 方法中,以临时方式进行处理。这些更改会使 API 方法代码变得混乱,并且由于验证不完整,我们需要创建多个补丁。
如果使用 JSON Schema 定义,可接受的请求格式是清晰的,并且我们将来不需要进行临时工作。
Pecan 框架:Pecan 一些项目(Ironic、Ceilometer 等)使用 Pecan/WSME 框架实现,我们可以从这些框架中自动获取 API 文档。在 WSME 实现中,开发人员应该为每个 API 定义 API 参数。Pecan 可以简化 API 路由(URL、METHOD)的实现。
数据模型影响¶
无
REST API 影响¶
在 Manila 中,自 Liberty 以来,我们一直努力保持完整的 API 向后兼容性。因此,如果客户端正在与不同的 manila 版本通信,如果使用相同的 API 版本,它们应该体验到相同的行为。我们很少违反此规则,但始终坚持如果纠正错误行为,我们将考虑破坏向后兼容性。
API 响应代码更改
在添加模式层时,某些 API 响应代码会发生变化。例如,在当前 master 中,“services” 表在数据库表中具有最大 255 个字符的“host”和“binary”。在更新服务时,用户可以传递超过 255 个字符的“host”和“binary”,这显然会导致 404 ServiceNotFound 错误,浪费数据库调用。对于这种情况,我们可以将“host”和“binary”限制为“services”模式定义中的最大 255 个字符。如果用户传递超过 255 个字符,他/她将收到 400 BadRequest 响应。
API 响应错误消息
返回给用户的错误消息将会发生变化。例如,在当前 master 中,如果用户传递超过 255 个共享名称字符,则 manila-api 会返回以下错误消息
接收到无效输入:name 具有 <用户传递的实际字符数> 个字符,超过 255 个。
使用模式验证,将向用户返回以下错误消息
字段/属性 name 的输入无效。值:<用户传递的值>。‘<用户传递的值>’ 太长。
安全影响¶
请求验证层输出不应损害数据或向外部用户暴露私有数据。请求验证不应在验证成功后返回任何信息。如果请求体无效,则验证层应返回无效值和/或请求所需的值,最终用户应了解这些值。正在验证的资源的参数是公共信息,在共享文件 API 规范中进行了描述,不包括私有数据。如果用户的私有数据验证失败,则可以在验证器的错误处理中构建一个检查,以不返回私有数据的实际值。
jsonschema 文档说明了模式和实例的安全注意事项:https://schema.json.js.cn/latest/json-schema-core.html#anchor21
更早期的输入验证将减少恶意用户输入利用安全漏洞的能力。
通知影响¶
无
其他最终用户影响¶
无
性能影响¶
Manila 将为此全面的请求参数验证付出一些性能成本,因为对现在未验证的 API 参数的检查将会增加。
其他部署者影响¶
无
开发人员影响¶
这将要求为 Manila 贡献新扩展的开发人员提供一个适当的模式,以表示扩展的 API。
实现¶
负责人¶
主要负责人:dongdongpei (Dongdong Pei <peidongdong121@163.com>)
其他贡献者:wosunoozzy (Yang Zhang <wosunoozzy@gmail.com>)
工作项¶
初始验证器实现,它将包含通用的验证器代码,旨在在验证请求体的所有资源控制器之间共享。
引入现有核心 API 资源的验证模式。
引入现有 API 扩展的验证模式。
强制对提议的核心 API 添加项和扩展进行验证。
删除重复的临时验证代码。
添加相关 API 的单元和端到端测试。
添加/更新 Manila 文档。
依赖项¶
manila/api/v1 下的代码被 v2 调用。因此,如果我们在 v2 中添加模式验证,那么我们将不得不删除控制器方法中现有的参数验证,这将再次破坏 v2 API。
解决方案
使用类似于 Nova 的 @validation.schema 装饰器对 v2 API 进行模式验证,并保留方法中的验证代码以保持 v1 的工作状态。
一旦决定停止对 v1 的支持,我们应该删除方法中的验证代码。
测试¶
可以像 Nova V3 API 一样,在每个资源针对其模式进行验证时添加 Tempest 测试。这些测试应该遍历无效的请求类型。
我们可以遵循 Nova V3 API 中已经完成的一些验证工作
负面验证测试应使用 tempest.test.NegativeAutoTest
文档影响¶
Manila API 文档需要更新以反映 REST API 的更改。
Manila 开发人员文档需要更新,以解释模式验证的工作方式以及如何为新的 API 添加 json 模式。
参考资料¶
理解 JSON Schema
https://schema.json.js.cn/understanding-json-schema/index.html
Nova 验证示例
https://opendev.org/openstack/nova/src/branch/master/nova/api/validation
PyPI 上的 JSON Schema
JSON Schema 核心定义和术语
JSON Schema 文档