Turbine Security
The Turbine Security includes a Security Service as well as Actions and Screens that can be extended in the Templating Services.
Users, Groups, Roles and Permissions
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;
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.
Access Control Lists
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.
//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"); }
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;
//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"); }
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.
Templates and Logging In
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.
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); } }
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.
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); } }
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.