| /* |
| * 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.sling.cms.core.internal.filters; |
| |
| import java.io.IOException; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Optional; |
| |
| import javax.jcr.RepositoryException; |
| import javax.jcr.Session; |
| 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 javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.http.HttpStatus; |
| import org.apache.jackrabbit.api.JackrabbitSession; |
| import org.apache.jackrabbit.api.security.user.Authorizable; |
| import org.apache.jackrabbit.api.security.user.Group; |
| import org.apache.jackrabbit.api.security.user.User; |
| import org.apache.jackrabbit.api.security.user.UserManager; |
| import org.apache.sling.api.SlingHttpServletRequest; |
| import org.apache.sling.cms.CMSUtils; |
| import org.apache.sling.cms.PublishableResource; |
| import org.apache.sling.cms.publication.PUBLICATION_MODE; |
| import org.apache.sling.cms.publication.PublicationManagerFactory; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.component.annotations.ReferenceCardinality; |
| import org.osgi.service.component.annotations.ReferencePolicyOption; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Checks to ensure that the user is logged in for requests which otherwise |
| * would be allowed when accessing through a CMS-specific domain name. |
| */ |
| @Component(service = { Filter.class }, property = { "sling.filter.scope=request" }, immediate = true) |
| public class CMSSecurityFilter implements Filter { |
| |
| private static final Logger log = LoggerFactory.getLogger(CMSSecurityFilter.class); |
| |
| @Reference(cardinality = ReferenceCardinality.MULTIPLE, policyOption = ReferencePolicyOption.GREEDY) |
| private List<CMSSecurityConfigInstance> securityConfigInstances; |
| |
| @Reference |
| private PublicationManagerFactory pubMgrFactory; |
| |
| @Override |
| public void init(FilterConfig filterConfig) throws ServletException { |
| // Nothing required |
| } |
| |
| @Override |
| public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
| throws IOException, ServletException { |
| |
| if (pubMgrFactory.getPublicationMode() == PUBLICATION_MODE.STANDALONE) { |
| SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request; |
| for (CMSSecurityConfigInstance securityConfig : securityConfigInstances) { |
| log.trace("Checking to see if security config {} applies to request", securityConfig); |
| if (securityConfig.applies(slingRequest)) { |
| boolean allowed = checkAllowed(securityConfig, slingRequest); |
| // permission checked failed, so return an unauthorized error |
| if (!allowed) { |
| log.trace("Request to {} not allowed for user {}", slingRequest.getRequestURI(), |
| slingRequest.getResourceResolver().getUserID()); |
| ((HttpServletResponse) response).sendError(HttpStatus.SC_UNAUTHORIZED); |
| return; |
| } |
| } |
| } |
| } else { |
| log.trace("Publication mode {} is not standalone", pubMgrFactory.getPublicationMode()); |
| } |
| |
| chain.doFilter(request, response); |
| } |
| |
| private boolean checkAllowed(CMSSecurityConfigInstance securityConfig, SlingHttpServletRequest slingRequest) { |
| log.trace("Filtering requests to host {}", slingRequest.getServerName()); |
| String uri = slingRequest.getRequestURI(); |
| boolean allowed = false; |
| if (securityConfig.isUriAllowed(uri)) { |
| log.trace("Allowing request to uri {} based on allow patterns", uri); |
| allowed = true; |
| } |
| |
| PublishableResource publishableResource = Optional |
| .ofNullable(CMSUtils.findPublishableParent(slingRequest.getResource())) |
| .map(r -> r.adaptTo(PublishableResource.class)).orElse(null); |
| |
| if (publishableResource != null && publishableResource.isPublished()) { |
| log.trace("Resource is published"); |
| allowed = true; |
| } |
| |
| // the uri isn't allowed automatically, so check user permissions |
| if (!allowed) { |
| log.trace("Request to {} not public, checking user permissions", uri); |
| // check to see if the user is a member of the specified group |
| if (StringUtils.isNotBlank(securityConfig.getGroupName())) { |
| allowed = checkGroupMembership(securityConfig, slingRequest); |
| } else { |
| // just check to make sure the user is logged in |
| if (!"anonymous".equals(slingRequest.getResourceResolver().getUserID())) { |
| allowed = true; |
| } |
| } |
| } else { |
| log.trace("Request to {} allowed", uri); |
| } |
| return allowed; |
| } |
| |
| private boolean checkGroupMembership(CMSSecurityConfigInstance securityConfig, |
| SlingHttpServletRequest slingRequest) { |
| boolean allowed = false; |
| try { |
| Session session = slingRequest.getResourceResolver().adaptTo(Session.class); |
| UserManager userManager = null; |
| if (session instanceof JackrabbitSession) { |
| userManager = ((JackrabbitSession) session).getUserManager(); |
| } |
| if (userManager == null) { |
| log.warn("Unable to retrieve user manager"); |
| return false; |
| } |
| log.trace("Retrieved user manager {} with session {}", userManager, session); |
| Authorizable auth = userManager.getAuthorizable(slingRequest.getUserPrincipal()); |
| if (auth == null) { |
| log.warn("Unable to retrieve user from principal {}", slingRequest.getUserPrincipal()); |
| return false; |
| } |
| |
| log.trace("Checking to see if user {} is in required group {}", auth.getID(), |
| securityConfig.getGroupName()); |
| Iterator<Group> groups = ((User) auth).memberOf(); |
| while (groups.hasNext()) { |
| if (groups.next().getID().equals(securityConfig.getGroupName())) { |
| allowed = true; |
| break; |
| } |
| } |
| |
| } catch (RepositoryException e) { |
| log.error("Unexpected exception checking group membership", e); |
| return false; |
| } |
| return allowed; |
| } |
| |
| @Override |
| public void destroy() { |
| // Nothing required |
| } |
| |
| } |