2 * Copyright 2001-2006 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Protocols20SchemaValidators.cpp
20 * Schema-based validators for SAML 2.0 Protocols classes
24 #include "exceptions.h"
25 #include "saml2/core/Protocols.h"
27 using namespace opensaml::saml2p;
28 using namespace opensaml::saml2;
29 using namespace opensaml;
30 using namespace xmltooling;
36 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,Artifact);
37 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,GetComplete);
38 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,NewID);
39 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,RequesterID);
40 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,SessionIndex);
41 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,StatusMessage);
43 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,RespondTo);
45 //TODO wildcard NS ##other - spec says must be a "non-SAML defined" namespace,
46 // not just other than the target namespace
47 class SAML_DLLLOCAL checkWildcardNS {
49 void operator()(const XMLObject* xmlObject) const {
50 const XMLCh* ns=xmlObject->getElementQName().getNamespaceURI();
51 if (XMLString::equals(ns,SAMLConstants::SAML20P_NS) || !ns || !*ns) {
52 throw ValidationException(
53 "Object contains an illegal extension child element ($1).",
54 params(1,xmlObject->getElementQName().toString().c_str())
60 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,RequestAbstractType);
61 XMLOBJECTVALIDATOR_REQUIRE(RequestAbstractType,ID);
62 XMLOBJECTVALIDATOR_REQUIRE(RequestAbstractType,Version);
63 XMLOBJECTVALIDATOR_REQUIRE(RequestAbstractType,IssueInstant);
64 if (!XMLString::equals(SAMLConstants::SAML20_VERSION, ptr->getVersion()))
65 throw ValidationException("Request has wrong SAML Version.");
66 END_XMLOBJECTVALIDATOR;
68 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,SubjectQuery,RequestAbstractType);
69 RequestAbstractTypeSchemaValidator::validate(xmlObject);
70 XMLOBJECTVALIDATOR_REQUIRE(SubjectQuery,Subject);
71 END_XMLOBJECTVALIDATOR;
73 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,StatusResponseType);
74 XMLOBJECTVALIDATOR_REQUIRE(StatusResponseType,ID);
75 XMLOBJECTVALIDATOR_REQUIRE(StatusResponseType,Version);
76 XMLOBJECTVALIDATOR_REQUIRE(StatusResponseType,IssueInstant);
77 XMLOBJECTVALIDATOR_REQUIRE(StatusResponseType,Status);
78 if (!XMLString::equals(SAMLConstants::SAML20_VERSION, ptr->getVersion()))
79 throw ValidationException("StatusResponse has wrong SAML Version.");
80 END_XMLOBJECTVALIDATOR;
82 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Extensions);
83 if (!ptr->hasChildren())
84 throw ValidationException("Extensions must have at least one child element.");
85 const list<XMLObject*>& anys=ptr->getXMLObjects();
86 for_each(anys.begin(),anys.end(),checkWildcardNS());
87 END_XMLOBJECTVALIDATOR;
89 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,StatusCode);
90 XMLOBJECTVALIDATOR_REQUIRE(StatusCode,Value);
93 // If this is a top-level StatusCode (ie. parent is a Status),
94 // then there are only 4 valid values per SAML Core.
95 if (ptr->getParent()!=NULL && ptr->getParent()->getElementQName().hasLocalPart())
97 QName pq = ptr->getParent()->getElementQName();
99 if ( XMLString::equals(pq.getNamespaceURI(), SAMLConstants::SAML20P_NS) &&
100 XMLString::equals(pq.getLocalPart(), Status::LOCAL_NAME))
102 const XMLCh* code = ptr->getValue();
104 if (!XMLString::equals(code, StatusCode::SUCCESS) &&
105 !XMLString::equals(code, StatusCode::REQUESTER) &&
106 !XMLString::equals(code, StatusCode::RESPONDER) &&
107 !XMLString::equals(code, StatusCode::VERSION_MISMATCH) )
109 throw ValidationException("Invalid value for top-level StatusCode");
113 END_XMLOBJECTVALIDATOR;
115 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Status);
116 XMLOBJECTVALIDATOR_REQUIRE(Status,StatusCode);
117 END_XMLOBJECTVALIDATOR;
119 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AssertionIDRequest,RequestAbstractType);
120 RequestAbstractTypeSchemaValidator::validate(xmlObject);
121 XMLOBJECTVALIDATOR_NONEMPTY(AssertionIDRequest,AssertionIDRef);
122 END_XMLOBJECTVALIDATOR;
124 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,RequestedAuthnContext);
125 if (ptr->getAuthnContextClassRefs().empty() && ptr->getAuthnContextDeclRefs().empty())
126 throw xmltooling::ValidationException("RequestedAuthnContext must have at least one AuthnContextClassRef or AuthnContextDeclRef");
127 if (!ptr->getAuthnContextClassRefs().empty() && !ptr->getAuthnContextDeclRefs().empty())
128 throw xmltooling::ValidationException("RequestedAuthnContext may not have both AuthnContextClassRef and AuthnContextDeclRef");
129 if (!XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_EXACT) &&
130 !XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_MINIMUM) &&
131 !XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_MAXIMUM) &&
132 !XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_BETTER))
133 throw ValidationException("RequestedAuthnContext Comparison attribute must be one of: 'exact', 'minimum', 'maximum', or 'better'.");
134 END_XMLOBJECTVALIDATOR;
136 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AuthnQuery,SubjectQuery);
137 SubjectQuerySchemaValidator::validate(xmlObject);
138 END_XMLOBJECTVALIDATOR;
140 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AttributeQuery,SubjectQuery);
141 SubjectQuerySchemaValidator::validate(xmlObject);
142 //TODO Name/NameFormat pairs of child Attributes must be unique
143 // - whether and how to implement efficiently?
144 END_XMLOBJECTVALIDATOR;
146 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AuthzDecisionQuery,SubjectQuery);
147 SubjectQuerySchemaValidator::validate(xmlObject);
148 XMLOBJECTVALIDATOR_REQUIRE(AuthzDecisionQuery,Resource);
149 XMLOBJECTVALIDATOR_NONEMPTY(AuthzDecisionQuery,Action);
150 END_XMLOBJECTVALIDATOR;
152 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,IDPEntry);
153 XMLOBJECTVALIDATOR_REQUIRE(IDPEntry,ProviderID);
154 END_XMLOBJECTVALIDATOR;
156 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,IDPList);
157 XMLOBJECTVALIDATOR_NONEMPTY(IDPList,IDPEntry);
158 END_XMLOBJECTVALIDATOR;
160 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Scoping);
161 pair<bool,int> pc = ptr->getProxyCount();
162 if (pc.first && pc.second < 0)
163 throw xmltooling::ValidationException("ProxyCount attribute on Scoping element must be non-negative");
164 END_XMLOBJECTVALIDATOR;
166 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AuthnRequest,RequestAbstractType);
167 RequestAbstractTypeSchemaValidator::validate(xmlObject);
168 if (ptr->getAssertionConsumerServiceIndex().first
169 && (ptr->getAssertionConsumerServiceURL()!=NULL || ptr->getProtocolBinding()!=NULL))
170 throw xmltooling::ValidationException("On AuthnRequest AssertionConsumerServiceIndex is mutually exclusive with both AssertionConsumerServiceURL and ProtocolBinding");
171 END_XMLOBJECTVALIDATOR;
173 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,Response,StatusResponseType);
174 StatusResponseTypeSchemaValidator::validate(xmlObject);
175 END_XMLOBJECTVALIDATOR;
177 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ArtifactResolve,RequestAbstractType);
178 RequestAbstractTypeSchemaValidator::validate(xmlObject);
179 XMLOBJECTVALIDATOR_REQUIRE(ArtifactResolve,Artifact);
180 END_XMLOBJECTVALIDATOR;
182 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ArtifactResponse,StatusResponseType);
183 StatusResponseTypeSchemaValidator::validate(xmlObject);
184 END_XMLOBJECTVALIDATOR;
186 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,NewEncryptedID);
187 XMLOBJECTVALIDATOR_REQUIRE(NewEncryptedID,EncryptedData);
188 END_XMLOBJECTVALIDATOR;
190 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ManageNameIDRequest,RequestAbstractType);
191 RequestAbstractTypeSchemaValidator::validate(xmlObject);
192 XMLOBJECTVALIDATOR_ONLYONEOF(ManageNameIDRequest,NameID,EncryptedID);
193 XMLOBJECTVALIDATOR_ONLYONEOF3(ManageNameIDRequest,NewID,NewEncryptedID,Terminate);
194 END_XMLOBJECTVALIDATOR;
196 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ManageNameIDResponse,StatusResponseType);
197 StatusResponseTypeSchemaValidator::validate(xmlObject);
198 END_XMLOBJECTVALIDATOR;
200 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,LogoutRequest,RequestAbstractType);
201 RequestAbstractTypeSchemaValidator::validate(xmlObject);
202 XMLOBJECTVALIDATOR_ONLYONEOF3(LogoutRequest,BaseID,NameID,EncryptedID);
203 END_XMLOBJECTVALIDATOR;
205 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,LogoutResponse,StatusResponseType);
206 StatusResponseTypeSchemaValidator::validate(xmlObject);
207 END_XMLOBJECTVALIDATOR;
209 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,NameIDMappingRequest,RequestAbstractType);
210 RequestAbstractTypeSchemaValidator::validate(xmlObject);
211 XMLOBJECTVALIDATOR_ONLYONEOF3(NameIDMappingRequest,BaseID,NameID,EncryptedID);
212 XMLOBJECTVALIDATOR_REQUIRE(NameIDMappingRequest,NameIDPolicy);
213 END_XMLOBJECTVALIDATOR;
215 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,NameIDMappingResponse,StatusResponseType);
216 StatusResponseTypeSchemaValidator::validate(xmlObject);
217 XMLOBJECTVALIDATOR_ONLYONEOF(NameIDMappingResponse,NameID,EncryptedID);
218 END_XMLOBJECTVALIDATOR;
224 #define REGISTER_ELEMENT(cname) \
225 q=QName(SAMLConstants::SAML20P_NS,cname::LOCAL_NAME); \
226 XMLObjectBuilder::registerBuilder(q,new cname##Builder()); \
227 SchemaValidators.registerValidator(q,new cname##SchemaValidator())
229 #define REGISTER_TYPE(cname) \
230 q=QName(SAMLConstants::SAML20P_NS,cname::TYPE_NAME); \
231 XMLObjectBuilder::registerBuilder(q,new cname##Builder()); \
232 SchemaValidators.registerValidator(q,new cname##SchemaValidator())
234 #define REGISTER_ELEMENT_NOVAL(cname) \
235 q=QName(SAMLConstants::SAML20P_NS,cname::LOCAL_NAME); \
236 XMLObjectBuilder::registerBuilder(q,new cname##Builder());
238 #define REGISTER_TYPE_NOVAL(cname) \
239 q=QName(SAMLConstants::SAML20P_NS,cname::TYPE_NAME); \
240 XMLObjectBuilder::registerBuilder(q,new cname##Builder());
242 void opensaml::saml2p::registerProtocolClasses() {
244 REGISTER_ELEMENT(Artifact);
245 REGISTER_ELEMENT(ArtifactResolve);
246 REGISTER_ELEMENT(ArtifactResponse);
247 REGISTER_ELEMENT(AssertionIDRequest);
248 REGISTER_ELEMENT(AttributeQuery);
249 REGISTER_ELEMENT(AuthnQuery);
250 REGISTER_ELEMENT(AuthnRequest);
251 REGISTER_ELEMENT(AuthzDecisionQuery);
252 REGISTER_ELEMENT(Extensions);
253 REGISTER_ELEMENT(GetComplete);
254 REGISTER_ELEMENT(IDPEntry);
255 REGISTER_ELEMENT(IDPList);
256 REGISTER_ELEMENT(LogoutRequest);
257 REGISTER_ELEMENT(LogoutResponse);
258 REGISTER_ELEMENT(ManageNameIDRequest);
259 REGISTER_ELEMENT(ManageNameIDResponse);
260 REGISTER_ELEMENT(NameIDMappingRequest);
261 REGISTER_ELEMENT(NameIDMappingResponse);
262 REGISTER_ELEMENT_NOVAL(NameIDPolicy);
263 REGISTER_ELEMENT(NewEncryptedID);
264 REGISTER_ELEMENT(NewID);
265 REGISTER_ELEMENT(RequestedAuthnContext);
266 REGISTER_ELEMENT(RequesterID);
267 REGISTER_ELEMENT(Response);
268 REGISTER_ELEMENT(Scoping);
269 REGISTER_ELEMENT(SessionIndex);
270 REGISTER_ELEMENT(Status);
271 REGISTER_ELEMENT(StatusCode);
272 REGISTER_ELEMENT_NOVAL(StatusDetail);
273 REGISTER_ELEMENT(StatusMessage);
274 REGISTER_ELEMENT_NOVAL(Terminate);
275 REGISTER_TYPE(ArtifactResolve);
276 REGISTER_TYPE(ArtifactResponse);
277 REGISTER_TYPE(AssertionIDRequest);
278 REGISTER_TYPE(AttributeQuery);
279 REGISTER_TYPE(AuthnQuery);
280 REGISTER_TYPE(AuthnRequest);
281 REGISTER_TYPE(AuthzDecisionQuery);
282 REGISTER_TYPE(Extensions);
283 REGISTER_TYPE(IDPEntry);
284 REGISTER_TYPE(IDPList);
285 REGISTER_TYPE(LogoutRequest);
286 REGISTER_TYPE(ManageNameIDRequest);
287 REGISTER_TYPE(NameIDMappingRequest);
288 REGISTER_TYPE(NameIDMappingResponse);
289 REGISTER_TYPE_NOVAL(NameIDPolicy);
290 REGISTER_TYPE(RequestedAuthnContext);
291 REGISTER_TYPE(Response);
292 REGISTER_TYPE(Scoping);
293 REGISTER_TYPE(Status);
294 REGISTER_TYPE(StatusCode);
295 REGISTER_TYPE_NOVAL(StatusDetail);
296 REGISTER_TYPE_NOVAL(Terminate);
298 q=QName(SAMLConstants::SAML20P_THIRDPARTY_EXT_NS,RespondTo::LOCAL_NAME);
299 XMLObjectBuilder::registerBuilder(q,new RespondToBuilder());
300 SchemaValidators.registerValidator(q,new RespondToSchemaValidator());