001/*
002 *  Copyright 2017 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.proxiedcontent;
017
018import java.util.HashMap;
019import java.util.Map;
020
021import org.apache.avalon.framework.parameters.Parameters;
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024import org.apache.cocoon.acting.ServiceableAction;
025import org.apache.cocoon.environment.ObjectModelHelper;
026import org.apache.cocoon.environment.Redirector;
027import org.apache.cocoon.environment.Request;
028import org.apache.cocoon.environment.Session;
029import org.apache.cocoon.environment.SourceResolver;
030import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
031import org.jasig.cas.client.util.AbstractCasFilter;
032import org.jasig.cas.client.validation.Assertion;
033
034import org.ametys.core.authentication.CredentialProvider;
035import org.ametys.core.user.population.UserPopulation;
036import org.ametys.core.user.population.UserPopulationDAO;
037import org.ametys.core.util.SessionAttributeProvider;
038import org.ametys.core.util.URIUtils;
039import org.ametys.plugins.extrausermgt.authentication.cas.AmetysCas20ProxyReceivingTicketValidationFilter;
040import org.ametys.plugins.extrausermgt.authentication.cas.CASCredentialProvider;
041import org.ametys.web.repository.page.ZoneItem;
042
043/**
044 * Get the page url to integrate
045 */
046public class GetUrlAction extends ServiceableAction
047{
048    /** The DAO for user populations */
049    protected UserPopulationDAO _userPopulationDAO;
050    /** The session attribute provider */
051    protected SessionAttributeProvider _sessionAttributeProvider;
052
053    @Override
054    public void service(ServiceManager smanager) throws ServiceException
055    {
056        super.service(manager);
057        _sessionAttributeProvider = (SessionAttributeProvider) smanager.lookup(SessionAttributeProvider.ROLE);
058        _userPopulationDAO = (UserPopulationDAO) smanager.lookup(UserPopulationDAO.ROLE);
059    }
060    
061    @Override
062    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
063    {
064        Map<String, String> result = new HashMap<>();
065        
066        Request request = ObjectModelHelper.getRequest(objectModel);
067        
068        String url = request.getParameter("url");
069        String server = request.getParameter("server");
070        String isForm = request.getParameter("isForm");
071        String baseUrl = Utils.normalizeUrl(source);
072        String baseHost = Utils.getRemoteHostFromUrl(baseUrl);
073        String remoteHost;
074        String completeUrl = "";
075        
076        if (url == null)
077        {
078            url = source;
079        }
080        if (server == null)
081        {
082            server = source;
083        }
084        
085        url = Utils.normalizeUrl(url);
086        server = Utils.normalizeUrl(server);
087        
088        remoteHost = Utils.getRemoteHostFromUrl(url);
089        
090        if (baseHost.equals(remoteHost))
091        {
092            if (isForm != null)
093            {
094                result.put("queryString", "?" + request.getQueryString());
095            }
096            
097            completeUrl = url.substring(0, url.lastIndexOf("/") + 1);
098            
099            result.put("url", url);
100            result.put("server", server);
101            result.put("remote-server", remoteHost);
102            result.put("complete-url", completeUrl);
103            ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName());
104            result.put("zoneitemid", zoneItem.getId());
105            
106            if (zoneItem.getServiceParameters().getValueOrDefault("cas", false))
107            {
108                _addCasProxyTicketInUrl(request, url, result);
109            }
110            
111            return result;
112        }
113        else
114        {
115            getLogger().error("The specified page '" + url + "' is not on the same host than the base page, and therefore could not be proxified.");
116            return null;
117        }
118    }
119    
120    private void _addCasProxyTicketInUrl(Request request, String url, Map<String, String> result)
121    {
122        String proxyTicket = null;
123        
124        // Case BO: try to get a proxy ticket from the assertion in the current session
125        Session session = request.getSession(false);
126        if (session != null)
127        {
128            Assertion assertion = (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
129            if (assertion != null)
130            {
131                proxyTicket = assertion.getPrincipal().getProxyTicketFor(url);
132            }
133        }
134        
135        // Case FO request
136        if ("true".equals(request.getHeader("X-Ametys-FO")))
137        {
138            proxyTicket = _sessionAttributeProvider.getSessionAttribute(AmetysCas20ProxyReceivingTicketValidationFilter.SESSION_ATTRIBUTE_PROXY_GRANTING_TICKET)
139                .filter(String.class::isInstance)
140                .map(String.class::cast)
141                .map(proxyGrantingTicket -> {
142                    String populationId = request.getHeader("X-Ametys-FO-Population");
143                    UserPopulation population = _userPopulationDAO.getUserPopulation(populationId);
144                    
145                    if (population != null)
146                    {
147                        String cpId = request.getHeader("X-Ametys-FO-Credential-Provider");
148                        CredentialProvider credentialProvider = population.getCredentialProvider(cpId);
149                        if (credentialProvider instanceof CASCredentialProvider)
150                        {
151                            String casUrl = (String) credentialProvider.getParameterValues().get(CASCredentialProvider.PARAM_SERVER_URL);
152                            return new Cas20ProxyRetriever(casUrl, "UTF-8", null).getProxyTicketIdFor(proxyGrantingTicket, url);
153                        }
154                    }
155                    return null;
156                })
157                .orElse(null);
158        }
159        
160        // If proxy ticket was found, change url and append the ticket
161        if (proxyTicket != null)
162        {
163            StringBuilder urlWithTicket = new StringBuilder(url);
164            urlWithTicket.append(!url.contains("?") ? "?" : "&")
165                .append("ticket=")
166                .append(URIUtils.encodeParameter(proxyTicket));
167            result.put("url", urlWithTicket.toString());
168        }
169        else
170        {
171            getLogger().warn(String.format("The application was unable to retrieve a proxy ticket from CAS for target service '%s'", url));
172        }
173    }
174}