At line 1 changed 199 lines |
This page contains a quick overview of the AAA development for JSPWiki 2.3. There has been quite a bit of development done on it since January to merge the classic custom JSPWiki authorization scheme with web container authorization and standard Java 2 security. Here are the highlights. |
''--Andrew Jaquith'' |
|
|
---- |
!Authentication |
The core concept is the WikiSession, an object that is stored in the user's HTTPSession. |
It contains a standard J2SE Subject with a collection of Principals. The Principals |
can be one of two types (more on how they get there in a minute). |
|
1. User Principals -- one or more, as supplied by the JAAS LoginModules |
2. Built-in Role Principals - represents the logical roles "admin", "authenticated", |
"anonymous", "asserted" (user supplies a cookie saying who they |
are, but doesn't authenticate), and "all" (any of the preceding four) |
|
So, how do the Principals get there? Easy---JAAS. Recall that JSPWiki can use either |
container-managed auth, or its own custom authentication scheme. Let's handle the |
container case first. |
|
For container-managed auth, the WebContainerLoginModule tries to "sniff" |
the Principal from the HTTP request. If it's there, we use it and add that Principal |
and two Role Principals: ALL and AUTHENTICATED. If not, we try the RemoteUser property |
and manufacture a synthetic user Principal and the ALL and AUTHENTICATED Roles. |
If neither of these methods succeed, we manufacture a generic user Principal represeting |
an asserted user (if we can find a cookie using CookieAssertionLoginModule) or an |
anonymous user (if we cannot). The role principals in this case would be ALL (of |
course) and either ASSERTED or ANONYMOUS. The AnonymousLoginModule that runs last |
*always* succeeds. |
|
The process I've just described happens automatically without user intervention. |
What's point to remember is that we will always have a populated WikiSession with |
a Subject containing some built-in roles that we can use for authorization purposes. |
|
Let's go to the custom-auth scenario---we're not using container authentication, |
but JSPWiki's own authentication. In this case the UserDatabaseLoginModule takes |
data submitted on the login form (Login.jsp) and authenticates against the UserDatabase |
we've configured to use with JSPWiki. The default database persists to an XML file, |
but the interface is documented so you could use it with an RDBMS or LDAP if you |
wish. If the login suceeds, it replaces the Subject's Principal set with three user |
principals representing the user's login name, full name, and wiki name (for flexibility, |
so we don't need to be super-precise in ACLs). It also adds the built-in roles ALL |
and AUTHENTICATED. |
|
The net result of the whole process is that we have at least one user Principal |
representing the user's identity, and at least two built-in role principals: ALL |
plus either ANONYMOUS, ASSERTED or AUTHENTICATED. (I'm glossing over some important |
stuff about how the ADMIN role gets populated, and about the anti-spoofing checks |
we need to do, but we can have that discussion later.) Because we're using JAAS |
for login, too, we can configure the login process however we like---although I |
think the defaults are pretty decent. |
|
!Authorization |
Authorization grants access based on 1) examination of the Principals the user possesses |
and 2) whether the user belongs to an external role or Wiki group. Recall that even |
"anonymous" users will still have the ANONYMOUS Role, so we can make decisions |
based on that. |
|
What constitutes a request for access? A read, write, rename or delete operation |
on a page; attempt to self-register; create, save or delete a wiki group; create |
a page, etc. The calling JSP or Java code asks JSPWiki to authorize the action by |
calling AuthorizationManager.checkPermission(WikiContext, Permission). The Permission |
will either be a PagePermission (if it involves page-level operations) or a WikiPermission |
(for Wiki-level operations like registering a user, or creating a group, or page). |
|
The authorization process works as follows: |
- If the Subject's principal set includes the Role principal that is the administrator |
group, always allow the permission. |
- If there is no ACL at all, check to see if the Permission is allowed according |
to the "static" security policy. The security policy (jspwiki.policy, |
see below) specifies what permissions are available by default |
- If there is an ACL, get the list of Principals assigned this permission in the |
ACL: these will be Principals representing a role, arbitrary wiki group or user. |
Then determine whether the user (Subject) is considered to have any of these roles |
or principals. THAT process is as follows: |
- If the desired Principal is a built-in Role, the algorithm simply checks |
to see if the Subject possesses it in its Principal set |
- If the desired Principal is a Role but not built-in, the external Authorizer's |
isInRole method is called |
- If the desired principal is a wiki Group, the GroupManager's group authorizer |
isInRole method is called |
- If the desired Principal is a user, check whether the Subject possesses it |
in its Principal set |
- Otherwise, deny the permission |
|
What this algorithm gives us is a way to use built-in roles (AUTHENTICATED, ALL |
etc) in our access checks, but also external groups (such as those provided by the |
web container) and ad-hoc, arbitary wiki groups. So, if you've got an external LDAP |
server wired up to your web container for authentication and authorization, you |
can use it! And if your users want to create their own wiki groups by creating a |
special Group* page, we can use that too. |
|
Again, I'm glossing over some important details about name resolution, anti-spoofing |
and the like, but I think you can see how the overall authorization process works. |
|
!Security Policy |
Underpinning the authorization process is a standard Java 2 security policy (jspwiki.policy). |
The policy file gives us a way to express what the static page permissions of the |
wiki should be in the absence of page ACLs---these are the PagePermission grant |
statements. It also controls certain aspects of what a user can do for non-page |
operations---these are the WikiPermission grants. It's a standard Java policy file, |
so if you are familiar with the syntax it should be easy to understand. The PagePermission |
grant statements, in particular, support wildcards. I think the default policy is |
pretty decent---the defaults are aimed at a standard "internal wiki" use |
case. |
|
One might think that an administrator could limit anonymous access by removing |
the "AnonymousLoginModule SUFFICIENT" line from jspwiki.jaas. That's a |
very good guess, but not quite correct. The right way to do it would be to edit |
jspwiki.policy and look for this grant entry: |
|
grant signedBy "jspwiki" principal com.ecyrd.jspwiki.auth.authorize.Role |
"Anonymous" { |
|
You'd edit this line: |
|
permission com.ecyrd.jspwiki.auth.permissions.PagePermission "*", |
"view,comment"; |
|
...to reflect that users should be able to see the "Oops! You need to register!" |
wiki page, and probably a few others, but nothing else. That's something we will |
tweak as development on JSPWiki continues... feedback would be much appreciated. |
|
Likewise, if you wanted to limit the capabilities of "asserted" users |
(as I would in a production environment), you could simply remove the block for |
asserted users, e.g.,: |
|
grant signedBy "jspwiki" principal com.ecyrd.jspwiki.auth.authorize.Role |
"Asserted" {...}; |
|
An important limitation (for the moment) in the security policy implementation is |
that the policy grammar (PagePermission/WikiPermission) doesn't yet support multiple |
wikis---you could get around this by having separate policy files per wiki if you |
wanted. This isn't as big of a limitation as you might think---the default policies |
for all wikis on a server are likely to be the same: i.e., anonymous users either |
can/cannot self-register, create new pages or groups, etc. So you will probably |
just need one policy file. |
|
!Groups and Authorizers |
Recall that the authorization algorithm checks for the presence of built-in role |
Principals or user Principals when making decisions. It also can make decisions |
based on wiki group membership or membership in a role as determined by an external |
Authorizer. The external authorizer has priority. The default Authorizer (WebContainerAuthorizer) |
delegates to the web container via the HttpRequest's isUserInRole(String) method. |
There's a documented interface, so you could easily add your own implementation |
such as an RDBMS authorizer. If you use your own Authorizer, you'd need to specify |
the implementation class in jspwiki.properties. |
|
Group membership is much more ad hoc---these are groups that JSPWiki users create |
themselves. The default implementation reads membership lists from WikiPages with |
the prefix "Group." Janne and I have discussed doing this using other |
methods such as sub-pages, but for now they're just plain wiki pages. The wiki markup |
"[{SET members='Foo, Bar'}]" enumerates the members. Again, |
you are free to use your own GroupManager implementation. |
|
!Access Control Lists |
The Java security policy for JSPWiki applies to all pages that don't have an ACL. |
But ACLs are often required to refine (usually, reduce) the privileges for particular |
pages. Here's the wiki markup syntax---it's just like earlier versions of JSPWiki: |
|
[{ALLOW view Janne,Mike Morris}] |
|
This allows Janne and Mike to view the page, but nobody else can view it. Note that |
if this was the *only* ACL entry for the page, only ADMIN would be able to edit |
it! Thus, I've implemented JSP logic so when pages are created, the ACL is "pre-populated" |
with the current user's name. |
|
Note: there is no support for "deny" access control entries. That's a |
deliberate, philosophical choice---it's far easier to deny by default than to worry |
about whether the grants or denies take precedence. The code's much cleaner too. |
That said, there's nothing in the design that precludes "deny" ACL entries. |
The consensus we got during the requirements phase was that this was a wish-list |
item, but not critical for the next release. |
|
!Implementation |
At present, to use the new AAA scheme you need to specify the location of the JSPWiki |
security policy (jspwiki.policy) and JAAS login configuration file (jspwiki.jaas). |
Janne's recent e-mails were on this subject. |
|
To make the security policy active, you will need to specify its location by setting |
the JVM system property 'java.security.policy' in the command line script you use |
to start your web container. The file location should be the absolute path to the |
jspwiki.policy file. For example: |
|
java -jar myservletcontainer.jar -Djava.security.policy=/path-to/jspwiki.policy |
|
Some servlet containers make this very easy by looking for an environment variable |
and automatically appending the contents to the 'java' command. For example, Tomcat |
users just need to set the CATALINA_OPTS variable: |
|
export CATALINA_OPTS="-Djava.security.policy=/path-to/jspwiki.policy" |
|
The system property for the JAAS login configuration works the same way, except |
the property is called "java.security.auth.login.config". In security-conscious |
environments you will probably want to store jspwiki.policy and jspwiki.jaas in |
the Tomcat config directory (CATALINA_HOME/conf). |
|
Short-term development will likely focus on making the AAA configuration easier, |
so that a decent default policy & login configuration are loaded "by default" |
from WEB-INF... thus preserving the "it just works" quality of JSPWiki. |
This page has been [moved to the official documentation wiki | http://doc.jspwiki.org/2.4/wiki/Wiki.Admin.Security]. |