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.common.relyingparty.provider;
19  
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.concurrent.locks.Lock;
24  
25  import org.opensaml.saml2.metadata.EntitiesDescriptor;
26  import org.opensaml.saml2.metadata.EntityDescriptor;
27  import org.opensaml.saml2.metadata.provider.MetadataProvider;
28  import org.opensaml.saml2.metadata.provider.MetadataProviderException;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  import org.springframework.context.ApplicationContext;
32  
33  import edu.internet2.middleware.shibboleth.common.config.BaseReloadableService;
34  import edu.internet2.middleware.shibboleth.common.config.relyingparty.RelyingPartyGroup;
35  import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
36  import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfigurationManager;
37  import edu.internet2.middleware.shibboleth.common.service.ServiceException;
38  
39  /**
40   * A relying party manager that uses SAML metadata to lookup information about requested entities. Relying party
41   * configuration information is looked up as follows:
42   * 
43   * If the given entity ID is null, empty, or contains only whitespace the anonymous relying party configuration is
44   * returned. Otherwise, the given relying party entity ID is looked for in the list of registered
45   * {@link RelyingPartyConfiguration}s and if found is returned. If no configuration is registered for the specific
46   * entity ID the entity descriptor for the relying party is located using the {@link MetadataProvider}. The name of
47   * ancestral entities descriptors are then looked up, in ascending order (i.e. the parent entities descriptor, then the
48   * grandparent, great-grandparent, etc.), with the first configuration found being returned. If no configuration is
49   * found once the top of the tree is reached the default configuration is returned.
50   */
51  public class SAMLMDRelyingPartyConfigurationManager extends BaseReloadableService implements
52          RelyingPartyConfigurationManager {
53  
54      /** ID used for anonymous relying party. */
55      public static final String ANONYMOUS_RP_NAME = "anonymous";
56  
57      /** ID used for default relying party. */
58      public static final String DEFAULT_RP_NAME = "default";
59  
60      /** Class logger. */
61      private final Logger log = LoggerFactory.getLogger(SAMLMDRelyingPartyConfigurationManager.class);
62  
63      /** Metadata provider used to lookup information about entities. */
64      private MetadataProvider metadataProvider;
65  
66      /** Registered relying party configurations. */
67      private HashMap<String, RelyingPartyConfiguration> rpConfigs;
68  
69      /** Constructor. */
70      public SAMLMDRelyingPartyConfigurationManager() {
71          super();
72          rpConfigs = new HashMap<String, RelyingPartyConfiguration>();
73      }
74  
75      /** {@inheritDoc} */
76      public RelyingPartyConfiguration getAnonymousRelyingConfiguration() {
77          Lock readLock = getReadWriteLock().readLock();
78          readLock.lock();
79          try {
80              return rpConfigs.get(ANONYMOUS_RP_NAME);
81          } finally {
82              readLock.unlock();
83          }
84      }
85  
86      /** {@inheritDoc} */
87      public RelyingPartyConfiguration getDefaultRelyingPartyConfiguration() {
88          Lock readLock = getReadWriteLock().readLock();
89          readLock.lock();
90          try {
91              return rpConfigs.get(DEFAULT_RP_NAME);
92          } finally {
93              readLock.unlock();
94          }
95      }
96  
97      /**
98       * Gets the metadata provider used to lookup information about entities.
99       * 
100      * @return metadata provider used to lookup information about entities
101      */
102     public MetadataProvider getMetadataProvider() {
103         Lock readLock = getReadWriteLock().readLock();
104         readLock.lock();
105         try {
106             return metadataProvider;
107         } finally {
108             readLock.unlock();
109         }
110     }
111 
112     /**
113      * Sets the metadata provider used to lookup information about entities.
114      * 
115      * @param provider metadata provider used to lookup information about entities
116      */
117     public void setMetadataProvider(MetadataProvider provider) {
118         metadataProvider = provider;
119     }
120 
121     /** {@inheritDoc} */
122     public RelyingPartyConfiguration getRelyingPartyConfiguration(String relyingPartyEntityID) {
123         Lock readLock = getReadWriteLock().readLock();
124         readLock.lock();
125 
126         try {
127             log.debug("Looking up relying party configuration for {}", relyingPartyEntityID);
128             if (rpConfigs.containsKey(relyingPartyEntityID)) {
129                 log.debug("Custom relying party configuration found for {}", relyingPartyEntityID);
130                 return rpConfigs.get(relyingPartyEntityID);
131             }
132 
133             log.debug("No custom relying party configuration found for {}, looking up configuration based on metadata groups.",
134                             relyingPartyEntityID);
135             try {
136                 if (metadataProvider == null) {
137                     log.debug("No metadata provider available, unable to lookup configuration based on entity group");
138                 } else {
139                     EntityDescriptor entityDescriptor = metadataProvider.getEntityDescriptor(relyingPartyEntityID);
140                     if (entityDescriptor != null) {
141                         EntitiesDescriptor entityGroup = (EntitiesDescriptor) entityDescriptor.getParent();
142                         while (entityGroup != null) {
143                             if (rpConfigs.containsKey(entityGroup.getName())) {
144                                 log.debug("Relying party configuration found for {} as member of metadata group {}",
145                                         relyingPartyEntityID, entityGroup.getName());
146                                 return rpConfigs.get(entityGroup.getName());
147                             }
148                             entityGroup = (EntitiesDescriptor) entityGroup.getParent();
149                         }
150                     }
151                 }
152             } catch (MetadataProviderException e) {
153                 log.error("Error fetching metadata for relying party " + relyingPartyEntityID, e);
154             }
155 
156             log.debug("No custom or group-based relying party configuration found for {}. Using default relying party configuration.",
157                     relyingPartyEntityID);
158             return getDefaultRelyingPartyConfiguration();
159         } finally {
160             readLock.unlock();
161         }
162     }
163 
164     /** {@inheritDoc} */
165     public Map<String, RelyingPartyConfiguration> getRelyingPartyConfigurations() {
166         return rpConfigs;
167     }
168 
169     /** {@inheritDoc} */
170     protected void onNewContextCreated(ApplicationContext newServiceContext) throws ServiceException {
171         MetadataProvider oldProvider = metadataProvider;
172         HashMap<String, RelyingPartyConfiguration> oldRpConfigs = rpConfigs;
173         try {
174             String[] relyingPartyGroupNames = newServiceContext.getBeanNamesForType(RelyingPartyGroup.class);
175             RelyingPartyGroup newRpGroup = (RelyingPartyGroup) newServiceContext.getBean(relyingPartyGroupNames[0]);
176 
177             metadataProvider = newRpGroup.getMetadataProvider();
178 
179             HashMap<String, RelyingPartyConfiguration> newRpConfigs = new HashMap<String, RelyingPartyConfiguration>();
180             List<RelyingPartyConfiguration> loadRpConfigs = newRpGroup.getRelyingParties();
181             if (loadRpConfigs != null) {
182                 for (RelyingPartyConfiguration newRpConfig : loadRpConfigs) {
183                     newRpConfigs.put(newRpConfig.getRelyingPartyId(), newRpConfig);
184                     log.debug("Registering configuration for relying party: {}", newRpConfig.getRelyingPartyId());
185                 }
186             }
187             newRpConfigs.put(ANONYMOUS_RP_NAME, newRpGroup.getAnonymousRP());
188             newRpConfigs.put(DEFAULT_RP_NAME, newRpGroup.getDefaultRP());
189             rpConfigs = newRpConfigs;
190 
191         } catch (Exception e) {
192             metadataProvider = oldProvider;
193             rpConfigs = oldRpConfigs;
194             throw new ServiceException(getId() + " configuration is not valid, retaining old configuration", e);
195         }
196     }
197 }