View Javadoc

1   /*
2    * Copyright [2007] [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.common.attribute.resolver.provider.dataConnector;
18  
19  import java.security.MessageDigest;
20  import java.security.NoSuchAlgorithmException;
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.Map;
24  
25  import org.opensaml.xml.util.Base64;
26  import org.opensaml.xml.util.DatatypeHelper;
27  import org.opensaml.xml.util.LazyMap;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
32  import edu.internet2.middleware.shibboleth.common.attribute.provider.BasicAttribute;
33  import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
34  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.ShibbolethResolutionContext;
35  
36  /**
37   * A data connector that generates a unique ID by computing the SHA-1 hash of a given attribute value, the entity ID of
38   * the inbound message issuer, and a provided salt.
39   * 
40   * @deprecated use {@link StoredIDDataConnector}.
41   */
42  public class ComputedIDDataConnector extends BaseDataConnector {
43  
44      /** Class logger. */
45      private final Logger log = LoggerFactory.getLogger(ComputedIDDataConnector.class);
46  
47      /** ID of the attribute generated by this data connector. */
48      private String generatedAttribute;
49  
50      /** ID of the attribute whose first value is used when generating the computed ID. */
51      private String sourceAttribute;
52  
53      /** Salt used when computing the ID. */
54      private byte[] salt;
55  
56      /**
57       * Constructor.
58       * 
59       * @param generatedAttributeId ID of the attribute generated by this data connector
60       * @param sourceAttributeId ID of the attribute whose first value is used when generating the computed ID
61       * @param idSalt salt used when computing the ID
62       */
63      public ComputedIDDataConnector(String generatedAttributeId, String sourceAttributeId, byte[] idSalt) {
64          log.warn("This data connector is deprecated.  The StoredID data connector should be used in its place.");
65  
66          if (DatatypeHelper.isEmpty(generatedAttributeId)) {
67              throw new IllegalArgumentException("Provided generated attribute ID must not be empty");
68          }
69          generatedAttribute = generatedAttributeId;
70  
71          if (DatatypeHelper.isEmpty(sourceAttributeId)) {
72              throw new IllegalArgumentException("Provided source attribute ID must not be empty");
73          }
74          sourceAttribute = sourceAttributeId;
75  
76          if (idSalt.length < 16) {
77              throw new IllegalArgumentException("Provided salt must be at least 16 bytes in size.");
78          }
79          salt = idSalt;
80      }
81  
82      /**
83       * Gets the salt used when computing the ID.
84       * 
85       * @return salt used when computing the ID
86       */
87      public byte[] getSalt() {
88          return salt;
89      }
90  
91      /**
92       * Gets the ID of the attribute whose first value is used when generating the computed ID.
93       * 
94       * @return ID of the attribute whose first value is used when generating the computed ID
95       */
96      public String getSourceAttributeId() {
97          return sourceAttribute;
98      }
99  
100     /**
101      * Gets the ID of the attribute generated by this connector.
102      * 
103      * @return ID of the attribute generated by this connector
104      */
105     public String getGeneratedAttributeId() {
106         return generatedAttribute;
107     }
108 
109     /** {@inheritDoc} */
110     public Map<String, BaseAttribute> resolve(ShibbolethResolutionContext resolutionContext)
111             throws AttributeResolutionException {
112 
113         String inboundMessageIssuer = resolutionContext.getAttributeRequestContext().getInboundMessageIssuer();
114         if (inboundMessageIssuer == null) {
115             log.error("No inbound message issuer identified, unable to compute ID");
116             throw new AttributeResolutionException("No inbound message issuer identified");
117         }
118 
119         Collection<Object> sourceIdValues = getValuesFromAllDependencies(resolutionContext, getSourceAttributeId());
120         if (sourceIdValues == null || sourceIdValues.isEmpty()) {
121             log.debug("Source attribute {} for connector {} provide no values", getSourceAttributeId(), getId());
122             return Collections.EMPTY_MAP;
123         }
124 
125         if (sourceIdValues.size() > 1) {
126             log.warn("Source attribute {} for connector {} has more than one value, only the first value is used",
127                     getSourceAttributeId(), getId());
128         }
129         String sourceId = sourceIdValues.iterator().next().toString();
130 
131         BasicAttribute<String> computedIdAttrib = new BasicAttribute<String>();
132         computedIdAttrib.setId(getGeneratedAttributeId());
133 
134         try {
135             MessageDigest md = MessageDigest.getInstance("SHA");
136             md.update(inboundMessageIssuer.getBytes());
137             md.update((byte) '!');
138             md.update(sourceId.getBytes());
139             md.update((byte) '!');
140 
141             computedIdAttrib.getValues().add(Base64.encodeBytes(md.digest(salt)));
142 
143             LazyMap<String, BaseAttribute> attribtues = new LazyMap<String, BaseAttribute>();
144             attribtues.put(getGeneratedAttributeId(), computedIdAttrib);
145             return attribtues;
146         } catch (NoSuchAlgorithmException e) {
147             log.error("JVM error, SHA-1 hash is not supported.");
148             throw new AttributeResolutionException("SHA-1A is not supported, unable to compute ID");
149         }
150     }
151 
152     /** {@inheritDoc} */
153     public void validate() throws AttributeResolutionException {
154         if (getDependencyIds() == null || getDependencyIds().size() != 1) {
155             log.error("Computed ID " + getId() + " data connectore requires exactly one dependency");
156             throw new AttributeResolutionException("Computed ID " + getId()
157                     + " data connectore requires exactly one dependency");
158         }
159     }
160 }