From a02a8a88b5796916acc69913041d28e21c5f949d Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 7 Feb 2011 18:20:50 -0600 Subject: [PATCH] HHH-5898 - Improve authentication for Gradle uploads --- build.gradle | 5 +- buildSrc/build.gradle | 3 +- .../gradle/upload/AuthenticationHandler.java | 87 ++++++++++++ .../gradle/upload/AuthenticationProvider.java | 45 ++++++ .../AuthenticationProviderRegistry.java | 53 +++++++ .../build/gradle/upload/Authenticator.java | 125 ----------------- .../StandardMavenAuthenticationProvider.java | 130 ++++++++++++++++++ ....java => UploadAuthenticationManager.java} | 44 +++--- 8 files changed, 341 insertions(+), 151 deletions(-) create mode 100644 buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationHandler.java create mode 100644 buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationProvider.java create mode 100644 buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationProviderRegistry.java delete mode 100644 buildSrc/src/main/java/org/hibernate/build/gradle/upload/Authenticator.java create mode 100644 buildSrc/src/main/java/org/hibernate/build/gradle/upload/StandardMavenAuthenticationProvider.java rename buildSrc/src/main/java/org/hibernate/build/gradle/upload/{UploadManager.java => UploadAuthenticationManager.java} (56%) diff --git a/build.gradle b/build.gradle index 3768b05cc9..f207dacdf5 100644 --- a/build.gradle +++ b/build.gradle @@ -2,10 +2,9 @@ apply plugin: 'eclipse' apply plugin: 'idea' allprojects { - apply plugin: org.hibernate.build.gradle.maven.LocalMavenRepoSniffer - repositories { mavenCentral() + mavenLocal() mavenRepo name: 'jboss-nexus', urls: "https://repository.jboss.org/nexus/content/groups/public/" mavenRepo name: "jboss-snapshots", urls: "http://snapshots.jboss.org/maven2/" } @@ -93,7 +92,7 @@ subprojects { subProject -> if ( ! subProject.name.startsWith( 'release' ) ) { apply plugin: 'java' apply plugin: 'maven' // for install task as well as deploy dependencies - apply plugin: org.hibernate.build.gradle.upload.UploadManager + apply plugin: org.hibernate.build.gradle.upload.UploadAuthenticationManager configurations { provided { diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 7d2eb6b2b5..f19f7c3fb7 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -5,7 +5,7 @@ buildDir = "target" repositories { mavenCentral() - mavenRepo urls: "file://" + System.getProperty('user.home') + "/.m2/repository/" + mavenLocal() } @@ -15,6 +15,7 @@ dependencies { compile 'org.apache.ant:ant:1.7.0' compile 'org.apache.maven:maven-ant-tasks:2.1.0' compile 'org.apache.maven.wagon:wagon-http:1.0-beta-6' + compile 'dom4j:dom4j:1.6.1@jar' groovy localGroovy() } diff --git a/buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationHandler.java b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationHandler.java new file mode 100644 index 0000000000..c9e0202443 --- /dev/null +++ b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationHandler.java @@ -0,0 +1,87 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ + +package org.hibernate.build.gradle.upload; + +import org.apache.maven.artifact.ant.Authentication; +import org.apache.maven.artifact.ant.RemoteRepository; +import org.gradle.api.Action; +import org.gradle.api.DefaultTask; +import org.gradle.api.artifacts.maven.MavenDeployer; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.Upload; + +/** + * Responsible for locating and injecting authentication information into the JBoss Nexus repository config for upload + * which it does, based on set up in + * + * @author Steve Ebersole + */ +public class AuthenticationHandler extends DefaultTask { + private AuthenticationProviderRegistry authenticationProviderRegistry; + private Upload uploadTask; + + public void injectProviderRegistry(AuthenticationProviderRegistry authenticationProviderRegistry) { + this.authenticationProviderRegistry = authenticationProviderRegistry; + } + + public void injectUploadTask(Upload uploadTask) { + this.uploadTask = uploadTask; + } + + @TaskAction + public void configureUploadAuthentication() { + // todo : unfortunately I have no idea how to apply this to non MavenDeployer-type repos... + uploadTask.getRepositories().withType( MavenDeployer.class ).allObjects( + new Action() { + public void execute(MavenDeployer deployer) { + final RemoteRepository repository = deployer.getRepository(); + if ( repository != null ) { + final Authentication authentication = locateAuthenticationDetails( repository ); + if ( authentication != null ) { + repository.addAuthentication( authentication ); + } + } + final RemoteRepository snapshotRepository = deployer.getSnapshotRepository(); + if ( snapshotRepository != null ) { + final Authentication authentication = locateAuthenticationDetails( snapshotRepository ); + if ( authentication != null ) { + snapshotRepository.addAuthentication( authentication ); + } + } + } + } + ); + } + + private Authentication locateAuthenticationDetails(RemoteRepository repository) { + for ( AuthenticationProvider provider : authenticationProviderRegistry.providers() ) { + Authentication authentication = provider.determineAuthentication( repository ); + if ( authentication != null ) { + return authentication; + } + } + return null; + } +} diff --git a/buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationProvider.java b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationProvider.java new file mode 100644 index 0000000000..26c592a4bb --- /dev/null +++ b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationProvider.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.build.gradle.upload; + +import org.apache.maven.artifact.ant.Authentication; +import org.apache.maven.artifact.ant.RemoteRepository; + +/** + * Contract for providers of {@link Authentication} details for authenticating against remote repositories. + * + * @author Steve Ebersole + */ +public interface AuthenticationProvider { + /** + * The contract method. Given a repository, determine the {@link Authentication} according to this provider's + * contract. Return {@literal null} to indicate no {@link Authentication} applied for this repository by this + * provider. + * + * @param remoteRepository The repository to check for authentication details. + * + * @return The authentication details, or {@literal null} to indicate none. + */ + public Authentication determineAuthentication(RemoteRepository remoteRepository); +} diff --git a/buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationProviderRegistry.java b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationProviderRegistry.java new file mode 100644 index 0000000000..13590a5cf0 --- /dev/null +++ b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/AuthenticationProviderRegistry.java @@ -0,0 +1,53 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.build.gradle.upload; + +import java.util.LinkedList; + +/** + * A registry of {@link AuthenticationProvider} instances. + * + * @author Steve Ebersole + */ +public class AuthenticationProviderRegistry { + private final LinkedList authenticationProviders = buildStandardAuthenticationProviders(); + + private static LinkedList buildStandardAuthenticationProviders() { + LinkedList providers = new LinkedList(); + providers.add( new StandardMavenAuthenticationProvider() ); + return providers; + } + + public void appendAuthenticationProvider(AuthenticationProvider provider) { + authenticationProviders.addLast( provider ); + } + + public void prependAuthenticationProvider(AuthenticationProvider provider) { + authenticationProviders.addFirst( provider ); + } + + public Iterable providers() { + return authenticationProviders; + } +} diff --git a/buildSrc/src/main/java/org/hibernate/build/gradle/upload/Authenticator.java b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/Authenticator.java deleted file mode 100644 index c838e2bce0..0000000000 --- a/buildSrc/src/main/java/org/hibernate/build/gradle/upload/Authenticator.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2010, Red Hat Inc. or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ - -package org.hibernate.build.gradle.upload; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.HashSet; -import java.util.Properties; -import java.util.Set; - -import org.apache.maven.artifact.ant.Authentication; -import org.apache.maven.artifact.ant.RemoteRepository; -import org.gradle.api.DefaultTask; -import org.gradle.api.tasks.TaskAction; - -/** - * Responsible for locating and injecting authentication information into the JBoss Nexus repository config for upload - * which it does, based on set up in - * - * @author Steve Ebersole - */ -public class Authenticator extends DefaultTask { - private Set nexusRepositories = new HashSet(); - - void addRepository(RemoteRepository remoteRepository) { - nexusRepositories.add( remoteRepository ); - } - - @TaskAction - public void configureNexusAuthentication() { - // See if we have username/password... - Authentication authentication = locateAuthenticationDetails(); - if ( authentication == null ) { - if ( ! nexusRepositories.isEmpty() ) { - getLogger().warn( "Unable to locate JBoss Nexus username/password; upload will most likely fail" ); - } - return; - } - - for ( RemoteRepository remoteRepository : nexusRepositories ) { - remoteRepository.addAuthentication( authentication ); - } - } - - public static final String ALT_PROP_FILE_NAME = "jboss-nexus.properties"; - public static final String USER = "JBOSS_NEXUS_USERNAME"; - public static final String PASS = "JBOSS_NEXUS_PASSWORD"; - - private Authentication locateAuthenticationDetails() { - String username = (String) getProject().getProperties().get( USER ); - String password = (String) getProject().getProperties().get( PASS ); - - if ( username != null && password != null ) { - return newAuthentication( username, password ); - } - - File alternateFile = new File( new File( System.getProperty( "user.home" ), ".gradle" ), ALT_PROP_FILE_NAME ); - Properties alternateProperties = new Properties(); - // in case one or the other were specified... - if ( username != null ) { - alternateProperties.put( USER, username ); - } - if ( password != null ) { - alternateProperties.put( PASS, password ); - } - try { - FileInputStream fis = new FileInputStream( alternateFile ); - try { - alternateProperties.load( fis ); - } - catch ( IOException e ) { - getLogger().debug( "Unable to load alternate JBoss Nexus properties file", e ); - } - finally { - try { - fis.close(); - } - catch ( IOException ignore ) { - } - } - } - catch ( FileNotFoundException e ) { - getLogger().debug( "Unable to locate alternate JBoss Nexus properties file" ); - } - - username = alternateProperties.getProperty( USER ); - password = alternateProperties.getProperty( PASS ); - if ( username != null && password != null ) { - return newAuthentication( username, password ); - } - - return null; - } - - private Authentication newAuthentication(String username, String password) { - Authentication authentication = new Authentication(); - authentication.setUserName( username ); - authentication.setPassword( password ); - return authentication; - } -} diff --git a/buildSrc/src/main/java/org/hibernate/build/gradle/upload/StandardMavenAuthenticationProvider.java b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/StandardMavenAuthenticationProvider.java new file mode 100644 index 0000000000..80e2bd1ff1 --- /dev/null +++ b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/StandardMavenAuthenticationProvider.java @@ -0,0 +1,130 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.build.gradle.upload; + +import org.apache.maven.artifact.ant.Authentication; +import org.apache.maven.artifact.ant.RemoteRepository; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.gradle.api.artifacts.maven.MavenDeployer; +import org.xml.sax.InputSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Provider of {@link org.apache.maven.artifact.ant.RemoteRepository} {@link Authentication} based on standard Maven + * conventions using {@literal settings.xml}. + * + * @author Steve Ebersole + */ +public class StandardMavenAuthenticationProvider implements AuthenticationProvider { + private static final Logger log = LoggerFactory.getLogger( StandardMavenAuthenticationProvider.class ); + + public static final String SETTINGS_LOCATION_OVERRIDE = "maven.settings"; + + private ConcurrentHashMap repositoryAuthenticationMap; + + @Override + public Authentication determineAuthentication(RemoteRepository remoteRepository) { + if ( ! MavenDeployer.class.isInstance( remoteRepository ) ) { + return null; + } + + if ( repositoryAuthenticationMap == null ) { + loadRepositoryAuthenticationMap(); + } + + return repositoryAuthenticationMap.get( remoteRepository.getId() ); + } + + private void loadRepositoryAuthenticationMap() { + repositoryAuthenticationMap = new ConcurrentHashMap(); + + final File settingsFile = determineSettingsFileLocation(); + try { + InputSource inputSource = new InputSource( new FileInputStream( settingsFile ) ); + try { + final Document document = buildSAXReader().read( inputSource ); + final Element settingsElement = document.getRootElement(); + final Element serversElement = settingsElement.element( "servers" ); + final Iterator serversIterator = serversElement.elementIterator( "server" ); + while ( serversIterator.hasNext() ) { + final Element serverElement = (Element) serversIterator.next(); + final String id = extractValue( serverElement.element( "id" ) ); + if ( id == null ) { + continue; + } + final Authentication authentication = new Authentication(); + authentication.setUserName( extractValue( serverElement.element( "username" ) ) ); + authentication.setPassword( extractValue( serverElement.element( "password" ) ) ); + authentication.setPrivateKey( extractValue( serverElement.element( "privateKey" ) ) ); + authentication.setPassphrase( extractValue( serverElement.element( "passphrase" ) ) ); + repositoryAuthenticationMap.put( id, authentication ); + } + } + catch (DocumentException e) { + log.error( "Error reading Maven settings.xml", e ); + } + } + catch ( FileNotFoundException e ) { + log.info( "Unable to locate Maven settings.xml" ); + } + } + + private String extractValue(Element element) { + if ( element == null ) { + return null; + } + + final String value = element.getTextTrim(); + if ( value != null && value.length() == 0 ) { + return null; + } + + return value; + } + + private SAXReader buildSAXReader() { + SAXReader saxReader = new SAXReader(); + saxReader.setMergeAdjacentText( true ); + saxReader.setValidation( true ); + return saxReader; + } + + private File determineSettingsFileLocation() { + final String overrideLocation = System.getProperty( SETTINGS_LOCATION_OVERRIDE ); + return overrideLocation == null + ? new File( new File( System.getProperty( "user.home" ), ".m2" ), "settings.xml" ) + : new File( overrideLocation ); + } +} diff --git a/buildSrc/src/main/java/org/hibernate/build/gradle/upload/UploadManager.java b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/UploadAuthenticationManager.java similarity index 56% rename from buildSrc/src/main/java/org/hibernate/build/gradle/upload/UploadManager.java rename to buildSrc/src/main/java/org/hibernate/build/gradle/upload/UploadAuthenticationManager.java index d8a2b55a83..2137882669 100644 --- a/buildSrc/src/main/java/org/hibernate/build/gradle/upload/UploadManager.java +++ b/buildSrc/src/main/java/org/hibernate/build/gradle/upload/UploadAuthenticationManager.java @@ -21,48 +21,48 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ - package org.hibernate.build.gradle.upload; -import org.apache.maven.artifact.ant.RemoteRepository; import org.gradle.api.Action; import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.gradle.api.artifacts.maven.MavenDeployer; import org.gradle.api.tasks.Upload; /** - * Plugin to manage authentication + * Manages authentication aspects of artifact uploading by delegation to registered {@link AuthenticationProvider} + * instances. * * @author Steve Ebersole */ -public class UploadManager implements Plugin { +public class UploadAuthenticationManager implements Plugin { @Override - public void apply(Project project) { - final Authenticator authenticator = project.getTasks().add( "nexusAuthHandler", Authenticator.class ); + public void apply(final Project project) { + // todo : ideally the registry would be handled by a convention to allow configuration (aka, adding more providers)... + // for our purposes here in Hibernate we only care about the Maven settings.xml based way so we + // code for just that. + final AuthenticationProviderRegistry registry = new AuthenticationProviderRegistry(); + project.getTasks().withType( Upload.class ).allTasks( new Action() { @Override public void execute(final Upload uploadTask) { - uploadTask.getRepositories().withType( MavenDeployer.class ).allObjects( - new Action() { - public void execute(MavenDeployer deployer) { - RemoteRepository repository = deployer.getRepository(); - if ( repository != null ) { - authenticator.addRepository( repository ); - uploadTask.getDependsOn().add( authenticator ); - } - repository = deployer.getSnapshotRepository(); - if ( repository != null ) { - authenticator.addRepository( repository ); - uploadTask.getDependsOn().add( authenticator ); - } - } - } + // create a auth task for each upload task... + final AuthenticationHandler authenticationHandler = project.getTasks().add( + "uploadAuthenticationHandler", + AuthenticationHandler.class ); + + // link the auth task with the upload task + authenticationHandler.injectUploadTask( uploadTask ); + + // todo: Also in conjunction, would be best to have the handler lookup the registry rather than pushing it + authenticationHandler.injectProviderRegistry( registry ); + + uploadTask.getDependsOn().add( authenticationHandler ); } } ); } + }