When passing a session id into services_session_restart, PHP's session_id() is called to set the id, and the id is passed to Drupal's sess_read function. The first thing sess_read does is check for the presence of the session cookie, which in the vast majority of cases will not be set because the remote client does is not actually a browser. Therefore the user will be set to be drupal's anonymous user, and no session is actually created. This makes it impossible to verify users against specific roles for security reasons (you can still allow/ban things from the anonymous user, although if you ban it from the anonymous user you are basically banning it from everyone.) As it stands now, you can actually pass anything in as a sess_id with no consequence.
I don't know of a good way to handle this. The only thing I can think of is checking the session table by hand when a sess_id is passed in, but that seems less than ideal.
| Comment | File | Size | Author |
|---|---|---|---|
| #14 | services.module.patch | 3.36 KB | ahwayakchih |
| #11 | services_no_cookie_d6.patch | 525 bytes | snelson |
| #6 | services.module.patch | 487 bytes | ahwayakchih |
Comments
Comment #1
gddThis title is more accurate. The more I look into this the more it looks like this will be difficult to implement, since Drupal currently does not allow sessions without cookies. However I still think it will be an issue for a lot of web services clients. For instance, Drupal's own XMLRPC service does not manage cookies. It would be really nice to figure out a way to make this work (other than implementing session checking in services itself.)
Comments?
Comment #2
marcingy commentedCertainly something we need to look at in preperation for getting this into D7.
Comment #3
ahwayakchih commentedMaybe adding this before call to sess_read() (inside services_session_restart() function) will help?
Comment #4
snelson commentedI spent quite a bit of time trying to work this out when I first wrote services, but ended up setting it aside as I haven't had a use for session IDs myself. You're right about the problem, Drupal does not allow sessions without cookies, so it may take a patch to core to make this happen. Otherwise, services will probably have to have it's own session code.
Comment #5
gddI would love to see a discussion had about whether or not core should allow cookie-less logins. With more and more non-browser-based clients out there, I think its a talk worth having at least. It would also be nice if cookie support could be built into Drupal's XMLRPC client, so at least Drupal itself could talk to other Drupal installations with proper credentials. Being core patches, neither of these is trivial of course.
It would be so nice if we could leverage existing Drupal users/permissions. I hate the idea of bringing up a new system from scratch.
Comment #6
ahwayakchih commentedI'm sorry, i forgot to add important parto of explanation: code i pasted above should be added in services.module :). No need to patch core for that.
I'm attaching tiny patch for review. Can someone test it with some xmlrpc client? Maybe try to use it with user_service and patched node_service (so it checks node_access) modules?
Comment #7
steven jones commentedMore of the issue is that during the drupal bootstrap a session gets started by session_start(). The php docs say that subsequent calls to session_start() are ignored, so services is way to late to do anything with the session. We'll have to modify core in some way
Comment #8
ahwayakchih commentedRight. But why do we really need to restart session?
If i understood description of this issue correctly, whole thing is about logging in and calling services as logged in user (with some specific access permissions). We don't really need to regenerate session_id, create new session, etc... we can use current session all the time (nevermind that it has different id than the one we got after logging in :).
Here is how it could work (correct me if i'm wrong :).
1. client connects and calls user.login function
2. drupal creates session with ID = A
3. user.login authenticates user and returns sessid A (BTW user.module regenerates session_id in user_login_submit() function. is it really needed? :).
4. drupal saves session A with user data
5. client connects and calls node.save function (modified to check permissions with node_access call)
6. drupal creates session with ID = B (new session is created because client doesn't handle cookies and PHP thinks it's new client)
7. services gets sessid = A from arguments passed from client
8. services calls sess_read(A) which makes user logged in
9. services calls node.save method
10. services calls session_destroy() to prevent user data propagating to many sessions
So in short:
1. remove services_session_restart() function
2. instead of calling services_session_restart() call sess_read($sssid).
3. after method call returns, call session_destroy() before returning from services_method_call() function.
The only problem is that if something updates $_SESSION variable, it will not be saved for next call from client. But we could workaround even that by loading session data (session_decode(sess_read($ssid))) and then encoding it again and calling sess_write($ssid, session_encode()) before session_destroy();
More about tricks with session data here: http://pl2.php.net/manual/en/function.session-decode.php#69111 (so basically, we can split that function in half, and call service method in the middle :).
Comment #9
steven jones commentedsess_read() always checks for a cookie. Thus the above method will fail, as one is not set.
Comment #10
ahwayakchih commentedAnd that is where code from patch i made comes in and saves the day ;).
Comment #11
snelson commentedI tested this patch with an xmlrpc client, seems to solve the problem. Can somebody else double check this before I commit. Also attached a patch for D6.
Comment #12
steven jones commentedThis does indeed fix this issue. Albeit in a slightly hackish way, oh well, such is life.
Comment #13
ahwayakchih commentedI also tested it with Flash (and amfphp module) - it works ok.
But maybe it would be better to create bigger patch, with changes i described above (http://drupal.org/node/231869#comment-804519 -> loading session to current one, making method call, saving session to user's session and cleaning current session)?
Comment #14
ahwayakchih commentedOK, here is more "advanced" patch. It takes session data into account. It also doesn't mess with sessions if current user data was already loaded ($user->sid == $sessid).
I tested it with Flash+amfphp and it works ok (even better than before because user.logout actually logs me out of Drupal ;).
AFAIK Flash does handle cookies (only they are separate from browser's cookies), so if someone could try it with XMLRPC it would be great.
Comment #15
gddThis is for the patch in #14, on Drupal 5.7, via XMLRPC without cookies.
I tried this patch and it seems to work well. I tested it in conjunction with the access control / node patch (#222268. Varying combinations of access to "load raw node data" all acted as predicted, admin sessions always had access, and random session IDs always got denied. I did not dig into the code much, someone may want to review it at that level, but from a functionality standpoint thumbs up. Even passed a Coder review!
Comment #16
marcingy commentedCommitted drupal 5 and drupal 6
Comment #17
Anonymous (not verified) commentedAutomatically closed -- issue fixed for two weeks with no activity.