### Eclipse Workspace Patch 1.0 #P wagon Index: wagon-provider-api/src/main/java/org/apache/maven/wagon/AbstractWagon.java =================================================================== --- wagon-provider-api/src/main/java/org/apache/maven/wagon/AbstractWagon.java (revision 1053317) +++ wagon-provider-api/src/main/java/org/apache/maven/wagon/AbstractWagon.java (working copy) @@ -27,6 +27,12 @@ import java.io.OutputStream; import java.util.List; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + import org.apache.maven.wagon.authentication.AuthenticationException; import org.apache.maven.wagon.authentication.AuthenticationInfo; import org.apache.maven.wagon.authorization.AuthorizationException; @@ -195,6 +201,12 @@ authenticationInfo.setPassword( repository.getPassword() ); } } + // If there's no user/pass in the URL, see if there's a CallbackHandler + // specified and call out to it to get the username and password. + else + { + setUsernameAndPasswordUsingCallbackHandler( authenticationInfo ); + } } // TODO: Do these needs to be fields, or are they only used in openConnection()? @@ -208,7 +220,86 @@ fireSessionOpened(); } - + + /** + * Attempts to take the callbackHandlerClass name, create an instance of it and interrogate it for a + * username and password. If successful, adds the username and password details into authenticationInfo. + * If unsuccessful, fails relatively quietly; logging the error but not throwing an Exception. + * + * @param authenticationInfo username and password are updated if this method is successful + */ + protected void setUsernameAndPasswordUsingCallbackHandler(AuthenticationInfo authenticationInfo) + { + assert authenticationInfo != null; + + if(authenticationInfo.getCallbackHandlerClass() == null) { + return; + } + + Class callbackHandlerClassObject; + try + { + callbackHandlerClassObject = Class.forName(authenticationInfo.getCallbackHandlerClass()); + } catch (ClassNotFoundException e) { + //log the failure to find the CallbackHandler class + //Could throw a ConnectionException but that would stop + //the successful download of an artifact if no authentication + //is actually required. + return; + } + + if(!CallbackHandler.class.isAssignableFrom(callbackHandlerClassObject)) + { + //log the fact that the specified CallbackHandler class + //is not assignable to the CallbackHandler interface + return; + } + + CallbackHandler callbackHandler; + try { + callbackHandler = (CallbackHandler)callbackHandlerClassObject.newInstance(); + } + catch (InstantiationException e) + { + //Log the fact that the CallbackHandler class does not have a + //public default constructor + return; + } + catch (IllegalAccessException e) + { + //Log the fact that the CallbackHandler class is hidden + return; + } + + //A SingleSignOn solution will not display these text prompts. + NameCallback usernameCallback = new NameCallback("SingleSignOn username"); + PasswordCallback passwordCallback = new PasswordCallback("SingleSignOn password", false); + + Callback[] callbacks = new Callback[] { + usernameCallback, + passwordCallback + }; + + try + { + callbackHandler.handle(callbacks); + } + catch (IOException e) + { + //log + return; + } + catch (UnsupportedCallbackException e) + { + //log + return; + } + + authenticationInfo.setUserName(usernameCallback.getName()); + authenticationInfo.setPassword(new String(passwordCallback.getPassword())); + + } + protected abstract void openConnectionInternal() throws ConnectionException, AuthenticationException; Index: wagon-provider-api/src/main/java/org/apache/maven/wagon/authentication/AuthenticationInfo.java =================================================================== --- wagon-provider-api/src/main/java/org/apache/maven/wagon/authentication/AuthenticationInfo.java (revision 1053317) +++ wagon-provider-api/src/main/java/org/apache/maven/wagon/authentication/AuthenticationInfo.java (working copy) @@ -51,6 +51,8 @@ * The absolute path to private key file */ private String privateKey; + + private String callbackHandlerClass; /** * Get the passphrase of the private key file. The passphrase is used only @@ -133,4 +135,14 @@ { this.userName = userName; } + + public String getCallbackHandlerClass() + { + return callbackHandlerClass; + } + + public void setCallbackHandlerClass(String callbackHandlerClass) + { + this.callbackHandlerClass = callbackHandlerClass; + } } Index: wagon-provider-api/src/test/java/org/apache/maven/wagon/AbstractWagonTest.java =================================================================== --- wagon-provider-api/src/test/java/org/apache/maven/wagon/AbstractWagonTest.java (revision 1053317) +++ wagon-provider-api/src/test/java/org/apache/maven/wagon/AbstractWagonTest.java (working copy) @@ -25,6 +25,12 @@ import java.io.InputStream; import java.io.OutputStream; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + import junit.framework.TestCase; import org.apache.maven.wagon.authentication.AuthenticationException; @@ -601,4 +607,38 @@ return baos.toByteArray(); } } + + static class CBHandler implements CallbackHandler { + public static final String TESTUSER = "testuser"; + public static final String TESTPASSWORD = "testpassword"; + + public void handle(javax.security.auth.callback.Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for(int i = 0; i < callbacks.length; i++) { //source level is 1.5 so we have to use old style loops + Callback callback = callbacks[i]; + if(callback instanceof NameCallback) { + ((NameCallback)callback).setName(TESTUSER); + } else if(callback instanceof PasswordCallback) { + ((PasswordCallback)callback).setPassword(TESTPASSWORD.toCharArray()); + } + } + }; + } + + public void testSingleSignOnWithCallbackHandler() { + AbstractWagon wagon = new TestWagon(); + AuthenticationInfo authenticationInfo = new AuthenticationInfo(); + + //No exception gets thrown when passing in no CallbackHandlerClass + wagon.setUsernameAndPasswordUsingCallbackHandler(authenticationInfo); + + //No exception gets thrown when a non-existent CallbackHandlerClass gets used + authenticationInfo.setCallbackHandlerClass("com.example.DoesNotExist"); + wagon.setUsernameAndPasswordUsingCallbackHandler(authenticationInfo); + + //Test a valid scenario + authenticationInfo.setCallbackHandlerClass(CBHandler.class.getName()); + wagon.setUsernameAndPasswordUsingCallbackHandler(authenticationInfo); + assertEquals(CBHandler.TESTUSER, authenticationInfo.getUserName()); + assertEquals(CBHandler.TESTPASSWORD, authenticationInfo.getPassword()); + } } #P maven-3 Index: pom.xml =================================================================== --- pom.xml (revision 1053317) +++ pom.xml (working copy) @@ -45,12 +45,12 @@ 1.14 2.0.4 1.4.3.1 - 1.0-beta-7 + 1.0-beta-8-SNAPSHOT 1.3 1.4 1.4 1.3 - 1.8 + 1.9-SNAPSHOT true Index: maven-core/src/main/java/org/apache/maven/RepositoryUtils.java =================================================================== --- maven-core/src/main/java/org/apache/maven/RepositoryUtils.java (revision 1053317) +++ maven-core/src/main/java/org/apache/maven/RepositoryUtils.java (working copy) @@ -222,7 +222,7 @@ if ( auth != null ) { result = - new Authentication( auth.getUsername(), auth.getPassword(), auth.getPrivateKey(), auth.getPassphrase() ); + new Authentication( auth.getUsername(), auth.getPassword(), auth.getPrivateKey(), auth.getPassphrase(), auth.getCallbackHandlerClass() ); } return result; } Index: maven-core/src/main/java/org/apache/maven/DefaultMaven.java =================================================================== --- maven-core/src/main/java/org/apache/maven/DefaultMaven.java (revision 1053317) +++ maven-core/src/main/java/org/apache/maven/DefaultMaven.java (working copy) @@ -398,7 +398,7 @@ { Authentication auth = new Authentication( server.getUsername(), server.getPassword(), server.getPrivateKey(), - server.getPassphrase() ); + server.getPassphrase(), server.getCallbackHandlerClass() ); authSelector.add( server.getId(), auth ); if ( server.getConfiguration() != null ) Index: maven-settings/src/main/mdo/settings.mdo =================================================================== --- maven-settings/src/main/mdo/settings.mdo (revision 1053317) +++ maven-settings/src/main/mdo/settings.mdo (working copy) @@ -560,6 +560,17 @@ String + callbackHandlerClass + 1.0.0+ + + + + String + + privateKey 1.0.0+ Index: maven-artifact/src/main/java/org/apache/maven/artifact/repository/Authentication.java =================================================================== --- maven-artifact/src/main/java/org/apache/maven/artifact/repository/Authentication.java (revision 1053317) +++ maven-artifact/src/main/java/org/apache/maven/artifact/repository/Authentication.java (working copy) @@ -25,6 +25,8 @@ private String privateKey; private String passphrase; + + private String callbackHandlerClass; public Authentication( String userName, String password ) { @@ -123,4 +125,12 @@ this.privateKey = privateKey; } + public String getCallbackHandlerClass() { + return callbackHandlerClass; + } + + public void setCallbackHandlerClass(String callbackHandlerClass) { + this.callbackHandlerClass = callbackHandlerClass; + } + }