Protecting your Macros and Functions with Capabilities

Protect your functions on open instances using capabilities. They can restrict the access of several sensitive functions to specific users.

Protecting your Macros and Functions with Capabilities

Warp 10 instances can be accessed by many users and may even be open to anyone. To protect your instances from abuse or simply bad use, limits are put in place to avoid WarpScript running amok. Although this can be used for access control, this is a very crude way of doing this.

If, for instance, you add an extension to your instance to interact with a SQL database. You may want to control which user can access the SQLEXEC function. On a production instance, you may want to add debug and logging extensions to identify a problem while limiting those functions to the dev or ops team.

Starting with the 2.8.0 version, you can now use capabilities in tokens to finely control accesses.

The Capabilities Mechanism

Capabilities are special token attributes. They can be added to or removed from the execution environment. Those capabilities can then be checked by macros and functions to allow, deny or restrict their usage.

To set a token capability, add .cap:name_of_capability as a key to the attribute of the token. The capability must have a value, which can be of use or not. Here is an example of how to add the my_capability with foo value:

'da7aba5e-ea75-b055-cafe-fee15ca1ab1e' 'owner' STORE
{
  'type' 'READ'
  'owner' $owner
  'application' 'noapp'
  'expiry' '2121-06-01T00:00Z' TOTIMESTAMP 1 ms /
  'owners' [ $owner ]
  'producers' [ $owner ]
  'applications' [ 'noapp' ]
  'attributes' {
    '.cap:my_capability' 'foo'
  }
}
'my_token_secret' // token.secret configuration
TOKENGEN

The above script requires you to enable the TokenGen extension by adding

warpscript.extension.token = io.warp10.script.ext.token.TokenWarpScriptExtension

To one of your configuration files. Then, you need to restart your instance for the configuration to take effect.

The resulting token can now be used to add the my_capability capability to the execution environment. To do so, simply use CAPADD on the token. CAPADD does not overwrite already existing capabilities so, if you need to combine tokens with overlapping capabilities, make sure to use CAPDEL to remove preexisting capabilities you want to overwrite.

If you add "my_capability" CAPCHECK after CAPADD, you can check if the capability is present or not. This will be very useful for macros to check for required capabilities, as you will see below. You can also get the capability value with CAPGET, for instance, to get a limit.

You can add several capabilities per token. Also, if you want pure-capabilities tokens, you can add the .noauth and .nofind attributes, with any value, to make the token unusable with AUTHENTICATE, FIND and FETCH.

Capabilities and Shadowing

One of the most useful uses of capabilities is to protect functions. In the following example, we will protect the REV function. This is of little use, except for demonstration purposes.

To be able to change the behavior of the REV function we first need to enable the Shadow extension and configure it. In configuration file, add:

warpscript.extension.shadow = io.warp10.script.ext.shadow.ShadowWarpScriptExtension
shadow.macroprefix = shadow
shadow.rename.REV = 07ed7492-3dcd-4d07-8b21-6b4cf6e8fc34.REV
shadow.macro = REV

Next, restart your instance after this change. In the configuration above, the first line enables the extension which will allow us to override functions with macros. The second line tells shadow that when overriding a function FOO, it will be done using the @shadow/FOO macro. The third line renames the original REV function to some impossible-to-guess name. The last line overrides REV with @shadow/REV.

Now that the configuration is done we have to define this macro which will use capabilities to determine whether to display the revision number or not. Add a REV.mc2 file in ${WARP10_HOME}/macros/shadow/ with the following content:

<%  
  'my_capability' CAPCHECK 
  <% 07ed7492-3dcd-4d07-8b21-6b4cf6e8fc34.REV %> 
  <% 'Not allowed to check revision' %>
  IFTE
%>

Now whenever you execute REV, it will return "Not allowed to check revision" if you didn't add the capability or the revision if you did. This effectively restricts the access to REV to those who have a token with the my_capability capability.

Capabilities and Extensions

You can also use capabilities directly into your extension. It can be used to limit access to a function or a group of function. It can also be used to restrict the parameters which can be given to a function. If you need to build an extension which make use of capabilities, the related functions are in the Capabilities class. The most useful method in this class is the get(stack, capability_name) to get the capability value of the execution environment.

As an example, the HTTP function, introduced in the 2.8.0 version, makes heavy use of capabilities. It can check whether the user is allowed to do an HTTP call and how many, as well as the allowed download size. You can check in the code how it is done if you want to replicate this behavior.

This shows that either the presence of a capability or its value can be useful to control or limit the use of sensitive functions.

Takeaways

Capabilities is a mechanism introduced in the 2.8.0 version of Warp 10. It allows very fine control of how functions can be used: you can restrict functions to certain users based on provided tokens.

As this mechanism is based on tokens, this is fairly transparent to the user who can use its tokens both to fetch, update and delete data as well as to access restricted functions.