ReplayCache, some decoder work, and merged schema validators into one suite.
[shibboleth/cpp-opensaml.git] / saml / saml2 / core / impl / Protocols20SchemaValidators.cpp
1 /*
2 *  Copyright 2001-2006 Internet2
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 /**
18  * Protocols20SchemaValidators.cpp
19  * 
20  * Schema-based validators for SAML 2.0 Protocols classes
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "saml2/core/Protocols.h"
26
27 using namespace opensaml::saml2p;
28 using namespace opensaml::saml2;
29 using namespace opensaml;
30 using namespace xmltooling;
31 using namespace std;
32
33 namespace opensaml {
34     namespace saml2p {
35         
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);
42         
43         XMLOBJECTVALIDATOR_SIMPLE(SAML_DLLLOCAL,RespondTo);
44
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 {
48         public:
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())
55                         );
56                 }
57             }
58         };
59
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;
67
68         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,SubjectQuery,RequestAbstractType);
69             RequestAbstractTypeSchemaValidator::validate(xmlObject);
70             XMLOBJECTVALIDATOR_REQUIRE(SubjectQuery,Subject);
71         END_XMLOBJECTVALIDATOR;
72
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;
81
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;
88
89         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,StatusCode);
90             XMLOBJECTVALIDATOR_REQUIRE(StatusCode,Value);
91
92             //TODO test this !!!
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())
96             {
97                 QName pq = ptr->getParent()->getElementQName();
98
99                 if ( XMLString::equals(pq.getNamespaceURI(), SAMLConstants::SAML20P_NS) &&
100                         XMLString::equals(pq.getLocalPart(), Status::LOCAL_NAME))
101                 {
102                     const XMLCh* code = ptr->getValue();
103
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) )
108                     {
109                         throw ValidationException("Invalid value for top-level StatusCode");
110                     }
111                 }
112             }
113         END_XMLOBJECTVALIDATOR;
114
115         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Status);
116             XMLOBJECTVALIDATOR_REQUIRE(Status,StatusCode);
117         END_XMLOBJECTVALIDATOR;
118
119         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AssertionIDRequest,RequestAbstractType);
120             RequestAbstractTypeSchemaValidator::validate(xmlObject);
121             XMLOBJECTVALIDATOR_NONEMPTY(AssertionIDRequest,AssertionIDRef);
122         END_XMLOBJECTVALIDATOR;
123
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;
135
136         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AuthnQuery,SubjectQuery);
137             SubjectQuerySchemaValidator::validate(xmlObject);
138         END_XMLOBJECTVALIDATOR;
139
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;
145
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;
151
152         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,IDPEntry);
153             XMLOBJECTVALIDATOR_REQUIRE(IDPEntry,ProviderID);
154         END_XMLOBJECTVALIDATOR;
155
156         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,IDPList);
157             XMLOBJECTVALIDATOR_NONEMPTY(IDPList,IDPEntry);
158         END_XMLOBJECTVALIDATOR;
159
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;
165
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;
172
173         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,Response,StatusResponseType);
174             StatusResponseTypeSchemaValidator::validate(xmlObject);
175         END_XMLOBJECTVALIDATOR;
176
177         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ArtifactResolve,RequestAbstractType);
178             RequestAbstractTypeSchemaValidator::validate(xmlObject);
179             XMLOBJECTVALIDATOR_REQUIRE(ArtifactResolve,Artifact);
180         END_XMLOBJECTVALIDATOR;
181
182         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ArtifactResponse,StatusResponseType);
183             StatusResponseTypeSchemaValidator::validate(xmlObject);
184         END_XMLOBJECTVALIDATOR;
185
186         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,NewEncryptedID);
187             XMLOBJECTVALIDATOR_REQUIRE(NewEncryptedID,EncryptedData);
188         END_XMLOBJECTVALIDATOR;
189
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;
195
196         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ManageNameIDResponse,StatusResponseType);
197             StatusResponseTypeSchemaValidator::validate(xmlObject);
198         END_XMLOBJECTVALIDATOR;
199
200         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,LogoutRequest,RequestAbstractType);
201             RequestAbstractTypeSchemaValidator::validate(xmlObject);
202             XMLOBJECTVALIDATOR_ONLYONEOF3(LogoutRequest,BaseID,NameID,EncryptedID);
203         END_XMLOBJECTVALIDATOR;
204
205         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,LogoutResponse,StatusResponseType);
206             StatusResponseTypeSchemaValidator::validate(xmlObject);
207         END_XMLOBJECTVALIDATOR;
208
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;
214
215         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,NameIDMappingResponse,StatusResponseType);
216             StatusResponseTypeSchemaValidator::validate(xmlObject);
217             XMLOBJECTVALIDATOR_ONLYONEOF(NameIDMappingResponse,NameID,EncryptedID);
218         END_XMLOBJECTVALIDATOR;
219
220
221     };
222 };
223
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())
228     
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())
233
234 #define REGISTER_ELEMENT_NOVAL(cname) \
235     q=QName(SAMLConstants::SAML20P_NS,cname::LOCAL_NAME); \
236     XMLObjectBuilder::registerBuilder(q,new cname##Builder());
237     
238 #define REGISTER_TYPE_NOVAL(cname) \
239     q=QName(SAMLConstants::SAML20P_NS,cname::TYPE_NAME); \
240     XMLObjectBuilder::registerBuilder(q,new cname##Builder());
241
242 void opensaml::saml2p::registerProtocolClasses() {
243     QName q;
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);
297
298     q=QName(SAMLConstants::SAML20P_THIRDPARTY_EXT_NS,RespondTo::LOCAL_NAME);
299     XMLObjectBuilder::registerBuilder(q,new RespondToBuilder());
300     SchemaValidators.registerValidator(q,new RespondToSchemaValidator());
301 }