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}