On 31.03.2017 19:40, Christopher Schultz wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

André,

On 3/30/17 3:41 PM, André Warnier (tomcat) wrote:
On 30.03.2017 20:10, Christopher Schultz wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256

Konstantin,

On 3/30/17 4:19 AM, Konstantin Kolinko wrote:
2017-03-30 11:02 GMT+03:00 Jan Vávra <va...@602.cz>:
Hello, I have written a custom Realm and I need to access to
the request headers. The authentication should be computed
from client certificate + id from custom http header
X-IdUser. Can I somehow access to the HTTPServletRequest
instance  ?

Not possible, by design.

An Authenticator (a valve) can access request and its headers.
A Realm cannot.

I've always been frustrated by this, and it's one reason I do not
use Tomcat's build-in authentication. I need to log
authentication failures and their sources (IP address) and this
information is simply not available through the Tomcat-provided
APIs.

I think there is definitely an opportunity here for improvement.


A naive question or three (I can't really ask any other kind in
Java) :

1) what is calling the following method ?
https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/realm
/DataSourceRealm.html#authenticate%28java.lang.String,%20java.lang.Strin
g%29

Within

Tomcat, one of the Authenticator classes is going to be calling
that method. (BasicAuthenticator, FormAuthenticator, etc.).

2) And (assuming that if you want to do non-standard things in the
Realm, it means that you are writing your own custom Realm), isn't
there a possibility for any caller of (1) above, to pass anything
it wants in the "credentials" argument ?

(such as a caller IP address, the content of a HTTP header, etc.)
(to be parsed out and used by the authenticate method, for logging
e.g.)

Absolutely, you could do that.

But let's say that I want to write my own Realm implementation so I
can e.g. write the user's IP address to a table when they
authenticate. Okay, I write a Realm but the interface doesn't contain
any of that data.

So, I decide that I want to package the IP address into the username
field for authenticate(String username, String password). Sounds good,
but then I'd need to write my own Authenticator that knows that the
username is actually IP-address + username instead of just username
like usual.

And if I'm going to write my own Realm *and* Authenticator, why don't
I just write everything myself and skip the Tomcat-provided code?

3) and, still assuming much, might one then perhaps use this
element to specify a class which would perform ditto parsing, prior
to the authentication itself ?
http://tomcat.apache.org/tomcat-9.0-doc/config/credentialhandler.html

The

CredentialHandler's job is to take the user's credential (the
password) and compare it to the value that was stored in the user
database. Providing a custom CredentialHandler only allows you to
support different types of password-munging.

Actually, that is what led me to think of this way of passing extra parameters.
I was looking at :
https://tomcat.apache.org/tomcat-8.0-doc/realm-howto.html#Digested_Passwords
and thinking that if in that case, instead of just the password, the string passed is {username}:{realm}:{cleartext-password}, then why not something like {IPaddress}:{header_content}:{cleartext-password}..

which would have "satisfied" the original OP, perhaps.


Note that the CredentialHandler doesn't have access to any data
storage system (like the user database). The Realm is designed to
fetch the user's stored credential from the user database and then
delegate the comparison to the CredentialHandler. This allows someone
to support e.g. a hashing algorithm that Tomcat has never seen before.
But it doesn't allow the user database to store anything new.

Tomcat is attempting to separate the concerns of various things into
separate classes / interfaces. This allows a great deal of re-use of
code. For example, there are "authenticators" (which don't
authenticate) for the following: HTTP Basic, HTTP Digest, CLIENT-CERT,
FORM. And there are Realms (which actually authenticate) for the
following user databases: JDBC, DataStore(also JDBC), LDAP/JNDI, JAAS,
and "memory" (really "file").

If there was one class for each setup, you'd need an explosion of classe
s:

HttpBasicLDAPAuthenticator
HttpBasicJDBCAuthenticator
HttpBasicDataSourceAuthenticator
HttpBasicJAASAuthenticator
HttpDigestLDAPAuthenticator
HttpDigestJDBCAuthenticator
HttpDigestDataSourceAuthenticator
etc.


Ok, I see the kind of problem now.

There are some authenticators that only make sense for some realm
methods. For example, the SSLAuthenticator is never going to call
Realm.authenticate(username, password). So in a way, these interfaces
are already a bit "impure".

But the idea for the Realm was "let's have a class/interface whose job
is simply to say yes/no to authentication requests and also to
determine if a user is authorized for a role-based permission".
There's really no reason to make that HTTP-specific. What does it mean
to have a username and password that match with or without an IP address
?

There is nothing HTTP-specific about a Realm, which is why this
complexity is left-out of the Realm interface.

But some people need their applications to use some information
available from the HTTP request as part of their authentication (or,
possibly their authentication-failure) processes.

So the solution currently requires programmers to step outside of
Tomcat's existing code and built a lot of their own things from scratch.

This used to be the case if you wanted to use a different hashing
algorithm (e.g. bcrypt): you'd have to write your own Realm mostly
from scratch and repeat a lot of code from the existing Realms. That's
why we introduced the CredentialHandler: so users could customize that
that /little/ piece of the puzzle. (It's also got some other goodies
in there like being able to access the CredentialHandler from
application code, so you can use the same configuration for
authentication as you do for changing user passwords within the
application.)

I think the same thing can be done for the authenticator-realm
relationship so that other information can be available for the Realm
to do its work. We just need to come up with a reasonable use-case and
a solution to it that doesn't require dozens of new
classes/interfaces. It is quite easy to over-design something in an OO
language :)


Thank you in any case for this thorough explanation.

Could not a solution be, to provide in the Realm, another authenticate() 
signature, with
authenticate(user,credentials,extra_params)
with "extra_params" being some kind of HashMap able to potentially contain any kind of key=>value thing ? Of course you'd still need to write the appropriate caller, but it would at least open the door. And any existing standard Realm can just ignore the extra argument.
(or does that sound like a "too-perl-y" suggestion ?)

I was also wondering why Konstantin, in his response, mentioned that it was "by design" that the Realm has no access to the Request. Was that to avoid some kind of problem, or to match the Specs or something ?


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to