View Javadoc

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