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.site; 017 018import java.io.ByteArrayInputStream; 019import java.io.ByteArrayOutputStream; 020import java.nio.charset.StandardCharsets; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.Enumeration; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028import java.util.regex.Pattern; 029 030import org.apache.avalon.framework.configuration.Configuration; 031import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 032import org.apache.avalon.framework.parameters.Parameters; 033import org.apache.cocoon.environment.ObjectModelHelper; 034import org.apache.cocoon.environment.Redirector; 035import org.apache.cocoon.environment.Request; 036import org.apache.cocoon.environment.Session; 037import org.apache.cocoon.environment.http.HttpCookie; 038import org.apache.commons.lang3.StringUtils; 039import org.apache.http.NameValuePair; 040import org.apache.http.client.entity.UrlEncodedFormEntity; 041import org.apache.http.client.methods.CloseableHttpResponse; 042import org.apache.http.client.methods.HttpGet; 043import org.apache.http.client.methods.HttpPost; 044import org.apache.http.impl.client.CloseableHttpClient; 045import org.apache.http.message.BasicNameValuePair; 046 047import org.ametys.core.authentication.AuthenticateAction; 048import org.ametys.core.authentication.CredentialProvider; 049import org.ametys.core.user.UserIdentity; 050import org.ametys.plugins.core.user.UserDAO; 051import org.ametys.plugins.site.Site; 052import org.ametys.runtime.config.Config; 053import org.ametys.runtime.workspace.WorkspaceMatcher; 054 055/** 056 * The authenticate action for front side 057 */ 058public class FrontAuthenticateAction extends AuthenticateAction 059{ 060 /** url requires for authentication */ 061 protected Collection<Pattern> _acceptedSiteUrlPatterns = Arrays.asList(new Pattern[]{Pattern.compile("^plugins/site/authenticate/[0-9]+$")}); 062 063 @Override 064 protected boolean _acceptedUrl(Request request) 065 { 066 // URL without server context and leading slash. 067 String url = (String) request.getAttribute(WorkspaceMatcher.IN_WORKSPACE_URL); 068 for (Pattern pattern : _acceptedSiteUrlPatterns) 069 { 070 if (pattern.matcher(url).matches()) 071 { 072 // Anonymous request 073 request.setAttribute(REQUEST_ATTRIBUTE_GRANTED, true); 074 075 return true; 076 } 077 } 078 079 return false; 080 } 081 082 @Override 083 protected void _setUserIdentityInSession(Request request, UserIdentity userIdentity, CredentialProvider credentialProvider, boolean blockingMode) 084 { 085 setUserIdentityInSession(request, userIdentity, credentialProvider, blockingMode); 086 } 087 088 /** 089 * Save user identity in request 090 * @param request The request 091 * @param userIdentity The useridentity to save 092 * @param credentialProvider The credential provider used to connect 093 * @param blockingMode The mode used for the credential provider 094 */ 095 public static void setUserIdentityInSession(Request request, UserIdentity userIdentity, CredentialProvider credentialProvider, boolean blockingMode) 096 { 097 Site site = (Site) request.getAttribute("site"); 098 String siteName = site.getName(); 099 100 Session session = request.getSession(true); 101 _resetConnectingStateToSession(request); 102 session.setAttribute(SESSION_USERIDENTITY + "-" + siteName, userIdentity); 103 session.setAttribute(SESSION_CREDENTIALPROVIDER + "-" + siteName, credentialProvider); 104 session.setAttribute(SESSION_CREDENTIALPROVIDER_MODE + "-" + siteName, blockingMode); 105 } 106 107 @Override 108 protected UserIdentity _getUserIdentityFromSession(Request request) 109 { 110 UserIdentity userIdentityFromSession = getUserIdentityFromSession(request); 111 if (userIdentityFromSession != null) 112 { 113 return userIdentityFromSession; 114 } 115 116 Session session = request.getSession(false); 117 if (session != null) 118 { 119 Set<String> availableUserPopulationsIds = _getAvailableUserPopulationsIds(request, _getContexts(request, null)); 120 121 // check if connected in the site application, not only on this site 122 userIdentityFromSession = super._getUserIdentityFromSession(request); 123 if (userIdentityFromSession != null && availableUserPopulationsIds.contains(userIdentityFromSession.getPopulationId())) 124 { 125 _setUserIdentityInSession(request, userIdentityFromSession, new UserDAO.ImpersonateCredentialProvider(), true); 126 return userIdentityFromSession; 127 } 128 129 Enumeration<String> attributeNames = session.getAttributeNames(); 130 while (attributeNames.hasMoreElements()) 131 { 132 String attributeName = attributeNames.nextElement(); 133 if (attributeName.startsWith(SESSION_USERIDENTITY + "-")) 134 { 135 UserIdentity userIdentity = (UserIdentity) session.getAttribute(attributeName); 136 137 if (availableUserPopulationsIds.contains(userIdentity.getPopulationId())) 138 { 139 _setUserIdentityInSession(request, userIdentity, new UserDAO.ImpersonateCredentialProvider(), true); 140 return userIdentity; 141 } 142 } 143 } 144 } 145 146 return null; 147 } 148 149 /** 150 * Get the user identity of the connected user from the session 151 * @param request The request 152 * @return The connected useridentity or null 153 */ 154 public static UserIdentity getUserIdentityFromSession(Request request) 155 { 156 Site site = (Site) request.getAttribute("site"); 157 String siteName = site.getName(); 158 159 return getUserIdentityFromSession(request, siteName); 160 } 161 162 /** 163 * Get the user identity of the connected user from the session 164 * @param request The request 165 * @param siteName The current site name 166 * @return The connected useridentity or null 167 */ 168 public static UserIdentity getUserIdentityFromSession(Request request, String siteName) 169 { 170 Session session = request.getSession(false); 171 if (session != null) 172 { 173 return (UserIdentity) session.getAttribute(SESSION_USERIDENTITY + "-" + siteName); 174 } 175 return null; 176 } 177 178 @Override 179 protected CredentialProvider _getCredentialProviderFromSession(Request request) 180 { 181 return getCredentialProviderFromSession(request); 182 } 183 184 /** 185 * Get the credential provider used for the current connection 186 * @param request The request 187 * @return The credential provider used or null 188 */ 189 public static CredentialProvider getCredentialProviderFromSession(Request request) 190 { 191 Site site = (Site) request.getAttribute("site"); 192 193 if (site == null) 194 { 195 return null; 196 } 197 198 String siteName = site.getName(); 199 200 return getCredentialProviderFromSession(request, siteName); 201 } 202 203 /** 204 * Get the credential provider used for the current connection 205 * @param request The request 206 * @param siteName The current site name 207 * @return The credential provider used or null 208 */ 209 public static CredentialProvider getCredentialProviderFromSession(Request request, String siteName) 210 { 211 Session session = request.getSession(false); 212 if (session != null) 213 { 214 return (CredentialProvider) session.getAttribute(SESSION_CREDENTIALPROVIDER + "-" + siteName); 215 } 216 return null; 217 } 218 219 @Override 220 protected Boolean _getCredentialProviderModeFromSession(Request request) 221 { 222 return getCredentialProviderModeFromSession(request); 223 } 224 225 /** 226 * Get the credential provider mode used for the current connection 227 * @param request The request 228 * @return The credential provider mode used or null 229 */ 230 public static Boolean getCredentialProviderModeFromSession(Request request) 231 { 232 Site site = (Site) request.getAttribute("site"); 233 String siteName = site.getName(); 234 235 return getCredentialProviderModeFromSession(request, siteName); 236 } 237 238 /** 239 * Get the credential provider mode used for the current connection 240 * @param request The request 241 * @param siteName The current site name 242 * @return The credential provider mode used or null 243 */ 244 public static Boolean getCredentialProviderModeFromSession(Request request, String siteName) 245 { 246 Session session = request.getSession(false); 247 if (session != null) 248 { 249 return (Boolean) session.getAttribute(SESSION_CREDENTIALPROVIDER_MODE + "-" + siteName); 250 } 251 return null; 252 } 253 254 @Override 255 protected List<String> _getContexts(Request request, Parameters parameters) 256 { 257 Site site = (Site) request.getAttribute("site"); 258 String siteName = site.getName(); 259 return Arrays.asList("/sites/" + siteName, "/sites-fo/" + siteName); 260 } 261 262 @Override 263 protected String getLoginURL(Request request) 264 { 265 Site site = (Site) request.getAttribute("site"); 266 String siteName = site.getName(); 267 268 return getLoginURLParameters(request, "cocoon://_generate/plugins/web/frontoffice-formbasedauthentication/login/login/" + siteName); 269 } 270 271 @Override 272 protected String getLogoutURL(Request request) 273 { 274 Site site = (Site) request.getAttribute("site"); 275 String siteName = site.getName(); 276 return "cocoon://_generate/plugins/web/frontoffice-formbasedauthentication/login/logout/" + siteName; 277 } 278 279 @Override 280 protected boolean _handleLogout(Redirector redirector, Map objectModel, String source, Parameters parameters) throws Exception 281 { 282 boolean logout = super._handleLogout(redirector, objectModel, source, parameters); 283 if (logout) 284 { 285 // Additionally we need to destroy potential server side session 286 Request request = ObjectModelHelper.getRequest(objectModel); 287 HttpCookie sessionId = (HttpCookie) request.getCookieMap().get(GeneratePageAction.__BACKOFFICE_JSESSION_ID); 288 if (sessionId != null) 289 { 290 try (CloseableHttpClient httpClient = BackOfficeRequestHelper.getHttpClient()) 291 { 292 String cmsURL = Config.getInstance().getValue("org.ametys.site.bo"); 293 HttpGet httpGet = new HttpGet(cmsURL + "/logout.html"); 294 httpGet.addHeader("Cookie", "JSESSIONID=" + sessionId.getValue()); 295 httpClient.execute(httpGet); 296 } 297 } 298 } 299 return logout; 300 } 301 302 @Override 303 protected UserIdentity _validateToken(String token) 304 { 305 // Get site names and URLs from the CMS 306 String cmsURL = Config.getInstance().getValue("org.ametys.site.bo"); 307 308 try (CloseableHttpClient httpClient = BackOfficeRequestHelper.getHttpClient()) 309 { 310 HttpPost httpPost = new HttpPost(cmsURL + "/_validate_token.xml"); 311 httpPost.addHeader("X-Ametys-FO", "true"); 312 313 List<NameValuePair> nvps = new ArrayList<>(); 314 nvps.add(new BasicNameValuePair("token", token)); 315 httpPost.setEntity(new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8)); 316 317 try (CloseableHttpResponse response = httpClient.execute(httpPost); ByteArrayOutputStream os = new ByteArrayOutputStream()) 318 { 319 switch (response.getStatusLine().getStatusCode()) 320 { 321 case 200: 322 break; 323 324 case 403: 325 throw new IllegalStateException("The CMS back-office refused the connection"); 326 327 case 500: 328 default: 329 throw new IllegalStateException("The CMS back-office returned an error"); 330 } 331 332 response.getEntity().writeTo(os); 333 try (ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray())) 334 { 335 Configuration conf = new DefaultConfigurationBuilder().build(is); 336 337 String login = conf.getChild("login").getValue(null); 338 String populationId = conf.getChild("populationId").getValue(null); 339 340 if (StringUtils.isNoneBlank(login, populationId)) 341 { 342 return new UserIdentity(login, populationId); 343 } 344 else 345 { 346 return null; 347 } 348 } 349 } 350 } 351 catch (Exception e) 352 { 353 throw new RuntimeException("Unable to synchronize site data", e); 354 } 355 } 356}