Skip to content

Instantly share code, notes, and snippets.

@subnetmarco
Last active January 28, 2016 20:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save subnetmarco/c25ccc6d08e8481e9931 to your computer and use it in GitHub Desktop.
Save subnetmarco/c25ccc6d08e8481e9931 to your computer and use it in GitHub Desktop.

This document is a logical reasoning that takes elements from our meetings, a brainstorming that evolves from the beginnign to the end. Don't make any assumptions until you reach the end. Because the argument is very complex, it incrementally address the whole picture step by step.

This Gist comes from the realization that plugin mappings can also be adapted to consumers, and that grouping and filters would be available for both consumers and plugin configurations.

It specifically covers the topic of plugin and consumer mapping from the user's perspective, thus the interface and not the internals.

Consumers

Let's suppose that by default every consumer can consume any API:

POST /consumers/
  name="thefosk"

And then restrict the consumer to a specific API:

POST /mappings/
  api="mockbin"
  consumers=["thefosk"]

Or add the consumer to a group, and restrict the group to a specific API:

POST /groups/
  name="admin_users"
  consumers=["thefosk"]
POST /mappings/
  api="mockbin"
  groups=["admin_users"]

Plugins

Let's also suppose that by default a plugin applies to any API:

POST /plugins/
  id="plugin_configuration_1"
  name="key-auth"
  config.keynames=["apikey"]

Restrict the plugin to a specific API:

POST /mappings/
  api="mockbin"
  plugins=["plugin_configuration_1"]

Or add the plugin to a group, and apply the group to a specific API:

POST /groups/
  name="internal_apis_configuration"
  plugins=["plugin_configuration_1"]
POST /mappings/
  api="mockbin"
  groups=["internal_apis_configuration"]

Some consumers are only allowed to use GET methods

Let's suppose we want to restrict the consumer to a specific API with a specific method, we could extend the previous request to:

POST /mappings/
  api="mockbin"
  consumers=["thefosk"]
  methods=["GET"]

Some plugins are customized for specific users

And let's also say we want to restrict the consumer to a specific API with a specific method:

POST /mappings/
  api="mockbin"
  consumers=["thefosk", "sinzone"]
  plugins=["plugin_configuration_1"]
  methods=["GET"]

Uhm? Does the above mean that only those consumers can consume the API, or does it mean the plugin should apply for those consumers? Maybe this is a solution, to divide the mappings in two types:

  • ACLS
  • Plugins

So that it becomes:

POST /mappings/plugins/
  api="mockbin"
  plugins=["plugin_configuration_1"]
  consumers=["thefosk", "sinzone"]
  methods=["GET"]

Let's improve this by considering an extreme use case

I want to divide my users in two groups, those who can make only GET requests and those who can make both POST and GET requests, and the users who can use both methods also have a different rate limiting:

Create the consumers:

POST /consumers/
  name="thefosk"
POST /consumers/
  name="sinzone"

Add consumers to only_get and get_and_post groups:

POST /groups/
  name="only_get"
  consumers=["thefosk"]
POST /groups/
  name="get_and_post"
  consumers=["sinzone"]

Add the mappings:

POST /mappings/acls/
  api="mockbin"
  groups=["only_get"]
  methods=["GET"]

POST /mappings/acls/
  api="mockbin"
  groups=["get_and_post"]
  methods=["GET", "POST"]

Now let's configure the rate-limiting plugin:

POST /plugins/
  id="plugin_configuration_1"
  name="rate-limiting"
  config.seconds=10

POST /mappings/plugins/
  api="mockbin"
  plugins=["plugin_configuration_1"]

Add add a special rate-limiting for those who can use both HTTP methods:

POST /plugins/
  id="plugin_configuration_2"
  name="rate-limiting"
  config.seconds=1000

POST /mappings/plugins/
  api="mockbin"
  groups=["get_and_post"]
  plugins=["plugin_configuration_2"]

This will work, but....

BUT THIS IS COMPLEX!

We can hide the complexity almost under the hood for 99% of users from the interface, while still keeping the complex routes for the 1% that needs to do crazy things:

The following code does exactly the same things of the previous chapter.

Create consumers and automatically associate them to a group in one request. If the group doesn't exist, the system will create it:

POST /consumers/
  name="thefosk"
  groups=["only_get"]

POST /consumers/
  name="sinzone"
  groups=["get_and_post"]

Add the HTTP method restrictions for the mockbin API:

