Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make each user access resources at a specific location according to their authority/role in JAAS?

I'm using GlassFish server 4.0 in which I have assigned different authorities/roles to different users.

A user may have multiple authorities/roles. For example, an admin user may be associated with ROLE_ADMIN (to perform administrative tasks) and ROLE_USER (to perform tasks as a registered user).

In my web.xml, this is configured as follows.

<security-constraint>
    <display-name>AdminConstraint</display-name>
    <web-resource-collection>
        <web-resource-name>ROLE_ADMIN</web-resource-name>
        <description/>
        <url-pattern>/admin_side/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <description/>
        <role-name>ROLE_ADMIN</role-name>
    </auth-constraint>
    <user-data-constraint>
        <description/>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

<security-constraint>
    <display-name>UserConstraint</display-name>
    <web-resource-collection>
        <web-resource-name>ROLE_USER</web-resource-name>
        <description/>
        <url-pattern>/user_side/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <description/>
        <role-name>ROLE_USER</role-name>
    </auth-constraint>
    <user-data-constraint>
        <description/>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

<login-config>
    <!--<auth-method>DIGEST</auth-method>-->
    <auth-method>FORM</auth-method>
    <realm-name>projectRealm</realm-name>
    <form-login-config>
        <form-login-page>/utility/Login.jsf</form-login-page>
        <form-error-page>/utility/ErrorPage.jsf</form-error-page>
    </form-login-config>
</login-config>

<security-role>
    <description/>
    <role-name>ROLE_ADMIN</role-name>
</security-role>

<security-role>
    <description/>
    <role-name>ROLE_USER</role-name>
</security-role>

This works just fine.


