I just got a chance to start looking through some of this stuff and
agree with a number the ideas. I'll also reply to the more current
reply in this thread, but wanted to start with some things here ...
David Jencks wrote:
i talked with Dave Johnson a bit about some of this at apachecon.
Fundamentally I'm interested in Roller working with javaee security and
a role-based access control framework. It's quite clear this will
require some additional capabilities in javaee security, but I think
Roller can be refactored to make this plausible, and that this
refactoring will also make "stand-alone" roller security easier to
understand and work with.
Sounds like a good goal to me. I agree that Roller should be able to
work with javaEE security.
My main angle right now is extensibility/pluggability of Roller. I'd
like to see more development in Roller that provides a very stable core
platform that allows and encourages enhancements to be made via plugins.
Up until now Roller has required a bit too much tweaking of the core
codebase to add features (IMO) and I'd like to work on improving that.
Naturally, security is an important aspect of that.
I've been working on this for a week or so and have some results that I
think are reasonable and working. I've opened ROLLER-1680 and attached
a patch. Working on the security code it looked to me as if there were
a lot of bugs: I've fixed the ones I've noticed but haven't tried to
track them individually.
I've had two main ideas here:
- From the business layer, make all security decisions by checking if
the current user has a particular permission
- Abstract what is tracking the current user.
I'm not sure I agree with the idea of enforcing security in the business
layer. I'd need to hear more about what you mean by that statement, but
IMO enforcing security at very low levels can lead to problems with
flexibility. I like the fact that security is enforced at the UI layer
where users are relevant actors, and the business layer doesn't enforce
any security constraints.
This results in a SecurityService with a method
boolean checkPermission(RollerPermission perm, UserSource userSource);
UserSource is the abstraction of what is tracking the current user.
Basically it attempts to avoid looking up the current User object unless
it's really necessary. For instance with a JACC based authorization
system the security service would already know the current user from the
container login and would not need to consult the UserSource.
I'm not sure what the full implications of that would be, but I'm not
sure this is really necessary. If someone implements their own
UserManager they would have the ability to manage the User objects
completely, so they get to define the semantics of what is being looked
up and when.
Sure, the methods we have may require a User object, but that doesn't
impose any rules on what the UserManager has to do to get that User. It
would be perfectly legal for a JACC based authorization system to take
the user it already knows from container login and simply translate that
into one of Roller's User objects.
I've also separated storage of security information such as which users
have which permissions from the Permission implementation itself. The
user administration code works with the data objects WeblogPermission
and GlobalPermission which are no longer Permission objects, whereas the
security code as we just saw works with RollerPermission, which is.
I really like this idea. Even though the term "permission" is being
used in both cases they don't really mean the same thing in the code, so
they should separate.
I've combined several bits of functionality into RollerPermission which
is now the only Permission class needed. Since I'm familiar with the
code I borrowed the JACC 1.1 UserDataPermission class and simplified it
by leaving out some functionality I'm pretty sure isn't needed. It
still has some capabilities that may or may not be useful and can
probably be simplified further.
Here's a brief description of what it can do now and what might be
simplified:
- name. This is adapted from the URLPattern handling of
UserDataPermission. We don't need exclusions so there's only one
pattern, which acts like URL patterns in web security constraints.
Currently global permissions get "/*" and permissions specific to a
particular blog, say "foo", get "/foo". This could be simplified a
little bit more, but what is there now allows hierarchical
categorization of blogs. For instance one might organize blogs under
/internal and /external: it would then be possible to give permissions
to categories of blogs, say /internal/*. I thought it would be worth
asking if this sounded interesting before removing the code that lets
you do this.
My gripe here is that the only context you are allowing here is "weblog"
because you assume that the first thing after a / is a weblog. For the
sake of pluggability I think that's a bit limiting because someone may
want to write a plugin that controls access to something else that lies
outside the context of a weblog.
The approach I had taken here was more like Dave's original approach
where a permission has a "type" or "context" associated with it. The
default context is 'application', then we would have a 'weblog' context,
but plugin writers would have the ability to define additional contexts
which would be used by their code if necessary.
An example of this would be 'planet', for the Roller Planet
functionality. Thus allowing sets of permissions to be defined and
managed regarding planets without having any ties to weblog permissions.
- actions. This is adapted from the HTTPMethod handling of
UserDataPermission. This is probably significantly more complicated
that necessary, but my questions as to what is needed have so far gone
unanswered. The actions I've found in the existing code ("admin",
"post", "editdraft", "weblog", "login") are represented in a bitmask.
Any additional actions are stored as strings. There's an "isExcluded"
flag that indicates whether the set of actions explicitly listed (in the
mask or as strings) is the set of granted actions or the set of denied
actions. Thus any finite set of actions or the complement of any finite
set of actions can be represented. I strongly suspect that there is a
known finite set of actions so a bitmap would be sufficient. I'm hoping
someone can explain whether or not this is the case.
Some of the actions are not independent. For instance, admin implies
post and editdraft. Rather than requiring code to check these I've
simply represented these in the masks for these permissions.
I am actually against the idea of using bitmasks due to the pluggability
issue again. If supporting a new action requires a code change then
that doesn't make things pluggable. I would prefer to consider the set
of actions to be dynamic, so it could be modified by the application at
runtime via the addition/removal of plugins.
I'm not sure I see the need for the "excluded" flag. Where did you find
a need for that?
Also, as far as actions implying other actions, I'd also like to see
this remain dynamic and configurable. This way admins as well as
plugins can make changes to the security model at runtime. For example,
assume an application level action called "editor" which implies the
actions "login,comment,weblog". You want to install a plugin which
would give users access to create planet aggregations. To do this you
would want to be able to add "planet" to the list of actions implied by
"editor".
Open questions:
- as already mentioned, I'd like to know what actions are possible.
- I don't really understand the thinking behind the ORM for
ObjectPermission. It doesn't look to me as if GlobalPermissions can be
persisted which I don't understand. In any case I suspect this area
might be possible to simplify.
Ideally I think the list of actions should be roughly the number of
struts2 actions that we have. It's not exact right now because some
struts2 actions actually represent multiple permission actions. i.e.
the "author" action on a weblog should really imply
"editEntry,editCategories,editBookmarks,etc,etc".
Yes, it requires more effort to build that list and make it work, so if
we can't do it on the first pass then that's fine, but I think that
should be our end goal. This way admins would have the ability to
literally grant and revoke access to any action on a per user basis.
i.e. you could invite someone to a weblog and only give them access to
modify your theme and templates, but not to edit and publish entries.
And I agree about simplifying the ObjectPermission thing. Like you
mentioned above, separating out the security permission stuff from the
permission persistence seems ideal. I believe we should only need 2
classes for this, 1 to represent the security permission and one to
represent persistent permissions.
-- Allen
Next steps
With something like this patch in place I could start looking at
running roller with javaee security and a role-based access control
system. The obvious problem with javaee security is that currently it
doesn't really support security changes while the app is running very
well. For instance, adding a new users and permissions for that user is
problematical, especially for content that isn't there until that new
user generates it (their new blog, for instance). Beyond this, I think
RBAC will provide some interesting capabilities that are currently
lacking. The basic idea is to, starting with a directed acyclic graph
of roles, assign permissions to roles rather than users, and assign
users to roles. For instance you might have an author role specific to
a particular department, "DevelopmentPoster". You could have a bunch of
blogs with post permissions assigned to that role. Then any user
assigned to that role could post to all of these blogs.
Any comments are welcome. Aside from running (and adding to) the unit
tests which I eventually discovered in the ant build despite their lack
of documentation using -p, I've tested this with the geronimo roller
plugin. I'm not a roller expert but everything I've tried seems to have
the same behavior as with plain roller.
thanks
david jencks