4 #include "ConfigParser.h"
5 #include "HttpRequest.h"
7 #include "acl/FilledChecklist.h"
8 #include "adaptation/Service.h"
9 #include "adaptation/ServiceGroups.h"
10 #include "adaptation/AccessRule.h"
11 #include "adaptation/Config.h"
12 #include "adaptation/AccessCheck.h"
15 cbdata_type
Adaptation::AccessCheck::CBDATA_AccessCheck
= CBDATA_UNKNOWN
;
18 Adaptation::AccessCheck::Start(Method method
, VectPoint vp
,
19 HttpRequest
*req
, HttpReply
*rep
, AccessCheckCallback
*cb
, void *cbdata
)
22 if (Config::Enabled
) {
23 // the new check will call the callback and delete self, eventually
24 AccessCheck
*check
= new AccessCheck(method
, vp
, req
, rep
, cb
, cbdata
);
29 debugs(83, 3, HERE
<< "adaptation off, skipping");
33 Adaptation::AccessCheck::AccessCheck(Method aMethod
,
37 AccessCheckCallback
*aCallback
,
38 void *aCallbackData
): AsyncJob("AccessCheck"), done(FALSE
)
40 // TODO: assign these at creation time
45 req
= HTTPMSGLOCK(aReq
);
46 rep
= aRep
? HTTPMSGLOCK(aRep
) : NULL
;
50 callback_data
= cbdataReference(aCallbackData
);
54 debugs(93, 5, HERE
<< "AccessCheck constructed for " << methodStr(method
) << " " << vectPointStr(point
));
57 Adaptation::AccessCheck::~AccessCheck()
62 cbdataReferenceDone(callback_data
);
66 * Walk the access rules list and find all classes that have at least
67 * one service with matching method and vectoring point.
70 Adaptation::AccessCheck::check()
72 debugs(93, 4, HERE
<< "start checking");
74 typedef AccessRules::iterator ARI
;
75 for (ARI i
= AllRules().begin(); i
!= AllRules().end(); ++i
) {
78 * We only find the first matching service because we only need
79 * one matching service to justify ACL-checking a class. We might
80 * use other services belonging to the class if the first service
81 * turns out to be unusable for some reason.
84 ServicePointer service
= findBestService(*r
, false);
85 if (service
!= NULL
) {
86 debugs(93, 5, HERE
<< "check: rule '" << r
->id
<< "' has candidate service '" << service
->cfg().key
<< "'");
94 // XXX: Here and everywhere we call FindRule(topCandidate()):
95 // Once we identified the candidate, we should not just ignore it
96 // if reconfigure changes rules. We should either lock the rule to
97 // prevent reconfigure from stealing it or restart the check with
98 // new rules. Throwing an exception may also be appropriate.
100 Adaptation::AccessCheck::checkCandidates()
102 debugs(93, 4, HERE
<< "has " << candidates
.size() << " rules");
104 while (!candidates
.empty()) {
105 if (AccessRule
*r
= FindRule(topCandidate())) {
106 /* BUG 2526: what to do when r->acl is empty?? */
107 // XXX: we do not have access to conn->rfc931 here.
108 acl_checklist
= new ACLFilledChecklist(r
->acl
, req
, dash_str
);
109 acl_checklist
->reply
= rep
? HTTPMSGLOCK(rep
) : NULL
;
110 acl_checklist
->nonBlockingCheck(AccessCheckCallbackWrapper
, this);
114 candidates
.shift(); // the rule apparently went away (reconfigure)
117 // when there are no canidates, fake answer 1
118 debugs(93, 4, HERE
<< "NO candidates left");
123 Adaptation::AccessCheck::AccessCheckCallbackWrapper(int answer
, void *data
)
125 debugs(93, 8, HERE
<< "callback answer=" << answer
);
126 AccessCheck
*ac
= (AccessCheck
*)data
;
128 /** \todo AYJ 2008-06-12: If answer == ACCESS_REQ_PROXY_AUTH
129 * we should be kicking off an authentication before continuing
130 * with this request. see bug 2400 for details.
132 ac
->noteAnswer(answer
==ACCESS_ALLOWED
);
136 Adaptation::AccessCheck::noteAnswer(int answer
)
138 debugs(93, 5, HERE
<< "AccessCheck::noteAnswer " << answer
);
139 if (candidates
.size())
140 debugs(93, 5, HERE
<< "was checking rule" << topCandidate());
143 candidates
.shift(); // the rule did not match
149 * We use an event here to break deep function call sequences
151 // XXX: use AsyncCall for callback and remove
152 CallJobHere(93, 5, this, Adaptation::AccessCheck::do_callback
);
156 Adaptation::AccessCheck::do_callback()
160 if (candidates
.size())
161 debugs(93, 3, HERE
<< "was checking rule" << topCandidate());
163 void *validated_cbdata
;
164 if (!cbdataReferenceValidDone(callback_data
, &validated_cbdata
)) {
165 debugs(93,3,HERE
<< "do_callback: callback_data became invalid, skipping");
169 ServicePointer service
= NULL
;
170 if (candidates
.size()) {
171 if (AccessRule
*r
= FindRule(topCandidate())) {
172 service
= findBestService(*r
, true);
174 debugs(93,3,HERE
<< "do_callback: with service " << service
->cfg().uri
);
176 debugs(93,3,HERE
<< "do_callback: no service for rule" << r
->id
);
178 debugs(93,3,HERE
<< "do_callback: no rule" << topCandidate());
180 candidates
.shift(); // done with topCandidate()
182 debugs(93,3,HERE
<< "do_callback: no candidate rules");
185 callback(service
, validated_cbdata
);
189 Adaptation::ServicePointer
190 Adaptation::AccessCheck::findBestService(AccessRule
&r
, bool preferUp
)
193 const char *what
= preferUp
? "up " : "";
194 debugs(93,7,HERE
<< "looking for the first matching " <<
195 what
<< "service in group " << r
.groupId
);
197 ServicePointer secondBest
;
199 ServiceGroup
*g
= FindGroup(r
.groupId
);
202 debugs(93,5,HERE
<< "lost " << r
.groupId
<< " group in rule" << r
.id
);
203 return ServicePointer();
206 ServiceGroup::Loop
loop(g
->initialServices());
207 typedef ServiceGroup::iterator SGI
;
208 for (SGI i
= loop
.begin
; i
!= loop
.end
; ++i
) {
210 ServicePointer service
= FindService(*i
);
215 if (method
!= service
->cfg().method
)
218 if (point
!= service
->cfg().point
)
221 // sending a message to a broken service is likely to cause errors
222 if (service
->cfg().bypass
&& service
->broken())
226 // sending a message to a service that does not want it is useless
227 // note that we cannot check wantsUrl for service that is not "up"
228 // note that even essential services are skipped on unwanted URLs!
229 if (!service
->wantsUrl(req
->urlpath
))
233 secondBest
= service
;
235 // the caller asked for an "up" service and we can bypass this one
236 if (service
->cfg().bypass
)
238 debugs(93,5,HERE
<< "cannot skip an essential down service");
239 what
= "down-but-essential ";
243 debugs(93,5,HERE
<< "found first matching " <<
244 what
<< "service for " << r
.groupId
<< " group in rule" << r
.id
<<
245 ": " << service
->cfg().key
);
250 if (secondBest
!= NULL
) {
252 debugs(93,5,HERE
<< "found first matching " <<
253 what
<< "service for " << r
.groupId
<< " group in rule" << r
.id
<<
254 ": " << secondBest
->cfg().key
);
258 debugs(93,5,HERE
<< "found no matching " <<
259 what
<< "services for " << r
.groupId
<< " group in rule" << r
.id
);
260 return ServicePointer();