Skip to content

Commit

Permalink
Fixed KapuaSession management on REST API
Browse files Browse the repository at this point in the history
Signed-off-by: coduz <alberto.codutti@eurotech.com>
  • Loading branch information
Coduz committed May 10, 2019
1 parent 6293ac3 commit c9d78d7
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 122 deletions.
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011, 2016 Eurotech and/or its affiliates and others
* Copyright (c) 2016, 2019 Eurotech and/or its affiliates and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
Expand All @@ -11,11 +11,6 @@
*******************************************************************************/
package org.eclipse.kapua.app.api.core.auth;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
Expand All @@ -25,6 +20,11 @@
import org.eclipse.kapua.service.authentication.AccessTokenCredentials;
import org.eclipse.kapua.service.authentication.CredentialsFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class KapuaTokenAuthenticationFilter extends AuthenticatingFilter {

private static final String OPTIONS = "OPTIONS";
Expand Down Expand Up @@ -58,6 +58,7 @@ protected AuthenticationToken createToken(ServletRequest request, ServletRespons
if (authorizationHeader != null) {
tokenId = httpRequest.getHeader(AUTHORIZATION_HEADER).replace(BEARER + " ", "");
}

//
// Build AccessToken for Shiro Auth
KapuaLocator locator = KapuaLocator.getInstance();
Expand Down
@@ -0,0 +1,80 @@
/*******************************************************************************
* Copyright (c) 2019 Eurotech and/or its affiliates and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eurotech - initial API and implementation
*******************************************************************************/
package org.eclipse.kapua.app.api.core.filter;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.eclipse.kapua.commons.security.KapuaSecurityUtils;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;


/**
* This {@link Filter} cleans up the {@link Subject#getSession()} state and the {@link KapuaSecurityUtils#getSession()} after a request.
* <p>
* The processing of the request can leave some information on the Shiro {@link Subject} or the {@link org.eclipse.kapua.commons.security.KapuaSession} and we must clean it to avoid that
* a subsequent request uses a {@link Thread} with dirty data inside.
* <p>
* Apache Shiro it is possible to define {@code noSessionCreation} on the urls mappings in the shiro.ini.
* Unfortunately using the {@code noSessionCreation} does not have any effect because our {@link org.eclipse.kapua.app.api.core.auth.KapuaTokenAuthenticationFilter} is invoked before the {@link org.apache.shiro.web.filter.session.NoSessionCreationFilter}
* so it has no effect (see {@link org.apache.shiro.web.filter.session.NoSessionCreationFilter javadoc}.
*
* @since 1.1.0
*/
public class KapuaSessionCleanupFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {
// No init required
}

@Override
public void destroy() {
// No destroy required
}

/**
* After the invokation of {@link FilterChain#doFilter(ServletRequest, ServletResponse)} the {@link Subject} and the {@link org.eclipse.kapua.commons.security.KapuaSession}
* are checked and cleaned accordingly.
* <p>
* See also {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)} javadoc.
*
* @param request See {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)} javadoc.
* @param response See {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)} javadoc.
* @param chain See {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)} javadoc.
* @throws IOException See {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)} javadoc.
* @throws ServletException See {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)} javadoc.
* @since 1.1.0
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

try {
chain.doFilter(request, response);
} finally {
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
subject.logout();
}

if (KapuaSecurityUtils.getSession() != null) {
KapuaSecurityUtils.clearSession();
}
}
}
}
Expand Up @@ -47,7 +47,7 @@
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

@Api(value = "Accounts", authorizations = { @Authorization(value = "kapuaAccessToken") })
@Api(value = "Accounts", authorizations = {@Authorization(value = "kapuaAccessToken")})
@Path("{scopeId}/accounts")
public class Accounts extends AbstractKapuaResource {

Expand Down Expand Up @@ -76,12 +76,13 @@ public class Accounts extends AbstractKapuaResource {
notes = "Returns the list of all the accounts associated to the current selected scope.", //
response = AccountListResult.class) //
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public AccountListResult simpleQuery( //
@ApiParam(value = "The ScopeId in which to search results.", required = true, defaultValue = DEFAULT_SCOPE_ID) @PathParam("scopeId") ScopeId scopeId, //
@ApiParam(value = "The account name to filter results.") @QueryParam("name") String name, //
@ApiParam(value = "The result set offset.", defaultValue = "0") @QueryParam("offset") @DefaultValue("0") int offset, //
@ApiParam(value = "The result set limit.", defaultValue = "50") @QueryParam("limit") @DefaultValue("50") int limit) throws Exception {
@ApiParam(value = "The ScopeId in which to search results.", required = true, defaultValue = DEFAULT_SCOPE_ID) @PathParam("scopeId") ScopeId scopeId, //
@ApiParam(value = "The account name to filter results.") @QueryParam("name") String name, //
@ApiParam(value = "The result set offset.", defaultValue = "0") @QueryParam("offset") @DefaultValue("0") int offset, //
@ApiParam(value = "The result set limit.", defaultValue = "50") @QueryParam("limit") @DefaultValue("50") int limit) throws Exception {

AccountQuery query = accountFactory.newQuery(scopeId);

AndPredicateImpl andPredicate = new AndPredicateImpl();
Expand Down Expand Up @@ -114,8 +115,8 @@ public AccountListResult simpleQuery( //
response = AccountListResult.class) //
@POST
@Path("_query")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public AccountListResult query(
@ApiParam(value = "The ScopeId in which to search results.", required = true, defaultValue = DEFAULT_SCOPE_ID) @PathParam("scopeId") ScopeId scopeId, //
@ApiParam(value = "The AccountQuery to use to filter results.", required = true) AccountQuery query) throws Exception {
Expand All @@ -142,8 +143,8 @@ public AccountListResult query(
response = CountResult.class)
@POST
@Path("_count")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public CountResult count(
@ApiParam(value = "The ScopeId in which to count results", required = true, defaultValue = DEFAULT_SCOPE_ID) @PathParam("scopeId") ScopeId scopeId, //
@ApiParam(value = "The AccountQuery to use to filter count results", required = true) AccountQuery query) throws Exception {
Expand All @@ -170,8 +171,8 @@ public CountResult count(
notes = "Creates a new Account based on the information provided in AccountCreator parameter.", //
response = Account.class)
@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Account create(
@ApiParam(value = "The ScopeId in which to create the Account", required = true, defaultValue = DEFAULT_SCOPE_ID) @PathParam("scopeId") ScopeId scopeId, //
@ApiParam(value = "Provides the information for the new Account to be created", required = true) AccountCreator accountCreator) throws Exception {
Expand All @@ -198,7 +199,7 @@ public Account create(
response = Account.class)
@GET
@Path("{accountId}")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Account find(
@ApiParam(value = "The ScopeId of the requested Account.", required = true, defaultValue = DEFAULT_SCOPE_ID) @PathParam("scopeId") ScopeId scopeId, //
@ApiParam(value = "The id of the requested Account", required = true) @PathParam("accountId") EntityId accountId) throws Exception {
Expand Down Expand Up @@ -231,8 +232,8 @@ public Account find(
response = Account.class)
@PUT
@Path("{accountId}")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Account update(
@ApiParam(value = "The ScopeId of the requested Account.", required = true, defaultValue = DEFAULT_SCOPE_ID) @PathParam("scopeId") ScopeId scopeId, //
@ApiParam(value = "The id of the requested Account", required = true) @PathParam("accountId") EntityId accountId, //
Expand Down

0 comments on commit c9d78d7

Please sign in to comment.