Multi-line svn commit, see body.
[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         //TODO wildcard NS ##other - spec says must be a "non-SAML defined" namespace,
44         // not just other than the target namespace
45         class SAML_DLLLOCAL checkWildcardNS {
46         public:
47             void operator()(const XMLObject* xmlObject) const {
48                 const XMLCh* ns=xmlObject->getElementQName().getNamespaceURI();
49                 if (XMLString::equals(ns,SAMLConstants::SAML20P_NS) || !ns || !*ns) {
50                     throw ValidationException(
51                         "Object contains an illegal extension child element ($1).",
52                         params(1,xmlObject->getElementQName().toString().c_str())
53                         );
54                 }
55             }
56         };
57
58         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Request);
59             XMLOBJECTVALIDATOR_REQUIRE(Request,ID);
60             XMLOBJECTVALIDATOR_REQUIRE(Request,Version);
61             XMLOBJECTVALIDATOR_REQUIRE(Request,IssueInstant);
62             if (!XMLString::equals(SAMLConstants::SAML20_VERSION, ptr->getVersion()))
63                 throw ValidationException("Request has wrong SAML Version.");
64         END_XMLOBJECTVALIDATOR;
65
66         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,SubjectQuery,Request);
67             RequestSchemaValidator::validate(xmlObject);
68             XMLOBJECTVALIDATOR_REQUIRE(SubjectQuery,Subject);
69         END_XMLOBJECTVALIDATOR;
70
71         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,StatusResponse);
72             XMLOBJECTVALIDATOR_REQUIRE(StatusResponse,ID);
73             XMLOBJECTVALIDATOR_REQUIRE(StatusResponse,Version);
74             XMLOBJECTVALIDATOR_REQUIRE(StatusResponse,IssueInstant);
75             XMLOBJECTVALIDATOR_REQUIRE(StatusResponse,Status);
76             if (!XMLString::equals(SAMLConstants::SAML20_VERSION, ptr->getVersion()))
77                 throw ValidationException("StatusResponse has wrong SAML Version.");
78         END_XMLOBJECTVALIDATOR;
79
80         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Extensions);
81             if (!ptr->hasChildren())
82                 throw ValidationException("Extensions must have at least one child element.");
83             const list<XMLObject*>& anys=ptr->getXMLObjects();
84             for_each(anys.begin(),anys.end(),checkWildcardNS());
85         END_XMLOBJECTVALIDATOR;
86
87         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,StatusCode);
88             XMLOBJECTVALIDATOR_REQUIRE(StatusCode,Value);
89
90             //TODO test this !!!
91             // If this is a top-level StatusCode (ie. parent is a Status),
92             // then there are only 4 valid values per SAML Core.
93             if (ptr->getParent()!=NULL && ptr->getParent()->getElementQName().hasLocalPart())
94             {
95                 QName pq = ptr->getParent()->getElementQName();
96
97                 if ( XMLString::equals(pq.getNamespaceURI(), SAMLConstants::SAML20P_NS) &&
98                         XMLString::equals(pq.getLocalPart(), Status::LOCAL_NAME))
99                 {
100                     const XMLCh* code = ptr->getValue();
101
102                     if (!XMLString::equals(code, StatusCode::SUCCESS) &&
103                         !XMLString::equals(code, StatusCode::REQUESTER) &&
104                         !XMLString::equals(code, StatusCode::RESPONDER) &&
105                         !XMLString::equals(code, StatusCode::VERSION_MISMATCH) )
106                     {
107                         throw ValidationException("Invalid value for top-level StatusCode");
108                     }
109                 }
110             }
111         END_XMLOBJECTVALIDATOR;
112
113         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Status);
114             XMLOBJECTVALIDATOR_REQUIRE(Status,StatusCode);
115         END_XMLOBJECTVALIDATOR;
116
117         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AssertionIDRequest,Request);
118             RequestSchemaValidator::validate(xmlObject);
119             XMLOBJECTVALIDATOR_NONEMPTY(AssertionIDRequest,AssertionIDRef);
120         END_XMLOBJECTVALIDATOR;
121
122         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,RequestedAuthnContext);
123             if (ptr->getAuthnContextClassRefs().empty() && ptr->getAuthnContextDeclRefs().empty())
124                 throw xmltooling::ValidationException("RequestedAuthnContext must have at least one AuthnContextClassRef or AuthnContextDeclRef"); 
125             if (!ptr->getAuthnContextClassRefs().empty() && !ptr->getAuthnContextDeclRefs().empty())
126                 throw xmltooling::ValidationException("RequestedAuthnContext may not have both AuthnContextClassRef and AuthnContextDeclRef"); 
127             if (!XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_EXACT) &&
128                 !XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_MINIMUM) &&
129                 !XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_MAXIMUM) &&
130                 !XMLString::equals(ptr->getComparison(),RequestedAuthnContext::COMPARISON_BETTER))
131                 throw ValidationException("RequestedAuthnContext Comparison attribute must be one of: 'exact', 'minimum', 'maximum', or 'better'.");
132         END_XMLOBJECTVALIDATOR;
133
134         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AuthnQuery,SubjectQuery);
135             SubjectQuerySchemaValidator::validate(xmlObject);
136         END_XMLOBJECTVALIDATOR;
137
138         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AttributeQuery,SubjectQuery);
139             SubjectQuerySchemaValidator::validate(xmlObject);
140             //TODO Name/NameFormat pairs of child Attributes must be unique 
141             //   - whether and how to implement efficiently?
142         END_XMLOBJECTVALIDATOR;
143
144         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AuthzDecisionQuery,SubjectQuery);
145             SubjectQuerySchemaValidator::validate(xmlObject);
146             XMLOBJECTVALIDATOR_REQUIRE(AuthzDecisionQuery,Resource);
147             XMLOBJECTVALIDATOR_NONEMPTY(AuthzDecisionQuery,Action);
148         END_XMLOBJECTVALIDATOR;
149
150         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,IDPEntry);
151             XMLOBJECTVALIDATOR_REQUIRE(IDPEntry,ProviderID);
152         END_XMLOBJECTVALIDATOR;
153
154         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,IDPList);
155             XMLOBJECTVALIDATOR_NONEMPTY(IDPList,IDPEntry);
156         END_XMLOBJECTVALIDATOR;
157
158         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,Scoping);
159             pair<bool,int> pc = ptr->getProxyCount();
160             if (pc.first && pc.second < 0) 
161                 throw xmltooling::ValidationException("ProxyCount attribute on Scoping element must be non-negative"); 
162         END_XMLOBJECTVALIDATOR;
163
164         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,AuthnRequest,Request);
165             RequestSchemaValidator::validate(xmlObject);
166             if (ptr->getAssertionConsumerServiceIndex().first 
167                     && (ptr->getAssertionConsumerServiceURL()!=NULL || ptr->getProtocolBinding()!=NULL))
168                 throw xmltooling::ValidationException("On AuthnRequest AssertionConsumerServiceIndex is mutually exclusive with both AssertionConsumerServiceURL and ProtocolBinding");
169         END_XMLOBJECTVALIDATOR;
170
171         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,Response,StatusResponse);
172             StatusResponseSchemaValidator::validate(xmlObject);
173         END_XMLOBJECTVALIDATOR;
174
175         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ArtifactResolve,Request);
176             RequestSchemaValidator::validate(xmlObject);
177             XMLOBJECTVALIDATOR_REQUIRE(ArtifactResolve,Artifact);
178         END_XMLOBJECTVALIDATOR;
179
180         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ArtifactResponse,StatusResponse);
181             StatusResponseSchemaValidator::validate(xmlObject);
182         END_XMLOBJECTVALIDATOR;
183
184         BEGIN_XMLOBJECTVALIDATOR(SAML_DLLLOCAL,NewEncryptedID);
185             XMLOBJECTVALIDATOR_REQUIRE(NewEncryptedID,EncryptedData);
186         END_XMLOBJECTVALIDATOR;
187
188         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ManageNameIDRequest,Request);
189             RequestSchemaValidator::validate(xmlObject);
190             XMLOBJECTVALIDATOR_ONLYONEOF(ManageNameIDRequest,NameID,EncryptedID);
191             XMLOBJECTVALIDATOR_ONLYONEOF3(ManageNameIDRequest,NewID,NewEncryptedID,Terminate);
192         END_XMLOBJECTVALIDATOR;
193
194         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,ManageNameIDResponse,StatusResponse);
195             StatusResponseSchemaValidator::validate(xmlObject);
196         END_XMLOBJECTVALIDATOR;
197
198         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,LogoutRequest,Request);
199             RequestSchemaValidator::validate(xmlObject);
200             XMLOBJECTVALIDATOR_ONLYONEOF3(LogoutRequest,BaseID,NameID,EncryptedID);
201         END_XMLOBJECTVALIDATOR;
202
203         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,LogoutResponse,StatusResponse);
204             StatusResponseSchemaValidator::validate(xmlObject);
205         END_XMLOBJECTVALIDATOR;
206
207         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,NameIDMappingRequest,Request);
208             RequestSchemaValidator::validate(xmlObject);
209             XMLOBJECTVALIDATOR_ONLYONEOF3(NameIDMappingRequest,BaseID,NameID,EncryptedID);
210             XMLOBJECTVALIDATOR_REQUIRE(NameIDMappingRequest,NameIDPolicy);
211         END_XMLOBJECTVALIDATOR;
212
213         BEGIN_XMLOBJECTVALIDATOR_SUB(SAML_DLLLOCAL,NameIDMappingResponse,StatusResponse);
214             StatusResponseSchemaValidator::validate(xmlObject);
215             XMLOBJECTVALIDATOR_ONLYONEOF(NameIDMappingResponse,NameID,EncryptedID);
216         END_XMLOBJECTVALIDATOR;
217
218
219     };
220 };
221
222 #define REGISTER_ELEMENT(cname) \
223     q=QName(SAMLConstants::SAML20P_NS,cname::LOCAL_NAME); \
224     XMLObjectBuilder::registerBuilder(q,new cname##Builder()); \
225     ProtocolSchemaValidators.registerValidator(q,new cname##SchemaValidator())
226     
227 #define REGISTER_TYPE(cname) \
228     q=QName(SAMLConstants::SAML20P_NS,cname::TYPE_NAME); \
229     XMLObjectBuilder::registerBuilder(q,new cname##Builder()); \
230     ProtocolSchemaValidators.registerValidator(q,new cname##SchemaValidator())
231
232 #define REGISTER_ELEMENT_NOVAL(cname) \
233     q=QName(SAMLConstants::SAML20P_NS,cname::LOCAL_NAME); \
234     XMLObjectBuilder::registerBuilder(q,new cname##Builder());
235     
236 #define REGISTER_TYPE_NOVAL(cname) \
237     q=QName(SAMLConstants::SAML20P_NS,cname::TYPE_NAME); \
238     XMLObjectBuilder::registerBuilder(q,new cname##Builder());
239
240 ValidatorSuite opensaml::saml2p::ProtocolSchemaValidators("ProtocolSchemaValidators");
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(StatusResponse);
296     REGISTER_TYPE_NOVAL(StatusDetail);
297     REGISTER_TYPE_NOVAL(Terminate);
298 }