POST /apis/mockbin/acls/
  groups=["only_get"]
  methods=["GET"]

POST /apis/mockbin/acls/
  groups=["get_and_post"]
  methods=["GET", "POST"]

Create the rate-limiting plugin configurations, and associate them to a group if specified:

POST /apis/mockbin/plugins/
  name="rate-limiting"
  config.seconds=10

POST /apis/mockbin/plugins/
  name="rate-limiting"
  groups=["get_and_post"]
  config.seconds=1000

Tadam! This will work.

How about advanced users who want to play with mappings?

They can still use the mapping API explicitly at the following endpoints:

  • /groups/
  • /mappings/acls/
  • /mappings/plugins/

or, if they know the API:

  • /apis/{api}/acls/
  • /apis/{api}/plugins/

Next improvement: Filters (aka "routes")

Let's introduce filters. A filter can be an object with the following properties:

locations=["us", "fr"]
hosts=["host.com"]
paths=["/hello", "/world"]
ips=["1.1.1.1","2.2.2.1/24"]
methods=["GET", "POST"]

And much more in the future. At least one property needs to be specified.

A filter can be associated to a mapping, for example let's create this filter:

POST /filters/
  name="north_america"
  locations=["us", "ca"]
  methods=["GET", "POST"]

and associate it to a mapping:

POST /apis/mockbin/acls/
  groups=["get_and_post"]
  filters=["north_america"]

The above operations allow the get_and_post consumer group to make GET and POST requests only from USA and Canada.

Filters are not routes because they don't necessarily specify a route, and their list of properties can grow over time (for example we could filter requests coming from a specific range of IPs and a host, etc).

Rewrite the extreme use-case with filters

Here is how the final extreme use-case will look like with filters.

Create consumers and automatically associate them to a group in one request. If the group doesn't exist, the system will create it:

POST /consumers/
  name="thefosk"
  groups=["only_get"]

POST /consumers/
  name="sinzone"
  groups=["get_and_post"]

Create the filter restrictions:

POST /filters/
  name="only_get_filter"
  methods=["GET"]

POST /filters/
  name="get_and_post_filter"
  methods=["GET", "POST"]

Add the HTTP method restrictions for the mockbin API:

POST /apis/mockbin/acls/
  groups=["only_get"]
  filters=["only_get_filter"]

POST /apis/mockbin/acls/
  groups=["get_and_post"]
  filters=["get_and_post_filter"]

Create the rate-limiting plugin configurations, and associate them to a group if specified:

POST /apis/mockbin/plugins/
  name="rate-limiting"
  config.seconds=10

POST /apis/mockbin/plugins/
  name="rate-limiting"
  groups=["get_and_post"]
  config.seconds=1000

Filters are optional

Filters are optional in the interface, they only make sense if they need to be re-used multiple times for multiple APIs.

Doing this:

POST /filters/
  name="only_get_filter"
  methods=["GET"]

POST /apis/mockbin/acls/
  groups=["only_get"]
  filters=["only_get_filter"]

could be the same as doing:

POST /apis/mockbin/acls/
  groups=["only_get"]
  methods=["GET"]

Making a filter a request filter

A filter can also filter incoming requests to detect the API to load:

POST /filters/
  name="north_america"
  locations=["us", "ca"]
  methods=["GET", "POST"]

and can be attached to an API object:

POST /apis/
  name="mockbin"
  filters=["north_america"]

Every GET or POST request coming from US or CA will be proxied to mockbin.

Final Conclusion

Endpoints available:

  • /groups/
  • /filters/
  • /mappings/acls/
  • /mappings/plugins/

or, if they know the API:

  • /apis/{api}/acls/
  • /apis/{api}/plugins/

Extreme example:

POST /consumers/
  name="thefosk"
  groups=["only_get"]

POST /consumers/
  name="sinzone"
  groups=["get_and_post"]

Add the HTTP method restrictions for the mockbin API (under the hood we are creating Filters - or a Filter can be explicitly set using the filters parameter):

POST /apis/mockbin/acls/
  groups=["only_get"]
  methods=["GET"]

POST /apis/mockbin/acls/
  groups=["get_and_post"]
  methods=["GET", "POST"]

Create the rate-limiting plugin configurations, and associate them to a group if specified:

POST /apis/mockbin/plugins/
  name="rate-limiting"
  config.seconds=10

POST /apis/mockbin/plugins/
  name="rate-limiting"
  groups=["get_and_post"]
  config.seconds=1000

The end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment