Does Java EE security model support ACL?

2020-07-09 02:31发布

问题:

I used Java EE 6 with Glassfish v3.0.1, and I wonder if Java EE security model support ACL, and if so how fine-grained is it get?

EDITED
I implement Security using jdbc realm via glassfish v3, that the realm at runtime look into table USER inside the database to check for authentication, by looking at the password field and authorization by looking at the role field. The roles field only contain 2 either ADMINISTRATOR or DESIGNER. So it is a One-to-one map between user and role. At the managed bean level, I implemented this

private Principal getLoggedInUser()
{
    HttpServletRequest request =
            (HttpServletRequest) FacesContext.getCurrentInstance().
                getExternalContext().getRequest();
    if(request.isUserInRole("ADMINISTRATORS")){
        admin = true;
    }else{
        admin = false;
    }
    return request.getUserPrincipal();
}

public boolean isUserNotLogin()
{
    Principal loginUser = getLoggedInUser();
    if (loginUser == null)
    {
        return true;
    }
    return false;
}

public String getLoginUserName()
{
    Principal loginUser = getLoggedInUser();
    if (loginUser != null)
    {
        return loginUser.getName();
    }
    return "None";
} 

by calling isUserInRole, I can determine if the user is admin or not, then the JSF will render the content appropriately. However, that is not fine-grained enough (real quick background info: There are multiple projects, a project contains multiple drawings). Because if u are a DESIGNER, you can see all the drawings from all the projects (what if I only want tom to work on project A, while peter will work on project B, Cindy can supervised over the two project A and B). I want that, at runtime, when I create the user, I can specifically set what project can he/she see. Is there a way to accomplish this? NOTE: There are more than just two projects, the above example is just for demonstration.

回答1:

The Java EE security model authenticates a 'Principal' which may one have or more 'Roles'.

In the other dimension you have services and resources which need configurable 'Permissions' or 'Capabilities'.

In the configuration you determine which 'Principals' or 'Roles' have which 'Permissions' or 'Capabilities'.

In other words, yes it supports ACL and it is as fine grained as you want it to be, but you'll have to get used to the terminology.

In the answer of Vineet is the excellent suggestion to create 'roles' per project id. Since people must be assigned to projects anyhow, it is straightforward to to add the people to these groups at that time. Alternatively a timed script can update the group memberships based on the roles. The latter approach can be preferable, because it is easier to verify security if these decisions are in one place instead of scattered all over the administration code.

Alternatively you can use "coarse-grained" roles e.g. designer and make use of the database (or program logic) to restrict the views for the user logged in

SELECT p.* FROM projects p, assignments a WHERE p.id = a.projectId AND a.finishdate < NOW();

or

@Stateless class SomeThing {

    @Resource SessionContext ctx;

    @RolesAllowed("DESIGNER")
    public void doSomething(Project project) {
        String userName = ctx.getCallerPrincipal.getName();

        if (project.getTeamMembers().contains(userName) {
            // do stuff
        }
    }

}

Note that the coarse grained access control has here been done with an annotation instead of code. This can move a lot of hard to test boilerplate out of the code and save a lot of time.

There are similar features to render webpages where you can render parts of the screen based on the current user using a tag typically.

Also because security is such a wide reaching concern, I think it is better to use the provided features to get at the context than to pass a battery of boolean flags like isAdmin around as this quickly becomes very messy. It increases coupling and it is another thing making the classes harder to unit-test.

In many JSF implementations there are tags which can help rendering optional things. Here is are examples for richfaces and seam:

<!-- richfaces -->
<rich:panel header="Admin panel" rendered="#{rich:isUserInRole('admin')}">
  Very sensitive information
</rich:panel>

<!-- seam -->
<h:commandButton value="edit" rendered="#{isUserInRole['admin']}"/>.

Here is an article explaining how to add it to ADF



回答2:

The Java EE security model implements RBAC (Role Based Access Control). To a Java EE programmer, this effectively means that permissions to access a resource can be granted to users. Resources could include files, databases, or even code. Therefore, it is possible to not only restrict access to objects like files and tables in databases, it is also possible to restrict access to executable code.

Now, permissions can be grouped together into roles that are eventually linked to users/subjects. This is the Java EE security model in a nutshell.

From the description of your problem, it appears that you wish to distinguish between two different projects as two different resources, and therefore have either two separate permission objects or two separate roles to account for the same. Given that you already have roles (more appropriately termed as user groups) like Administrator, Designer etc. this cannot be achieved in quite easily in Java EE. The reason is that you are distinguishing access to resources to users in a role, based on an additional property of the resource - the project ID. This technically falls into the area known as ABAC (Attribute Based Access Control).

One way of achieving ABAC in Java EE is to carry the properties/attributes granted to the role, in the role name. So instead of the following code:

if(request.isUserInRole("DESIGNERS")){
    access = true;
}else{
    access = false;
}

you ought to doing something like the following. Note the ":" character used as a separator to distinguish the role name from the accompanying attribute.

if(request.isUserInRole("DESIGNERS"+":"+projectId)){
    access = true;
}else{
    access = false;
}

Of course, there is the part where your login module should be modified (either in configuration or in code) to return Roles containing project IDs, instead of plain role names. Do note that all of these suggested changes need to reviewed comprehensively for issues - for instance, one should be disallowing the separator character from being part of a role name, otherwise it is quite possible to perform privilege escalation attacks.

If implementing the above proves to be a handful, you could look at systems like Shibboleth that provide support for ABAC, although I've never seen it being used in a Java EE application.