blob: a1f025009325d3caeeb38ab5b04d86801a6cbdf1 [file] [log] [blame]
<?xml version="1.0"?>
<!--
/*
* Copyright 2001-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<document>
<properties>
<title>Security Howto</title>
</properties>
<body>
<section name="Turbine Security">
<p>
The Turbine Security includes a <a href="../services/security-service.html">Security Service</a>
as well as Actions and Screens that can be extended in the Templating Services.
</p>
</section>
<!-- Start -->
<section name="Users, Groups, Roles and Permissions">
<p>
The default Relational Database schema that Turbine uses for the database security
service includes the
data structure for the managing of permissions.
The default or core schema can be viewed at;
</p>
<p>
<ul><a href="../turbine-schema.html">Turbine Core Schema</a></ul>
</p>
<p>
The main tables are TURBINE_GROUP, TURBINE_USER, TURBINE_ROLE and TURBINE_PERMISSION.
The Permissions are the individual actions a user is allowed to take in the system.
The Role is a container for the Permissions, in other words a Role can be made of
many Permissions. User is an account that is interaction with the System and the Group
is a something that a User would want to do something in. In the Turbine mailing lists
it has often been described in the same way as a project. In a project you have fulfill
a role, however a User doesnt "belong" to a project they merely have a role in that
project ( or group ). In this manner too a User can have many Roles within the one
Group. For instance a User may have the Role Developer in the Group, but may also have
the Role of Administrator as well. While initially confusing at first, as there
are no group-user or role-user containers, it is a flexible system and strong system.
</p>
</section>
<section name="Access Control Lists">
<p>
A User's interaction through the system is controlled by the Permissions they are
able to partake in. The AccessControlLists manage this information and present it via
RunData and User Interfaces to the application. The RunData interface through the
getACL() method presents the AccessControlList Object.
</p>
<source>
//get the AccessControlList Object
//from RunData
AccessControlList acl = data.getACL();
//check if the User ( from the http request )
//has permission to view the invoices
if( acl.hasPermission("viewinvoice") )
{
data.setMessage("You have permission to view the invoices.");
setTemplate(data, "Invoice.vm");
}
else
{
data.setMessage("You do not have Permission to view the Invoices");
setTemplate(data, "UnauthorizedRequest.vm");
}
</source>
<p>
This will check if the User has permission to view this in the Global Group which
is useful for managing Anonymous Users as well as logged in Users across your
application. If however you need stronger security, such as only allowing users that
have logged in, and have a Role in a specific Group, the Permission will need to be
matched to the Group and User. As an example, assume one of your groups is "Accounting"
and the Invoice information is only to be viewed by Usersthat have a Role in Accounting
as well as the Permission "viewinvoice", the above method would be re-written;
</p>
<source><![CDATA[
//get the User from RunData
User user = data.getUser();
//get the AccessControlList Object
//from RunData
AccessControlList acl = data.getACL();
//check if the User has logged in,
//has a role in the group and
//has permission to view the invoices
if( user.hasLoggedIn() &&
acl.hasPermission("viewinvoice", "Accounting") )
{
data.setMessage("You have permission to view the invoices.");
setTemplate(data, "Invoice.vm");
}
else
{
data.setMessage("You do not have Permission to view the Invoices");
setTemplate(data, "UnauthorizedRequest.vm");
}
]]></source>
<p>
If instead the Permission could be across any of the Roles the User has, the
method acl.hasPermission(String permission, GroupSet groups), can be used. As
always check the Javadocs for more detail.
</p>
</section>
<section name="Templates and Logging In">
<p>
Managing Anonymous Users and Logged In Users poses problems in applications for managing
the Secure/Strong parts of the application and the Unsecure/Weak parts of the
application. In Turbine, the Action and Screen components make managing this process
quite simple. Assume the only Velocity Template allowed to be viewed without being
logged in is the actual Login.vm template. As this is the only Screen that
needs to be Unsecure/Weak we can manage this via the parent of Login Screen.
</p>
<source>
package com.mycompany.modules.screens;
//parent which allows Users to view the screen
public class WeakScreen extends VelocityScreen
{
//nothing to check that the User
//can view this screen
protected void doBuildTemplate( RunData data, Context context )
throws Exception
{
//call to Super
super.doBuildTemplate(data, context);
}
}
package com.mycompany.modules.screens;
//the java component of the Login Velocity Template
public class Login extends WeakScreen
{
//nothing to check that the User
//can view this screen
protected void doBuildTemplate( RunData data, Context context )
throws Exception
{
context.put("date",new Date());
//call to Super
super.doBuildTemplate(data, context);
}
}
</source>
<p>
Note that there is nothing in that method which checks that the User has logged in.
On the other hand, for the Secure or Strong Actions and Screens we would want a
check to ensure that the User has logged in.
</p>
<source>
package com.mycompany.modules.screens;
//Strong screen which checks for login
public class StrongScreen extends VelocityScreen
{
//check that User has Logged in before bothering
//to add anything to the context.
protected void doBuildTemplate( RunData data )
throws Exception
{
if (data.getUser().hasLoggedIn())
{
doBuildTemplate( data, TurbineVelocity.getContext( data ) );
}
else
{
//send the User to the Login Template
data.setMessage("Please Login first!");
setTemplate(data,"Login.vm");
}
}
}
package com.mycompany.modules.screens;
//as an example use the Invoices again
public class Invoice extends StrongScreen
{
//can view this screen
protected void doBuildTemplate( RunData data, Context context )
throws Exception
{
context.put("invoice",new Invoice());
//call to Super
super.doBuildTemplate(data, context);
}
}
</source>
<p>
In the latter example, before the Screen populates the Context it will check for the
User being logged in by the doBuildTemplate(data) method in the parent. If the test
fails, the context isnt created for the Invoice screen. Another way to manage this
is to seperate the screens into two packages, com.mycompany.modules.screens.unsecure
and com.mycompany.modules.screens.secure and have a Default.java Screen in each
of the packages mimicing the above approaches.
</p>
</section>
</body>
</document>