2 * Copyright 2001-2005 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.
17 /* XMLAccessControl.cpp - an XML-based access control syntax
24 #include <shib-target/shib-target.h>
26 #ifndef HAVE_STRCASECMP
27 # define strcasecmp stricmp
32 using namespace shibboleth;
33 using namespace shibtarget;
34 using namespace xmlproviders::logging;
39 virtual bool authorized(ShibTarget* st, ISessionCacheEntry* entry) const=0;
42 class Rule : public IAuthz
45 Rule(const DOMElement* e);
47 bool authorized(ShibTarget* st, ISessionCacheEntry* entry) const;
51 vector <string> m_vals;
54 class Operator : public IAuthz
57 Operator(const DOMElement* e);
59 bool authorized(ShibTarget* st, ISessionCacheEntry* entry) const;
62 enum operator_t { OP_NOT, OP_AND, OP_OR } m_op;
63 vector<IAuthz*> m_operands;
66 class XMLAccessControlImpl : public ReloadableXMLFileImpl
69 XMLAccessControlImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { init(); }
70 XMLAccessControlImpl(const DOMElement* e) : ReloadableXMLFileImpl(e) { init(); }
72 ~XMLAccessControlImpl() {delete m_rootAuthz;}
77 class XMLAccessControl : public IAccessControl, public ReloadableXMLFile
80 XMLAccessControl(const DOMElement* e) : ReloadableXMLFile(e) {}
81 ~XMLAccessControl() {}
83 virtual bool authorized(ShibTarget* st, ISessionCacheEntry* entry) const;
86 virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
87 virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
91 IPlugIn* XMLAccessControlFactory(const DOMElement* e)
93 auto_ptr<XMLAccessControl> a(new XMLAccessControl(e));
94 a->getImplementation();
98 Rule::Rule(const DOMElement* e)
100 auto_ptr_char req(e->getAttributeNS(NULL,SHIB_L(require)));
101 if (!req.get() || !*req.get())
102 throw MalformedException("Access control rule missing require attribute");
105 auto_ptr_char vals(e->hasChildNodes() ? e->getFirstChild()->getNodeValue() : NULL);
108 const char* token=strtok_r(const_cast<char*>(vals.get())," ",&pos);
110 const char* token=strtok(const_cast<char*>(vals.get())," ");
113 m_vals.push_back(token);
115 token=strtok_r(NULL," ",&pos);
117 token=strtok(NULL," ");
122 bool Rule::authorized(ShibTarget* st, ISessionCacheEntry* entry) const
124 // Map alias in rule to the attribute.
125 Iterator<IAAP*> provs=st->getApplication()->getAAPProviders();
126 AAP wrapper(provs,m_alias.c_str());
127 if (wrapper.fail()) {
128 st->log(ShibTarget::LogLevelWarn, string("AccessControl plugin didn't recognize rule (") + m_alias + "), check AAP for corresponding Alias");
132 st->log(ShibTarget::LogLevelWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");
136 // Find the corresponding attribute. This isn't very efficient...
137 ISessionCacheEntry::CachedResponse cr=entry->getResponse();
138 Iterator<SAMLAssertion*> a_iter(cr.filtered ? cr.filtered->getAssertions() : EMPTY(SAMLAssertion*));
139 while (a_iter.hasNext()) {
140 SAMLAssertion* assert=a_iter.next();
141 Iterator<SAMLStatement*> statements=assert->getStatements();
142 while (statements.hasNext()) {
143 SAMLAttributeStatement* astate=dynamic_cast<SAMLAttributeStatement*>(statements.next());
146 Iterator<SAMLAttribute*> attrs=astate->getAttributes();
147 while (attrs.hasNext()) {
148 SAMLAttribute* attr=attrs.next();
149 if (!XMLString::compareString(attr->getName(),wrapper->getName()) &&
150 !XMLString::compareString(attr->getNamespace(),wrapper->getNamespace())) {
151 // Now we have to intersect the attribute's values against the rule's list.
152 Iterator<string> vals=attr->getSingleByteValues();
155 for (vector<string>::const_iterator ival=m_vals.begin(); ival!=m_vals.end(); ival++) {
157 while (vals.hasNext()) {
158 const string& v=vals.next();
159 if ((wrapper->getCaseSensitive() && v == *ival) || (!wrapper->getCaseSensitive() && !strcasecmp(v.c_str(),ival->c_str()))) {
160 st->log(ShibTarget::LogLevelDebug, string("XMLAccessControl plugin expecting (" + *ival + "), got it, authz granted"));
164 st->log(ShibTarget::LogLevelDebug, string("XMLAccessControl plugin expecting (" + *ival + "), got (" + v + "), authz not granted"));
176 Operator::Operator(const DOMElement* e)
178 if (saml::XML::isElementNamed(e,shibtarget::XML::SHIBTARGET_NS,SHIB_L(NOT)))
180 else if (saml::XML::isElementNamed(e,shibtarget::XML::SHIBTARGET_NS,SHIB_L(AND)))
182 else if (saml::XML::isElementNamed(e,shibtarget::XML::SHIBTARGET_NS,SHIB_L(OR)))
185 throw MalformedException("Unrecognized operator in access control rule");
188 e=saml::XML::getFirstChildElement(e);
189 if (saml::XML::isElementNamed(e,shibtarget::XML::SHIBTARGET_NS,SHIB_L(Rule)))
190 m_operands.push_back(new Rule(e));
192 m_operands.push_back(new Operator(e));
197 e=saml::XML::getNextSiblingElement(e);
199 if (saml::XML::isElementNamed(e,shibtarget::XML::SHIBTARGET_NS,SHIB_L(Rule)))
200 m_operands.push_back(new Rule(e));
202 m_operands.push_back(new Operator(e));
203 e=saml::XML::getNextSiblingElement(e);
206 catch (SAMLException&) {
212 Operator::~Operator()
214 for (vector<IAuthz*>::iterator i=m_operands.begin(); i!=m_operands.end(); i++)
218 bool Operator::authorized(ShibTarget* st, ISessionCacheEntry* entry) const
222 return !m_operands[0]->authorized(st,entry);
226 for (vector<IAuthz*>::const_iterator i=m_operands.begin(); i!=m_operands.end(); i++) {
227 if (!(*i)->authorized(st,entry))
235 for (vector<IAuthz*>::const_iterator i=m_operands.begin(); i!=m_operands.end(); i++) {
236 if ((*i)->authorized(st,entry))
242 st->log(ShibTarget::LogLevelWarn,"Unknown operation in access control policy, denying access");
246 void XMLAccessControlImpl::init()
251 Category* log=&Category::getInstance(XMLPROVIDERS_LOGCAT".AccessControl");
254 // We need to move below the AccessControl root element if the policy is in a separate file.
255 // Unlike most of the plugins, an inline policy will end up handing us the first inline
256 // content element, and not the outer wrapper.
257 const DOMElement* rootElement=ReloadableXMLFileImpl::m_root;
258 if (saml::XML::isElementNamed(rootElement,shibtarget::XML::SHIBTARGET_NS,SHIB_L(AccessControl)))
259 rootElement = saml::XML::getFirstChildElement(rootElement);
261 if (saml::XML::isElementNamed(rootElement,shibtarget::XML::SHIBTARGET_NS,SHIB_L(Rule)))
262 m_rootAuthz=new Rule(rootElement);
264 m_rootAuthz=new Operator(rootElement);
266 catch (SAMLException& e) {
267 log->errorStream() << "Error while parsing access control configuration: " << e.what() << xmlproviders::logging::eol;
273 log->error("Unexpected error while parsing access control configuration");
279 ReloadableXMLFileImpl* XMLAccessControl::newImplementation(const char* pathname, bool first) const
281 return new XMLAccessControlImpl(pathname);
284 ReloadableXMLFileImpl* XMLAccessControl::newImplementation(const DOMElement* e, bool first) const
286 return new XMLAccessControlImpl(e);
289 bool XMLAccessControl::authorized(ShibTarget* st, ISessionCacheEntry* entry) const
291 return static_cast<XMLAccessControlImpl*>(getImplementation())->m_rootAuthz->authorized(st,entry);