perusio / drupal-with-nginx

Running Drupal using nginx: an idiosyncratically crafted bleeding edge configuration.
854 stars 246 forks source link

Is it possible to detect if the client is an authenticated (logged in) Drupal user by a Nginx rule? #225

Closed acrolink closed 9 years ago

acrolink commented 9 years ago

Is that possible? Any idea how to let Nginx serve a location conditionally if the request is coming from an authenticated user?

I need this to serve some cached static files to authenticated users only and I don't want to use Authcache module.

Thanks.

perusio commented 9 years ago

Yes it is. But bear in mind that you won't be able to do anything more sophisticated than just matching the cookie value and name to a regex. You cannot make sure that the cookie is still valid.

For that you need some Lua code in configuration and a do a call to either memcache/redis/MySQL (MariaDB) to check if the session ID being sent in the Cookie header is still valid.

If the thing you're caching is not sensitive then proceed. You can check the microcache_fcgi_auth.conf file to see how it's done. Essentially the session ID is used in the cache key. So this way different users will have different session IDs and then different caches.

fastcgi_cache_key $cache_uid@$scheme$host$request_uri;

where:

## Set a cache_uid variable for authenticated users.
map $http_cookie $cache_uid {
    default nil; # hommage to Lisp :)
    ~SESS[[:alnum:]]+=(?<session_id>[[:graph:]]+) $session_id;
}

in map_cache.conf.

acrolink commented 9 years ago

@perusio,

Thanks very much for the valuable information. My current setup for serving certain content (namely, a JSON file including NID's and titles) doesn't discriminate between authenticated and non authenticated Drupal users.

I generate the JSON file using a PHP file that bootstraps Drupal, and then serve the JSON file directly (i.e. no in-built Drupal cache control at all). Like this:

Request to Server =>

if JSON file exists, serve it ..

else:

run script.php to generate the JSON file and then serve it ..

I do the above using Nginx try_files.

I understand that using Lua I can check if the SID is valid using a MySQL query. My question, is how to capture / read the SID from the request when the client is looking for a static JSON file? What do I need to do to read the SID upon a request made to the stand alone JSON file? Thanks.

acrolink commented 9 years ago

@perusio,

I figured it out. Now I can make:

ngx.say("Hit") if cjson.encode(res) is not equal to "{}", all based on SID and DB query within Lua.

My question, how to allow access to the JSON file to be served if cjson.encode(res) is not equal to "{}" and how to deny access if cjson.encode(res) is equal to "{}".

P.S. The Lua MySQL query takes about 90ms, is that normal? Any way to speed the query up?

Thanks very much :-)

acrolink commented 9 years ago

@perusio,

I have achieved what I need. Thank you very much for the valuable insight into the world of Lua & Nginx.

What I have done is this:

location /vip-content {`

access_by_lua '

....
....
....

if cjson.encode(res) == "{}" then -- i.e. SID is not in DB
    ngx.exit(403) 
end

' -- Lua stuff ends here..

try_files $uri $uri/ /vip-content/get.php;
}

Maybe there is a more professional way to do this, but this what I found functional. All the best António.

perusio commented 9 years ago

There are several options. You could check the result directly before doing the serialization cjson.encode and you can also use the lua-resty-mysql library. For speeding up the lookups you can use a shared dictionary to cache the MySQL queries.

acrolink commented 9 years ago

As for the slow speed (~100ms), I forgot that I was working on my remote server, so the delay is natural (it is total HTTP response time, not MySQL query execution time).

You are saying that I can skip the cjson.encode part to see if the result is positive (if row exists). I have looked into documentation but could not find any information regarding direct assessment of response without cjson.encode. If you have some time to guide me how to do that. Thanks.

acrolink commented 9 years ago

That's fine I have found the way to check for empty response without using cjson.encode. Thanks António.

perusio commented 9 years ago

Great. Maybe you can cache the HTTP requests for a short time using a shared dictionary. This way you won't need to reissue requests all the time. Another option, although it adds one more hop is to proxy the request via proxy_pass and use the proxy cache. Just an idea.