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