You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by "André Warnier (tomcat)" <aw...@ice-sa.com> on 2017/04/01 12:17:22 UTC

Re: how to access HTTPServletRequest in RealmBase

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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: how to access HTTPServletRequest in RealmBase

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Andr�,

On 4/1/17 8:17 AM, Andr� Warnier (tomcat) wrote:
> 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 think I'd want to do something like this:

public interface CustomAuthenticatorThing {
  public Object prepareCustom(...);
}

Then another method on the Realm like:

public boolean authenticate(Object customObject);

Then the Authenticator code would do something like this:

Object customRealmObject = null;
if(null != customizer) {
  customRealmObject = customized.prepareCustom(...);
  authenticated = realm.authenticate(customRealmObject);
} else {
  realm.authenticate(username, password);
}

There's a lot of missing complexity there, of course, but I think you
get the idea. The Realm plus its customizer would agree on an
intermediate object to use, and the authenticator would shuttle the
data between them.

Of course, if the authenticator is going to pass-in the "..." objects
to the customizer, maybe it would be more straightforward to just have
the Realm implement an authenticate(...) method instead.

I guess the only reason would be that the Realm interface hasn't
changed much in ... forever. Inertia, I guess.

I might try agitating to get this changed, because it's annoyed me
since the very beginning -- to the point where I don't use Tomcat's
built-in authentication and authorization capabilities at all. :(

- -chris
-----BEGIN PGP SIGNATURE-----
Comment: GPGTools - http://gpgtools.org
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQIcBAEBCAAGBQJY4rGSAAoJEBzwKT+lPKRYdNAP+wRlv685z44wtl9m7M9rc600
W/TPJMFGDcSOQku2Mp/T4zXayzdAdxjmbFNRn84lSrF67X+Ax2zSCxBmUfGv/zvL
+S85npuDa3qmu0ISsIPbKx+v360+LPhP+FUDtnEc2Rps6LNgR4ytqqJWctFPnKU/
EUK9UZM5po6OOL+wtj/b5JSlpXMrJsVe5wN9/cEwJaHITyaIhaRqStrXLAISxwcb
sESJek/Cp5Nq7lniud8QkTjN4/h23Fod3L2si/C4HwCKmMHP4wZ84uVRvngMvhbA
gtDzEJ/xdJIdGt1p/1DME4EDJBaSET/xO3bgdzCvo1UsPyqTL6EjOwrJoWQrPPYt
Hqnr67fHWlceP5/xpTBXYrtxF3aUFJ1212O/WYvOHB7BrK+rrTTB+EpDs5JycJSR
TtWq8QAe+gNldcE/24x6NGvLxh7IB5lVBoazsTCrpr91lViwYz4QO6zuvhtYOdFr
lfiJ59QsDUzl7qWpuyP8zHxmeCdxrl75IHHd6a9sbqqzhaxy2xs1XIFD6P9LRqXV
AdIWS5sT9MI3zwIm+Anfl7KhlUhJDxl2zJxrpg6YWHNVzqAl53H28RNxowYfI5m5
aH/N0a3R94TH8zNllH8NUGOdKRYZrw9ymfR2Ib9AT9JEw4go4xOad1kWpu/AfZ2M
auwd9DVkfnZK+3+f3J+K
=x+hi
-----END PGP SIGNATURE-----

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


Re: how to access HTTPServletRequest in RealmBase

Posted by Konstantin Kolinko <kn...@gmail.com>.
2017-04-01 15:17 GMT+03:00 André Warnier (tomcat) <aw...@ice-sa.com>:
>
> 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 ?
>

Documentation says so. E.g.:
"A Realm is a "database" of usernames and passwords"
http://tomcat.apache.org/tomcat-8.5-doc/realm-howto.html#What_is_a_Realm?


One design bit driven by the servlet spec is that an Authenticator
usually is not configured explicitly, but is created automatically,
mapping an <auth-method> onto a class name. See
Authenticators.properties file

https://svn.apache.org/viewvc/tomcat/tc8.5.x/tags/TOMCAT_8_5_13/java/org/apache/catalina/startup/Authenticators.properties?view=markup

The web application decides, what authentication protocol to use.

Realms are configured explicitly, and we have one by default.


In a few cases where I went with Tomcat authentication (instead of
usual Spring Security library)
and had special requirements (checks by IP, preauthentication)  I
implemented a Valve,
not even an Authenticator.
I am not proud of those, but they served a specific purpose. They were
inspired by SingleSignOn valve.


BTW, there are alternative technologies, JASPIC, JAAS, GSS (GSSContext
in SpnegoAuthenticator)
http://tomcat.apache.org/tomcat-8.5-doc/config/jaspic.html
http://tomcat.apache.org/tomcat-8.5-doc/config/realm.html#JAAS_Realm_-_org.apache.catalina.realm.JAASRealm

JASPIC has access to request & response..

Best regards,
Konstantin Kolinko

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