| <?xml version="1.0"?> |
| |
| <document> |
| <properties> |
| <title>Security Howto</title> |
| <author email="criley@ekmail.com">Cameron Riley</author> |
| </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, and which the TDK is generated with upon initialisation, 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> |