tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From André Warnier (tomcat) ...@ice-sa.com>
Subject Re: how to access HTTPServletRequest in RealmBase
Date Sat, 01 Apr 2017 12:17:22 GMT
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 <vavra@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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Mime
View raw message