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 #include <xmltooling/validation/ValidatorSuite.h>
29 using namespace opensaml::saml2p;
30 using namespace opensaml::saml2;
31 using namespace opensaml;
32 using namespace xmltooling;
34 using samlconstants::SAML20P_NS;
39 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,Artifact);
40 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,GetComplete);
41 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,NewID);
42 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,RequesterID);
43 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,SessionIndex);
44 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,StatusMessage);
46 XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,RespondTo);
48 //TODO wildcard NS ##other - spec says must be a "non-SAML defined" namespace,
49 // not just other than the target namespace
50 class SAML_DLLLOCAL checkWildcardNS {
52 void operator()(const XMLObject* xmlObject) const {
53 const XMLCh* ns=xmlObject->getElementQName().getNamespaceURI();
54 if (XMLString::equals(ns,SAML20P_NS) || !ns || !*ns) {
55 throw ValidationException(
56 "Object contains an illegal extension child element ($1).",
57 params(1,xmlObject->getElementQName().toString().c_str())
63 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,RequestAbstractType);
64 XMLOBJECTVALIDATOR_REQUIRE(RequestAbstractType,ID);
65 XMLOBJECTVALIDATOR_REQUIRE(RequestAbstractType,Version);
66 XMLOBJECTVALIDATOR_REQUIRE(RequestAbstractType,IssueInstant);
67 if (!XMLString::equals(samlconstants::SAML20_VERSION, ptr->getVersion()))
68 throw ValidationException("Request has wrong SAML Version.");
69 END_XMLOBJECTVALIDATOR;
71 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,SubjectQuery,RequestAbstractType);
72 RequestAbstractTypeSchemaValidator::validate(xmlObject);
73 XMLOBJECTVALIDATOR_REQUIRE(SubjectQuery,Subject);
74 END_XMLOBJECTVALIDATOR;
76 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,StatusResponseType);
77 XMLOBJECTVALIDATOR_REQUIRE(StatusResponseType,ID);
78 XMLOBJECTVALIDATOR_REQUIRE(StatusResponseType,Version);
79 XMLOBJECTVALIDATOR_REQUIRE(StatusResponseType,IssueInstant);
80 XMLOBJECTVALIDATOR_REQUIRE(StatusResponseType,Status);
81 if (!XMLString::equals(samlconstants::SAML20_VERSION, ptr->getVersion()))
82 throw ValidationException("StatusResponse has wrong SAML Version.");
83 END_XMLOBJECTVALIDATOR;
85 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Extensions);
86 if (!ptr->hasChildren())
87 throw ValidationException("Extensions must have at least one child element.");
88 const vector<XMLObject*>& anys=ptr->getUnknownXMLObjects();
89 for_each(anys.begin(),anys.end(),checkWildcardNS());
90 END_XMLOBJECTVALIDATOR;
92 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,StatusCode);
93 XMLOBJECTVALIDATOR_REQUIRE(StatusCode,Value);
96 // If this is a top-level StatusCode (ie. parent is a Status),
97 // then there are only 4 valid values per SAML Core.
98 if (ptr->getParent()!=NULL && ptr->getParent()->getElementQName().hasLocalPart())
100 QName pq = ptr->getParent()->getElementQName();
102 if ( XMLString::equals(pq.getNamespaceURI(), SAML20P_NS) &&
103 XMLString::equals(pq.getLocalPart(), Status::LOCAL_NAME))
105 const XMLCh* code = ptr->getValue();
107 if (!XMLString::equals(code, StatusCode::SUCCESS) &&
108 !XMLString::equals(code, StatusCode::REQUESTER) &&
109 !XMLString::equals(code, StatusCode::RESPONDER) &&
110 !XMLString::equals(code, StatusCode::VERSION_MISMATCH) )
112 throw ValidationException("Invalid value for top-level StatusCode");
116 END_XMLOBJECTVALIDATOR;
118 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Status);
119 XMLOBJECTVALIDATOR_REQUIRE(Status,StatusCode);
120 END_XMLOBJECTVALIDATOR;
122 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AssertionIDRequest,RequestAbstractType);
123 RequestAbstractTypeSchemaValidator::validate(xmlObject);
124 XMLOBJECTVALIDATOR_NONEMPTY(AssertionIDRequest,AssertionIDRef);
125 END_XMLOBJECTVALIDATOR;
127 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,RequestedAuthnContext);
128 if (ptr->getAuthnContextClassRefs().empty() && ptr->getAuthnContextDeclRefs().empty())
129 throw xmltooling::ValidationException("RequestedAuthnContext must have at least one AuthnContextClassRef or AuthnContextDeclRef");
130 if (!ptr->getAuthnContextClassRefs().empty() && !ptr->getAuthnContextDeclRefs().empty())
131 throw xmltooling::ValidationException("RequestedAuthnContext may not have both AuthnContextClassRef and AuthnContextDeclRef");
132 if (!XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_EXACT) &&
133 !XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_MINIMUM) &&
134 !XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_MAXIMUM) &&
135 !XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_BETTER))
136 throw ValidationException("RequestedAuthnContext Comparison attribute must be one of: 'exact', 'minimum', 'maximum', or 'better'.");
137 END_XMLOBJECTVALIDATOR;
139 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AuthnQuery,SubjectQuery);
140 SubjectQuerySchemaValidator::validate(xmlObject);
141 END_XMLOBJECTVALIDATOR;
143 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AttributeQuery,SubjectQuery);
144 SubjectQuerySchemaValidator::validate(xmlObject);
145 //TODO Name/NameFormat pairs of child Attributes must be unique
146 // - whether and how to implement efficiently?
147 END_XMLOBJECTVALIDATOR;
149 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AuthzDecisionQuery,SubjectQuery);
150 SubjectQuerySchemaValidator::validate(xmlObject);
151 XMLOBJECTVALIDATOR_REQUIRE(AuthzDecisionQuery,Resource);
152 XMLOBJECTVALIDATOR_NONEMPTY(AuthzDecisionQuery,Action);
153 END_XMLOBJECTVALIDATOR;
155 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,IDPEntry);
156 XMLOBJECTVALIDATOR_REQUIRE(IDPEntry,ProviderID);
157 END_XMLOBJECTVALIDATOR;
159 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,IDPList);
160 XMLOBJECTVALIDATOR_NONEMPTY(IDPList,IDPEntry);
161 END_XMLOBJECTVALIDATOR;
163 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Scoping);
164 pair<bool,int> pc = ptr->getProxyCount();
165 if (pc.first && pc.second < 0)
166 throw xmltooling::ValidationException("ProxyCount attribute on Scoping element must be non-negative");
167 END_XMLOBJECTVALIDATOR;
169 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AuthnRequest,RequestAbstractType);
170 RequestAbstractTypeSchemaValidator::validate(xmlObject);
171 if (ptr->getAssertionConsumerServiceIndex().first
172 && (ptr->getAssertionConsumerServiceURL()!=NULL || ptr->getProtocolBinding()!=NULL))
173 throw xmltooling::ValidationException("On AuthnRequest AssertionConsumerServiceIndex is mutually exclusive with both AssertionConsumerServiceURL and ProtocolBinding");
174 END_XMLOBJECTVALIDATOR;
176 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,Response,StatusResponseType);
177 StatusResponseTypeSchemaValidator::validate(xmlObject);
178 END_XMLOBJECTVALIDATOR;
180 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ArtifactResolve,RequestAbstractType);
181 RequestAbstractTypeSchemaValidator::validate(xmlObject);
182 XMLOBJECTVALIDATOR_REQUIRE(ArtifactResolve,Artifact);
183 END_XMLOBJECTVALIDATOR;
185 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ArtifactResponse,StatusResponseType);
186 StatusResponseTypeSchemaValidator::validate(xmlObject);
187 END_XMLOBJECTVALIDATOR;
189 BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,NewEncryptedID);
190 XMLOBJECTVALIDATOR_REQUIRE(NewEncryptedID,EncryptedData);
191 END_XMLOBJECTVALIDATOR;
193 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ManageNameIDRequest,RequestAbstractType);
194 RequestAbstractTypeSchemaValidator::validate(xmlObject);
195 XMLOBJECTVALIDATOR_ONLYONEOF(ManageNameIDRequest,NameID,EncryptedID);
196 XMLOBJECTVALIDATOR_ONLYONEOF3(ManageNameIDRequest,NewID,NewEncryptedID,Terminate);
197 END_XMLOBJECTVALIDATOR;
199 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ManageNameIDResponse,StatusResponseType);
200 StatusResponseTypeSchemaValidator::validate(xmlObject);
201 END_XMLOBJECTVALIDATOR;
203 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,LogoutRequest,RequestAbstractType);
204 RequestAbstractTypeSchemaValidator::validate(xmlObject);
205 XMLOBJECTVALIDATOR_ONLYONEOF3(LogoutRequest,BaseID,NameID,EncryptedID);
206 END_XMLOBJECTVALIDATOR;
208 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,LogoutResponse,StatusResponseType);
209 StatusResponseTypeSchemaValidator::validate(xmlObject);
210 END_XMLOBJECTVALIDATOR;
212 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,NameIDMappingRequest,RequestAbstractType);
213 RequestAbstractTypeSchemaValidator::validate(xmlObject);
214 XMLOBJECTVALIDATOR_ONLYONEOF3(NameIDMappingRequest,BaseID,NameID,EncryptedID);
215 XMLOBJECTVALIDATOR_REQUIRE(NameIDMappingRequest,NameIDPolicy);
216 END_XMLOBJECTVALIDATOR;
218 BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,NameIDMappingResponse,StatusResponseType);
219 StatusResponseTypeSchemaValidator::validate(xmlObject);
220 XMLOBJECTVALIDATOR_ONLYONEOF(NameIDMappingResponse,NameID,EncryptedID);
221 END_XMLOBJECTVALIDATOR;
227 #define REGISTER_ELEMENT(cname) \
228 q=QName(SAML20P_NS,cname::LOCAL_NAME); \
229 XMLObjectBuilder::registerBuilder(q,new cname##Builder()); \
230 SchemaValidators.registerValidator(q,new cname##SchemaValidator())
232 #define REGISTER_TYPE(cname) \
233 q=QName(SAML20P_NS,cname::TYPE_NAME); \
234 XMLObjectBuilder::registerBuilder(q,new cname##Builder()); \
235 SchemaValidators.registerValidator(q,new cname##SchemaValidator())
237 #define REGISTER_ELEMENT_NOVAL(cname) \
238 q=QName(SAML20P_NS,cname::LOCAL_NAME); \
239 XMLObjectBuilder::registerBuilder(q,new cname##Builder());
241 #define REGISTER_TYPE_NOVAL(cname) \
242 q=QName(SAML20P_NS,cname::TYPE_NAME); \
243 XMLObjectBuilder::registerBuilder(q,new cname##Builder());
245 void opensaml::saml2p::registerProtocolClasses() {
247 REGISTER_ELEMENT(Artifact);
248 REGISTER_ELEMENT(ArtifactResolve);
249 REGISTER_ELEMENT(ArtifactResponse);
250 REGISTER_ELEMENT(AssertionIDRequest);
251 REGISTER_ELEMENT(AttributeQuery);
252 REGISTER_ELEMENT(AuthnQuery);
253 REGISTER_ELEMENT(AuthnRequest);
254 REGISTER_ELEMENT(AuthzDecisionQuery);
255 REGISTER_ELEMENT(Extensions);
256 REGISTER_ELEMENT(GetComplete);
257 REGISTER_ELEMENT(IDPEntry);
258 REGISTER_ELEMENT(IDPList);
259 REGISTER_ELEMENT(LogoutRequest);
260 REGISTER_ELEMENT(LogoutResponse);
261 REGISTER_ELEMENT(ManageNameIDRequest);
262 REGISTER_ELEMENT(ManageNameIDResponse);
263 REGISTER_ELEMENT(NameIDMappingRequest);
264 REGISTER_ELEMENT(NameIDMappingResponse);
265 REGISTER_ELEMENT_NOVAL(NameIDPolicy);
266 REGISTER_ELEMENT(NewEncryptedID);
267 REGISTER_ELEMENT(NewID);
268 REGISTER_ELEMENT(RequestedAuthnContext);
269 REGISTER_ELEMENT(RequesterID);
270 REGISTER_ELEMENT(Response);
271 REGISTER_ELEMENT(Scoping);
272 REGISTER_ELEMENT(SessionIndex);
273 REGISTER_ELEMENT(Status);
274 REGISTER_ELEMENT(StatusCode);
275 REGISTER_ELEMENT_NOVAL(StatusDetail);
276 REGISTER_ELEMENT(StatusMessage);
277 REGISTER_ELEMENT_NOVAL(Terminate);
278 REGISTER_TYPE(ArtifactResolve);
279 REGISTER_TYPE(ArtifactResponse);
280 REGISTER_TYPE(AssertionIDRequest);
281 REGISTER_TYPE(AttributeQuery);
282 REGISTER_TYPE(AuthnQuery);
283 REGISTER_TYPE(AuthnRequest);
284 REGISTER_TYPE(AuthzDecisionQuery);
285 REGISTER_TYPE(Extensions);
286 REGISTER_TYPE(IDPEntry);
287 REGISTER_TYPE(IDPList);
288 REGISTER_TYPE(LogoutRequest);
289 REGISTER_TYPE(ManageNameIDRequest);
290 REGISTER_TYPE(NameIDMappingRequest);
291 REGISTER_TYPE(NameIDMappingResponse);
292 REGISTER_TYPE_NOVAL(NameIDPolicy);
293 REGISTER_TYPE(RequestedAuthnContext);
294 REGISTER_TYPE(Response);
295 REGISTER_TYPE(Scoping);
296 REGISTER_TYPE(Status);
297 REGISTER_TYPE(StatusCode);
298 REGISTER_TYPE_NOVAL(StatusDetail);
299 REGISTER_TYPE_NOVAL(Terminate);
301 q=QName(samlconstants::SAML20P_THIRDPARTY_EXT_NS,RespondTo::LOCAL_NAME);
302 XMLObjectBuilder::registerBuilder(q,new RespondToBuilder());
303 SchemaValidators.registerValidator(q,new RespondToSchemaValidator());