4 #include "ConfigParser.h"
6 #include "HttpRequest.h"
8 #include "ACLChecklist.h"
9 #include "adaptation/Service.h"
10 #include "adaptation/ServiceGroups.h"
11 #include "adaptation/AccessRule.h"
12 #include "adaptation/Config.h"
13 #include "adaptation/AccessCheck.h"
16 cbdata_type
Adaptation::AccessCheck::CBDATA_AccessCheck
= CBDATA_UNKNOWN
;
19 Adaptation::AccessCheck::Start(Method method
, VectPoint vp
,
20 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, "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, "Adaptation::AccessCheck::check");
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, "Adaptation::AccessCheck::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, "Adaptation::AccessCheck has " << candidates
.size() << " rules");
104 while (!candidates
.empty()) {
105 if (AccessRule
*r
= FindRule(topCandidate())) {
106 // XXX: we do not have access to conn->rfc931 here.
107 acl_checklist
= aclChecklistCreate(r
->acl
, req
, dash_str
);
108 acl_checklist
->reply
= rep
? HTTPMSGLOCK(rep
) : NULL
;
109 acl_checklist
->nonBlockingCheck(AccessCheckCallbackWrapper
, this);
113 candidates
.shift(); // the rule apparently went away (reconfigure)
116 // when there are no canidates, fake answer 1
117 debugs(93, 4, "Adaptation::AccessCheck::check: NO candidates left");
122 Adaptation::AccessCheck::AccessCheckCallbackWrapper(int answer
, void *data
)
124 debugs(93, 8, "AccessCheckCallbackWrapper: answer=" << answer
);
125 AccessCheck
*ac
= (AccessCheck
*)data
;
127 /** \todo AYJ 2008-06-12: If answer == ACCESS_REQ_PROXY_AUTH
128 * we should be kicking off an authentication before continuing
129 * with this request. see bug 2400 for details.
131 ac
->noteAnswer(answer
==ACCESS_ALLOWED
);
135 Adaptation::AccessCheck::noteAnswer(int answer
)
137 debugs(93, 5, HERE
<< "AccessCheck::noteAnswer " << answer
);
138 if (candidates
.size())
139 debugs(93, 5, HERE
<< "was checking rule" << topCandidate());
142 candidates
.shift(); // the rule did not match
148 * We use an event here to break deep function call sequences
150 // XXX: use AsyncCall for callback and remove
151 CallJobHere(93, 5, this, Adaptation::AccessCheck::do_callback
);
155 Adaptation::AccessCheck::do_callback()
157 debugs(93, 3, "Adaptation::AccessCheck::do_callback");
159 if (candidates
.size())
160 debugs(93, 3, HERE
<< "was checking rule" << topCandidate());
162 void *validated_cbdata
;
163 if (!cbdataReferenceValidDone(callback_data
, &validated_cbdata
)) {
164 debugs(93,3,HERE
<< "do_callback: callback_data became invalid, skipping");
168 ServicePointer service
= NULL
;
169 if (candidates
.size()) {
170 if (AccessRule
*r
= FindRule(topCandidate())) {
171 service
= findBestService(*r
, true);
173 debugs(93,3,HERE
<< "do_callback: with service " << service
->cfg().uri
);
175 debugs(93,3,HERE
<< "do_callback: no service for rule" << r
->id
);
177 debugs(93,3,HERE
<< "do_callback: no rule" << topCandidate());
179 candidates
.shift(); // done with topCandidate()
181 debugs(93,3,HERE
<< "do_callback: no candidate rules");
184 callback(service
, validated_cbdata
);
188 Adaptation::ServicePointer
189 Adaptation::AccessCheck::findBestService(AccessRule
&r
, bool preferUp
) {
191 const char *what
= preferUp
? "up " : "";
192 debugs(93,7,HERE
<< "looking for the first matching " <<
193 what
<< "service in group " << r
.groupId
);
195 ServicePointer secondBest
;
197 ServiceGroup
*g
= FindGroup(r
.groupId
);
200 debugs(93,5,HERE
<< "lost " << r
.groupId
<< " group in rule" << r
.id
);
201 return ServicePointer();
204 ServiceGroup::Loop
loop(g
->initialServices());
205 typedef ServiceGroup::iterator SGI
;
206 for (SGI i
= loop
.begin
; i
!= loop
.end
; ++i
) {
208 ServicePointer service
= FindService(*i
);
213 if (method
!= service
->cfg().method
)
216 if (point
!= service
->cfg().point
)
219 // sending a message to a broken service is likely to cause errors
220 if (service
->cfg().bypass
&& service
->broken())
224 // sending a message to a service that does not want it is useless
225 // note that we cannot check wantsUrl for service that is not "up"
226 // note that even essential services are skipped on unwanted URLs!
227 if (!service
->wantsUrl(req
->urlpath
))
231 secondBest
= service
;
233 // the caller asked for an "up" service and we can bypass this one
234 if (service
->cfg().bypass
)
236 debugs(93,5,HERE
<< "cannot skip an essential down service");
237 what
= "down-but-essential ";
241 debugs(93,5,HERE
<< "found first matching " <<
242 what
<< "service for " << r
.groupId
<< " group in rule" << r
.id
<<
243 ": " << service
->cfg().key
);
248 if (secondBest
!= NULL
) {
250 debugs(93,5,HERE
<< "found first matching " <<
251 what
<< "service for " << r
.groupId
<< " group in rule" << r
.id
<<
252 ": " << secondBest
->cfg().key
);
256 debugs(93,5,HERE
<< "found no matching " <<
257 what
<< "services for " << r
.groupId
<< " group in rule" << r
.id
);
258 return ServicePointer();