There are two URL patterns /admin_side/* and /user_side/*. The administrator has two roles ROLE_ADMIN and ROLE_USER.

When the administrator logs in using the authority ROLE_USER, the only resources located in /user_side/* should be accessed. The resources located in /admin_side/* should be forbidden from being accessed because the admin is logged in as a registered user and not as an admin.

Until now what happens in my case is that when admin logs in using any of the authorities, the resources in both the locations can be accessed which is perfectly illegal. It is because the system is able to locate both the authorities for that particular user.

How to have each user access resources at a specific location according to their authority/role?


The authentication filter:

@WebFilter(filterName = "SecurityCheck", urlPatterns = {"/jass/*"})
public final class SecurityCheck implements Filter
{
    private FilterConfig filterConfig = null;

    @Resource(mappedName="jms/destinationFactory")
    private ConnectionFactory connectionFactory;
    @Resource(mappedName="jms/destination")
    private Queue queue;
    @EJB
    private final UserBeanLocal userService=null;

    public SecurityCheck() {}

    private void sendJMSMessageToDestination(String message) throws JMSException
    {
        Connection connection = null;
        Session session = null;

        try
        {
            connection = connectionFactory.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageProducer messageProducer = session.createProducer(queue);
            TextMessage textMessage = session.createTextMessage();
            textMessage.setText(message);
            messageProducer.send(textMessage);
        }
        finally
        {
            if(session!=null){session.close();}
            if(connection!=null){connection.close();}
        }
    }

    private void doBeforeProcessing(ServletRequest request, ServletResponse response) throws IOException, ServletException
    {
        HttpServletRequest httpServletRequest=(HttpServletRequest)request;
        httpServletRequest.login(httpServletRequest.getParameter("userName"), httpServletRequest.getParameter("password"));
    }

    private void doAfterProcessing(ServletRequest request, ServletResponse response) throws IOException, ServletException, JMSException
    {
        HttpServletRequest httpServletRequest=(HttpServletRequest)request;
        HttpServletResponse httpServletResponse=(HttpServletResponse)response;
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        Map<String, Object> sessionMap = externalContext.getSessionMap();

        if(httpServletRequest.isUserInRole("ROLE_USER"))
        {
            sendJMSMessageToDestination(httpServletRequest.getLocalName());
            UserTable userTable = userService.setLastLogin(httpServletRequest.getParameter("userName"));
            userTable.setPassword(null);
            sessionMap.put("userName", userTable!=null?userTable.getFirstName():"Unknown");
            sessionMap.put("user", userTable);

            httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            httpServletResponse.setHeader("Pragma", "no-cache");
            httpServletResponse.setDateHeader("Expires", 0);
            httpServletResponse.sendRedirect("../user_side/Home.jsf");
        }
        else if(httpServletRequest.isUserInRole("ROLE_ADMIN"))
        {
            sendJMSMessageToDestination(httpServletRequest.getLocalName());
            UserTable userTable = userService.setLastLogin(httpServletRequest.getParameter("userName"));
            userTable.setPassword(null);
            sessionMap.put("adminName", userTable!=null?userTable.getFirstName():"Unknown");
            sessionMap.put("user", userTable);

            httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            httpServletResponse.setHeader("Pragma", "no-cache");
            httpServletResponse.setDateHeader("Expires", 0);
            httpServletResponse.sendRedirect("../admin_side/Home.jsf");
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
    {
        try
        {
            doBeforeProcessing(request, response);
        }
        catch (Exception e)
        {
            HttpServletResponse httpServletResponse=(HttpServletResponse)response;
            //FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "Incorrect user name and/or password. Access denied."));
            httpServletResponse.sendRedirect("../utility/Login.jsf");
            return;
        }

        chain.doFilter(request, response);

        try
        {
            doAfterProcessing(request, response);
        }
        catch (JMSException ex)
        {
            Logger.getLogger(SecurityCheck.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    //The rest of the filter.
}

If you need to see other things in my application then, please let me know.

like image 903
Tiny Avatar asked Nov 02 '13 18:11

Tiny


People also ask

Who uses role-based access control?

Most large organizations use role-based access control to provide their employees with varying levels of access based on their roles and responsibilities. This protects sensitive data and ensures employees can only access information and perform actions they need to do their jobs.

Why is role-based management better than per user access controls?

Why is role-based management better than per-user access controls? A: Because users sometimes change job function. B: Because there are too many users to manage individually. Because role-based management reduces errors.

What is role-based login in web technology?

The concept of Role-based Access Control is to create a set of permissions and assign these permissions to a user or group. With the help of these permissions, only limited access to users can be provided therefore level of security is increased.

What is role-based security?

A role-based security model provides a way for administrators to control user and group access to objects that are under a defined security point within the object hierarchy according to the role the user or group is expected to perform within the organization.


2 Answers

You seem to think that when an user has multiple roles, then this user can login using only one of those roles simultaneously. This is not true. Users are not logged in on a per-role basis. Users are logged in on a per-user basis. If an user has multiple roles, then they will all be used and applied throughout the entire login session.

Actually, it is not possible to let an user pick and use only one of the assigned roles throughout the session. So far it sounds too much like that your admin shouldn't have the ROLE_USER in first place. But this makes in real world little sense. Roles aren't supposed to "extend" existing roles. I.e. ROLE_ADMIN shouldn't copy the same restrictions as ROLE_USER and then add some more on top of that. No, it should solely represent exactly that "some more". Admin users are then just assigned the both roles (you did that part correctly). Otherwise you end up with duplicate checks throughout the code at places which may be accessed/used by both an user and an admin. And then I'm not talking about a third role above this which may require triple checks in the code. You'd need to edit the existing code over all place.

If you'd like to programmatically toggle roles during runtime, perhaps because you'd like being able to "preview" the site as a regular user (e.g. checking how the site look like when admin-only sections/buttons are hidden), then there are basically two options:

  1. Set some flag as session attribute or perhaps as request parameter and have the code check on that. E.g.

    <h:form>
        <h:selectBooleanCheckbox value="#{sessionScope.preview}">
            <f:ajax render="@all" />
        </h:selectBooleanCheckbox>
    </h:form>
    

    (note: the code is as-is, #{sessionScope} is an implicit EL variable referring ExternalContext#getSessionMap(); no additional backing bean necessary)

    And then in the master tamplate:

    <c:set var="userIsAdmin" value="#{request.isUserInRole('ROLE_ADMIN') and not preview}" scope="request" />
    

    And in the target views containing some admin specific stuff:

    <h:commandButton value="Some awesome admin button" rendered="#{userIsAdmin}" />
    

  2. Perform programmatic login as a regular user. You can use HttpServletRequest#login() to programmatically trigger container managed authentication. This way an admin can "impersonate" a different user and browse the site as if he's logged-in as the particular user. E.g. in a session scoped bean:

    public void runAs(User user) {
        // ...
        try {
            request.login(user.getUsername(), user.getPassword());
            originalUser = currentUser;
            currentUser = user;
            // ...
        } catch (ServletException e) {
            // ...
        }
    }
    
    public void releaseRunAs() {
        // ...
        try {
            request.login(originalUser.getUsername(), originalUser.getPassword());
            currentUser = originalUser;
            // ...
        } catch (ServletException e) {
            // ...
        }
    }
    

    You can even extend it by holding all previous users in a LILO (last in, last out) queue in the session scope. Most security frameworks like Apache Shiro have builtin APIs for this.

like image 129
BalusC Avatar answered Oct 03 '22 23:10

BalusC


I think the way to get the behavior you asked for being very careful with this, maybe add another admin account instead and remove the ROLE_USER from that account.

like image 20
Elliott Frisch Avatar answered Oct 04 '22 00:10

Elliott Frisch