001/* 002 * Copyright 2016 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.web; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.List; 021import java.util.Map; 022import java.util.Optional; 023 024import org.apache.avalon.framework.parameters.Parameters; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.cocoon.environment.ObjectModelHelper; 028import org.apache.cocoon.environment.Redirector; 029import org.apache.cocoon.environment.Request; 030import org.apache.cocoon.environment.SourceResolver; 031import org.apache.commons.collections.CollectionUtils; 032 033import org.ametys.core.authentication.AuthenticateAction; 034import org.ametys.core.authentication.CredentialProvider; 035import org.ametys.core.user.UserIdentity; 036import org.ametys.core.user.population.UserPopulationDAO; 037import org.ametys.core.util.StringUtils; 038import org.ametys.runtime.authentication.AccessDeniedException; 039import org.ametys.runtime.config.Config; 040import org.ametys.web.repository.site.SiteManager; 041 042/** 043 * Special authentication process for web context and aware of front-offices. 044 */ 045public class WebAuthenticateAction extends AuthenticateAction 046{ 047 /** The request attribute to set front office user identity */ 048 public static final String REQUEST_ATTRIBUTE_FRONTOFFICE_USERIDENTITY = "Web:FrontOffice:UserIdentity"; 049 /** The request attribute to set front office credential provider id */ 050 public static final String REQUEST_ATTRIBUTE_FRONTOFFICE_CREDENTIALPROVIDER_ID = "Web:FrontOffice:CredentialProviderId"; 051 /** The request attribute set to "true" when the request came from the front and was IP checked */ 052 public static final String REQUEST_ATTRIBUTE_FRONTOFFICE_REQUEST = "Web:FrontOffice:Request"; 053 054 private static UserPopulationDAO _staticUserPopulationDAO; 055 056 private SiteManager _siteManager; 057 058 @Override 059 public void service(ServiceManager smanager) throws ServiceException 060 { 061 super.service(smanager); 062 _staticUserPopulationDAO = (UserPopulationDAO) smanager.lookup(UserPopulationDAO.ROLE); 063 } 064 065 @Override 066 protected List<String> _getContexts(Request request, Parameters parameters) 067 { 068 List<String> contextsWithoutSite = super._getContexts(request, parameters); 069 return _getSiteManager() 070 .map(SiteManager::getSiteNames) 071 .map(siteNames -> _getContextsWithSite(contextsWithoutSite, siteNames)) 072 .orElse(contextsWithoutSite); 073 } 074 075 private Optional<SiteManager> _getSiteManager() 076 { 077 if (_siteManager == null) 078 { 079 try 080 { 081 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 082 } 083 catch (ServiceException e) 084 { 085 // Do nothing, the site manager is not available in safe mode 086 } 087 } 088 089 return Optional.ofNullable(_siteManager); 090 } 091 092 private List<String> _getContextsWithSite(List<String> contextsWithoutSite, Collection<String> siteNames) 093 { 094 // We return all the populations linked to at least one site 095 List<String> contexts = new ArrayList<>(); 096 097 for (String context : contextsWithoutSite) 098 { 099 for (String siteName : siteNames) 100 { 101 String siteContext = context + "/" + siteName; 102 contexts.add(siteContext); 103 } 104 } 105 106 return contexts; 107 } 108 109 @Override 110 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 111 { 112 Request request = ObjectModelHelper.getRequest(objectModel); 113 if ("true".equals(request.getHeader("X-Ametys-FO"))) 114 { 115 // The request seems to come from an FO, verify the IP address 116 String conf = Config.getInstance().getValue("org.ametys.web.front.ip"); 117 Collection<String> ips = StringUtils.stringToCollection(conf); 118 119 // The real client IP may have been put in the non-standard "X-Forwarded-For" request header, in case of reverse proxy 120 String xff = request.getHeader("X-Forwarded-For"); 121 Collection<String> remoteIps = StringUtils.stringToCollection(xff); 122 remoteIps.add(request.getRemoteAddr()); 123 124 if (!ips.isEmpty() && !CollectionUtils.containsAny(ips, remoteIps)) 125 { 126 throw new AccessDeniedException("IP '" + org.apache.commons.lang.StringUtils.join(remoteIps, ", ") + "' is not an authorized front-office IP (" + conf + ")"); 127 } 128 129 String login = request.getHeader("X-Ametys-FO-Login"); 130 String populationId = request.getHeader("X-Ametys-FO-Population"); 131 if (org.apache.commons.lang3.StringUtils.isNoneBlank(login, populationId)) 132 { 133 UserIdentity frontUserIdentity = new UserIdentity(login, populationId); 134 request.setAttribute(REQUEST_ATTRIBUTE_FRONTOFFICE_USERIDENTITY, frontUserIdentity); 135 } 136 137 String credentialProvicerId = request.getHeader("X-Ametys-FO-Credential-Provider"); 138 if (org.apache.commons.lang3.StringUtils.isNotBlank(credentialProvicerId)) 139 { 140 request.setAttribute(REQUEST_ATTRIBUTE_FRONTOFFICE_CREDENTIALPROVIDER_ID, credentialProvicerId); 141 } 142 143 request.setAttribute(REQUEST_ATTRIBUTE_AUTHENTICATED, "true"); 144 request.setAttribute(REQUEST_ATTRIBUTE_FRONTOFFICE_REQUEST, "true"); 145 return EMPTY_MAP; 146 } 147 148 request.setAttribute(REQUEST_ATTRIBUTE_FRONTOFFICE_REQUEST, "false"); // A request using the dispatch generator can add anything in the request attributes 149 return super.act(redirector, resolver, objectModel, source, parameters); 150 } 151 152 @Override 153 protected CredentialProvider _getCredentialProviderFromSession(Request request) 154 { 155 return getCredentialProviderFromSession(request); 156 } 157 158 /** 159 * Get the credential provider used for the current connection 160 * @param request The request 161 * @return The credential provider used or null 162 */ 163 public static CredentialProvider getCredentialProviderFromSession(Request request) 164 { 165 String credentialProviderId = (String) request.getAttribute(REQUEST_ATTRIBUTE_FRONTOFFICE_CREDENTIALPROVIDER_ID); 166 if (org.apache.commons.lang3.StringUtils.isNotBlank(credentialProviderId)) 167 { 168 UserIdentity userIdentity = (UserIdentity) request.getAttribute(REQUEST_ATTRIBUTE_FRONTOFFICE_USERIDENTITY); 169 String populationId = userIdentity.getPopulationId(); 170 return _staticUserPopulationDAO.getUserPopulation(populationId) 171 .getCredentialProvider(credentialProviderId); 172 } 173 else 174 { 175 return AuthenticateAction.getCredentialProviderFromSession(request); 176 } 177 } 178}