I'm sorry, but this won't work. The fault here is certainly not on
repoze.wh* side, but what you presented so far is just working to
authenticate the login call itself. But obviously the OP wants a bunch
of authenticated (and possibly authorized) API-calls, not just one.
So there are basically three ways of doing this, each of them means
you got to die one particular death:
1) use HTTP-header-information to identify authenticated requests.
That means that in whatever I* of repoze.wh* you need to set a cookie
(as with some other authentications), and make sure that cookie is
transferred by the client each and every time. Then some other I* of
repoze.wh* needs to identify the current user based on that cookie.
Cookie could be any kind of header information of course.
This has the major problem of not being part of XMLRPC standard. So
you can't for example use the simple xmlrpclib-client of Python.
Instead, you'd have to provide your own implementation, based on e.g.
urllib2 or mechanize + using dump/load from xmlrpclib. And this is
only for Python - if you want any other language, you must investigate
if and how easy it is to comply with this. It might be a PITA.
2) use a URI-based session identification. This works nicely with
repoze.wh*. What you do is that on login, you return an *URI* that
points to an authenticated version of your service. So let's say your
service is available at
Then on successful login, you return something like
http://myhost/service/<sessionid>
The client has to re-connect to this URI. Then some I*-part of
repoze.wh* will be able to identify this as part of the PATH_INFO in
environ, and strip it of whilst additionally preparing the
authenticated identity.
The obious disadvantage of this approach is the rather hackish re-
connection thing, especially nasty if you happen to expose the service
under various DNS-names. But otherwise, it's the easiest.
3) the pure XMLRPC-way is to have login() return a session-id (numeric
or string-based doesn't matter, although for security reasons, it
probably should be a hash) which then on *EACH AND EVERY XMLRPC
METHOD* is the first parameter. Like this:
login(user:str, password:str) -> str
delete_all_my_data(session:str) -> bool
Then there are essentially two ways to deal with this:
- using repoze.wh*, you need to fully read, parse & understand the
XMLRPC-request, extract the first parameter, establish the identity
information (or render an XMLRPC-fault if un-authorized), and pass the
payload down again.
- instead, bypass repoze.what alltogether. Fully. Instead, use a
custom decorator, like
@xrequired()
def my_action(self, parameter):
....
@xrequired(predicate)
def my_other_action(self, parameter):
...
which will
- take the first parameter to my_action, use it as session, look
that session up, if proper, install the identity somehow so that
predicates work (if you really need them), and possibly even strip it
from the delegating call to my_action so that in your app-code you
don't have to bother.
I'd personally go for the last option. It's the most standard-conform,
and easy to implement. And the additional burden on the client can be
abstracted away easily.
Diez
If he wants to authenticate once and accept subsequent calls from the
authenticated client, there are much easier ways to accomplish that.
Taking your example of returning a token which would be sent back in
subsequent connections, my original code would be modified to something like:
"""
from repoze.who.interfaces import IIdentifier
from webob import Request
from zope.interfaces import implements
class XmlRpcIdentifier(object):
implements(IIdentifier)
classifications = {IIdentifier: ["xmlrpc"]}
def identify(self, environ):
request = Request(environ)
if "login" in request.POST and "password" in request.POST:
credentials = {
'login': request.POST['login'],
'password': request.POST['password'],
}
environ['repoze.who.application'] = AuthnResponse(**credentials)
else:
credentials = None
return credentials
class AuthnResponse(object):
def __init__(self, login, password):
self._token = hash_it("s3cr3t", login, password)
def __call__(self, environ, start_response):
headers = [
("Content-Type", "text/plain"),
("Content-Length", str(len(self._token))),
]
start_response("200 OK", headers)
return [self._token]
"""
That's it. Then repoze.who and repoze.what would behave as usual, with no
additional steps/workarounds/etc.
HTH,
- Gustavo.
The above is obviously not working. You might not know how XMLRPC
works - but it has no POST or GET parameters.
It has a POST-body that's an XML-document like this:
<?xml version='1.0'?>
<methodCall>
<methodName>login</methodName>
<params>
<param>
<value><string>user</string></value>
</param>
<param>
<value><string>password</string></value>
</param>
</params>
</methodCall>
So you can't instantiate a request and get login and password.
Instead, you need xmlrpclib.loads on the full wsgi.input body (as I
already explained).
And where does the token get identified by repoze.wh*, and how are the
credentials then set? Is the token part of the HTTP header? Not
working out of the box. Is the token a parameter to the underlying
XMLRPC-call? Then it's not working because you don't inspect the body.
To re-iterate again: XMLRPC works over HTTP, but it does *NOT* work
with the whole browser-semantics of cookies and headers.
Diez
Diez
> --
> You received this message because you are subscribed to the Google
> Groups "TurboGears" group.
> To post to this group, send email to turbo...@googlegroups.com.
> To unsubscribe from this group, send email to turbogears+...@googlegroups.com
> .
> For more options, visit this group at http://groups.google.com/group/turbogears?hl=en
> .
>
--
You received this message because you are subscribed to the Google Groups "TurboGears" group.
To post to this group, send email to turbo...@googlegroups.com.
To unsubscribe from this group, send email to turbogears+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/turbogears?hl=en.
Michael,
We will definitely try the xmlrpc controller.. but at this point it
might be bit late for us.
Some questions along those lines.
a. Will it work with TG2 2.0.3 ?
b. Will a simple login method and others work  (setup session ?)
What is an XMLRPC-handle?
> This is similar to option to suggested by Diez. The problem with this
> is that we need to implement login in each language for this to work.
> (among feeling it to be a hack)
Which option?
>
> I was/am still hoping.. :)
> a. XML-RPC controller to "parse" the user id..etc.
> b. We calling some repos.who) to create/populate session.
> (for us @ decorators are not very high priority.. I will have to
> double check though)
>
> From the discussion.. I still have some questions..
> a. how does repos.who auth extensions/plugin suggested by Gustavo
> go ? i.e how does it tie in to TG2)
Well, as eager as Gustavo always is to point out the power of
repoze.wh*, he has so far shown little if any understanding of the
problem at hand. His suggestions don't solve anything so far.
The options that exist I've already outlined. If you insist on using
repoze.wh* on request ingress, I *strongly* suggest using the session-
key-as-part-of-the-connection-URI-approach.
There are two reasons for this:
- it fits the repoze.wh* model of identifying authentication
information solely on the headers/keys in the WSGI-environ.
- not having to inspect the wsgi.input body saves you a *lot* of
computation time. You really only want to parse the XMLRPC-call once.
Believe me, we had a XMLRPC-based system with several tiers running -
and found out that it consumed 70% of it's time parsing and producing
XML....
If you find the specific URL-approach hackish, IMHO your best bet is
the decorator variant, stripping off the first session parameter. The
identity that is then needed for repoze-predicates to work with is
stuffed into the environ under the key
'repoze.what.credentials'
I'm not aware of something that easily sets up thes identity through
one call inside a controller (or decorator in this case), so you need
to cobble that together.
Then a passed predicate can run with that enriched environ.
ATTENTION: this approach of course *requires* you to decorate all
protected actions! A controller-level predicate (allow_only) won't work!
For that, you sure need to set up the credentials in the repoze.who
middleware. Which IMHO only leaves the URI-based approach. But if you
are willing to deal with the cost of parsing each request twice, you
can of course also work with a repoze.who plugin that xmlrpclib.loads
the wsgi.input, identifies the user, and possibly even strips out the
first parameter - although I'd be cautious about that because I
frankly have no idea of the order of things happening inside
repoze.who, and you might end up modifying the request to early for
other parts that still rely on the session parameter.
> b. Would returning credentials suggested last would ....session etc.
> correctly ?
No. As I've already pointed out & so far blissfully ignored by
Gustavo: that only authenticates your login-call. Nothing else.
Diez
Well, as eager as Gustavo always is to point out the power of repoze.wh*, he has so far shown little if any understanding of the problem at hand. His suggestions don't solve anything so far.
You are exactly right Gustavo - the arrogant and attitude is exactly the
problem here.
But obviously I see it the other way round. If I read something like this from
you
"""
The point I'm trying to make is that this kind of things can perfectly be
done with repoze.who, without bringing any hack to the authorization
controls like that @xrequire decorator or the other things you suggested.
"""
when clearly you
- don't understand the problem
- disqualify proposed solutions as "hack"
- ignorantly insist that things are "that simple", when obviously they
aren't, and this repeatedly so (I lined out the problem with the subsequent
calls not being authenticated several times..)
then that's an arrogant & ignorant attitude in *my* book. Which was the reason
why *I* changed my tone.
And what the heck are you talking about "arrogant attitude since the very first
response"? How can you read this into that post totally escapes me. I laid out
he various options, including ways how to actually use repoze.who.
Which IMHO is more complicated (and impacted by performance issues) than it's
worth it, but I clearly stated that as an opinion, not as advice to fore go
repoze.wh*. Which by now, I'm obviously willing to do.
Which in turn I think is the real problem here: whenever one merely suggests
that repoze.wh* is anything since the greatest thing since sliced bread, you
get defensive. Instead of trying to see criticism as means to enhance it.
Diez
Actually I'd say it's option 1 - enriching header information.
There could a lot to be said about this that I don't have the time right now,
but a few observations, see below.
First, you are non-standard here, due to already explained reasons. If you
don't care about that because you in fact are the only one really working with
that service, I *strongly* suggest you instead move to JSON as protocol. By
doing that, you can use auth* as is, are *much* more lightweight. And most
probably recycle a lot of common code between webapp and RPC-api.
> I do not think we need to parse the xml-rpc request. The xml-rpc
> controller will already do this.
You think wrong, because the way repoze.who works is that it identifies users
on the ingress, way before the request hits your controller. So if you go with
the officially appraised repoze.who-way, you in fact *do* have to parse each and
every XMLRCP-request, filter out the login-calls, and then perform the
identification as written in the tutorials Gustavo mentioned (unless you reside
to using regexp to parse the body of course.. . another can of worms....)
So IMHO this is prohibitive to use the normal way of working with repoze.who
because you end up eating CPU-cycles for nothing. But see below.
>
> Here is the xml-rpc controller method
> def login(user, password):
> # need some calls that would "authenticate" using repos.who
> # and set session context, if the auth is correct. (similar to
> login success.)
>
> return success or failure.
For above mentioned reason, this is not the way repoze.who wants to work. You
don't see a login-call inside a TG2-project because it is intercepted by the
middleware. Normally that's fine, because repoze.who just has to get the POST
or GET-parameters (you of course should only use POST...) of a submitted
login-call. And it can easily identify the requests to intercept because of
the PATH_INFO they have.
So there is no easy call inside a controller to set the current identity &
make sure subsequent requests get the same user. Of course you can still make
it work, but that requires to exactly trace back what repoze.who does to
establish the user inside a session, and set the respective headers yourself
inside the controller.
IMHO a short-coming and one of the reasons I created my own authentication
framework, but mentioning that will most likely spawn angered responses ....
> Hope this is clear and hoping to get some closure on this.
> Thanks
Much more so, yes. But I would not implement it that way, because it combines
non-standard-conformity with either not following the book of repoze.who or
parsing each and every request on ingress twice.
So again my suggestion would be to
- either use JSON if you can. It's *much* more lightweight, and you don't
have any of these issues here.
- go for the standard-conforming way of passing an exlpicit session value in
every call as actual method-parameter, and simply strip that away using a
decorator, which additionally sets up the identity to make predicates work (if
you need that)
Diez
Jd said:
> Thanks to everyone for their replies..
>
> Diez, Gustavo,
>
> * We have
> * a URL based login method (which sets up the cookies)
> * and we attache the cookies to the xml-rpc handle.
> This is similar to option to suggested by Diez. The problem with this
> is that we need to implement login in each language for this to work.
> (among feeling it to be a hack)
Basic/digest authentication is a widely used authentication method and also
very common in XML-RPC, so it might be an option.
repoze.who has a built-in plugin for that and it'd be quite easy to configure
it so that it only comes into play in XML-RPC requests.
> I was/am still hoping.. :)
> a. XML-RPC controller to "parse" the user id..etc.
> b. We calling some repos.who) to create/populate session.
> (for us @ decorators are not very high priority.. I will have to
> double check though)
I'd recommend leaving that to repoze.who for 4 reasons:
1.- Any 3rd party package that depends on authentication will work
automatically, like repoze.what.
2.- Authentication happens early on, not later in the controller.
3.- The user's metadata would be available as usual.
4.- You can focus on the features that add value to your Web site, without
worrying about or maintaining authentication code. If you want a functionality
not offered by existing repoze.who plugin, you'd just need to implement that
functionality instead of the whole authn system.
In other words, you'd keep the ability to use TG the "standard" way without
affecting other places that need not be affected, and given repoze.who's
pluggability, it'd be easy to change this setup in the future slightly or
completely without changing anything in your controllers.
I think repoze.who is good enough for this and it won't influence your
decision on how to handle authentication. If you tell us what you'd like to do
exactly, I'll tell you what's available already and if you'd need to write
some code.
I'd personally recommend using basic authentication because there's no code to
write and it's widely used, so you won't have to implement clients for
different languages.
> From the discussion.. I still have some questions..
> a. how does repos.who auth extensions/plugin suggested by Gustavo
> go ? i.e how does it tie in to TG2)
You'd put this information in {yourapp}.config.app_cfg:
http://www.turbogears.org/2.1/docs/main/Auth/Customization.html
That's the standard way to do it in TG2, but you can put that somewhere else
if you need to (e.g., ini files or a separate Python file).
> b. Would returning credentials suggested last would ....session etc.
> correctly ?
Sorry, I don't understand what you mean by that.
If you're talking about the session as in the Beaker library, AFAIK, it
doesn't look at the REMOTE_USER variable so it's completely independent from
repoze.who or any authn library.
HTH.
--
Gustavo Narea <xri://=Gustavo>.
| Tech blog: =Gustavo/(+blog)/tech ~ About me: =Gustavo/about |
There's no officially appraised way to do it, you can do it however you
want/need.
You said that the only way to pass that information was in the XML-based body
in the request and therefore I explained how to do it that way.
But HTTP authentication is pretty common in XML-RPC too, where there's no code
to write and nothing to parse. It will Just Work.
> > Here is the xml-rpc controller method
> > def login(user, password):
> > # need some calls that would "authenticate" using repos.who
> > # and set session context, if the auth is correct. (similar to
> >
> > login success.)
> >
> >
> > return success or failure.
>
> For above mentioned reason, this is not the way repoze.who wants to work.
> You don't see a login-call inside a TG2-project because it is intercepted
> by the middleware. Normally that's fine, because repoze.who just has to
> get the POST or GET-parameters (you of course should only use POST...) of
> a submitted login-call. And it can easily identify the requests to
> intercept because of the PATH_INFO they have.
>
> So there is no easy call inside a controller to set the current identity &
> make sure subsequent requests get the same user. Of course you can still
> make it work, but that requires to exactly trace back what repoze.who
> does to establish the user inside a session, and set the respective
> headers yourself inside the controller.
>
> IMHO a short-coming and one of the reasons I created my own authentication
> framework, but mentioning that will most likely spawn angered responses
> ....
That's correct, repoze.who 1 can only be used as pluggable middleware;
repoze.who 2 (under development) can be used as both pluggable middleware and
like a regular library in your code.
But in this case I don't think it would be a problem to move that code up in
repoze.who.
Don't you realize that you're actually describing yourself?
- You didn't understand the problem, based on the incorrect statements you
made about repoze.who which I pointed out in other emails, and the same for
XML-RPC, when you said that the credentials must be in the body because
"XMLRPC works over HTTP, but it does NOT work with the whole browser-semantics
of cookies and headers"... While it can perfectly use HTTP authentication.
- You disqualified my suggestion as something that wouldn't even work, while
at least I acknowledged that your solutions would work ("His suggestions don't
solve anything so far").
- You ignorantly said that there are three solutions and none of them was
good ("So there are basically three ways of doing this, each of them means
you got to die one particular death").
> And what the heck are you talking about "arrogant attitude since the very
> first response"? How can you read this into that post totally escapes me.
> I laid out he various options, including ways how to actually use
> repoze.who.
"I'm sorry, but this won't work. The fault here is certainly not on
repoze.wh* side, but what you presented so far is just working to
authenticate the login call itself. But obviously the OP wants a bunch
of authenticated (and possibly authorized) API-calls, not just one."
> Which IMHO is more complicated (and impacted by performance issues) than
> it's worth it, but I clearly stated that as an opinion, not as advice to
> fore go repoze.wh*. Which by now, I'm obviously willing to do.
That option is more complicated because you wanted it to be more complicated.
The credentials don't necessarily have to go in the XML-based body.
> Which in turn I think is the real problem here: whenever one merely
> suggests that repoze.wh* is anything since the greatest thing since
> sliced bread, you get defensive. Instead of trying to see criticism as
> means to enhance it.
I love criticism, but in this case I think you failed to present a valid
issue. There's no reason why repoze.who wouldn't be able to deal with this.
I'm personally not 100% satisfied with repoze.what 1. I am aware of
limitations in these packages, and so are the other people involved. That's
why repoze.who 2 and repoze.what 2 are being developed actively at present.
If there's something that you don't like, now is a perfect time to let us know
and possibly join us. Who knows, maybe the goals you have for that auth
library you're working on are compatible with our goals for these new
packages.
to begin this: I appreciate the non-ad-hominem response. I do not
agree with what you said and imply what I said, which you can read
below. But I hereby publically excuse myself for ramping up the tone
to a personal level. I shouldn't have done that.
And I hope this post - while certainly taking a stance towards things
you said - does not inflame the discussion further, but makes clear
what I perceived and what drove me.
I never said they must be in the body. I said they can be in the body,
in the URL, or in headers. Regarding the latter, the XMLRPC standard
does not make any assumptions about headers except content type &
content length.
""
Header requirements
The format of the URI in the first line of the header is not
specified. For example, it could be empty, a single slash, if the
server is only handling XML-RPC calls. However, if the server is
handling a mix of incoming HTTP requests, we allow the URI to help
route the request to the code that handles XML-RPC requests. (In the
example, the URI is /RPC2, telling the server to route the request to
the "RPC2" responder.)
A User-Agent and Host must be specified.
The Content-Type is text/xml.
The Content-Length must be specified and must be correct.
"""
I grant you that it is common that XMLRPC implementations allow for
basic auth to be transmitted. I didn't know that. Now please, be so
honest and admit: YOU didn't know that either, otherwise this whole
discussion wouldn't have taken place. Because then you would have
mentioned it earlier, instead of producing code snippets that clearly
do not rely on basic auth.
And basic auth isn't excactly the most secure authentication-kid on
the block, so relying on it and saying everything is just golden isn't
exactly the truth either. A proper, XMLRPC-call-based authentication
with challenge/responses would be better - but then, one is back on
square one. I don't claim this was in my mind the whole time. But it's
nonetheless a fact.
> - You disqualified my suggestion as something that wouldn't even
> work, while
> at least I acknowledged that your solutions would work ("His
> suggestions don't
> solve anything so far").
Which they didn't. They weren't basic auth, they relied on reading the
body either not xmlrpc-aware, or failed to provide means for
authenticating subsequent calls. So I stand by my statement: your
suggestions up to that point didn't solve the problem. And I really
mean your concrete suggestions in form of code snippets to illustrate
an idea, and not a general notion of repoze.wh* not being fit for the
job at all that I never uttered.
> - You ignorantly said that there are three solutions and none of
> them was
> good ("So there are basically three ways of doing this, each of them
> means
> you got to die one particular death").
I didn't ignorantly say that. I said what is technically the pure &
simple truth regarding XMLRPC. I wasn't aware that Python's xmlrpclib
supported basic auth (which, btw, isn't even mentioned in the docs),
and thus assumed you'd have to write non-standard-lib code to support
setting headers. Which, incidentially, the OP did. And at that point
in time, it wasn't clear if he only used python, and thus I wrote
under the assumption of the simple, pure XMLRPC-standard. So that's
one death. The other two neither of us disputes.
And for the record, I mentioned that URL and header based auth were
perfectly fine suited for repzoz.wh*.
>
>> And what the heck are you talking about "arrogant attitude since
>> the very
>> first response"? How can you read this into that post totally
>> escapes me.
>> I laid out he various options, including ways how to actually use
>> repoze.who.
>
> "I'm sorry, but this won't work. The fault here is certainly not on
> repoze.wh* side, but what you presented so far is just working to
> authenticate the login call itself. But obviously the OP wants a bunch
> of authenticated (and possibly authorized) API-calls, not just one."
First of all, I fail to see how saying that "the fault here is
certainly not on repoze.wh*" qualifies as "arrogant/trolly", from that
very first repsonse.
Then the statement was in context of your post, which showed this:
"""
def identify(self, environ):
request = Request(environ)
if "login" in request.POST and "password" in request.POST:
credentials = {
'login': request.POST['login'],
'password': request.POST['password'],
}
else:
credentials = None
"""
Are you trying to tell me that this works even for the request going
on? No. It doesn't. Is this basic auth? No, it isn't.
And that was what I was clearly referring to when I said "presented so
far".
Again, I did *not* imply that repozew.wh* isn't capable of dealing
with the problem at all. I merely said that so far, no working
solution was presented.
>> Which IMHO is more complicated (and impacted by performance issues)
>> than
>> it's worth it, but I clearly stated that as an opinion, not as
>> advice to
>> fore go repoze.wh*. Which by now, I'm obviously willing to do.
>
> That option is more complicated because you wanted it to be more
> complicated.
> The credentials don't necessarily have to go in the XML-based body.
Which I never implied. I said *if* one wants to use XMLRPC calls
themselves as authentication method, *then* one has to resort to parse
the body twice given the current design of repoze.who.
Again, I listed all three options from the very start. Pointing out
that one of them is problematic makes me "wanting it to be more
complicated"?
>> Which in turn I think is the real problem here: whenever one merely
>> suggests that repoze.wh* is anything since the greatest thing since
>> sliced bread, you get defensive. Instead of trying to see criticism
>> as
>> means to enhance it.
>
> I love criticism, but in this case I think you failed to present a
> valid
> issue. There's no reason why repoze.who wouldn't be able to deal
> with this.
>
> I'm personally not 100% satisfied with repoze.what 1. I am aware of
> limitations in these packages, and so are the other people involved.
> That's
> why repoze.who 2 and repoze.what 2 are being developed actively at
> present.
>
> If there's something that you don't like, now is a perfect time to
> let us know
> and possibly join us. Who knows, maybe the goals you have for that
> auth
> library you're working on are compatible with our goals for these new
> packages.
Well, I appreciate the offer. Really. If you care, you can look at my
current implementation here:
http://bitbucket.org/deets/tgextsimpleauthorization
The few key points it does differently which IMHO you should consider
in your design are these:
- it does allow for predicates to be evaluated with an implicit
environ. Because it has a middleware that knows it's environ. So even
serveral nested apps safely can work, because each of them is shielded
by it's own auth-middleware, and thus knows about it's own auth-
context, not some global one (global in the sense of the environ, not
variables obviously).
- it allows application controller code to call it to log in or out
a given user. Something for example the current OP would have wished
for. And this I think is a huge advantage because people write
controllers all the time, where on the other hand writing repoze.who
plugins you do only every now and then, and thus getting it right is
harder. Harder, not impossible!
Think of e.g. the simple, but very desirable feature of passing
additional parameters to the login page to make it redirect to a
specific page after success. Being able to use standard TG/
ToscaWidgets code & validation for this instead of having to write a
bare-bone WSGI implementation.
Yes, I'm aware of friendlyform-plugin. But while it's good that it is
available, it might not fit all needs, and customization is harder
than to simply adapt the login-controller in your own application.
And the current discussion is an example as well, providing a user/
password based authentication for XMLRPC at controller level would be
much easier & not require double-body-parsing if one could modify the
current request's identity information easily from within the
application.
I could bring more examples, but I guess the point is clear.
- it provides convenience methods for dealing with redirects, both
internally ond externally. This again makes writing code much easier.
Consider your own example that wanted to return a token on successful
login. To do so, one has to go down a rather arcane road of passing an
WSGI-application to the middleware through an environ key, and then of
course use WSGI-standard to render a response. OTOH my framework in
that situation allows you for e.g. an internal redirect that renders
the result in whatever framework you are in.
- it uses operators "&" and "|" and "-" to create compound
predicates which work both in templates (being evaluated directly), or
in declarative situations like controller actions, so it overcomes the
limitations of "and" and "or" and "not" only being suitable for
templates, but not for controllers.
- it has rich exception based predicate evaluation + easy text-based
configuration for those, so you can e.g. make different predicates
raise different authorization errors, and the middleware will redirect
appropriately - even with GET-parameter-passing support, so e.g. the
MissingGroupError will not only redirect to a page, but also tell you
easily *which* group you don't belong to.
Regarding the working together on this: I don't see that. The reason
are previous discussions we had about feature wishes, that prodded me
into writing tgext.simplauth in the first place. We seem to have very
different opinions about what is good design and what is hackish...
and fighting these out all the time I fear isn't fruitful.
Diez
Diez said:
> I never said they must be in the body. I said they can be in the body,
> in the URL, or in headers. Regarding the latter, the XMLRPC standard
> does not make any assumptions about headers except content type &
> content length.
>
> ""
> Header requirements
>
> The format of the URI in the first line of the header is not
> specified. For example, it could be empty, a single slash, if the
> server is only handling XML-RPC calls. However, if the server is
> handling a mix of incoming HTTP requests, we allow the URI to help
> route the request to the code that handles XML-RPC requests. (In the
> example, the URI is /RPC2, telling the server to route the request to
> the "RPC2" responder.)
>
> A User-Agent and Host must be specified.
>
> The Content-Type is text/xml.
>
> The Content-Length must be specified and must be correct.
>
> """
>
> (http://www.xmlrpc.com/spec)
>
> I grant you that it is common that XMLRPC implementations allow for
> basic auth to be transmitted. I didn't know that. Now please, be so
> honest and admit: YOU didn't know that either, otherwise this whole
> discussion wouldn't have taken place. Because then you would have
> mentioned it earlier, instead of producing code snippets that clearly
> do not rely on basic auth.
I didn't, either.
> And basic auth isn't excactly the most secure authentication-kid on
> the block, so relying on it and saying everything is just golden isn't
> exactly the truth either. A proper, XMLRPC-call-based authentication
> with challenge/responses would be better - but then, one is back on
> square one. I don't claim this was in my mind the whole time. But it's
> nonetheless a fact.
I consider basic authentication, digest authentication, sending the
credentials in the XML-RPC body, etc, equally insecure because if you
intercept the connection you got the credentials. I'd just pick the easiest
method and make it work under HTTPS.
> > - You disqualified my suggestion as something that wouldn't even
> > work, while
> > at least I acknowledged that your solutions would work ("His
> > suggestions don't
> > solve anything so far").
>
> Which they didn't. They weren't basic auth, they relied on reading the
> body either not xmlrpc-aware, or failed to provide means for
> authenticating subsequent calls. So I stand by my statement: your
> suggestions up to that point didn't solve the problem. And I really
> mean your concrete suggestions in form of code snippets to illustrate
> an idea, and not a general notion of repoze.wh* not being fit for the
> job at all that I never uttered.
The two suggestions I made initially (before HTTP authentication) would work:
1st suggestion: Sending the credentials in request arguments
------------------------------------------------------------
I suggested that because years ago I wrote a XML-RPC server which expected the
credentials to be in the query string, so it's the first thing that came to
mind.
There I (repeatedly) used "POST" by mistake, which is certainly impossible. If
you replace "POST" with "GET" in that code, that would be what I meant to
show.
Authentication would have to be done on every request but that's absolutely
fine. In fact, that's exactly how both basic and digest authentication work.
If you ask me these days, I wouldn't take that approach again because it's not
exactly the most elegant one. I personally find it better than including the
credentials in the call, though... I don't think they fit there: it's
generally meant for the authentication layer of the system, not the XML-RPC
server per se.
2nd suggestion: Sending the credentials in the XML-RPC request body
-------------------------------------------------------------------
This one is based on your third suggestion.
Although it is not mandatory to authenticate in the first request and identify
in subsequent requests, this method offered that functionality.
It also involves sending the credentials in the call, which means it'd need to
be parsed twice in the first request. But this is the only disadvantage,
AFAIK.
> >> Which IMHO is more complicated (and impacted by performance issues)
> >> than
> >> it's worth it, but I clearly stated that as an opinion, not as
> >> advice to
> >> fore go repoze.wh*. Which by now, I'm obviously willing to do.
> >
> > That option is more complicated because you wanted it to be more
> > complicated.
> > The credentials don't necessarily have to go in the XML-based body.
>
> Which I never implied. I said *if* one wants to use XMLRPC calls
> themselves as authentication method, *then* one has to resort to parse
> the body twice given the current design of repoze.who.
Your 3rd suggestion is about sending the credentials in the call, which is the
only one of your suggestions that I explicitly commented on.
> Well, I appreciate the offer. Really. If you care, you can look at my
> current implementation here:
>
>
> http://bitbucket.org/deets/tgextsimpleauthorization
>
>
> The few key points it does differently which IMHO you should consider
> in your design are these:
>
> - it does allow for predicates to be evaluated with an implicit
> environ. Because it has a middleware that knows it's environ. So even
> serveral nested apps safely can work, because each of them is shielded
> by it's own auth-middleware, and thus knows about it's own auth-
> context, not some global one (global in the sense of the environ, not
> variables obviously).
That sounds good to me, and I suspect it wouldn't be that hard to do.
I'll give it some thought on how to make that work optionally without making
the library itself depend on thread locals, but yes, I think it's a good idea
for the next release.
> - it allows application controller code to call it to log in or out
> a given user. Something for example the current OP would have wished
> for. And this I think is a huge advantage because people write
> controllers all the time, where on the other hand writing repoze.who
> plugins you do only every now and then, and thus getting it right is
> harder. Harder, not impossible!
>
> Think of e.g. the simple, but very desirable feature of passing
> additional parameters to the login page to make it redirect to a
> specific page after success. Being able to use standard TG/
> ToscaWidgets code & validation for this instead of having to write a
> bare-bone WSGI implementation.
That sounds like an excellent idea. It's a known limitation in repoze.who 1,
which fortunately is addressed in v2.
> Yes, I'm aware of friendlyform-plugin. But while it's good that it is
> available, it might not fit all needs, and customization is harder
> than to simply adapt the login-controller in your own application.
Agreed.
> And the current discussion is an example as well, providing a user/
> password based authentication for XMLRPC at controller level would be
> much easier & not require double-body-parsing if one could modify the
> current request's identity information easily from within the
> application.
Also a good point.
> I could bring more examples, but I guess the point is clear.
>
> - it provides convenience methods for dealing with redirects, both
> internally ond externally. This again makes writing code much easier.
> Consider your own example that wanted to return a token on successful
> login. To do so, one has to go down a rather arcane road of passing an
> WSGI-application to the middleware through an environ key, and then of
> course use WSGI-standard to render a response. OTOH my framework in
> that situation allows you for e.g. an internal redirect that renders
> the result in whatever framework you are in.
I assume that would be accomplished by making reusing the WSGI application
object passed to your middleware, and calling it with a copy of the environ
with some items changed (e.g., PATH_INFO).
If this correct, the problem I see is that the WSGI middleware defined before
yours wouldn't participate in the internal redirects, which may or may not
make a difference.
> - it uses operators "&" and "|" and "-" to create compound
> predicates which work both in templates (being evaluated directly), or
> in declarative situations like controller actions, so it overcomes the
> limitations of "and" and "or" and "not" only being suitable for
> templates, but not for controllers.
That's one of the first things I checked in the development branch. :) They're
really handy. I'll backport them to v1 eventually.
> - it has rich exception based predicate evaluation + easy text-based
> configuration for those, so you can e.g. make different predicates
> raise different authorization errors, and the middleware will redirect
> appropriately - even with GET-parameter-passing support, so e.g. the
> MissingGroupError will not only redirect to a page, but also tell you
> easily *which* group you don't belong to.
That sounds like a very different approach to predicates. Maybe I'd change my
mind if I see some code examples, but I think I prefer the current approach.
> Regarding the working together on this: I don't see that. The reason
> are previous discussions we had about feature wishes, that prodded me
> into writing tgext.simplauth in the first place. We seem to have very
> different opinions about what is good design and what is hackish...
> and fighting these out all the time I fear isn't fruitful.
You may be right, and also now may be too early to talk about that. I'd say we
both have clear ideas on what to do, but our implementations are work in
progress at the moment.
I'd love to have this discussion at some point later. Even if we decide not to
work together, we could always borrow ideas from each other.
Cheers.