tags:

views:

654

answers:

4

We have a static method in a utility class that will download a file from a URL. An authenticator has been set up so that if a username and password is required, the credentials can be retrieved. The problem is that the credentials from the first successful connection are used for every connection afterwords, as long as the credentials are valid. This is a problem because our code is multi user, and since the credentials are not checked for every connection, it's possible that a user without proper credentials could download a file.

Here's the code we're using

private static URLAuthenticator auth;

public static File download(String url, String username, String password, File newFile)
{
    auth.set(username, password);
    Authenticator.setDefault(auth);
    URL fURL = new URL(url);
    OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile));
    URLConnection conn = fURL.openConnection();
    InputStream in = conn.getInputStream();

    try
    {
        copyStream(in, out);
    }
    finally
    {
        if (in != null)
            in.close();
        if (out != null)
            out.close();
    }

    return newFile;
}

public class URLAuthenticator extends Authenticator
{
    private String username;
    private String password;

    public URLAuthenticator(String username, String password)
    {
         set(username, password);
    }

    public void set(String username, String password)
    {
        this.username = username;
        this.password = password;
    }

    protected PasswordAuthentication getPasswordAuthentication()
    {
        log.debug("Retrieving credentials '" + username + "', '" + password + "'.");
        return new PasswordAuthentication(username, password.toCharArray());
    }
}

I only see the log statement from getPasswordAuthentication once, the first time that a file is downloaded. After that first successful attempt, getPasswordAuthentication is not called again, even though the credentials have been reset. The result is that after the first successful connection, invalid credentials can be entered, and a successful connection can still be made. Is this possibly a result of the download method being static, and in a static class?

Edit I forgot to mention that this is in a JSF webapp running under tomcat - maybe one of those technologies is setting some default credentials somewhere?

I've pulled the URLAuthenticator out into its own class, and made it as non-static as possible, but the problem still exists. I've read that if the default authenticator is set to null with Authenticator.setDefault(null), then on windows the NTLM authentication will be used. That shouldn't be the problem here since I'm setting the Authenticator everytime, but I thought I'd throw it out there. The NTLM authentication is definately getting used, because if the server is run as a user that has access to the downloaded file, the credentials aren't even asked for, the file just downloads. So something obviously is grabbing my credentials and passing them in before the authenticator is called.

+1  A: 

It looks like in your finally block you need to call

Authenticator.setDefault(null);
confusedGeek
This didn't work - the behavior stayed the same.
Matt McMinn
Weird, that looked like it from the Java Docs. Have you tried getting rid of the static UrlAuthenticator? Not sure why that would need to be static.
confusedGeek
I've refactored so that it's not static, and pulled the URLAuthenticator into its own class, but the problem is still there.
Matt McMinn
Ok, I'm at a loss on this. The only (bad) idea that I have is instead of setting setDefault to null to assign it a bad (won't validate) Authenticator. Sorry I don't have any decent ideas.
confusedGeek
A: 

I've figured something out at least. It appears that this behavior is a bug. A workaround is to use a Sun specific class to explicitly reset the cache, like so:

import sun.net.www.protocol.http.AuthCacheValue;
import sun.net.www.protocol.http.AuthCacheImpl;
....
AuthCacheValue.setAuthCache(new AuthCacheImpl());
Authenticator.setDefault(new URLAuthenticator(username, password));

I'm resetting the AuthCache at the top of the download function described in the question. During compile, you'll get warnings about using these classes. This doesn't completely fix the problem: if NTLM authentication works, the authenticator still won't get called, but as long as the server is running under a user that has does not have permission for the requested file, this should clear the cache out.

Matt McMinn
A: 

I am running into exactly the same problem. How do you actually call:

import sun.net.www.protocol.http.AuthCacheValue;
import sun.net.www.protocol.http.AuthCacheImpl;
....
AuthCacheValue.setAuthCache(new AuthCacheImpl());
Authenticator.setDefault(new URLAuthenticator(username, password));

Those classes are package level, so there is no way to access the methods. Any suggestions? Thnaks!

I actually didn't have any problems calling those methods - my full method is here http://pastebin.com/m6ab08714. This is in a util class in my own package. Are you getting a compile error?
Matt McMinn
Yes. I am gettingAccess restriction: The type AuthCacheValue is not accessible due to restriction on required library C:\java\jdk1.6.0_10\jre\lib\rt.jarWhich JDK version are you using?
I tried both with 1.5 and 1.6.11, and didn't have that problem with either version - doesn't look like I'll be much help, so this might be better done as a new question. Google also found a few things you could try to get rid of that error.
Matt McMinn
A: 

Whoa ! This worked ! Thanks a ton.

Gireesh