Thursday, June 5, 2014

JAAS Revisited (Using Database)

On this post, we have tried simple JAAS application. It uses declarative configuration and permission files. Instead of writing those informations on text files, now we will take advantage of database. JAAS provides us classes we can extend to gather those informations. For this simple application, we will use PostgreSQL database. Below is SQL script to create tables and insert default data.



APP_CONFIGURATION holds configuration data that we used to write on configuration file. Column APP_NAME is used to save package/application name. LOGIN_MODULE_CLASS is used to save login module class name. CONTROL_FLAG is used to save login module control flag (REQUIRED, OPTIONAL, SUFFICIENT, REQUISITE).



Table PERMISSION has information about permissions. It holds permission class name, permission name, and its actions.



PRINCIPAL_PERMISSION maps principals and permissions. In this table we save the principal class and its corresponding permission classes.



PRINCIPAL_DB_USER is used to save mapping between users and principals. In this class we save the username and its corresponding principal classes.



DB_USER holds information about user. In this table we are only interested in saving username and password.

Next we will see how configuration is implemented using Configuration class and table APP_CONFIGURATION. To get configuration information from database, we need to create a class DBConfiguration that extends JAAS Configuration class as shown below. In this class, we override method getAppConfigurationEntry where we can query the table APP_CONFIGURATION and construct an array of AppConfigurationEntry. Below is the code on how to get a list of configuration information from the table.


In the method getAppConfigurationEntry above we query the table, iterate the result set, and for each configuration we create an object of AppConfigurationEntry. As the result, we put all the AppConfigurationEntry objects inside a list and return it. So how to load this configuration? This is done by this line of code:


In the code above, we make the DBConfiguration as singleton and inside its init method we set the login configuration to be our DBConfiguration singleton object. The last method to visit in DBConfiguration class is shown below:


Method resolveControlFlag above is called within our getAppConfigurationEntry before.

Next we will see how permission is implemented using Policy class and PERMISSION & PRINCIPAL_PERMISSION tables. We need to create our own DBPolicy class that extends JAAS Policy class to get permission information from those two tables. Below is the code to do this:



In the codes above we create DBPolicy class that extends JAAS Policy class. We override two overloaded getPermissions methods there. Method getPermissions(CodeSource) returns a list of permission for running some codes. This is equivalent to giving permissions to all as shown in policy file below:


In method getPermissions(ProtectionDomain), we give permissions to a principal if one exists. This is equivalent to giving permission as shown in policy file below:


Below is the code that fetch permission data from PERMISSION table by its principal. For each result set, we create a Permission object and add it to a list. Then we return the list of Permission.


Next is the code in the main method where we initialize configuration, policy, and security manager.


First, we call DBConfiguration.init() that will set JAAS configuration. Then we create DBPolicy instance and set JAAS policy to be our DBPolicy object. After that we try to test user's access to a file, input.txt. We will see this later.









In our APP_CONFIGURATION table, we insert DBLoginModule as our login module class. Next we will see this class. Since it implements LoginModule, we need to implement its methods.

  1. initialize(): this is the first method called when we authenticate a user. We can initialize our class variables here. initialize() has 4 arguments on its method header and three of them are useful for us. First is Subject, it is the Subject that is being logged in. Then CallbackHandler, it is an object that is responsible for gathering credentials (username & password). Last is a Map of options that we don't currently use. Our DBLoginModule's initialize() method above initialized our private variables Subject and CallbackHandler for future usage.
  2. login(): this method is used to gather and validate username and password. If the validation succeeds, it returns true. Otherwise we should throw LoginException. In the login() method, we declared two callback objects, NameCallback and PasswordCallback. They are used to gather username and password credentials through callbackHandler.handle(callbacks) method. After getting the credentials, we simply validated them by comparing input password and user's password from database. If the validation succeed, we returns true, otherwise we throw a FailedLoginException. Note that we didn't return false if the validation fails. If it returns true, then commit() method is called; but if it throws LoginException, then abort() method is called instead. Returning false (Boolean.FALSE) will not make the abort() method called.
  3. commit(): this method is called if login() method returns true. We can put principles to a Subject here. In our commit() method, we added principles to Subject since he/she has successfully logged in. We get the principals from table PRINCIPAL_DB_USER by passing the username.
  4. abort(): this method is called if login() method throws LoginException. We can remove all credentials (username, password) here. In this method we cleaned up all credentials.
  5. logout(): this method is called if we logout from JAAS. When a user logout, we clean up all credentials and remove all principals that were assigned when he/she logged in.
Next we will see the principal classes. In table PRINCIPAL_DB_USER and PRINCIPAL_PERMISSION we insert two principals, SysAdminPrincipal and UserPrincipal.



As we can see, all principals implements Principal interface. It has one method that returns its name. It is the name that is set on commit() method of DBLoginModule class. The name is set to be username since we get the principals by username. Once we get the principal, we can query the permissions by principal class name which is done by our DBPolicy class before.

Next we will see callback handler class, SimpleCallbackHandler.


Our SimpleCallbackHandler above implements CallbackHandler. We need to implement handle() method. In the handle() method we iterate callbacks array that was sent by DBLoginModule.login() method as we did before. During iteration, we populate NameCallback with username and PasswordCallback with password. Now one thing missing is how we get the username and password credentials to be sent to this DBLoginModule. This is where LoginContext plays its role which we will see soon.

In main method, we have set configuration and policy. Now we can authenticate and check user's authorization. Below is complete code of main class.



To authenticate a user, first we create SimpleCallbackHandler object and pass the username and password. Then we create LoginContext object by passing package/application name from table APP_CONFIGURATION and the SimpleCallbackHandler object. LoginContext.login() is used to log the user in by passing control to the DBLoginModule. If the authentication succeeds, we can obtain the Subject. If fails, LoginException is thrown.

Now that the user is authenticated, we can start checking if a user is authorized to do some action. We have granted permissions for users in database before. These permissions will be used to check user authorization. Subject.doAsPrivileged is the method we need. In the code above we test if a user is allowed to read file input.txt. If he/she is allowed, it simply returns. Otherwise, it throws SecurityException.

Below is the result from running the code:


From the result we can see that the Subject "user" and "sysadmin" authentication succeeds. The "user" is not allowed to access the input.txt file since we didn't grant any permission to it while "sysadmin" can.

Below is the project structure:


1 comments:

Margareth said...

Emg bener2 'Java Enthusiast' :)

 

©2009 Stay the Same | by TNB