/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ package org.apache.brooklyn.rest.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.core.mgmt.entitlement.Entitlements; import org.apache.brooklyn.core.mgmt.entitlement.WebEntitlementContext; import org.apache.brooklyn.rest.security.provider.DelegatingSecurityProvider; import org.apache.brooklyn.util.text.Strings; import com.sun.jersey.core.util.Base64; import org.apache.brooklyn.rest.util.OsgiCompat; /** * Provides basic HTTP authentication. */ public class BrooklynPropertiesSecurityFilter implements Filter { /** * The session attribute set for authenticated users; for reference * (but should not be relied up to confirm authentication, as * the providers may impose additional criteria such as timeouts, * or a null user (no login) may be permitted) */ public static final String AUTHENTICATED_USER_SESSION_ATTRIBUTE = "brooklyn.user"; /** * The session attribute set to indicate the remote address of the HTTP request. * Corresponds to {@link javax.servlet.http.HttpServletRequest#getRemoteAddr()}. */ public static final String REMOTE_ADDRESS_SESSION_ATTRIBUTE = "request.remoteAddress"; private static final Logger log = LoggerFactory.getLogger(BrooklynPropertiesSecurityFilter.class); protected DelegatingSecurityProvider provider; private static ThreadLocal<String> originalRequest = new ThreadLocal<String>(); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String uri = httpRequest.getRequestURI(); if (provider == null) { log.warn("No security provider available: disallowing web access to brooklyn"); httpResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); return; } if (originalRequest.get() != null) { // clear the entitlement context before setting to avoid warnings Entitlements.clearEntitlementContext(); } else { originalRequest.set(uri); } boolean authenticated = provider.isAuthenticated(httpRequest.getSession()); if ("/logout".equals(uri) || "/v1/logout".equals(uri)) { httpResponse.setHeader("WWW-Authenticate", "Basic realm=\"brooklyn\""); if (authenticated && httpRequest.getSession().getAttributeNames().hasMoreElements()) { logout(httpRequest); httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED); } else { RequestDispatcher dispatcher = httpRequest.getRequestDispatcher("/"); log.debug("Not authenticated, forwarding request for {} to {}", uri, dispatcher); dispatcher.forward(httpRequest, httpResponse); } return; } if (!(httpRequest.getSession().getAttributeNames().hasMoreElements() && provider.isAuthenticated(httpRequest.getSession())) || "/logout".equals(originalRequest.get())) { authenticated = authenticate(httpRequest); } if (!authenticated) { httpResponse.setHeader("WWW-Authenticate", "Basic realm=\"brooklyn\""); httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } // Note that the attribute AUTHENTICATED_USER_SESSION_ATTRIBUTE is only set in the call to authenticate(httpRequest), // so must not try to get the user until that is done. String uid = RequestTaggingFilter.getTag(); String user = Strings.toString(httpRequest.getSession().getAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE)); try { WebEntitlementContext entitlementContext = new WebEntitlementContext(user, httpRequest.getRemoteAddr(), uri, uid); Entitlements.setEntitlementContext(entitlementContext); chain.doFilter(request, response); } catch (Throwable e) { if (!response.isCommitted()) { httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } finally { originalRequest.remove(); Entitlements.clearEntitlementContext(); } } protected boolean authenticate(HttpServletRequest request) { HttpSession session = request.getSession(); if (provider.isAuthenticated(session)) { return true; } session.setAttribute(REMOTE_ADDRESS_SESSION_ATTRIBUTE, request.getRemoteAddr()); String user = null, pass = null; String authorization = request.getHeader("Authorization"); if (authorization != null) { String userpass = Base64.base64Decode(authorization.substring(6)); user = userpass.substring(0, userpass.indexOf(":")); pass = userpass.substring(userpass.indexOf(":") + 1); } if (provider.authenticate(session, user, pass)) { if (user != null) { session.setAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE, user); } return true; } return false; } @Override public void init(FilterConfig config) throws ServletException { ManagementContext mgmt = OsgiCompat.getManagementContext(config.getServletContext()); provider = new DelegatingSecurityProvider(mgmt); } @Override public void destroy() { } protected void logout(HttpServletRequest request) { log.info("REST logging {} out of session {}", request.getSession().getAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE), request.getSession().getId()); provider.logout(request.getSession()); request.getSession().removeAttribute(AUTHENTICATED_USER_SESSION_ATTRIBUTE); request.getSession().removeAttribute(REMOTE_ADDRESS_SESSION_ATTRIBUTE); request.getSession().invalidate(); } }