/*\r
- * Copyright 2001-2005 Internet2\r
+ * Copyright 2001-2007 Internet2\r
* \r
* Licensed under the Apache License, Version 2.0 (the "License");\r
* you may not use this file except in compliance with the License.\r
#include "exceptions.h"\r
#include "AccessControl.h"\r
#include "SessionCache.h"\r
+#include "SPRequest.h"\r
+#include "attribute/Attribute.h"\r
\r
#include <xmltooling/util/ReloadableXMLFile.h>\r
#include <xmltooling/util/XMLHelper.h>\r
#include <xercesc/util/XMLUniDefs.hpp>\r
+#include <xercesc/util/regx/RegularExpression.hpp>\r
\r
#ifndef HAVE_STRCASECMP\r
# define strcasecmp _stricmp\r
Lockable* lock() {return this;}\r
void unlock() {}\r
\r
- bool authorized(const SPRequest& request, const Session* session) const;\r
+ aclresult_t authorized(const SPRequest& request, const Session* session) const;\r
\r
private:\r
string m_alias;\r
vector <string> m_vals;\r
};\r
\r
+ class RuleRegex : public AccessControl\r
+ {\r
+ public:\r
+ RuleRegex(const DOMElement* e);\r
+ ~RuleRegex() {\r
+ delete m_re;\r
+ }\r
+ \r
+ Lockable* lock() {return this;}\r
+ void unlock() {}\r
+\r
+ aclresult_t authorized(const SPRequest& request, const Session* session) const;\r
+ \r
+ private:\r
+ string m_alias;\r
+ auto_arrayptr<char> m_exp;\r
+ RegularExpression* m_re;\r
+ };\r
+ \r
class Operator : public AccessControl\r
{\r
public:\r
Lockable* lock() {return this;}\r
void unlock() {}\r
\r
- bool authorized(const SPRequest& request, const Session* session) const;\r
+ aclresult_t authorized(const SPRequest& request, const Session* session) const;\r
\r
private:\r
enum operator_t { OP_NOT, OP_AND, OP_OR } m_op;\r
class XMLAccessControl : public AccessControl, public ReloadableXMLFile\r
{\r
public:\r
- XMLAccessControl(const DOMElement* e) : ReloadableXMLFile(e), m_rootAuthz(NULL) {\r
+ XMLAccessControl(const DOMElement* e)\r
+ : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".AccessControl")), m_rootAuthz(NULL) {\r
load(); // guarantees an exception or the policy is loaded\r
}\r
\r
delete m_rootAuthz;\r
}\r
\r
- bool authorized(const SPRequest& request, const Session* session) const;\r
+ aclresult_t authorized(const SPRequest& request, const Session* session) const;\r
\r
protected:\r
pair<bool,DOMElement*> load();\r
return new XMLAccessControl(e);\r
}\r
\r
- static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);\r
+ static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);\r
+ static const XMLCh ignoreCase[] = UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);\r
+ static const XMLCh ignoreOption[] = UNICODE_LITERAL_1(i);\r
+ static const XMLCh _list[] = UNICODE_LITERAL_4(l,i,s,t);\r
static const XMLCh require[] = UNICODE_LITERAL_7(r,e,q,u,i,r,e);\r
static const XMLCh NOT[] = UNICODE_LITERAL_3(N,O,T);\r
static const XMLCh AND[] = UNICODE_LITERAL_3(A,N,D);\r
static const XMLCh OR[] = UNICODE_LITERAL_2(O,R);\r
static const XMLCh _Rule[] = UNICODE_LITERAL_4(R,u,l,e);\r
+ static const XMLCh _RuleRegex[] = UNICODE_LITERAL_9(R,u,l,e,R,e,g,e,x);\r
}\r
\r
void SHIBSP_API shibsp::registerAccessControls()\r
{\r
- SPConfig::getConfig().AccessControlManager.registerFactory(XML_ACCESS_CONTROL, XMLAccessControlFactory);\r
+ SPConfig& conf=SPConfig::getConfig();\r
+ conf.AccessControlManager.registerFactory(XML_ACCESS_CONTROL, XMLAccessControlFactory);\r
+ conf.AccessControlManager.registerFactory("edu.internet2.middleware.shibboleth.sp.provider.XMLAccessControl", XMLAccessControlFactory);\r
}\r
\r
Rule::Rule(const DOMElement* e)\r
{\r
- xmltooling::auto_ptr_char req(e->getAttributeNS(NULL,require));\r
+ auto_ptr_char req(e->getAttributeNS(NULL,require));\r
if (!req.get() || !*req.get())\r
throw ConfigurationException("Access control rule missing require attribute");\r
m_alias=req.get();\r
+\r
+ auto_arrayptr<char> vals(toUTF8(e->hasChildNodes() ? e->getFirstChild()->getNodeValue() : NULL));\r
+ if (!vals.get())\r
+ return;\r
+ \r
+ const XMLCh* flag = e->getAttributeNS(NULL,_list);\r
+ if (flag && (*flag == chLatin_f || *flag == chDigit_0)) {\r
+ if (*vals.get())\r
+ m_vals.push_back(vals.get());\r
+ return;\r
+ }\r
\r
- xmltooling::auto_ptr_char vals(e->hasChildNodes() ? e->getFirstChild()->getNodeValue() : NULL);\r
#ifdef HAVE_STRTOK_R\r
char* pos=NULL;\r
- const char* token=strtok_r(const_cast<char*>(vals.get()),"/",&pos);\r
+ const char* token=strtok_r(const_cast<char*>(vals.get())," ",&pos);\r
#else\r
- const char* token=strtok(const_cast<char*>(vals.get()),"/");\r
+ const char* token=strtok(const_cast<char*>(vals.get())," ");\r
#endif\r
while (token) {\r
m_vals.push_back(token);\r
#ifdef HAVE_STRTOK_R\r
- token=strtok_r(NULL,"/",&pos);\r
+ token=strtok_r(NULL," ",&pos);\r
#else\r
- token=strtok(NULL,"/");\r
+ token=strtok(NULL," ");\r
#endif\r
}\r
}\r
\r
-bool Rule::authorized(const SPRequest& request, const Session* session) const\r
+AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Session* session) const\r
{\r
- /*\r
- TODO: port...\r
+ // We can make this more complex later using pluggable comparison functions,\r
+ // but for now, just a straight port to the new Attribute API.\r
+\r
// Map alias in rule to the attribute.\r
- Iterator<IAAP*> provs=st->getApplication()->getAAPProviders();\r
- AAP wrapper(provs,m_alias.c_str());\r
- if (wrapper.fail()) {\r
- st->log(ShibTarget::LogLevelWarn, string("AccessControl plugin didn't recognize rule (") + m_alias + "), check AAP for corresponding Alias");\r
- return false;\r
+ if (!session) {\r
+ request.log(SPRequest::SPWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");\r
+ return shib_acl_false;\r
}\r
- else if (!entry) {\r
- st->log(ShibTarget::LogLevelWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");\r
- return false;\r
+ \r
+ if (m_alias == "valid-user") {\r
+ if (session) {\r
+ request.log(SPRequest::SPDebug,"AccessControl plugin accepting valid-user based on active session");\r
+ return shib_acl_true;\r
+ }\r
+ return shib_acl_false;\r
+ }\r
+ if (m_alias == "user") {\r
+ for (vector<string>::const_iterator i=m_vals.begin(); i!=m_vals.end(); ++i) {\r
+ if (*i == request.getRemoteUser()) {\r
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting REMOTE_USER (") + *i + "), authz granted");\r
+ return shib_acl_true;\r
+ }\r
+ }\r
+ return shib_acl_false;\r
+ }\r
+ else if (m_alias == "authnContextClassRef") {\r
+ const char* ref = session->getAuthnContextClassRef();\r
+ for (vector<string>::const_iterator i=m_vals.begin(); ref && i!=m_vals.end(); ++i) {\r
+ if (!strcmp(i->c_str(),ref)) {\r
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextClassRef (") + *i + "), authz granted");\r
+ return shib_acl_true;\r
+ }\r
+ }\r
+ return shib_acl_false;\r
+ }\r
+ else if (m_alias == "authnContextDeclRef") {\r
+ const char* ref = session->getAuthnContextDeclRef();\r
+ for (vector<string>::const_iterator i=m_vals.begin(); ref && i!=m_vals.end(); ++i) {\r
+ if (!strcmp(i->c_str(),ref)) {\r
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextDeclRef (") + *i + "), authz granted");\r
+ return shib_acl_true;\r
+ }\r
+ }\r
+ return shib_acl_false;\r
+ }\r
+\r
+ // Find the attribute(s) matching the require rule.\r
+ pair<multimap<string,const Attribute*>::const_iterator, multimap<string,const Attribute*>::const_iterator> attrs =\r
+ session->getIndexedAttributes().equal_range(m_alias);\r
+ if (attrs.first == attrs.second) {\r
+ request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session");\r
+ return shib_acl_false;\r
+ }\r
+\r
+ for (; attrs.first != attrs.second; ++attrs.first) {\r
+ bool caseSensitive = attrs.first->second->isCaseSensitive();\r
+\r
+ // Now we have to intersect the attribute's values against the rule's list.\r
+ const vector<string>& vals = attrs.first->second->getSerializedValues();\r
+ for (vector<string>::const_iterator i=m_vals.begin(); i!=m_vals.end(); ++i) {\r
+ for (vector<string>::const_iterator j=vals.begin(); j!=vals.end(); ++j) {\r
+ if ((caseSensitive && *i == *j) || (!caseSensitive && !strcasecmp(i->c_str(),j->c_str()))) {\r
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting (") + *j + "), authz granted");\r
+ return shib_acl_true;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return shib_acl_false;\r
+}\r
+\r
+RuleRegex::RuleRegex(const DOMElement* e) : m_exp(toUTF8(e->hasChildNodes() ? e->getFirstChild()->getNodeValue() : NULL))\r
+{\r
+ auto_ptr_char req(e->getAttributeNS(NULL,require));\r
+ if (!req.get() || !*req.get() || !m_exp.get() || !*m_exp.get())\r
+ throw ConfigurationException("Access control rule missing require attribute or element content.");\r
+ m_alias=req.get();\r
+ \r
+ const XMLCh* flag = e->getAttributeNS(NULL,ignoreCase);\r
+ bool ignore = (flag && (*flag == chLatin_t || *flag == chDigit_1));\r
+ try {\r
+ m_re = new RegularExpression(e->getFirstChild()->getNodeValue(), (ignore ? ignoreOption : &chNull)); \r
+ }\r
+ catch (XMLException& ex) {\r
+ auto_ptr_char tmp(ex.getMessage());\r
+ throw ConfigurationException("Caught exception while parsing RuleRegex regular expression: $1", params(1,tmp.get()));\r
+ }\r
+}\r
+\r
+AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const Session* session) const\r
+{\r
+ // Map alias in rule to the attribute.\r
+ if (!session) {\r
+ request.log(SPRequest::SPWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?");\r
+ return shib_acl_false;\r
}\r
\r
- // Find the corresponding attribute. This isn't very efficient...\r
- pair<const char*,const SAMLResponse*> filtered=entry->getFilteredTokens(false,true);\r
- Iterator<SAMLAssertion*> a_iter(filtered.second ? filtered.second->getAssertions() : EMPTY(SAMLAssertion*));\r
- while (a_iter.hasNext()) {\r
- SAMLAssertion* assert=a_iter.next();\r
- Iterator<SAMLStatement*> statements=assert->getStatements();\r
- while (statements.hasNext()) {\r
- SAMLAttributeStatement* astate=dynamic_cast<SAMLAttributeStatement*>(statements.next());\r
- if (!astate)\r
- continue;\r
- Iterator<SAMLAttribute*> attrs=astate->getAttributes();\r
- while (attrs.hasNext()) {\r
- SAMLAttribute* attr=attrs.next();\r
- if (!XMLString::compareString(attr->getName(),wrapper->getName()) &&\r
- !XMLString::compareString(attr->getNamespace(),wrapper->getNamespace())) {\r
- // Now we have to intersect the attribute's values against the rule's list.\r
- Iterator<string> vals=attr->getSingleByteValues();\r
- if (!vals.hasNext())\r
- return false;\r
- for (vector<string>::const_iterator ival=m_vals.begin(); ival!=m_vals.end(); ival++) {\r
- vals.reset();\r
- while (vals.hasNext()) {\r
- const string& v=vals.next();\r
- if ((wrapper->getCaseSensitive() && v == *ival) || (!wrapper->getCaseSensitive() && !strcasecmp(v.c_str(),ival->c_str()))) {\r
- st->log(ShibTarget::LogLevelDebug, string("XMLAccessControl plugin expecting " + *ival + ", authz granted"));\r
- return true;\r
- }\r
- }\r
- }\r
+ if (m_alias == "valid-user") {\r
+ if (session) {\r
+ request.log(SPRequest::SPDebug,"AccessControl plugin accepting valid-user based on active session");\r
+ return shib_acl_true;\r
+ }\r
+ return shib_acl_false;\r
+ }\r
+\r
+ try {\r
+ if (m_alias == "user") {\r
+ if (m_re->matches(request.getRemoteUser().c_str())) {\r
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting REMOTE_USER (") + m_exp.get() + "), authz granted");\r
+ return shib_acl_true;\r
+ }\r
+ return shib_acl_false;\r
+ }\r
+ else if (m_alias == "authnContextClassRef") {\r
+ if (session->getAuthnContextClassRef() && m_re->matches(session->getAuthnContextClassRef())) {\r
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextClassRef (") + m_exp.get() + "), authz granted");\r
+ return shib_acl_true;\r
+ }\r
+ return shib_acl_false;\r
+ }\r
+ else if (m_alias == "authnContextDeclRef") {\r
+ if (session->getAuthnContextDeclRef() && m_re->matches(session->getAuthnContextDeclRef())) {\r
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting authnContextDeclRef (") + m_exp.get() + "), authz granted");\r
+ return shib_acl_true;\r
+ }\r
+ return shib_acl_false;\r
+ }\r
+\r
+ // Find the attribute(s) matching the require rule.\r
+ pair<multimap<string,const Attribute*>::const_iterator, multimap<string,const Attribute*>::const_iterator> attrs =\r
+ session->getIndexedAttributes().equal_range(m_alias);\r
+ if (attrs.first == attrs.second) {\r
+ request.log(SPRequest::SPWarn, string("rule requires attribute (") + m_alias + "), not found in session");\r
+ return shib_acl_false;\r
+ }\r
+\r
+ for (; attrs.first != attrs.second; ++attrs.first) {\r
+ // Now we have to intersect the attribute's values against the regular expression.\r
+ const vector<string>& vals = attrs.first->second->getSerializedValues();\r
+ for (vector<string>::const_iterator j=vals.begin(); j!=vals.end(); ++j) {\r
+ if (m_re->matches(j->c_str())) {\r
+ request.log(SPRequest::SPDebug, string("AccessControl plugin expecting (") + m_exp.get() + "), authz granted");\r
+ return shib_acl_true;\r
}\r
}\r
}\r
}\r
- */\r
- return true;\r
+ catch (XMLException& ex) {\r
+ auto_ptr_char tmp(ex.getMessage());\r
+ request.log(SPRequest::SPError, string("caught exception while parsing RuleRegex regular expression: ") + tmp.get());\r
+ }\r
+ \r
+ return shib_acl_false;\r
}\r
\r
Operator::Operator(const DOMElement* e)\r
e=XMLHelper::getFirstChildElement(e);\r
if (XMLString::equals(e->getLocalName(),_Rule))\r
m_operands.push_back(new Rule(e));\r
+ else if (XMLString::equals(e->getLocalName(),_RuleRegex))\r
+ m_operands.push_back(new RuleRegex(e));\r
else\r
m_operands.push_back(new Operator(e));\r
\r
while (e) {\r
if (XMLString::equals(e->getLocalName(),_Rule))\r
m_operands.push_back(new Rule(e));\r
+ else if (XMLString::equals(e->getLocalName(),_RuleRegex))\r
+ m_operands.push_back(new RuleRegex(e));\r
else\r
m_operands.push_back(new Operator(e));\r
e=XMLHelper::getNextSiblingElement(e);\r
for_each(m_operands.begin(),m_operands.end(),xmltooling::cleanup<AccessControl>());\r
}\r
\r
-bool Operator::authorized(const SPRequest& request, const Session* session) const\r
+AccessControl::aclresult_t Operator::authorized(const SPRequest& request, const Session* session) const\r
{\r
switch (m_op) {\r
case OP_NOT:\r
- return !m_operands[0]->authorized(request,session);\r
+ switch (m_operands.front()->authorized(request,session)) {\r
+ case shib_acl_true:\r
+ return shib_acl_false;\r
+ case shib_acl_false:\r
+ return shib_acl_true;\r
+ default:\r
+ return shib_acl_indeterminate;\r
+ }\r
\r
case OP_AND:\r
{\r
for (vector<AccessControl*>::const_iterator i=m_operands.begin(); i!=m_operands.end(); i++) {\r
- if (!(*i)->authorized(request,session))\r
- return false;\r
+ if ((*i)->authorized(request,session) != shib_acl_true)\r
+ return shib_acl_false;\r
}\r
- return true;\r
+ return shib_acl_true;\r
}\r
\r
case OP_OR:\r
{\r
for (vector<AccessControl*>::const_iterator i=m_operands.begin(); i!=m_operands.end(); i++) {\r
- if ((*i)->authorized(request,session))\r
- return true;\r
+ if ((*i)->authorized(request,session) == shib_acl_true)\r
+ return shib_acl_true;\r
}\r
- return false;\r
+ return shib_acl_false;\r
}\r
}\r
- //st->log(ShibTarget::LogLevelWarn,"Unknown operation in access control policy, denying access");\r
- return false;\r
+ request.log(SPRequest::SPWarn,"unknown operation in access control policy, denying access");\r
+ return shib_acl_false;\r
}\r
\r
pair<bool,DOMElement*> XMLAccessControl::load()\r
AccessControl* authz;\r
if (XMLString::equals(raw.second->getLocalName(),_Rule))\r
authz=new Rule(raw.second);\r
+ else if (XMLString::equals(raw.second->getLocalName(),_RuleRegex))\r
+ authz=new RuleRegex(raw.second);\r
else\r
authz=new Operator(raw.second);\r
\r
return make_pair(false,(DOMElement*)NULL);\r
}\r
\r
-bool XMLAccessControl::authorized(const SPRequest& request, const Session* session) const\r
+AccessControl::aclresult_t XMLAccessControl::authorized(const SPRequest& request, const Session* session) const\r
{\r
- return m_rootAuthz ? m_rootAuthz->authorized(request,session) : false;\r
+ return m_rootAuthz ? m_rootAuthz->authorized(request,session) : shib_acl_false;\r
}\r