2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
22 * TimeAccessControl.cpp
24 * Access control plugin for time-based policies.
29 #include <shibsp/exceptions.h>
30 #include <shibsp/AccessControl.h>
31 #include <shibsp/SessionCache.h>
32 #include <shibsp/SPRequest.h>
34 #include <boost/lexical_cast.hpp>
35 #include <boost/algorithm/string.hpp>
36 #include <boost/ptr_container/ptr_vector.hpp>
37 #include <xmltooling/unicode.h>
38 #include <xmltooling/util/DateTime.h>
39 #include <xmltooling/util/XMLHelper.h>
40 #include <xercesc/util/XMLUniDefs.hpp>
42 using namespace shibsp;
43 using namespace xmltooling;
44 using namespace xercesc;
45 using namespace boost;
50 class Rule : public AccessControl
53 Rule(const DOMElement* e);
56 Lockable* lock() {return this;}
59 aclresult_t authorized(const SPRequest& request, const Session* session) const;
62 enum { TM_AUTHN, TM_TIME, TM_YEAR, TM_MONTH, TM_DAY, TM_HOUR, TM_MINUTE, TM_SECOND, TM_WDAY } m_type;
63 enum { OP_LT, OP_LE, OP_EQ, OP_GE, OP_GT } m_op;
67 class TimeAccessControl : public AccessControl
70 TimeAccessControl(const DOMElement* e);
71 ~TimeAccessControl() {}
79 aclresult_t authorized(const SPRequest& request, const Session* session) const;
82 enum { OP_AND, OP_OR } m_op;
83 ptr_vector<Rule> m_rules;
86 AccessControl* SHIBSP_DLLLOCAL TimeAccessControlFactory(const DOMElement* const & e)
88 return new TimeAccessControl(e);
91 static const XMLCh _operator[] = UNICODE_LITERAL_8(o,p,e,r,a,t,o,r);
92 static const XMLCh AND[] = UNICODE_LITERAL_3(A,N,D);
93 static const XMLCh OR[] = UNICODE_LITERAL_2(O,R);
94 static const XMLCh Day[] = UNICODE_LITERAL_3(D,a,y);
95 static const XMLCh DayOfWeek[] = UNICODE_LITERAL_9(D,a,y,O,f,W,e,e,k);
96 static const XMLCh Hour[] = UNICODE_LITERAL_4(H,o,u,r);
97 static const XMLCh Minute[] = UNICODE_LITERAL_6(M,i,n,u,t,e);
98 static const XMLCh Month[] = UNICODE_LITERAL_5(M,o,n,t,h);
99 static const XMLCh Second[] = UNICODE_LITERAL_6(S,e,c,o,n,d);
100 static const XMLCh Time[] = UNICODE_LITERAL_4(T,i,m,e);
101 static const XMLCh TimeSinceAuthn[] = UNICODE_LITERAL_14(T,i,m,e,S,i,n,c,e,A,u,t,h,n);
102 static const XMLCh Year[] = UNICODE_LITERAL_4(Y,e,a,r);
105 Rule::Rule(const DOMElement* e)
107 if (XMLString::equals(e->getLocalName(), TimeSinceAuthn)) {
109 DateTime dur(e->getTextContent());
111 m_value = dur.getEpoch(true);
115 auto_ptr_char temp(e->getTextContent());
116 string s(temp.get() ? temp.get() : "");
117 vector<string> tokens;
118 if (split(tokens, s, is_space(), algorithm::token_compress_on).size() != 2)
119 throw ConfigurationException("Time-based rule requires element content of the form \"LT|LE|EQ|GE|GT value\".");
120 string& op = tokens.front();
121 if (op == "LT") m_op = OP_LT;
122 else if (op == "LE") m_op = OP_LE;
123 else if (op == "EQ") m_op = OP_EQ;
124 else if (op == "GE") m_op = OP_GE;
125 else if (op == "GT") m_op = OP_GT;
127 throw ConfigurationException("First component of time-based rule must be one of LT, LE, EQ, GE, GT.");
129 if (XMLString::equals(e->getLocalName(), Time)) {
131 auto_ptr_XMLCh widen(tokens.back().c_str());
132 DateTime dt(widen.get());
134 m_value = dt.getEpoch();
138 m_value = lexical_cast<time_t>(tokens.back());
140 if (XMLString::equals(e->getLocalName(), Year)) m_type = TM_YEAR;
141 else if (XMLString::equals(e->getLocalName(), Month)) m_type = TM_MONTH;
142 else if (XMLString::equals(e->getLocalName(), Day)) m_type = TM_DAY;
143 else if (XMLString::equals(e->getLocalName(), Hour)) m_type = TM_HOUR;
144 else if (XMLString::equals(e->getLocalName(), Minute)) m_type = TM_MINUTE;
145 else if (XMLString::equals(e->getLocalName(), Second)) m_type = TM_SECOND;
146 else if (XMLString::equals(e->getLocalName(), DayOfWeek)) m_type = TM_WDAY;
148 throw ConfigurationException("Unrecognized time-based rule.");
152 <AccessControlProvider type="Time" operator="AND|OR">
153 <TimeSinceAuthn>PT1H</TimeSinceAuthn>
154 <Time> LT|LE|EQ|GE|GT ISO </Time>
155 <Year> LT|LE|EQ|GE|GT nn </Year>
156 <Month> LT|LE|EQ|GE|GT nn </Month>
157 <Day> LT|LE|EQ|GE|GT nn </Day>
158 <Hour> LT|LE|EQ|GE|GT nn </Hour>
159 <Minute> LT|LE|EQ|GE|GT nn </Minute>
160 <Second> LT|LE|EQ|GE|GT nn </Second>
161 <DayOfWeek> LT|LE|EQ|GE|GT 0-6 </DayOfWeek>
162 </AccessControlProvider>
165 AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Session* session) const
169 if (m_type == TM_AUTHN) {
171 auto_ptr_XMLCh atime(session->getAuthnInstant());
174 DateTime dt(atime.get());
176 if (time(nullptr) - dt.getEpoch() <= m_value)
177 return shib_acl_true;
178 request.log(SPRequest::SPDebug, "elapsed time since authentication exceeds limit");
179 return shib_acl_false;
181 catch (std::exception& e) {
182 request.log(SPRequest::SPError, e.what());
186 request.log(SPRequest::SPDebug, "session or authentication time unavailable");
187 return shib_acl_false;
190 // Extract value from tm struct or time directly.
191 operand = time(nullptr);
192 if (m_type != TM_TIME) {
193 #ifndef HAVE_LOCALTIME_R
194 struct tm* ptime = localtime(&operand);
197 struct tm* ptime = localtime_r(&operand, &res);
201 operand = ptime->tm_year + 1900;
204 operand = ptime->tm_mon + 1;
207 operand = ptime->tm_mday;
210 operand = ptime->tm_hour;
213 operand = ptime->tm_min;
216 operand = ptime->tm_sec;
219 operand = ptime->tm_wday;
224 // Compare operand to test value in rule using rule operator.
227 return (operand < m_value) ? shib_acl_true : shib_acl_false;
229 return (operand <= m_value) ? shib_acl_true : shib_acl_false;
231 return (operand == m_value) ? shib_acl_true : shib_acl_false;
233 return (operand >= m_value) ? shib_acl_true : shib_acl_false;
235 return (operand > m_value) ? shib_acl_true : shib_acl_false;
237 return shib_acl_false;
240 TimeAccessControl::TimeAccessControl(const DOMElement* e) : m_op(OP_AND)
242 const XMLCh* op = e ? e->getAttributeNS(nullptr, _operator) : nullptr;
243 if (XMLString::equals(op, OR))
245 else if (op && *op && !XMLString::equals(op, AND))
246 throw ConfigurationException("Unrecognized operator in Time AccessControl configuration.");
248 e = XMLHelper::getFirstChildElement(e);
250 auto_ptr<Rule> np(new Rule(e));
251 m_rules.push_back(np.get());
253 e = XMLHelper::getNextSiblingElement(e);
256 throw ConfigurationException("Time AccessControl plugin requires at least one rule.");
259 AccessControl::aclresult_t TimeAccessControl::authorized(const SPRequest& request, const Session* session) const
264 for (ptr_vector<Rule>::const_iterator i = m_rules.begin(); i != m_rules.end(); ++i) {
265 if (i->authorized(request, session) != shib_acl_true) {
266 request.log(SPRequest::SPDebug, "time-based rule unsuccessful, denying access");
267 return shib_acl_false;
270 return shib_acl_true;
275 for (ptr_vector<Rule>::const_iterator i = m_rules.begin(); i != m_rules.end(); ++i) {
276 if (i->authorized(request,session) == shib_acl_true)
277 return shib_acl_true;
279 request.log(SPRequest::SPDebug, "all time-based rules unsuccessful, denying access");
280 return shib_acl_false;
283 request.log(SPRequest::SPWarn, "unknown operator in access control policy, denying access");
284 return shib_acl_false;