Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to refer to predefined database entries in source code

On our company we had an discussion on how to refer in our java source code to certain database entries.The situation is as follows:

The java web-application is connected to a MySQL Database which is setup by an SQL script, JPA/Hibernate is used for ORM. In this script we insert 3 Roles in the "roles" table (i.e. roles a user can have in a web application, i.e. the user table has a foreign key to the roles table). The roles have predefined primary keys/IDs (BIGINT) and names (VARCHAR) as given in the SQL script. The roles are not used for an security framework, but for the business logic. At the moment it looks something like this:

if(user.getRole.getId()==1) {
     // user is in role A
} else if(user.getRole().getId()==2) {
    // user is in role B
} ...

As the roles must be known by the source code at compile time (as the logic depends on them), we must check for user to have certain roles. The question is now how to do that. We we have discussed:

a) better checking by ID or by Name b) using String/Long constants or Enums to check for equality

to a) I would prefer checking for the ID (as it is unlikely to change as we insert the IDs at database setup with a script), the roles name are more likely to be changed during the lifetime of the application.

to b) I would prefer to use constants, as I don't like to rely on the Enums index/ordinal number. Also we don't need type safety here. The only advantage I see with enums is that its easy to fetch all values, afaik thats only possible with reflection in the case of constants. But as we have all roles defined on the DB, they could be fetched from there at runtime.

Any suggestions?

like image 713
Raphael Roth Avatar asked Oct 16 '15 05:10

Raphael Roth


Video Answer


2 Answers

With your approach the roles are needed at compile time and therefore fetching them from the database is no help.

I would go with enums storing the id:

public enum Role {
     ROLE1(1), ROLE2(2), ROLE(3);

     private final int id;
     private Role(int id) {
         this.id=id;
     }

     public int getId() {
          return this.id;
     }
}

If you want to match them against database entries (e.g. to get some additional metadata), you have stored the matched id. This enables you to convert from the database role to the enum role and vise versa.

like image 132
flo Avatar answered Oct 03 '22 06:10

flo


I would like to tell you that @flo answer is right. But here I will give another option, that I belive it is not straightforward fro your code, but maybe it will result of your interest.

@flo answer, fits your question nicely, but you described the role use in such way you wire on code business logic access and roles, this can be improved with no too much effort.

So, you can do it in a more flexible way, using a class that does not require to be changed if new roles added to DB.

I mean the following:

public class Role {
    private Integer roleId = null;
    private String roleName = null;

    private static Map<Integer, String> roleMap = null;
    ...
}

You can populate roleMap at the beginning of your app, when it is loading, from DB or using some properties file or even using Spring Beans (*In this two last cases, you will need to modify this files to add new roles or to change its name) to assign which ID has the role into DB.

But the big question is, How do I check user authorization this way?

This maybe a bit disruptive with your code, but it would worth a while to think about it. At least, there are two options:

Using actions

Obviously if you don't want to get compile dependencies on role names or role ID's I will suggest you using actions. Actions could be another join table to match a set of actions (Strings) to roles.

Adding a second static map to Role class

private Map<String, Set<String>> roleActionMap = null;

Would allow you to implement a method as following

public boolean isAuthorized(String action, String role){
    boolean result = false;

    Set<String> actions = getActionSet(role);
    if(actions != null)
        result = actions.contains(action);

    return result;
}

You have to wire actions in methods of bussines logic to ask for authorization. Once you reorder your code it gets more clear and it is more flexible to readjust role behavior even without changing code.

Mapping methods and roles

In a very similar way, instead of creating a map to relate roles and actions, you can relate roles an method names, such way you can use reflection to check if a method is authorized to a role.

So you can check user authorization invoking the same above method but instead passing action name, passing

Thread.currentThread().getStackTrace()[1].getMethodName();

Which would give you the name of the running method from where you want to check authorization. (There are other ways to get the method name using reflection, that maybe more appropriate, depending on the scenario)

Finally, there are more complex and better frameworks, SpringSecurity is a beast, but if you want to authorize business logic, and you are not concern about use authorization at view logic and UI, I think my two alternatives are nice.

Hope it helps.

like image 43
malaguna Avatar answered Oct 03 '22 06:10

malaguna