Let me start by giving some background on the existing design and
implementation and later argue how we can make admin's life easier. Sorry for
the long email, I don't want people to miss anything I want to propose :)
Background
==========
At present, all the plugins which provide any sort of api has their own
commands.properties file that is basically a mapping of apiname (which they
provide but can be used irresponsibly) and role mask.
To check API access, the code that the same was moved out as plugin called
StaticRoleBasedAPIAccessChecker in which checking is done based on role. This
plugin assumes that it will somehow have a mapping of apiname and role mask.
To make this process generic we have a APIChecker interface;
boolean checkAccess(User user, String apiCommandName) throws
PermissionDeniedException;
Design
======
There may be multiple plugins which are called by for loop and they return:
- true: if user is allowed
- false: plugin is unable to handle that
- exception: if operation is not allowed
Problem
======
Each plugin has a commands.properties file which has no use for the plugin
itself, it's actually useful for the StaticRoleBasedAPIAccessChecker to get the
apiname, role mask mapping on which basis it can do the checking. I can see
moving out existing features as plugins and new features like API throttling,
API discovery being implemented as plugins. It would be difficult and confusing
for an admin to keep track to so many configuration files and to edit them.
Further it's not dynamic enough, every time you want to change things in
properties files admin would need to restart the management server.
Proposed Fix (aka make admin's life easier)
===========
1. Append this mapping in one single commands.properties file get rid of all
the other commands.properties files, because the apiname:mask mapping is not
for the plugin or mgmt server, it's just for the
StaticRoleBasedAPIAccessChecker. This way an APIChecker plugin won't have to
depend on other plugins and vice-versa. It would be a clear separation of
policy local to a plugin. In future one can be free to implement another
APIChecker which may be dynamic and instead of reading from a properties file,
queries an API service. Plugin authors pl. comment?
2. Make online reloading easier. Implement an api (updateAccessChecker or
something) with StaticRoleBasedAPIAccessChecker itself which an admin can call
to reload the commands.properties mapping in case the admin changes it and
wants the new policy to be reflected without having to restart mgmt server.
3. Each plugin (if they implement a service exposed via an API) would return a
list of API cmd classes. Right now the code uses java reflections to grab the
classes with @APICommand annotation and generate the mappings. This can be
incorrect in case there is some component which was not loaded but exists in
class path. Such plugins implement PluggableService which is a contract that
implementing entity will have a way to return list of cmd classes.
Convention: We've to have a standard convention for writing plugins. So, plugin
writers should use the @APICommand and other annotations for writing API cmd
and response classes. The only artifacts from cloudstack they should depend on
should be cloud-api and/or cloud-util. The plugins are not required to be under
org.apache.cloudstack or com.cloud package names.
Future
=====
Online pluggable service: (Hugo, Alex and I talked about this during ccc12, we
can do it in future maybe, but at least we should start packaging them
separately)
As part of packaging, all plugins be bundled as separate debs/rpms, the core
system (cloudstack-server) can depend on few ought-to have plugins.
The plugins get installed in a specific directory, say
/usr/share/java/cloudstack/plugins. This directory is within class path of the
server process.
If an admin trusts a plugin, he installs/place the jar in this directory,
changes ACL either in /etc/cloud/commands.properties or depending upon the
choice of APIChecker plugin and calls the an API to tell mgmt server to load
the plugin. It would be tricky and some plugins may require restart.
Regards.