CBDATA_CLASS_INIT(ExternalACLEntry);
-ExternalACLEntry::ExternalACLEntry()
+ExternalACLEntry::ExternalACLEntry() :
+ notes()
{
lru.next = lru.prev = NULL;
result = ACCESS_DENIED;
{
date = squid_curtime;
result = someData.result;
+
+ // replace all notes. not combine
+ notes.entries.clean();
+ notes.append(&someData.notes);
+
#if USE_AUTH
user = someData.user;
password = someData.password;
#include "acl/Acl.h"
#include "cbdata.h"
#include "hash.h"
+#include "Notes.h"
#include "SquidString.h"
class external_acl;
ExternalACLEntryData() : result(ACCESS_DUNNO) {}
allow_t result;
+
+ /// list of all kv-pairs returned by the helper
+ NotePairs notes;
+
#if USE_AUTH
// TODO use an AuthUser to hold this info
String user;
dlink_node lru;
allow_t result;
time_t date;
+
+ /// list of all kv-pairs returned by the helper
+ NotePairs notes;
+
#if USE_AUTH
String user;
String password;
int.cc \
MasterXaction.cc \
MasterXaction.h \
+ Notes.cc \
+ Notes.h \
SquidList.h \
SquidList.cc \
mem_node.cc \
NotePairs::hasPair(const char *key, const char *value) const
{
for (Vector<NotePairs::Entry *>::const_iterator i = entries.begin(); i != entries.end(); ++i) {
- if ((*i)->name.cmp(key) == 0 || (*i)->value.cmp(value) == 0)
+ if ((*i)->name.cmp(key) == 0 && (*i)->value.cmp(value) == 0)
return true;
}
return false;
}
}
+void
+NotePairs::appendNewOnly(const NotePairs *src)
+{
+ for (Vector<NotePairs::Entry *>::const_iterator i = src->entries.begin(); i != src->entries.end(); ++i) {
+ if (!hasPair((*i)->name.termedBuf(), (*i)->value.termedBuf()))
+ entries.push_back(new NotePairs::Entry((*i)->name.termedBuf(), (*i)->value.termedBuf()));
+ }
+}
+
NotePairs &
SyncNotes(AccessLogEntry &ale, HttpRequest &request)
{
+ // XXX: auth code only has access to HttpRequest being authenticated
+ // so we must handle the case where HttpRequest is set without ALE being set.
+
if (!ale.notes) {
- assert(!request.notes);
- ale.notes = request.notes = new NotePairs;
+ if (!request.notes)
+ request.notes = new NotePairs;
+ ale.notes = request.notes;
} else {
assert(ale.notes == request.notes);
}
*/
void append(const NotePairs *src);
+ /**
+ * Append any new entries of the src NotePairs list to our list.
+ * Entries which already exist in the destination set are ignored.
+ */
+ void appendNewOnly(const NotePairs *src);
+
/**
* Returns a comma separated list of notes with key 'noteKey'.
* Use findFirst instead when a unique kv-pair is needed.
config(aConfig),
ipcount(0),
expiretime(0),
+ notes(),
credentials_state(Auth::Unchecked),
username_(NULL)
{
debugs(29, 5, HERE << "auth_user '" << from << "' into auth_user '" << this << "'.");
+ // combine the helper response annotations. Ensuring no duplicates are copied.
+ notes.appendNewOnly(&from->notes);
+
/* absorb the list of IP address sources (for max_user_ip controls) */
AuthUserIP *new_ipdata;
while (from->ip_list.head != NULL) {
#include "base/RefCount.h"
#include "dlink.h"
#include "ip/Address.h"
+#include "Notes.h"
class AuthUserHashPointer;
class StoreEntry;
size_t ipcount;
long expiretime;
+ /// list of key=value pairs the helper produced
+ NotePairs notes;
+
public:
static void cacheInit();
static void CachedACLsReset();
static Auth::UserRequest::Pointer
authTryGetUser(Auth::UserRequest::Pointer auth_user_request, ConnStateData * conn, HttpRequest * request)
{
+ Auth::UserRequest::Pointer res;
+
if (auth_user_request != NULL)
- return auth_user_request;
+ res = auth_user_request;
else if (request != NULL && request->auth_user_request != NULL)
- return request->auth_user_request;
+ res = request->auth_user_request;
else if (conn != NULL)
- return conn->getAuth();
- else
- return NULL;
+ res = conn->getAuth();
+
+ // attach the credential notes from helper to the transaction
+ if (request != NULL && res != NULL && res->user() != NULL) {
+ // XXX: we have no access to the transaction / AccessLogEntry so cant SyncNotes().
+ // workaround by using anything already set in HttpRequest
+ // OR use new and rely on a later Sync copying these to AccessLogEntry
+ if (!request->notes)
+ request->notes = new NotePairs;
+
+ request->notes->appendNewOnly(&res->user()->notes);
+ }
+
+ return res;
}
/* returns one of
assert(r->auth_user_request != NULL);
assert(r->auth_user_request->user()->auth_type == Auth::AUTH_BASIC);
+ // add new helper kv-pair notes to the credentials object
+ // so that any transaction using those credentials can access them
+ r->auth_user_request->user()->notes.appendNewOnly(&reply.notes);
+
/* this is okay since we only play with the Auth::Basic::User child fields below
* and dont pass the pointer itself anywhere */
Auth::Basic::User *basic_auth = dynamic_cast<Auth::Basic::User *>(r->auth_user_request->user().getRaw());
assert(replyData->auth_user_request != NULL);
Auth::UserRequest::Pointer auth_user_request = replyData->auth_user_request;
+ // add new helper kv-pair notes to the credentials object
+ // so that any transaction using those credentials can access them
+ auth_user_request->user()->notes.appendNewOnly(&reply.notes);
+
static bool oldHelperWarningDone = false;
switch (reply.result) {
case HelperReply::Unknown: {
Auth::UserRequest::Pointer auth_user_request = r->auth_user_request;
assert(auth_user_request != NULL);
+ // add new helper kv-pair notes to the credentials object
+ // so that any transaction using those credentials can access them
+ auth_user_request->user()->notes.appendNewOnly(&reply.notes);
+
Auth::Negotiate::UserRequest *lm_request = dynamic_cast<Auth::Negotiate::UserRequest *>(auth_user_request.getRaw());
assert(lm_request != NULL);
assert(lm_request->waiting);
Auth::UserRequest::Pointer auth_user_request = r->auth_user_request;
assert(auth_user_request != NULL);
+ // add new helper kv-pair notes to the credentials object
+ // so that any transaction using those credentials can access them
+ auth_user_request->user()->notes.appendNewOnly(&reply.notes);
+
Auth::Ntlm::UserRequest *lm_request = dynamic_cast<Auth::Ntlm::UserRequest *>(auth_user_request.getRaw());
assert(lm_request != NULL);
assert(lm_request->waiting);
/*Add notes*/
// The al->notes and request->notes must point to the same object.
- // Enable the following assertion to check for possible bugs.
- // assert(request->notes == al->notes);
+ (void)SyncNotes(*al, *request);
typedef Notes::iterator ACAMLI;
for (ACAMLI i = Config.notes.begin(); i != Config.notes.end(); ++i) {
if (const char *value = (*i)->match(request, al->reply)) {
// XXX: make entryData store a proper HelperReply object instead of copying.
+ entryData.notes.append(&reply.notes);
+
const char *label = reply.notes.findFirst("tag");
if (label != NULL && *label != '\0')
entryData.tag = label;
{
ACLFilledChecklist *checklist = Filled(static_cast<ACLChecklist*>(data));
checklist->extacl_entry = cbdataReference((external_acl_entry *)result);
+
+ // attach the helper kv-pair to the transaction
+ if (HttpRequest * req = checklist->request) {
+ // XXX: we have no access to the transaction / AccessLogEntry so cant SyncNotes().
+ // workaround by using anything already set in HttpRequest
+ // OR use new and rely on a later Sync copying these to AccessLogEntry
+ if (!req->notes)
+ req->notes = new NotePairs;
+
+ req->notes->appendNewOnly(&checklist->extacl_entry->notes);
+ }
+
checklist->resumeNonBlockingCheck(ExternalACLLookup::Instance());
}