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