package org.ff4j.web.api.security; /* * #%L * ff4j-web * %% * Copyright (C) 2013 - 2014 Ff4J * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import java.util.Set; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.SecurityContext; import org.ff4j.web.ApiConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sun.jersey.core.util.Base64; import com.sun.jersey.spi.container.ContainerRequest; import com.sun.jersey.spi.container.ContainerRequestFilter; import com.sun.jersey.spi.container.ContainerResponseFilter; import com.sun.jersey.spi.container.ResourceFilter; import static org.ff4j.web.FF4jWebConstants.*; /** * Filter to get security. * * @author <a href="mailto:cedrick.lunven@gmail.com">Cedrick LUNVEN</a> */ public class FF4jSecurityContextFilter implements ContainerRequestFilter, ResourceFilter { /** logger for this class. */ private final Logger log = LoggerFactory.getLogger(getClass()); /** security configuration. */ private static ApiConfig securityConfig = null; /** * Apply the filter : check input request, validate or not with user auth * * @param containerRequest * The request from Tomcat server */ @Override public ContainerRequest filter(ContainerRequest containerRequest) throws WebApplicationException { String method = containerRequest.getMethod(); String path = containerRequest.getPath(true); log.debug("Entering security filter for <" + path + ">"); //We do allow wadl to be retrieve if (method.equals("GET") && (path.equals("application.wadl") || path.equals("application.wadl/xsd0.xsd"))) { log.info("Accessing schema and wadl ok"); return containerRequest; } // Get the authentification passed in HTTP headers parameters String auth = containerRequest.getHeaderValue(HEADER_AUTHORIZATION); if (auth == null) { handleUnAuthorized("<p>'authorization' parameter is required in header for authentication (HTTP-Basic or ApiKey)</p>"); } // Identification of an Application with its api key if (auth.contains(PARAM_AUTHKEY)) { auth = auth.replaceFirst(PARAM_AUTHKEY + "=", ""); // Checking api Key if (!securityConfig.getApiKeys().contains(auth)) { handleUnAuthorized("The api key provided '" + auth + "' is invalid "); } // Positionning Roles Set<String> perms = securityConfig.getPermissions().get(auth); SecurityContext sc = new FF4jSecurityContext(auth, PARAM_AUTHKEY, perms); containerRequest.setSecurityContext(sc); log.info("Client successfully logged with an ApiKey"); return containerRequest; } // Identification of a final user in HTTP-BASIC MODE if (auth.toUpperCase().contains("BASIC")) { byte[] decodedBytes = Base64.decode(auth.replaceFirst("[B|b]asic ", "")); String[] lap = new String(decodedBytes).split(":", 2); if (lap == null || lap.length != 2) { handleUnAuthorized("Invalid BASIC Token, cannot parse"); } // Validation login/password String expectedPassword = securityConfig.getUsers().get(lap[0]); if (expectedPassword == null || !(lap[1].equals(expectedPassword))) { handleUnAuthorized("<p>Invalid username or password.</p>"); } // Positionning Roles Set<String> perms = securityConfig.getPermissions().get(lap[0]); SecurityContext sc = new FF4jSecurityContext(lap[0], "BASIC", perms); containerRequest.setSecurityContext(sc); log.info("Client successfully logged with a user/pasword pair "); return containerRequest; } handleUnAuthorized("Cannot parse authorisation header attribute, valid are basic and apiKey"); return null; } /** * Dedicated error. * * @param message * target message */ private void handleUnAuthorized(String message) { StringBuilder msg = new StringBuilder("<p style=\"color:#880000\">"); msg.append("<H1>ERROR HTTP 401 : Unauthorized</H1>"); msg.append("<p>" + message + "</p>"); log.error("Authentication error :" + message); throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).entity(msg.toString()) .type(MediaType.TEXT_HTML_TYPE).build()); } /** {@inheritDoc} */ @Override public ContainerRequestFilter getRequestFilter() { return this; } /** {@inheritDoc} */ @Override public ContainerResponseFilter getResponseFilter() { // No response filter return null; } public static ApiConfig getSecurityConfig() { return securityConfig; } public static void setSecurityConfig(ApiConfig securityConfig) { FF4jSecurityContextFilter.securityConfig = securityConfig; } }