View Javadoc

1   /*
2    * Licensed to the University Corporation for Advanced Internet Development, 
3    * Inc. (UCAID) under one or more contributor license agreements.  See the 
4    * NOTICE file distributed with this work for additional information regarding
5    * copyright ownership. The UCAID licenses this file to You under the Apache 
6    * License, Version 2.0 (the "License"); you may not use this file except in 
7    * compliance with the License.  You may obtain a copy of the License at
8    *
9    *    http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package edu.internet2.middleware.shibboleth.idp.ui;
19  
20  import java.net.URI;
21  import java.net.URISyntaxException;
22  import java.util.List;
23  
24  import javax.servlet.ServletContext;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.jsp.tagext.BodyTagSupport;
27  
28  import org.opensaml.saml2.common.Extensions;
29  import org.opensaml.saml2.metadata.AttributeConsumingService;
30  import org.opensaml.saml2.metadata.EntityDescriptor;
31  import org.opensaml.saml2.metadata.LocalizedString;
32  import org.opensaml.saml2.metadata.RoleDescriptor;
33  import org.opensaml.saml2.metadata.SPSSODescriptor;
34  import org.opensaml.saml2.metadata.ServiceName;
35  import org.opensaml.samlext.saml2mdui.DisplayName;
36  import org.opensaml.samlext.saml2mdui.UIInfo;
37  import org.opensaml.xml.XMLObject;
38  import org.owasp.esapi.ESAPI;
39  import org.owasp.esapi.Encoder;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfigurationManager;
44  import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
45  import edu.internet2.middleware.shibboleth.idp.util.HttpServletHelper;
46  
47  
48  /**
49   * Display the serviceName.
50   * 
51   * This is taken in order
52   *  1) From the mdui
53   *  2) AttributeConsumeService
54   *  3) HostName from the EntityId
55   *  4) EntityId.
56   */
57  public class ServiceTagSupport extends BodyTagSupport{
58  
59      /**
60       * checkstyle requires this serialization info.
61       */
62      private static final long serialVersionUID = 7988646597267865255L;
63      
64      /** Class logger. */
65      private static Logger log = LoggerFactory.getLogger(ServiceTagSupport.class);
66  
67      /** Bean storage. class reference*/
68      private String cssClass;
69      /** Bean storage. id reference*/
70      private String cssId;
71      /** Bean storage. style reference*/
72      private String cssStyle;
73  
74      /** Bean setter.
75       * @param value what to set
76       */
77      public void setCssClass(String value) {
78          cssClass = value;
79      }
80      /** Bean setter.
81       * @param value what to set
82       */
83      public void setCssId(String value) {
84          cssId = value;
85      }
86  
87      /** Bean setter.
88       * @param value what to set
89       */
90      public void setCssStyle(String value) {
91          cssStyle = value;
92      }
93  
94      /**
95       * Add the class and Id if present.
96       * @param sb the stringbuilder to asdd to.
97       */
98      protected void addClassAndId(StringBuilder sb) {
99          if (cssClass != null) {
100             sb.append(" class=\"").append(cssClass).append('"');
101         }
102         if (cssId != null) {
103             sb.append(" id=\"").append(cssId).append('"');
104         }
105         if (cssStyle != null) {
106             sb.append(" style=\"").append(cssStyle).append('"');
107         }
108     }
109     
110     /**
111      * build a hyperlink from the parameters.
112      * @param url the URL
113      * @param text what to embed
114      * @return the hyperlink.
115      */
116     protected String buildHyperLink(String url, String text) {
117         String encodedUrl;
118         Encoder esapiEncoder = ESAPI.encoder();
119        
120         try {
121             URI theUrl = new URI(url);
122             String scheme = theUrl.getScheme();
123 
124             if (!"http".equals(scheme) && !"https".equals(scheme) && !"mailto".equals(scheme)) {
125                 log.warn("The URL " + url + " contained an invalid scheme");
126                 return "";
127             }
128             encodedUrl = esapiEncoder.encodeForHTMLAttribute(url);
129         } catch (URISyntaxException e) {
130             // 
131             // It wasn't an URI.
132             //
133             log.warn("The URL " + url + " was invalid: " + e.toString());
134             return "";
135         }
136         
137         StringBuilder sb = new StringBuilder("<a href=\"");
138         sb.append(encodedUrl).append('"');
139         addClassAndId(sb);
140         sb.append(">").append(text).append("</a>");
141         return sb.toString();
142     }
143     
144     /**
145      * Get the EntityDescriptor for the relying party.
146      * @return the SPs EntityDescriptor
147      */
148     protected EntityDescriptor getSPEntityDescriptor() {
149         LoginContext loginContext;
150         HttpServletRequest request;
151         ServletContext application;
152         RelyingPartyConfigurationManager rpConfigMngr;
153         EntityDescriptor spEntity;
154         
155         //
156         // Populate up those things that jsp gives us.
157         //
158         request = (HttpServletRequest) pageContext.getRequest();
159         application = pageContext.getServletContext();
160         
161         if (request == null || application == null) {
162            return null;
163         }
164         //
165         // grab the login context and the RP config mgr.
166         //
167         loginContext = HttpServletHelper.getLoginContext(HttpServletHelper.getStorageService(application),
168                 application, request);
169         rpConfigMngr = HttpServletHelper.getRelyingPartyConfigurationManager(application);
170         if (loginContext == null || rpConfigMngr == null) {
171             return null;
172         }
173         spEntity = HttpServletHelper.getRelyingPartyMetadata(loginContext.getRelyingPartyId(), rpConfigMngr);
174 
175         return spEntity;
176     }
177     /**
178      * Traverse the SP's EntityDescriptor and pick out the UIInfo.
179      * @return the first UIInfo for the SP.
180      */
181     protected UIInfo getSPUIInfo() {
182         EntityDescriptor spEntity = getSPEntityDescriptor();
183         Extensions exts;
184         
185         if (null == spEntity) {
186             //
187             // all done
188             //
189             return null;
190         }
191 
192         for (RoleDescriptor role:spEntity.getRoleDescriptors(SPSSODescriptor.DEFAULT_ELEMENT_NAME)) {
193             exts = role.getExtensions();
194             if (exts != null) {
195                 for (XMLObject object:exts.getOrderedChildren()) {
196                     if (object instanceof UIInfo) {
197                         return (UIInfo) object;
198                     }
199                 }
200             }
201         }
202         return null;
203     }
204             
205     /**
206      * Pluck the language from the browser.
207      * @return the two letter language
208      */
209     protected String getBrowserLanguage() {
210         HttpServletRequest request;
211         request = (HttpServletRequest) pageContext.getRequest();
212         
213         return request.getLocale().getLanguage();
214     }
215     /**
216      * If the entityId can look like a host return that otherwise the string.
217      * @return either the host or the entityId.
218      */
219     private String getNameFromEntityId() {
220         EntityDescriptor sp = getSPEntityDescriptor();
221         
222         if (null == sp) {
223             log.debug("No relying party, nothing to display");
224             return null;
225         }
226 
227         try {
228             URI entityId = new URI(sp.getEntityID());
229             String scheme = entityId.getScheme();
230 
231             if ("http".equals(scheme) || "https".equals(scheme)) {
232                 return entityId.getHost(); 
233             }
234         } catch (URISyntaxException e) {
235             // 
236             // It wasn't an URI.  return full entityId.
237             //
238             return sp.getEntityID();
239         }
240         //
241         // not a URL return full entityID
242         //
243         return sp.getEntityID();
244     }
245     
246     /** 
247      * look at &lt;Uiinfo&gt; if there and if so look for appropriate name.
248      * @return null or an appropriate name
249      */
250     private String getNameFromUIInfo() {
251         String lang = getBrowserLanguage();
252 
253         if (getSPUIInfo() != null) {
254             for (DisplayName name:getSPUIInfo().getDisplayNames()) {
255                 if (log.isDebugEnabled()){
256                     log.debug("Found name in UIInfo, language=" + name.getXMLLang());
257                 }
258                 if (name.getXMLLang().equals(lang)) {
259                     //
260                     // Found it
261                     //
262                     if (log.isDebugEnabled()){
263                         log.debug("returning name from UIInfo " + name.getName().getLocalString());
264                     }
265                     return name.getName().getLocalString();
266                 }
267             }
268             if (log.isDebugEnabled()){
269                 log.debug("No name in UIInfo");
270             }            
271         }
272         return null;
273     }
274 
275     /**
276      * look for an &ltAttributeConsumeService&gt and if its there look for an appropriate name.
277      * @return null or an appropriate name
278      */
279     private String getNameFromAttributeConsumingService(){
280         String lang = getBrowserLanguage();
281         List<RoleDescriptor> roles;
282         AttributeConsumingService acs = null;
283         EntityDescriptor sp = getSPEntityDescriptor();
284         
285         if (null == sp) {
286             log.warn("No relying party, nothing to display");
287             return null;
288         }
289 
290         roles = sp.getRoleDescriptors(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
291         if (!roles.isEmpty()) {
292             SPSSODescriptor spssod = (SPSSODescriptor) roles.get(0);
293             acs = spssod.getDefaultAttributeConsumingService();
294         }
295         if (acs != null) {
296             for (ServiceName name:acs.getNames()) {
297                 LocalizedString localName = name.getName();
298                 if (log.isDebugEnabled()){
299                     log.debug("Found name in AttributeConsumingService, language=" + localName.getLanguage());
300                 }
301                 if (localName.getLanguage().equals(lang)) {
302                     if (log.isDebugEnabled()){
303                         log.debug("returning name from AttributeConsumingService " + name.getName().getLocalString());
304                     }
305                     return localName.getLocalString();
306                 }
307             }
308             if (log.isDebugEnabled()){
309                 log.debug("No name in AttributeConsumingService");
310             }            
311         }        
312         return null;
313     }
314     
315     /**
316      * Get the identifier for the service name as per the rules above.
317      * @return something sensible for display.
318      */
319     protected String getServiceName() {
320         String result;
321         //
322         // First look for MDUI
323         //
324         if (getSPEntityDescriptor() == null) {
325             log.debug("No relying party, nothing to display");
326             return null;
327         }
328         //
329         // Look at <UIInfo>
330         //
331         result = getNameFromUIInfo();
332         if (result != null) {
333             return result;
334         }
335         
336         //
337         // Otherwise <AttributeConsumingService>
338         //
339         result = getNameFromAttributeConsumingService();
340         if (result != null) {
341             return result;
342         }
343         
344         //
345         // Or look at the entityName
346         //
347         return getNameFromEntityId();
348     }
349     
350 
351 }