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.plugins.core.authentication; 017 018import java.io.IOException; 019import java.util.Arrays; 020import java.util.List; 021import java.util.Optional; 022import java.util.stream.Collectors; 023 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.cocoon.ProcessingException; 027import org.apache.cocoon.environment.ObjectModelHelper; 028import org.apache.cocoon.environment.Request; 029import org.apache.cocoon.generation.ServiceableGenerator; 030import org.apache.cocoon.xml.AttributesImpl; 031import org.apache.cocoon.xml.XMLUtils; 032import org.apache.commons.lang3.StringUtils; 033import org.xml.sax.SAXException; 034 035import org.ametys.core.authentication.BlockingCredentialProvider; 036import org.ametys.core.authentication.CredentialProvider; 037import org.ametys.core.authentication.CredentialProviderFactory; 038import org.ametys.core.authentication.CredentialProviderModel; 039import org.ametys.core.user.population.UserPopulation; 040import org.ametys.core.user.population.UserPopulationDAO; 041import org.ametys.core.util.I18nUtils; 042import org.ametys.core.util.JSONUtils; 043import org.ametys.plugins.core.impl.authentication.FormCredentialProvider; 044import org.ametys.runtime.config.Config; 045 046/** 047 * SAX configuration of the login screen 048 */ 049public class LoginScreenGenerator extends ServiceableGenerator 050{ 051 /** Name of the input parameters html field */ 052 protected CredentialProviderFactory _credentialProviderFactory; 053 054 /** The DAO for user populations */ 055 protected UserPopulationDAO _userPopulationDAO; 056 057 /** The JSON helper */ 058 protected JSONUtils _jsonUtils; 059 060 @Override 061 public void service(ServiceManager smanager) throws ServiceException 062 { 063 super.service(smanager); 064 _credentialProviderFactory = (CredentialProviderFactory) smanager.lookup(CredentialProviderFactory.ROLE); 065 _userPopulationDAO = (UserPopulationDAO) smanager.lookup(UserPopulationDAO.ROLE); 066 _jsonUtils = (JSONUtils) smanager.lookup(JSONUtils.ROLE); 067 } 068 069 @Override 070 public void generate() throws IOException, SAXException, ProcessingException 071 { 072 Request request = ObjectModelHelper.getRequest(objectModel); 073 074 contentHandler.startDocument(); 075 XMLUtils.startElement(contentHandler, "LoginScreen"); 076 077 // LoginScreenGenerator can be used from the frontoffice AuthenticatAction : it can not use the requests attributes 078 boolean isAmetysPublic = Config.getInstance() != null ? Config.getInstance().getValue("runtime.ametys.public") : false/* in safe mode, we only have one population */; 079 080 boolean invalidPopulationIds = "true".equals(request.getParameter("invalidPopulationIds")); 081 082 boolean shouldDisplayUserPopulationsList = "true".equals(request.getParameter("shouldDisplayUserPopulationsList")); 083 084 List<UserPopulation> usersPopulations = null; 085 String usersPopulationsIdsAsString = request.getParameter("usersPopulations"); 086 if (usersPopulationsIdsAsString != null) 087 { 088 usersPopulations = Arrays.stream(usersPopulationsIdsAsString.split(",")).map(_userPopulationDAO::getUserPopulation).collect(Collectors.toList()); 089 } 090 091 String chosenPopulationId = request.getParameter("chosenPopulationId"); 092 093 if (shouldDisplayUserPopulationsList) 094 { 095 _generatePopulations(usersPopulations, isAmetysPublic, invalidPopulationIds, chosenPopulationId); 096 } 097 098 boolean availableCredentialProviders = "true".equals(request.getParameter("availableCredentialProviders")); 099 List<CredentialProvider> credentialProviders = null; 100 if (availableCredentialProviders && usersPopulations != null && !usersPopulations.isEmpty()) 101 { 102 if (StringUtils.isNotBlank(chosenPopulationId)) 103 { 104 credentialProviders = usersPopulations.stream().filter(userPop -> chosenPopulationId.equals(userPop.getId())).findAny().get().getCredentialProviders(); 105 } 106 else 107 { 108 credentialProviders = usersPopulations.get(0).getCredentialProviders(); 109 } 110 } 111 int credentialProviderIndex = Integer.parseInt(request.getParameter("credentialProviderIndex")); 112 113 _generateCredentialProviders(credentialProviders, credentialProviderIndex); 114 115 _generateLoginForm(request, credentialProviders, credentialProviderIndex, invalidPopulationIds, chosenPopulationId != null || usersPopulations == null ? chosenPopulationId : usersPopulations.get(0).getId()); 116 117 String contextsAsString = request.getParameter("contexts"); 118 XMLUtils.createElement(contentHandler, "contexts", contextsAsString); 119 120 XMLUtils.endElement(contentHandler, "LoginScreen"); 121 contentHandler.endDocument(); 122 } 123 124 private void _generatePopulations(List<UserPopulation> usersPopulations, boolean isAmetysPublic, boolean tryedAnInvalidPopulationId, String chosenPopulationId) throws SAXException 125 { 126 if (usersPopulations != null) 127 { 128 AttributesImpl attrs = new AttributesImpl(); 129 attrs.addCDATAAttribute("invalid", tryedAnInvalidPopulationId ? "true" : "false"); 130 attrs.addCDATAAttribute("public", isAmetysPublic ? "true" : "false"); 131 attrs.addCDATAAttribute("size", Integer.toString(usersPopulations.size())); 132 if (chosenPopulationId != null) 133 { 134 attrs.addCDATAAttribute("currentValue", chosenPopulationId); 135 } 136 XMLUtils.startElement(contentHandler, "UserPopulations", attrs); 137 138 if (!isAmetysPublic) 139 { 140 for (UserPopulation up : usersPopulations) 141 { 142 AttributesImpl attrs2 = new AttributesImpl(); 143 attrs2.addCDATAAttribute("id", up.getId()); 144 XMLUtils.startElement(contentHandler, "UserPopulation", attrs2); 145 up.getLabel().toSAX(contentHandler, "label"); 146 XMLUtils.endElement(contentHandler, "UserPopulation"); 147 } 148 } 149 150 XMLUtils.endElement(contentHandler, "UserPopulations"); 151 } 152 } 153 154 private void _generateCredentialProviders(List<CredentialProvider> credentialProviders, int currentCredentialProvider) throws SAXException 155 { 156 if (credentialProviders == null) 157 { 158 return; 159 } 160 161 XMLUtils.startElement(contentHandler, "CredentialProviders"); 162 for (int index = 0; index < credentialProviders.size(); index++) 163 { 164 CredentialProvider cp = credentialProviders.get(index); 165 if (cp instanceof BlockingCredentialProvider) 166 { 167 CredentialProviderModel cpModel = _credentialProviderFactory.getExtension(cp.getCredentialProviderModelId()); 168 169 AttributesImpl attrs = new AttributesImpl(); 170 attrs.addCDATAAttribute("index", String.valueOf(index)); 171 attrs.addCDATAAttribute("selected", index == currentCredentialProvider ? "true" : "false"); 172 attrs.addCDATAAttribute("isForm", cp instanceof FormCredentialProvider ? "true" : "false"); 173 attrs.addCDATAAttribute("isNewWindowRequired", cp instanceof BlockingCredentialProvider && ((BlockingCredentialProvider) cp).requiresNewWindow() ? "true" : "false"); 174 XMLUtils.startElement(contentHandler, "CredentialProvider", attrs); 175 XMLUtils.createElement(contentHandler, "label", I18nUtils.getInstance().translate(cpModel.getConnectionLabel())); 176 if (StringUtils.isNotBlank(cp.getLabel())) 177 { 178 XMLUtils.createElement(contentHandler, "additionalLabel", cp.getLabel()); 179 } 180 if (StringUtils.isNotEmpty(cpModel.getIconGlyph())) 181 { 182 XMLUtils.createElement(contentHandler, "iconGlyph", cpModel.getIconGlyph()); 183 XMLUtils.createElement(contentHandler, "iconDecorator", cpModel.getIconDecorator()); 184 } 185 else if (StringUtils.isNotEmpty(cpModel.getIconMedium())) 186 { 187 XMLUtils.createElement(contentHandler, "iconMedium", cpModel.getIconMedium()); 188 } 189 XMLUtils.createElement(contentHandler, "color", cpModel.getColor()); 190 XMLUtils.endElement(contentHandler, "CredentialProvider"); 191 } 192 } 193 XMLUtils.endElement(contentHandler, "CredentialProviders"); 194 } 195 196 private void _generateLoginForm(Request request, List<CredentialProvider> credentialProviders, int currentCredentialProvider, boolean invalidPopulationIds, String chosenPopulationId) throws SAXException 197 { 198 if (credentialProviders == null) 199 { 200 return; 201 } 202 203 FormCredentialProvider formBasedCP = null; 204 if (currentCredentialProvider != -1 && credentialProviders.get(currentCredentialProvider) instanceof FormCredentialProvider) 205 { 206 formBasedCP = (FormCredentialProvider) credentialProviders.get(currentCredentialProvider); 207 } 208 else 209 { 210 Optional<CredentialProvider> foundAnyFormCredentialProvider = credentialProviders.stream().filter(cp -> cp instanceof FormCredentialProvider).findAny(); 211 if (foundAnyFormCredentialProvider.isPresent()) 212 { 213 formBasedCP = (FormCredentialProvider) foundAnyFormCredentialProvider.get(); 214 } 215 else 216 { 217 // We found no form based 218 return; 219 } 220 } 221 222 boolean useCaptcha = (Boolean) formBasedCP.getParameterValues().get("runtime.authentication.form.captcha"); 223 224 boolean autoComplete = true; // FIXME not well implemented 225 boolean rememberMe = (Boolean) formBasedCP.getParameterValues().get("runtime.authentication.form.cookies"); 226 boolean allowLoginByEmail = (Boolean) formBasedCP.getParameterValues().get("runtime.authentication.form.login-by-email"); 227 228 boolean captcha = false; 229 230 if (useCaptcha) 231 { 232 String login = request.getParameter("login"); 233 if (StringUtils.isNotBlank(login) && StringUtils.isNotBlank(chosenPopulationId)) 234 { 235 int nbConnect = formBasedCP.requestNbConnectBDD(login, chosenPopulationId); 236 captcha = nbConnect >= FormCredentialProvider.NB_CONNECTION_ATTEMPTS; 237 } 238 } 239 240 boolean showErrors = !invalidPopulationIds; 241 242 AttributesImpl attrs = new AttributesImpl(); 243 XMLUtils.startElement(contentHandler, "LoginForm", attrs); 244 245 if (StringUtils.isNotBlank(formBasedCP.getLabel())) 246 { 247 XMLUtils.createElement(contentHandler, "additionalLabel", formBasedCP.getLabel()); 248 } 249 XMLUtils.createElement(contentHandler, "autocomplete", String.valueOf(autoComplete)); 250 XMLUtils.createElement(contentHandler, "rememberMe", String.valueOf(rememberMe)); 251 XMLUtils.createElement(contentHandler, "useCaptcha", String.valueOf(captcha)); 252 XMLUtils.createElement(contentHandler, "autocomplete", String.valueOf(autoComplete)); 253 XMLUtils.createElement(contentHandler, "allowLoginByEmail", String.valueOf(allowLoginByEmail)); 254 XMLUtils.createElement(contentHandler, "showErrors", String.valueOf(showErrors)); 255 256 XMLUtils.endElement(contentHandler, "LoginForm"); 257 } 258}