1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.principalConnector;
19
20 import java.sql.SQLException;
21
22 import org.opensaml.saml1.core.NameIdentifier;
23 import org.opensaml.saml2.core.AuthnRequest;
24 import org.opensaml.saml2.core.NameID;
25 import org.opensaml.saml2.core.SubjectQuery;
26 import org.opensaml.xml.XMLObject;
27 import org.opensaml.xml.util.DatatypeHelper;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
32 import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.ShibbolethResolutionContext;
33 import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.StoredIDDataConnector;
34 import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.StoredIDStore;
35 import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.StoredIDStore.PersistentIdEntry;
36 import edu.internet2.middleware.shibboleth.common.profile.provider.SAMLProfileRequestContext;
37
38
39
40
41 public class StoredIDPrincipalConnector extends BasePrincipalConnector {
42
43
44 private final Logger log = LoggerFactory.getLogger(StoredIDPrincipalConnector.class);
45
46
47 private StoredIDStore pidStore;
48
49
50 private boolean noResultIsError;
51
52
53
54
55
56
57 public StoredIDPrincipalConnector(StoredIDDataConnector idProducer) {
58 if (idProducer == null) {
59 throw new IllegalArgumentException("ID producing data connector may not be null");
60 }
61 pidStore = idProducer.getStoredIDStore();
62 noResultIsError = false;
63 }
64
65
66
67
68
69
70 public boolean isNoResultIsError() {
71 return noResultIsError;
72 }
73
74
75
76
77
78
79 public void setNoResultIsError(boolean isError) {
80 noResultIsError = isError;
81 }
82
83
84 public String resolve(ShibbolethResolutionContext resolutionContext) throws AttributeResolutionException {
85 SAMLProfileRequestContext requestContext = resolutionContext.getAttributeRequestContext();
86
87 String persistentId;
88 if (requestContext.getSubjectNameIdentifier() instanceof NameIdentifier) {
89 persistentId = ((NameIdentifier) requestContext.getSubjectNameIdentifier()).getNameIdentifier();
90 } else if (requestContext.getSubjectNameIdentifier() instanceof NameID) {
91 persistentId = ((NameID) requestContext.getSubjectNameIdentifier()).getValue();
92 } else {
93 throw new AttributeResolutionException("Subject name identifier is not of a supported type");
94 }
95
96 try {
97 PersistentIdEntry pidEntry = pidStore.getActivePersistentIdEntry(persistentId);
98 if (pidEntry == null) {
99 if (noResultIsError) {
100 log.warn("PersistentId '{}' not found", persistentId);
101 throw new AttributeResolutionException("No identifier found");
102 }
103 return null;
104 }
105
106 if (!DatatypeHelper.safeEquals(pidEntry.getPeerEntityId(), getPeerEntityId(resolutionContext))) {
107 log.warn(
108 "Requester '{}' attempted to use identifier '{}' which was issued to the entity '{}'",
109 new Object[] {requestContext.getInboundMessageIssuer(), pidEntry.getPersistentId(),
110 pidEntry.getPeerEntityId(),});
111 if (noResultIsError) {
112 throw new AttributeResolutionException("identifier mismatch");
113 }
114 return null;
115 }
116
117 return pidEntry.getPrincipalName();
118 } catch (SQLException e) {
119 log.error("Error retrieving persistent ID from database", e);
120 throw new AttributeResolutionException("Error retrieving persistent ID from database", e);
121 }
122
123 }
124
125
126
127
128
129
130
131
132
133 protected String getPeerEntityId(ShibbolethResolutionContext resolutionContext) {
134 SAMLProfileRequestContext requestContext = resolutionContext.getAttributeRequestContext();
135
136 String peerEntityId = null;
137
138 log.debug("Determining if peer entity ID will be the SPNameQualifier from a SAML 2 authentication statement");
139 XMLObject inboundMessage = requestContext.getInboundSAMLMessage();
140 if (inboundMessage instanceof AuthnRequest) {
141 AuthnRequest authnRequest = (AuthnRequest) inboundMessage;
142 if (authnRequest.getNameIDPolicy() != null) {
143 peerEntityId = DatatypeHelper.safeTrimOrNullString(authnRequest.getNameIDPolicy().getSPNameQualifier());
144 if (peerEntityId == null) {
145 log.debug("SAML 2 authentication request did not contain an SPNameQualifier within its NameIDPolicy");
146 } else {
147 log.debug("SAML 2 authentication request contained an SPNameQualifier, within its NameIDPolicy. Using that as peer entity ID");
148 }
149 } else {
150 log.debug("SAML 2 authentication request did not contain a NameIDPolicy");
151 }
152 } else if (inboundMessage instanceof SubjectQuery) {
153 SubjectQuery query = (SubjectQuery) inboundMessage;
154 if (query.getSubject().getNameID().getSPNameQualifier() != null) {
155 peerEntityId =
156 DatatypeHelper.safeTrimOrNullString(query.getSubject().getNameID().getSPNameQualifier());
157 if (peerEntityId == null) {
158 log.debug("SAML 2 subject query did not contain an SPNameQualifier within its NameID");
159 } else {
160 log.debug("SAML 2 subject query contained an SPNameQualifier, within its NameID. Using that as peer entity ID");
161 }
162 } else {
163 log.debug("SAML 2 attribute query did not contain a SPNameQualifier");
164 }
165 } else {
166 peerEntityId = requestContext.getInboundMessageIssuer();
167 }
168
169 if (peerEntityId == null) {
170 log.debug("Determining if inbound message issuer is available for use as peer entity ID");
171 peerEntityId = resolutionContext.getAttributeRequestContext().getInboundMessageIssuer();
172 }
173
174 return peerEntityId;
175 }
176
177
178 public void validate() throws AttributeResolutionException {
179 if (pidStore == null) {
180 throw new AttributeResolutionException("Persistent ID store was null");
181 }
182
183 try {
184 pidStore.getPersistentIdEntry("test", false);
185 } catch (SQLException e) {
186 throw new AttributeResolutionException("Persistent ID store can not perform persistent ID search", e);
187 }
188 }
189 }