Closed (won't fix)
Project:
Services
Version:
7.x-3.x-dev
Component:
Code
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
23 May 2010 at 13:20 UTC
Updated:
20 Sep 2011 at 15:50 UTC
Jump to comment: Most recent file
Comments
Comment #1
jhl.verona commentedForgot the attachment, sorry.
An example JSON string for the attributes could be:
{ "Name": "admin", "status": true }Comment #2
marcingy commentedPlease explain why you are replicating code from core user_load in this patch specifically in _user_service_load_user($attrs). User load deals with arrays or uid anyway as a result if we do need a second service then a lot of this code can be removed as it is duplication that we would need to maintain going forwards. The ideal of course would a single user.get service which either takes a uid or an array.
Comment #3
jhl.verona commentedThank you for pointing that out. I was worried about the information coming in from outside. However, I see that
_db_query_callbackseems to handle all that. The only other thing that springs to mind is that a JSON supplied value would arrive as an object, which theforeachtakes care of. Though I have warbled on about that elsewhere.All in all, it could be reduced to:
Badly formed requests will cause SQL errors (but they probably still could even with all that checking code) which will be logged - no bad thing. But it won't stop the request and the external caller will simply get a 404 not found error - not quite right, but good enough.
I agree that it would be better if the
user.getservice accepted either, but I don't know if that can be done for XML-RPC. It would need an either/or specification for the parameters to avoid breaking current client side code. Something like user.get(int (optional), struct (optional)) perhaps.Comment #4
gddWhen you originally brought up this idea I tried to come up with a way to have user_get support both, but couldn't think of one, which is too bad.
I am in favor of reducing it down to a call to user_load(). In general Drupal does not offer a lot of protection from people against themselves, and given that I'm less than inclined to add it in. I wouldn't mind throwing a services_error() in cases where an account isn't found maybe (as user.get does.)
All in all I think this is a good addition though.
Comment #5
jhl.verona commentedMerging into
user_getwould be the best solution, but changing the external API is a dangerous game, there's probably a lot of code out there using Services.I'm in favour of reducing it to a call to
user_loadonce the input data has been forced to an array.This could be:
and
At which point, further code reductions can be made, simply by passing the original calls to
xxx_getalong toxxx_load.For example:
and
HTH
John
Comment #6
greg.harveyHello everyone! After a long absence, I am using Services again for a new client. Yay! I love this module. Anyway...
Where are we with this? Need a new patch?
@heyrocker, what if user.get expected *either* a string or an integer. You could do it by type. If an integer is sent by the calling client then assume $user->uid (all existing code will still function fine) and if a string is sent assume $user->name.
Patch attached. Works like a charm for me! =)
Comment #7
greg.harveyHmmm, is the test bot running against 6.x-2.2 for this thread or against 6.x-2.x? I rolled that patch against 6.x-2.2, which may be why it failed? =/
Comment #8
greg.harveyHmm, no, Eclipse screwed up the paths to files, trying again having manually corrected.
Comment #9
greg.harveyBeen thinking about this. While this works, it's probably "illegal" in web services/XML land to have an argument that can be either one thing or another. I'm not sure - would be good to hear from a maintainer on that one. If this is not the way forwards, how about this:
Additional argument, username, string, optional ... *and* make uid optional too. I guess the problem with making uid optional is with protocols like XML-RPC, where all arguments are required, uid must have some "ignore me" value, and it can't be a zero because feasibly someone might want to alter the Anonymous user. Or should we call that an edge case and say if uid == 0 then carry on and check the username argument?
Comment #10
greg.harveyI should note I think this patch is really important, as it's highly unlikely an external client wanting to alter a user in Drupal will know Drupal's uid. It's far more likely they'll know the username or email address. uid-only look-up is not very useful. I'd be inclined to "critical" it. Just thinking.
Comment #11
jhl.verona commented@greg.harvey I think that the patch should take into account the various front-ends, as you yourself have remarked with respect to XMLRPC.
FYI Apart from this issue, I am also attempting to get a small group of patches applied to the users service:
http://drupal.org/node/806856 the error code goes AWOL when using the XMLRPC front end
http://drupal.org/node/807638 allow user.services to transparently apply additional protection against specific user modification/deletion - using the User Protect module (a necessary patch to that module has already been committed)
http://drupal.org/node/679494 a plea to change the argument to user.save back to
<struct>(as it was) instead of<array>(as it is now)You may not be interested in all of these requests, obviously.
Personally my opinion is that it is best not to change the existing published user.service API at all - it's an external API, and there will be all sorts of applications which use it. The last thing I want is to offer a patch which could potentially break these applications.
We seem to agree that while user.get(uid) is useful within the Drupal environment, it has less sense to an outside application. Better to use the username or email. I also agree with #10, though if things have been this way for so long, I don't see it as "critical".
Which is why I came to the conclusion of adding a new method - user.load - which would connect directly to the Drupal function user_load. Since this is essentially a superset of the user.get method, I don't think this would actually be such a huge increase in code (see #5).
I'll gladly join forces with you in getting this patch approved, though things seem to have gone very quiet recently. Of course this may be due to other reasons on the part of the maintainers, we all have lives to lead...
Comment #12
greg.harveyHi John - thanks for jumping back in. Of course, I had not considered that because the new field would be obligatory for the XML-RPC server (and probably others) then it *will* break people's production code.
Oops! Setting back to "needs work". Good catch/point.
Happy with a new method based on #5. We should offer it as a patch, but if the maintainers don't want to take it we can package it as a separate contrib module. I will write it today.
Ps - it's critical to me because I need to look up and alter usernames as a part of a single sign-on implementation and the other application has no concept of Drupal's user ID. Unless it's a Drupal-to-Drupal synchronisation, the current method is virtually unusable, especially since the developers of the other system have absolutely no intention of incorporating the ability to store a Drupal UID either. Why should they? So I think it is critical, because I think my use case is one of the more common ones for people needing this service (distributed single sign-on, username changes in another app and it needs to tell Drupal) - but clearly not many people are using it for this, or, as you say, it would've been raised earlier. Or perhaps they're reviewing the module and walking away/coding their own method and not giving it back? Anyway... I'm ranting ... ;-)
Comment #13
jhl.verona commentedGreg - I'd left #5 as an idea, because I was expecting some feedback before writing (yet another) patch. Look forward to seeing/reviewing your patch.
I'm doing (more or less) the same thing - an external application (not mine, and I really don't want to know about it ;-) wants to add/modify/delete users and their roles. Be wary of the fact that, much as within Drupal Web UI itself, uid = 1 can be deleted via Services. Uid = 0 can also be deleted via Services, though it's a little more difficult to do via the Drupal Web UI. The first will cause problems for administrators, the second will probably break Drupal. This is why I am also pushing for a transparent User Protect integration.
Comment #14
greg.harveyNew patch attached. Works fine for me.
Comment #15
jhl.verona commentedSorry Greg, but doesn't work for me...
Operations carried out:
admin/build/services/browse/user.getset 51 for the uid - perfectadmin/build/services/browse/user.loadset{ "uid": 51 }as the JSON input for 'array' - got the error "The parameters you supplied did not match any user in the system."One minor point. I've battled the last two weeks trying to clear up the difference between "array" and "struct" in services (particularly user.save) - now you've called the parameter for user.load "array", of type "struct"! Can we call it "attrs" or something?
A quick
dsm($uid);shows that the functionuser_service_load_access($uid)doesn't receive a number (how could it?), but the attributes. These need to be passed touser_loadto get the actual uid.Here then the patched patch... (I'm not trying to be facetious, I appreciate your help, and I'd really like to see this get through)
Comment #16
greg.harveyHi,
Summary, since this has ended up being longer than I'd like:
1. Works fine with XML-RPC. Feels like a JSON server bug, since your data looks fine - user 51 *does* exist, right?
2. Naming convention inline with core - I'd be reluctant to change that - maintainer needs to make a decision
3. Think this is ok, reasoning below.
Details:
Ok, with XML-RPC same steps, user arguments for method in an array, like so:
Just to be sure, tried:
Also worked.
So, I think there must be an issue with the JSON server or perhaps the test UI? Either way, if you try it in code it should work. It certainly works with XML-RPC, I just double-checked.
Onwards: $array is there because it matches the name of the parameter expected by user_load() in the core API. Since we refer users to the API docs, shouldn't the parameters match? I guess what I'm saying is if you don't like the param name $array, I hear you, but we should do what core does and they called it $array. =(
With regard to $uid on access, I couldn't work that out. I think Services handles this and we don't need to worry about it. The function works fine, and anyway the $uid required is the ID of the user wanting to load the user, which was never available in the arguments in hook_service() - hence me thinking this "just works". Thinking about it, if it didn't then we would always get FALSE and an Access Denied message and the method wouldn't work. It does work for me, so it must be ok?
Comment #17
jhl.verona commentedSorry if I'm being a pain - and yes, I've spent some time on this too. The service should run irrespective of the server.
1. Yes it does work fine with XMLRPC. I checked too. I won't bore you with all the XML - just the request, which I also checked:
Yes, uid 51 does exist (user.get worked just fine in my previous post). Yes, XMLRPC gives me back the user
<struct>correctly. Same values asuser.get(51).2. Well, yes I see your point. Of course it *is* an array in PHP, called
$array, just a<struct>in XMLRPC.Maybe there *is* an issue with the JSON server, or Web UI. I won't argue that one. Let me just say that it causes the error message because you're getting an object (via the JSON parser), not an array. That's why I simply cast to an array - just as node.save does (services/services/node_service.inc node_service_save($edit) and node_service_save_access($node)).
Ok, let's keep the Drupalers happy, if we're mapping a drupal function, we map the parameter names too - however confusing that may be to a non drupaler/XMLRPCer.
With regard to $uid on access, I simply put a watchdog in there:
watchdog('xmlrpc', print_r($uid, TRUE));just before
global $user;Try it, $uid is an array:Array ( [uid] => 51 ). In your (first) case it would probably beArray ( [name] => 'newname' )It works because in the test, while the first test fails;
$user->uidis not equal to$uid, the second test passes because$user->uidis still not equal to$uid, *and*user_access('get any user data')is true. So as long as the user logged in ($user) has "get any user data" permission (a very high probability, given the service), it will always return TRUE, not FALSE.I rest my case, m'lud. New patch with 'array' name attached.
Comment #18
greg.harveyThanks for the updated patch for the uid! Looks good to me - will apply shortly. Don't know how it worked for me, as - as you rightly point out - it should be broken! =/
Does that mean it doesn't work with JSON server at all at the moment? Or it does with a slight workaround?
Anyways, if this works I'll R&BC this patch and maybe it'll get in some time soon. =)
(Ps - don't worry, I never think anyone taking an active interest in a patch is a PITA... appreciate your time as much as you appreciate mine. Nothing wrong with a bit of back and forth when solving a problem!)
Comment #19
jhl.verona commentedYes it works for JSON - at least via the Web UI. The workaround is to cast to an array, as per node.save. However user.save is broken in that respect (IMHO), but that's another battle.
Give it a try XMLRPC and JSON (web UI) It works for me, but it's nice to know if it works for others too!
That it gets in soon is another matter ;-)
Good. A healthy discussion is always better than pistols at dawn (I'm a poor shot). Bon chance!
Comment #20
greg.harveyJust looking at this, and reading you notes on the $uid problem with access more carefully, it seems the access function tries to behave like the access function for user profiles, which doesn't make sense. Web services can either access user profiles or they can't. I can't think of a situation where you would want individual users to be able to access their *own* user object via a web service - it's not something individual "people" would be doing, right?
I think we could safely alter that to simply check
user_access('get any user data')and that would be fine. Thoughts?Comment #21
jhl.verona commentedAs always, it's a bit more complicated than that. If you use API keys (which I'm inferring you do) then you're quite likely to be an anonymous user - see http://drupal.org/node/807638#comment-3013154. I don't use that technique, preferring that the XMLRPC client logs in as a specific user, with obviously a specific role and permissions. (Call me paranoid if you like ;-)
So I think this is the reason for the double test. Though I admit that reading the last paragraph back, I can't see how an anon user can do anything with the user services, given the test. But (my) life is too short for such details...
I'm not prepared to touch that code per se, but the mentioned thread is mine (http://drupal.org/node/807638) where I am trying to intregrate User Protect in a fairly transparent way (using
menu_get_item) so that the XMLRPC client can't delete users that I'd prefer to keep, for sentimental reasons, such as uid = 0, and uid = 1, for example.Again, after a couple of abortive patches, I stopped with a question, or idea if you prefer, but again, it's still waiting a reply...
The point is that I'm coming to the conclusion that I have a different perspective of 'protection' to the maintainers - or the original module developers. This may not be very fair, because I haven't looked at the development code, and hayrocker does mention that "things are changing down the road".
While it is perfectly reasonable that there are no sanity checks within the drupal core functions, that is, if you're creating a module or theme, it's your responsibility to "do the right thing"TM. But if you make these functions available to the outside world, via XMLRPC for example, then some sanity checking is necessary - otherwise a zealous (to use just one adjective) client can easily "do the wrong thing"TM - delete uid = 0, uid = 1, and so on. True, the argument goes that you can do that via the Web UI too, well at least uid = 1, but does that make it right? OK, you say, "there's a module for that", so I'll add User Protect, for example, but I have to hack services because it's not using the 'classic' access functions that I can 'override' via
hook_menu_alter().As another example the XMLRPC parser doesn't take into account the declared parameters and their types. You can easily send a
<struct>touser.getand that works just fine (just tried it ;-) - because it doesn't check the type, the access method returns TRUE for the wrong reasons explained above, and you get the user back. Very flexible I admit, but by design? Hmm. Ah, it won't work in the Web UI because that *does* check the types, so there's no JSON filter...I think I'm ranting, but it's mostly frustration. This is a good module, wildly useful, I just don't want to be clobbered by a malicious competitor who is writing the client side code - hope he doesn't read these threads...
John
Comment #22
greg.harveyI understand. I too use the XML-RPC client logs in approach, so anonymous would never be able to use any services... but let's leave it as is. Sounds like tearing it out would probably hold up the patch and open a can of worms.
Comment #23
kylebrowning commentedUnfortunately 2.x is feature frozen. We can move this to 3.x thought.
Comment #24
Renee S commentedIs it possible to get this committed? I see in the latest 6 it still ain't :)
Comment #25
gddWell in 3.x you can do this with the index service, so the new branch doesn't need a patch. However, as kylebrowning pointed out, the 2.x branch is feature frozen. Therefore this is a won't fix. Sorry all.
Comment #26
mrryanjohnston commentedI was hoping to bring this up again. This feature would be pretty nice for the retrieve method on the user resource for the same reason; users shouldn't have to know uids. This could be a little more difficult since node_load() works a little bit differently than it did in D6: http://api.drupal.org/api/drupal/modules--node--node.module/function/nod.... Should I open up a new issue for this or will this one suffice?
Comment #27
marcingy commentedYou can use the index sevices as per comment #25 so this stays won't fix, and there is no point in opening a news issue as that will be marked won't as well.
Comment #28
mrryanjohnston commentedAre you able to do the index service while using a REST server? The gist on the services page (https://gist.github.com/affc9864487bb1b9c918) only lists Retrieve, Create, Update, Delete, Login, Logout.
Comment #29
marcingy commentedYes take a look at the simpletests they perform an index call for rest. Plus I would look at services pages on d.o rather than git as the project lives on d.o rather than git.
Comment #30
mrryanjohnston commentedOk, I will check out those simpletests! Also, I did find that link on the services page: http://drupal.org/project/services
Look under "Examples"
Comment #31
marcingy commentedYes but is an off site link which means that it may not be maintained, all I meant is don't depend on data about services that is on third party sites.
Comment #32
mrryanjohnston commentedGood call! I understand, and thanks for your help with this matter. Just to clarify, you suggest running an index which returns all of the users, then finding from this result set the user with a given username?