]>
Commit | Line | Data |
---|---|---|
62c7f90e AR |
1 | #include "squid.h" |
2 | #include "structs.h" | |
3 | ||
4 | #include "ConfigParser.h" | |
62c7f90e AR |
5 | #include "HttpRequest.h" |
6 | #include "HttpReply.h" | |
127dce76 | 7 | #include "acl/FilledChecklist.h" |
62c7f90e AR |
8 | #include "adaptation/Service.h" |
9 | #include "adaptation/ServiceGroups.h" | |
10 | #include "adaptation/AccessRule.h" | |
abd4b611 | 11 | #include "adaptation/Config.h" |
62c7f90e | 12 | #include "adaptation/AccessCheck.h" |
3d93a84d | 13 | #include "base/TextException.h" |
62c7f90e | 14 | |
d85b8894 | 15 | /** \cond AUTODOCS-IGNORE */ |
62c7f90e | 16 | cbdata_type Adaptation::AccessCheck::CBDATA_AccessCheck = CBDATA_UNKNOWN; |
d85b8894 | 17 | /** \endcond */ |
62c7f90e | 18 | |
abd4b611 AR |
19 | bool |
20 | Adaptation::AccessCheck::Start(Method method, VectPoint vp, | |
26ac0430 AJ |
21 | HttpRequest *req, HttpReply *rep, AccessCheckCallback *cb, void *cbdata) |
22 | { | |
abd4b611 AR |
23 | |
24 | if (Config::Enabled) { | |
25 | // the new check will call the callback and delete self, eventually | |
4299f876 AR |
26 | AsyncJob::Start(new AccessCheck( // we do not store so not a CbcPointer |
27 | ServiceFilter(method, vp, req, rep), cb, cbdata)); | |
28 | return true; | |
26ac0430 | 29 | } |
abd4b611 AR |
30 | |
31 | debugs(83, 3, HERE << "adaptation off, skipping"); | |
32 | return false; | |
33 | } | |
34 | ||
a22e6cd3 | 35 | Adaptation::AccessCheck::AccessCheck(const ServiceFilter &aFilter, |
26ac0430 | 36 | AccessCheckCallback *aCallback, |
a22e6cd3 | 37 | void *aCallbackData): |
e1381638 AJ |
38 | AsyncJob("AccessCheck"), filter(aFilter), |
39 | callback(aCallback), | |
40 | callback_data(cbdataReference(aCallbackData)), | |
41 | acl_checklist(NULL) | |
62c7f90e | 42 | { |
3ff65596 | 43 | #if ICAP_CLIENT |
a22e6cd3 | 44 | Adaptation::Icap::History::Pointer h = filter.request->icapHistory(); |
3ff65596 AR |
45 | if (h != NULL) |
46 | h->start("ACL"); | |
47 | #endif | |
48 | ||
a22e6cd3 | 49 | debugs(93, 5, HERE << "AccessCheck constructed for " << |
e1381638 | 50 | methodStr(filter.method) << " " << vectPointStr(filter.point)); |
62c7f90e AR |
51 | } |
52 | ||
53 | Adaptation::AccessCheck::~AccessCheck() | |
54 | { | |
3ff65596 | 55 | #if ICAP_CLIENT |
a22e6cd3 | 56 | Adaptation::Icap::History::Pointer h = filter.request->icapHistory(); |
3ff65596 AR |
57 | if (h != NULL) |
58 | h->stop("ACL"); | |
59 | #endif | |
7e81de2e AR |
60 | if (callback_data) |
61 | cbdataReferenceDone(callback_data); | |
62c7f90e AR |
62 | } |
63 | ||
dd5d7172 | 64 | void |
e1381638 AJ |
65 | Adaptation::AccessCheck::start() |
66 | { | |
67 | AsyncJob::start(); | |
53340485 AR |
68 | |
69 | if (!usedDynamicRules()) | |
70 | check(); | |
71 | } | |
72 | ||
73 | /// returns true if previous services configured dynamic chaining "rules" | |
74 | bool | |
75 | Adaptation::AccessCheck::usedDynamicRules() | |
76 | { | |
77 | Adaptation::History::Pointer ah = filter.request->adaptHistory(); | |
78 | if (!ah) | |
79 | return false; // dynamic rules not enabled or not triggered | |
80 | ||
81 | DynamicGroupCfg services; | |
82 | if (!ah->extractFutureServices(services)) { // clears history | |
83 | debugs(85,9, HERE << "no service-proposed rules stored"); | |
84 | return false; // earlier service did not plan for the future | |
85 | } | |
86 | ||
87 | debugs(85,3, HERE << "using stored service-proposed rules: " << services); | |
88 | ||
89 | ServiceGroupPointer g = new DynamicServiceChain(services, filter); | |
90 | callBack(g); | |
91 | Must(done()); | |
92 | return true; | |
dd5d7172 AR |
93 | } |
94 | ||
a22e6cd3 | 95 | /// Walk the access rules list to find rules with applicable service groups |
62c7f90e AR |
96 | void |
97 | Adaptation::AccessCheck::check() | |
98 | { | |
192378eb | 99 | debugs(93, 4, HERE << "start checking"); |
62c7f90e AR |
100 | |
101 | typedef AccessRules::iterator ARI; | |
102 | for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) { | |
62c7f90e | 103 | AccessRule *r = *i; |
a22e6cd3 AR |
104 | if (isCandidate(*r)) { |
105 | debugs(93, 5, HERE << "check: rule '" << r->id << "' is a candidate"); | |
62c7f90e AR |
106 | candidates += r->id; |
107 | } | |
108 | } | |
109 | ||
110 | checkCandidates(); | |
111 | } | |
112 | ||
113 | // XXX: Here and everywhere we call FindRule(topCandidate()): | |
114 | // Once we identified the candidate, we should not just ignore it | |
115 | // if reconfigure changes rules. We should either lock the rule to | |
116 | // prevent reconfigure from stealing it or restart the check with | |
117 | // new rules. Throwing an exception may also be appropriate. | |
118 | void | |
119 | Adaptation::AccessCheck::checkCandidates() | |
120 | { | |
192378eb | 121 | debugs(93, 4, HERE << "has " << candidates.size() << " rules"); |
62c7f90e AR |
122 | |
123 | while (!candidates.empty()) { | |
124 | if (AccessRule *r = FindRule(topCandidate())) { | |
b50e327b | 125 | /* BUG 2526: what to do when r->acl is empty?? */ |
62c7f90e | 126 | // XXX: we do not have access to conn->rfc931 here. |
a22e6cd3 AR |
127 | acl_checklist = new ACLFilledChecklist(r->acl, filter.request, dash_str); |
128 | acl_checklist->reply = filter.reply ? HTTPMSGLOCK(filter.reply) : NULL; | |
62c7f90e AR |
129 | acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this); |
130 | return; | |
131 | } | |
132 | ||
133 | candidates.shift(); // the rule apparently went away (reconfigure) | |
134 | } | |
135 | ||
192378eb | 136 | debugs(93, 4, HERE << "NO candidates left"); |
a22e6cd3 AR |
137 | callBack(NULL); |
138 | Must(done()); | |
62c7f90e AR |
139 | } |
140 | ||
141 | void | |
2efeb0b7 | 142 | Adaptation::AccessCheck::AccessCheckCallbackWrapper(allow_t answer, void *data) |
62c7f90e | 143 | { |
192378eb | 144 | debugs(93, 8, HERE << "callback answer=" << answer); |
62c7f90e | 145 | AccessCheck *ac = (AccessCheck*)data; |
f9cf4ba9 AJ |
146 | |
147 | /** \todo AYJ 2008-06-12: If answer == ACCESS_REQ_PROXY_AUTH | |
148 | * we should be kicking off an authentication before continuing | |
149 | * with this request. see bug 2400 for details. | |
150 | */ | |
7866ecc9 AR |
151 | |
152 | // convert to async call to get async call protections and features | |
2efeb0b7 | 153 | typedef UnaryMemFunT<AccessCheck, allow_t> MyDialer; |
7866ecc9 AR |
154 | AsyncCall::Pointer call = |
155 | asyncCall(93,7, "Adaptation::AccessCheck::noteAnswer", | |
2efeb0b7 | 156 | MyDialer(ac, &Adaptation::AccessCheck::noteAnswer, answer)); |
7866ecc9 AR |
157 | ScheduleCallHere(call); |
158 | ||
62c7f90e AR |
159 | } |
160 | ||
a22e6cd3 | 161 | /// process the results of the ACL check |
62c7f90e | 162 | void |
2efeb0b7 | 163 | Adaptation::AccessCheck::noteAnswer(allow_t answer) |
62c7f90e | 164 | { |
a22e6cd3 AR |
165 | Must(!candidates.empty()); // the candidate we were checking must be there |
166 | debugs(93,5, HERE << topCandidate() << " answer=" << answer); | |
167 | ||
2efeb0b7 | 168 | if (answer == ACCESS_ALLOWED) { // the rule matched |
a22e6cd3 AR |
169 | ServiceGroupPointer g = topGroup(); |
170 | if (g != NULL) { // the corresponding group found | |
171 | callBack(g); | |
172 | Must(done()); | |
173 | return; | |
174 | } | |
62c7f90e AR |
175 | } |
176 | ||
a22e6cd3 AR |
177 | // no match or the group disappeared during reconfiguration |
178 | candidates.shift(); | |
179 | checkCandidates(); | |
62c7f90e AR |
180 | } |
181 | ||
a22e6cd3 AR |
182 | /// call back with a possibly nil group; the job ends here because all failures |
183 | /// at this point are fatal to the access check process | |
62c7f90e | 184 | void |
a22e6cd3 | 185 | Adaptation::AccessCheck::callBack(const ServiceGroupPointer &g) |
62c7f90e | 186 | { |
a22e6cd3 | 187 | debugs(93,3, HERE << g); |
62c7f90e AR |
188 | |
189 | void *validated_cbdata; | |
a22e6cd3 AR |
190 | if (cbdataReferenceValidDone(callback_data, &validated_cbdata)) { |
191 | callback(g, validated_cbdata); | |
62c7f90e | 192 | } |
a22e6cd3 AR |
193 | mustStop("done"); // called back or will never be able to call back |
194 | } | |
62c7f90e | 195 | |
a22e6cd3 AR |
196 | Adaptation::ServiceGroupPointer |
197 | Adaptation::AccessCheck::topGroup() const | |
198 | { | |
199 | ServiceGroupPointer g; | |
62c7f90e AR |
200 | if (candidates.size()) { |
201 | if (AccessRule *r = FindRule(topCandidate())) { | |
a22e6cd3 AR |
202 | g = FindGroup(r->groupId); |
203 | debugs(93,5, HERE << "top group for " << r->id << " is " << g); | |
62c7f90e | 204 | } else { |
a22e6cd3 | 205 | debugs(93,5, HERE << "no rule for " << topCandidate()); |
62c7f90e | 206 | } |
26ac0430 | 207 | } else { |
a22e6cd3 | 208 | debugs(93,5, HERE << "no candidates"); // should not happen |
26ac0430 | 209 | } |
62c7f90e | 210 | |
a22e6cd3 | 211 | return g; |
62c7f90e AR |
212 | } |
213 | ||
a22e6cd3 AR |
214 | /** Returns true iff the rule's service group will be used after ACL matches. |
215 | Used to detect rules worth ACl-checking. */ | |
216 | bool | |
217 | Adaptation::AccessCheck::isCandidate(AccessRule &r) | |
26ac0430 | 218 | { |
a22e6cd3 | 219 | debugs(93,7,HERE << "checking candidacy of " << r.id << ", group " << |
e1381638 | 220 | r.groupId); |
62c7f90e | 221 | |
a22e6cd3 | 222 | ServiceGroupPointer g = FindGroup(r.groupId); |
62c7f90e AR |
223 | |
224 | if (!g) { | |
a22e6cd3 AR |
225 | debugs(93,7,HERE << "lost " << r.groupId << " group in rule" << r.id); |
226 | return false; | |
62c7f90e AR |
227 | } |
228 | ||
a22e6cd3 AR |
229 | const bool wants = g->wants(filter); |
230 | debugs(93,7,HERE << r.groupId << (wants ? " wants" : " ignores")); | |
231 | return wants; | |
62c7f90e | 232 | } |