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.Map;
23  
24  import org.opensaml.xml.util.Base64;
25  import org.opensaml.xml.util.DatatypeHelper;
26  import org.opensaml.xml.util.LazyMap;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  
30  import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
31  import edu.internet2.middleware.shibboleth.common.attribute.provider.BasicAttribute;
32  import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
33  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.ShibbolethResolutionContext;
34  
35  /**
36   * A data connector that generates a unique ID by computing the SHA-1 hash of a given attribute value, the entity ID of
37   * the inbound message issuer, and a provided salt.
38   * 
39   * @deprecated use {@link StoredIDDataConnector}.
40   */
41  public class ComputedIDDataConnector extends BaseDataConnector {
42  
43      /** Class logger. */
44      private final Logger log = LoggerFactory.getLogger(ComputedIDDataConnector.class);
45  
46      /** ID of the attribute generated by this data connector. */
47      private String generatedAttribute;
48  
49      /** ID of the attribute whose first value is used when generating the computed ID. */
50      private String sourceAttribute;
51  
52      /** Salt used when computing the ID. */
53      private byte[] salt;
54  
55      /**
56       * Constructor.
57       * 
58       * @param generatedAttributeId ID of the attribute generated by this data connector
59       * @param sourceAttributeId ID of the attribute whose first value is used when generating the computed ID
60       * @param idSalt salt used when computing the ID
61       */
62      public ComputedIDDataConnector(String generatedAttributeId, String sourceAttributeId, byte[] idSalt) {
63          log.warn("This data connector is deprecated.  The StoredID data connector should be used in its place.");
64  
65          if (DatatypeHelper.isEmpty(generatedAttributeId)) {
66              throw new IllegalArgumentException("Provided generated attribute ID must not be empty");
67          }
68          generatedAttribute = generatedAttributeId;
69  
70          if (DatatypeHelper.isEmpty(sourceAttributeId)) {
71              throw new IllegalArgumentException("Provided source attribute ID must not be empty");
72          }
73          sourceAttribute = sourceAttributeId;
74  
75          if (idSalt.length < 16) {
76              throw new IllegalArgumentException("Provided salt must be at least 16 bytes in size.");
77          }
78          salt = idSalt;
79      }
80  
81      /**
82       * Gets the salt used when computing the ID.
83       * 
84       * @return salt used when computing the ID
85       */
86      public byte[] getSalt() {
87          return salt;
88      }
89  
90      /**
91       * Gets the ID of the attribute whose first value is used when generating the computed ID.
92       * 
93       * @return ID of the attribute whose first value is used when generating the computed ID
94       */
95      public String getSourceAttributeId() {
96          return sourceAttribute;
97      }
98  
99      /**
100      * Gets the ID of the attribute generated by this connector.
101      * 
102      * @return ID of the attribute generated by this connector
103      */
104     public String getGeneratedAttributeId() {
105         return generatedAttribute;
106     }
107 
108     /** {@inheritDoc} */
109     public Map<String, BaseAttribute> resolve(ShibbolethResolutionContext resolutionContext)
110             throws AttributeResolutionException {
111 
112         String inboundMessageIssuer = resolutionContext.getAttributeRequestContext().getInboundMessageIssuer();
113         if (inboundMessageIssuer == null) {
114             log.error("No inbound message issuer identified, unable to compute ID");
115             throw new AttributeResolutionException("No inbound message issuer identified");
116         }
117 
118         Collection<Object> sourceIdValues = getValuesFromAllDependencies(resolutionContext, getSourceAttributeId());
119         if (sourceIdValues == null || sourceIdValues.isEmpty()) {
120             log.error("Source attribute {} for connector {} provide no values", getSourceAttributeId(), getId());
121             throw new AttributeResolutionException("Source attribute " + getSourceAttributeId() + " for connector "
122                     + getId() + " provided no values");
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 }