From 0b6b25705b45936a7c6729e2ad3ee14ca2c9bb73 Mon Sep 17 00:00:00 2001 From: Tim Gonzales Date: Thu, 17 Mar 2022 11:12:46 -0500 Subject: [PATCH 1/6] Adding connectedApp authentication to HTTP Wrapper Added connectedAppId/Secret as params to HTTP Wrapper and bubbled up through constructor calls and tests. Added additional function to handle connected app auth --- .../mule/cli/DeployerCommandLine.groovy | 13 ++- .../mule/cli/DeployerCommandLineTest.groovy | 92 +++++++++++++++++-- .../mule/deployment/api/Deployer.groovy | 4 + .../deployment/api/DeployerFactory.groovy | 4 + .../deployment/api/IDeployerFactory.groovy | 2 + .../internal/http/HttpClientWrapper.groovy | 34 ++++++- .../mule/deployment/BaseTest.groovy | 10 ++ .../http/HttpClientWrapperTest.groovy | 64 +++++++++++++ .../integrationtest/IntegrationTest.groovy | 13 ++- .../mule/maven/MuleDeployMojo.groovy | 11 ++- .../mule/maven/MuleDeployMojoTest.groovy | 24 ++++- pom.xml | 2 +- 12 files changed, 256 insertions(+), 17 deletions(-) diff --git a/cli/src/main/java/com/avioconsulting/mule/cli/DeployerCommandLine.groovy b/cli/src/main/java/com/avioconsulting/mule/cli/DeployerCommandLine.groovy index ab1e05c0..16dde83d 100644 --- a/cli/src/main/java/com/avioconsulting/mule/cli/DeployerCommandLine.groovy +++ b/cli/src/main/java/com/avioconsulting/mule/cli/DeployerCommandLine.groovy @@ -21,10 +21,14 @@ import java.util.concurrent.Callable class DeployerCommandLine implements Callable { @Parameters(index = '0', description = 'The path to your DSL file') private File groovyFile - @Option(names = ['-u', '--anypoint-username'], required = true) + @Option(names = ['-u', '--anypoint-username']) private String anypointUsername - @Option(names = ['-p', '--anypoint-password'], required = true) + @Option(names = ['-p', '--anypoint-password']) private String anypointPassword + @Option(names = ['-i', '--anypoint-connected-app-id']) + private String anypointConnectedAppId + @Option(names = ['-s', '--anypoint-connected-app-secret']) + private String anypointConnectedAppSecret @Option(names = ['-o', '--anypoint-org-name'], description = 'The org/business group to use. If you do not specify it, the default for your user will be used') private String anypointOrganizationName @@ -62,10 +66,15 @@ class DeployerCommandLine implements Callable { logger.println "Successfully processed ${groovyFile} through DSL" def deployer = deployerFactory.create(this.anypointUsername, this.anypointPassword, + this.anypointConnectedAppId, + this.anypointConnectedAppSecret, logger, this.dryRunMode, this.anypointOrganizationName, this.environmentsToDoDesignCenterDeploymentOn) + if (this.anypointUsername == null && this.anypointPassword == null && this.anypointConnectedAppId == null && this.anypointConnectedAppSecret == null) { + throw new Exception("Either --anypoint-username and --anypoint-password or --anypoint-connected-app-id and --anypoint-connected-app-secret must be defined.") + } if (this.dryRunMode == DryRunMode.OfflineValidate) { logger.println 'Offline validate was specified, so not deploying' return diff --git a/cli/src/test/java/com/avioconsulting/mule/cli/DeployerCommandLineTest.groovy b/cli/src/test/java/com/avioconsulting/mule/cli/DeployerCommandLineTest.groovy index fc7d84bb..5454979a 100644 --- a/cli/src/test/java/com/avioconsulting/mule/cli/DeployerCommandLineTest.groovy +++ b/cli/src/test/java/com/avioconsulting/mule/cli/DeployerCommandLineTest.groovy @@ -25,6 +25,8 @@ class DeployerCommandLineTest implements MavenInvoke { String groovyFileText, String user = 'our user', String pass = 'our pass', + String connId = null, + String connSecret = null, DryRunMode dryRunMode = DryRunMode.Run, String orgName = null, Map otherArgs = [:]) { @@ -35,14 +37,25 @@ class DeployerCommandLineTest implements MavenInvoke { groovyFile.text = groovyFileText } DeployerCommandLine.deployerFactory = mockDeployerFactory - def args = [ - '-u', - "\"${user}\"".toString(), - '-p', - "\"${pass}\"".toString(), - '-m', - dryRunMode.name(), - ] + def args + if (connId != null && connSecret != null) + args = [ + '-i', + "\"${connId}\"".toString(), + '-s', + "\"${connSecret}\"".toString(), + '-m', + dryRunMode.name() + ] + else + args = [ + '-u', + "\"${user}\"".toString(), + '-p', + "\"${pass}\"".toString(), + '-m', + dryRunMode.name(), + ] if (orgName) { args += [ '-o', @@ -120,6 +133,65 @@ muleDeploy { } } + @Test + void runs_with_connected_application() { + // arrange + FileBasedAppDeploymentRequest actualApp + ApiSpecificationList actualApiSpec + List actualFeatures + def mockDeployer = [ + deployApplication: { FileBasedAppDeploymentRequest appDeploymentRequest, + ApiSpecificationList apiSpecification, + List desiredPolicies, + List enabledFeatures -> + actualApp = appDeploymentRequest + actualApiSpec = apiSpecification + actualFeatures = enabledFeatures + } + ] as IDeployer + def mock = [ + create: { String connectedAppId, + String connectedAppSecret, + ILogger logger, + DryRunMode dryRunMode, + String anypointOrganizationName, + List environmentsToDoDesignCenterDeploymentOn -> + return mockDeployer + } + ] as IDeployerFactory + def dslText = """ +muleDeploy { + version '1.0' + + onPremApplication { + environment 'DEV' + applicationName 'the-app' + appVersion '1.2.3' + file '${builtFile}' + targetServerOrClusterName 'theServer' + } +} +""" + + // act + executeCommandLine(mock, + dslText) + + // assert + assertThat 'No policies since we omitted that section', + actualFeatures, + is(equalTo([Features.AppDeployment, Features.DesignCenterSync, Features.ApiManagerDefinitions])) + assertThat actualApiSpec, + is(equalTo([])) + assert actualApp instanceof OnPremDeploymentRequest + actualApp.with { + assertThat it.appName, + is(equalTo('the-app')) + assertThat it.environment, + is(equalTo('DEV')) + } + } + @Test void offline_validate() { // arrange @@ -160,6 +232,8 @@ muleDeploy { dslText, 'user', 'pass', + null, + null, DryRunMode.OfflineValidate) // assert @@ -216,6 +290,8 @@ muleDeploy { dslText, 'our user', 'our pass', + null, + null, DryRunMode.Run, null, [ diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/api/Deployer.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/api/Deployer.groovy index 1c4cdf83..c2bf9f7e 100644 --- a/library/src/main/java/com/avioconsulting/mule/deployment/api/Deployer.groovy +++ b/library/src/main/java/com/avioconsulting/mule/deployment/api/Deployer.groovy @@ -40,6 +40,8 @@ class Deployer implements IDeployer { */ Deployer(String username, String password, + String connectedAppId, + String connectedAppSecret, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName = null, @@ -48,6 +50,8 @@ class Deployer implements IDeployer { this(new HttpClientWrapper(baseUrl, username, password, + connectedAppId, + connectedAppSecret, logger, anypointOrganizationName), dryRunMode, diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/api/DeployerFactory.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/api/DeployerFactory.groovy index 03b4621b..e2c614f1 100644 --- a/library/src/main/java/com/avioconsulting/mule/deployment/api/DeployerFactory.groovy +++ b/library/src/main/java/com/avioconsulting/mule/deployment/api/DeployerFactory.groovy @@ -4,12 +4,16 @@ class DeployerFactory implements IDeployerFactory { @Override IDeployer create(String username, String password, + String connectedAppId, + String connectedAppSecret, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, List environmentsToDoDesignCenterDeploymentOn) { new Deployer(username, password, + connectedAppId, + connectedAppSecret, logger, dryRunMode, anypointOrganizationName, diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/api/IDeployerFactory.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/api/IDeployerFactory.groovy index c369c142..2ddfda74 100644 --- a/library/src/main/java/com/avioconsulting/mule/deployment/api/IDeployerFactory.groovy +++ b/library/src/main/java/com/avioconsulting/mule/deployment/api/IDeployerFactory.groovy @@ -3,6 +3,8 @@ package com.avioconsulting.mule.deployment.api interface IDeployerFactory { IDeployer create(String username, String password, + String connectedAppId, + String connectedAppSecret, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapper.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapper.groovy index 3e6f69b7..6b4429c3 100644 --- a/library/src/main/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapper.groovy +++ b/library/src/main/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapper.groovy @@ -18,6 +18,8 @@ import org.apache.http.protocol.HttpContext class HttpClientWrapper implements HttpRequestInterceptor { private final String username private final String password + private final String connectedAppId + private final String connectedAppSecret private String accessToken private String ownerGuid private final ILogger logger @@ -29,11 +31,15 @@ class HttpClientWrapper implements HttpRequestInterceptor { HttpClientWrapper(String baseUrl, String username, String password, + String connectedAppId = null, + String connectedAppSecret = null, ILogger logger, String anypointOrganizationName = null) { this.anypointOrganizationName = anypointOrganizationName this.password = password this.username = username + this.connectedAppId = connectedAppId + this.connectedAppSecret = connectedAppSecret this.logger = logger this.baseUrl = baseUrl this.httpClient = HttpClients.custom() @@ -50,7 +56,10 @@ class HttpClientWrapper implements HttpRequestInterceptor { private def authenticate() { if (!this.accessToken) { - fetchAccessToken() + if (username != null) + fetchAccessTokenAsUser() + else if (connectedAppId != null) + fetchAccessTokenWithConnectedApp() fetchUserInfo() } } @@ -91,7 +100,7 @@ class HttpClientWrapper implements HttpRequestInterceptor { } } - private def fetchAccessToken() { + private def fetchAccessTokenAsUser() { logger.println "Authenticating to Anypoint as user '${username}'" def payload = [ username: username, @@ -111,6 +120,27 @@ class HttpClientWrapper implements HttpRequestInterceptor { } } + private def fetchAccessTokenWithConnectedApp() { + logger.println "Authenticating to Anypoint with connected app '${connectedAppId}'" + def payload = [ + client_id: connectedAppId, + client_secret: connectedAppSecret, + grant_type: "client_credentials" + ] + def request = new HttpPost("${baseUrl}/accounts/api/v2/oauth2/token").with { + setEntity(new StringEntity(JsonOutput.toJson(payload))) + addHeader('Content-Type', + 'application/json') + it + } + httpClient.execute(request).with { response -> + def result = assertSuccessfulResponseAndReturnJson(response, + "authenticate to Anypoint with connected app '${connectedAppId}'") + logger.println 'Successfully authenticated' + accessToken = result.access_token + } + } + static def assertSuccessfulResponse(CloseableHttpResponse response, String failureContext) { def status = response.statusLine.statusCode diff --git a/library/src/test/java/com/avioconsulting/mule/deployment/BaseTest.groovy b/library/src/test/java/com/avioconsulting/mule/deployment/BaseTest.groovy index bc7477a4..f3ab6697 100644 --- a/library/src/test/java/com/avioconsulting/mule/deployment/BaseTest.groovy +++ b/library/src/test/java/com/avioconsulting/mule/deployment/BaseTest.groovy @@ -19,6 +19,7 @@ import java.util.concurrent.CompletableFuture class BaseTest { protected HttpServer httpServer protected HttpClientWrapper clientWrapper + protected HttpClientWrapper clientWrapperConnectedApp protected EnvironmentLocator environmentLocator protected Handler closure @@ -42,8 +43,17 @@ class BaseTest { clientWrapper = new HttpClientWrapper("http://localhost:${httpServer.actualPort()}", 'the user', 'the password', + null, + null, new TestConsoleLogger(), 'the-org-name') + clientWrapperConnectedApp = new HttpClientWrapper("http://localhost:${httpServer.actualPort()}", + null, + null, + 'the client', + 'the secret', + new TestConsoleLogger(), + 'the-org-name') environmentLocator = new EnvironmentLocator(clientWrapper, new TestConsoleLogger()) } diff --git a/library/src/test/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapperTest.groovy b/library/src/test/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapperTest.groovy index 34253de7..10e271d1 100644 --- a/library/src/test/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapperTest.groovy +++ b/library/src/test/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapperTest.groovy @@ -76,6 +76,70 @@ class HttpClientWrapperTest extends BaseTest { is(nullValue()) } + @Test + void authenticate_correct_request_connected_application() { + // arrange + String url = null + String method = null + String contentType = null + Map sentJson = null + String authHeader = null + withHttpServer { HttpServerRequest request -> + def payload = null + if (request.uri() == '/accounts/api/v2/oauth2/token') { + url = request.uri() + method = request.method().name() + contentType = request.getHeader('Content-Type') + authHeader = request.getHeader('Authorization') + request.bodyHandler { body -> + sentJson = new JsonSlurper().parseText(body.toString()) + } + payload = [ + access_token: 'the token' + ] + } else if (request.uri() == '/accounts/api/me') { + payload = [ + user: [ + id : 'the_id', + username : 'the_username', + memberOfOrganizations: [ + [ + name: 'the-org-name', + id : 'the-org-id' + ] + ] + ] + ] + } + request.response().with { + statusCode = 200 + putHeader('Content-Type', + 'application/json') + end(JsonOutput.toJson(payload)) + } + } + + // act + clientWrapperConnectedApp.authenticate() + + // assert + assertThat url, + is(equalTo('/accounts/api/v2/oauth2/token')) + assertThat method, + is(equalTo('POST')) + assertThat contentType, + is(equalTo('application/json')) + assertThat sentJson, + is(equalTo([ + client_id: 'the client', + client_secret: 'the secret', + grant_type: 'client_credentials' + ])) + assertThat 'We have not authenticated yet', + authHeader, + is(nullValue()) + } + @Test void authenticate_succeeds() { // arrange diff --git a/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy b/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy index cf24f764..b0688ab8 100644 --- a/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy +++ b/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy @@ -31,6 +31,8 @@ class IntegrationTest implements MavenInvoke { private static final String AVIO_SANDBOX_BIZ_GROUP_NAME = 'AVIO Sandbox' private static final String ANYPOINT_USERNAME = System.getProperty('anypoint.username') private static final String ANYPOINT_PASSWORD = System.getProperty('anypoint.password') + private static final String ANYPOINT_CONNECTED_APP_ID = System.getProperty('anypoint.connected-app-id') + private static final String ANYPOINT_CONNECTED_APP_SECRET = System.getProperty('anypoint.connected-app-secret') private static final String ANYPOINT_CLIENT_ID = System.getProperty('anypoint.client.id') private static final String ANYPOINT_CLIENT_SECRET = System.getProperty('anypoint.client.secret') private static final String ON_PREM_SERVER_NAME = System.getProperty('mule4.onprem.server.name') @@ -53,8 +55,13 @@ class IntegrationTest implements MavenInvoke { // cut down on the unit test noise here Configurator.setLevel('org.apache.http.wire', Level.INFO) - assert ANYPOINT_USERNAME: 'Did you forget -Danypoint.username?' - assert ANYPOINT_PASSWORD: 'Did you forget -Danypoint.password?' + if (!ANYPOINT_CLIENT_ID && !ANYPOINT_CLIENT_SECRET) { + assert ANYPOINT_USERNAME: 'Did you forget -Danypoint.username?' + assert ANYPOINT_PASSWORD: 'Did you forget -Danypoint.password?' + } else { + assert ANYPOINT_CONNECTED_APP_ID: 'Did you forget -Danypoint.connected-app-id?' + assert ANYPOINT_CONNECTED_APP_SECRET: 'Did you forget -Danypoint.connected-app-secret?' + } assert ANYPOINT_CLIENT_ID: 'Did you forget -Danypoint.client.id?' assert ANYPOINT_CLIENT_SECRET: 'Did you forget -Danypoint.client.secret?' } @@ -127,6 +134,8 @@ class IntegrationTest implements MavenInvoke { clientWrapper = new HttpClientWrapper('https://anypoint.mulesoft.com', ANYPOINT_USERNAME, ANYPOINT_PASSWORD, + ANYPOINT_CONNECTED_APP_ID, + ANYPOINT_CONNECTED_APP_SECRET, logger, AVIO_SANDBOX_BIZ_GROUP_NAME) def environmentLocator = new EnvironmentLocator(this.clientWrapper, diff --git a/maven-plugin/src/main/java/com/avioconsulting/mule/maven/MuleDeployMojo.groovy b/maven-plugin/src/main/java/com/avioconsulting/mule/maven/MuleDeployMojo.groovy index be286ea3..409c33b5 100644 --- a/maven-plugin/src/main/java/com/avioconsulting/mule/maven/MuleDeployMojo.groovy +++ b/maven-plugin/src/main/java/com/avioconsulting/mule/maven/MuleDeployMojo.groovy @@ -16,6 +16,10 @@ class MuleDeployMojo extends BaseMojo { private String anypointUsername @Parameter(property = 'anypoint.password') private String anypointPassword + @Parameter(property = 'anypoint.connected-app-id') + private String anypointConnectedAppId + @Parameter(property = 'anypoint.connected-app-secret') + private String anypointConnectedAppSecret @Parameter(property = 'anypoint.org.name') private String anypointOrganizationName @Parameter(defaultValue = 'DEV', property = 'design.center.deployments') @@ -24,7 +28,7 @@ class MuleDeployMojo extends BaseMojo { @Override void execute() throws MojoExecutionException, MojoFailureException { - if (dryRunMode != DryRunMode.OfflineValidate && !(anypointUsername || anypointPassword)) { + if (dryRunMode != DryRunMode.OfflineValidate && !(anypointUsername || anypointPassword || anypointConnectedAppId || anypointConnectedAppSecret)) { throw new Exception("In order to ${dryRunMode}, credentials must be supplied via the anypointUsername item/anypoint.username property and the anypointPassword item/anypoint.password property") } def deploymentPackage = processDsl() @@ -36,10 +40,15 @@ class MuleDeployMojo extends BaseMojo { logger.println 'Beginning deployment' def deployer = deployerFactory.create(this.anypointUsername, this.anypointPassword, + this.anypointConnectedAppId, + this.anypointConnectedAppSecret, logger, this.dryRunMode, this.anypointOrganizationName, this.environmentsToDoDesignCenterDeploymentOn) + if (this.anypointUsername == null && this.anypointPassword == null && this.anypointConnectedAppId == null && this.anypointConnectedAppSecret == null) { + throw new Exception("Either anypoint.username and anypoint.password or anypoint.connected-app-id and anypoint.connected-app-secret must be defined.") + } try { deployer.deployApplication(deploymentPackage.deploymentRequest, deploymentPackage.apiSpecifications, diff --git a/maven-plugin/src/test/java/com/avioconsulting/mule/maven/MuleDeployMojoTest.groovy b/maven-plugin/src/test/java/com/avioconsulting/mule/maven/MuleDeployMojoTest.groovy index 932db334..e8198173 100644 --- a/maven-plugin/src/test/java/com/avioconsulting/mule/maven/MuleDeployMojoTest.groovy +++ b/maven-plugin/src/test/java/com/avioconsulting/mule/maven/MuleDeployMojoTest.groovy @@ -34,7 +34,7 @@ class MuleDeployMojoTest implements MavenInvoke { @Test void gets_correct_params() { // arrange - String actualUser, actualPass, actualOrg + String actualUser, actualPass, actualConnectedAppId, actualConnectedAppSecret, actualOrg ILogger actualLogger DryRunMode actualDryRunMode List actualEnvs @@ -48,12 +48,16 @@ class MuleDeployMojoTest implements MavenInvoke { def mock = [ create: { String username, String password, + String connectedAppId, + String connectedAppSecret, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, List environmentsToDoDesignCenterDeploymentOn -> actualUser = username actualPass = password + actualConnectedAppId = connectedAppId + actualConnectedAppSecret = connectedAppSecret actualLogger = logger actualDryRunMode = dryRunMode actualEnvs = environmentsToDoDesignCenterDeploymentOn @@ -78,6 +82,8 @@ muleDeploy { dslText, 'the user', 'the pass', + 'the client', + 'the secret', // we don't want this thing to actually run DryRunMode.OnlineValidate, 'the org', @@ -91,6 +97,10 @@ muleDeploy { is(equalTo('the user')) assertThat actualPass, is(equalTo('the pass')) + assertThat actualConnectedAppId, + is(equalTo('the client')) + assertThat actualConnectedAppSecret, + is(equalTo('the secret')) assertThat actualOrg, is(equalTo('the org')) assertThat actualDryRunMode, @@ -103,6 +113,8 @@ muleDeploy { String groovyFileText, String user = 'our user', String pass = 'our pass', + String connId = null, + String connSecret = null, DryRunMode dryRunMode = DryRunMode.Run, String orgName = null, List envs = ['DEV'], @@ -112,6 +124,8 @@ muleDeploy { new MuleDeployMojo().with { it.anypointUsername = user it.anypointPassword = pass + it.anypointConnectedAppId = connId + it.anypointConnectedAppId = connSecret it.dryRunMode = dryRunMode it.groovyFile = groovyFile it.anypointOrganizationName = orgName @@ -222,6 +236,8 @@ muleDeploy { dslText, null, null, + null, + null, DryRunMode.OfflineValidate) // act mojo.execute() @@ -270,6 +286,8 @@ muleDeploy { dslText, null, null, + null, + null, DryRunMode.OnlineValidate) // act def exception = shouldFail { @@ -300,6 +318,8 @@ muleDeploy { def mock = [ create: { String username, String password, + String connectedAppId, + String ConnectedAppSecret, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, @@ -349,6 +369,8 @@ muleDeploy { dslText, 'user', 'pass', + null, + null, DryRunMode.Run, null, ['DEV'], diff --git a/pom.xml b/pom.xml index d98af3ef..24455805 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ library - cli + maven-plugin From 8c6a1262a8b5a49ec2fc5d4e68945312a2e926fa Mon Sep 17 00:00:00 2001 From: Tim Gonzales Date: Thu, 17 Mar 2022 11:16:25 -0500 Subject: [PATCH 2/6] Renaming plugin params for connected app auth --- .../mule/integrationtest/IntegrationTest.groovy | 8 ++++---- .../com/avioconsulting/mule/maven/MuleDeployMojo.groovy | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy b/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy index b0688ab8..3ccd5b02 100644 --- a/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy +++ b/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy @@ -31,8 +31,8 @@ class IntegrationTest implements MavenInvoke { private static final String AVIO_SANDBOX_BIZ_GROUP_NAME = 'AVIO Sandbox' private static final String ANYPOINT_USERNAME = System.getProperty('anypoint.username') private static final String ANYPOINT_PASSWORD = System.getProperty('anypoint.password') - private static final String ANYPOINT_CONNECTED_APP_ID = System.getProperty('anypoint.connected-app-id') - private static final String ANYPOINT_CONNECTED_APP_SECRET = System.getProperty('anypoint.connected-app-secret') + private static final String ANYPOINT_CONNECTED_APP_ID = System.getProperty('anypoint.connected-app.id') + private static final String ANYPOINT_CONNECTED_APP_SECRET = System.getProperty('anypoint.connected-app.secret') private static final String ANYPOINT_CLIENT_ID = System.getProperty('anypoint.client.id') private static final String ANYPOINT_CLIENT_SECRET = System.getProperty('anypoint.client.secret') private static final String ON_PREM_SERVER_NAME = System.getProperty('mule4.onprem.server.name') @@ -59,8 +59,8 @@ class IntegrationTest implements MavenInvoke { assert ANYPOINT_USERNAME: 'Did you forget -Danypoint.username?' assert ANYPOINT_PASSWORD: 'Did you forget -Danypoint.password?' } else { - assert ANYPOINT_CONNECTED_APP_ID: 'Did you forget -Danypoint.connected-app-id?' - assert ANYPOINT_CONNECTED_APP_SECRET: 'Did you forget -Danypoint.connected-app-secret?' + assert ANYPOINT_CONNECTED_APP_ID: 'Did you forget -Danypoint.connected-app.id?' + assert ANYPOINT_CONNECTED_APP_SECRET: 'Did you forget -Danypoint.connected-app.secret?' } assert ANYPOINT_CLIENT_ID: 'Did you forget -Danypoint.client.id?' assert ANYPOINT_CLIENT_SECRET: 'Did you forget -Danypoint.client.secret?' diff --git a/maven-plugin/src/main/java/com/avioconsulting/mule/maven/MuleDeployMojo.groovy b/maven-plugin/src/main/java/com/avioconsulting/mule/maven/MuleDeployMojo.groovy index 409c33b5..67911e9a 100644 --- a/maven-plugin/src/main/java/com/avioconsulting/mule/maven/MuleDeployMojo.groovy +++ b/maven-plugin/src/main/java/com/avioconsulting/mule/maven/MuleDeployMojo.groovy @@ -16,9 +16,9 @@ class MuleDeployMojo extends BaseMojo { private String anypointUsername @Parameter(property = 'anypoint.password') private String anypointPassword - @Parameter(property = 'anypoint.connected-app-id') + @Parameter(property = 'anypoint.connected-app.id') private String anypointConnectedAppId - @Parameter(property = 'anypoint.connected-app-secret') + @Parameter(property = 'anypoint.connected-app.secret') private String anypointConnectedAppSecret @Parameter(property = 'anypoint.org.name') private String anypointOrganizationName @@ -47,7 +47,7 @@ class MuleDeployMojo extends BaseMojo { this.anypointOrganizationName, this.environmentsToDoDesignCenterDeploymentOn) if (this.anypointUsername == null && this.anypointPassword == null && this.anypointConnectedAppId == null && this.anypointConnectedAppSecret == null) { - throw new Exception("Either anypoint.username and anypoint.password or anypoint.connected-app-id and anypoint.connected-app-secret must be defined.") + throw new Exception("Either anypoint.username and anypoint.password or anypoint.connected-app.id and anypoint.connected-app.secret must be defined.") } try { deployer.deployApplication(deploymentPackage.deploymentRequest, From 99a24a38575f5d0c5263e9fe118feb5cf469ad96 Mon Sep 17 00:00:00 2001 From: Tim Gonzales Date: Thu, 17 Mar 2022 11:20:17 -0500 Subject: [PATCH 3/6] Fixing comment --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 24455805..d98af3ef 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ library - + cli maven-plugin From 466f12c18da6e49d3666cc9b5633963f463facf1 Mon Sep 17 00:00:00 2001 From: TimGonz-AVIO <61711437+TimGonz-AVIO@users.noreply.github.com> Date: Tue, 22 Mar 2022 15:35:33 -0500 Subject: [PATCH 4/6] Update IntegrationTest.groovy --- .../avioconsulting/mule/integrationtest/IntegrationTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy b/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy index 3ccd5b02..2fef523a 100644 --- a/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy +++ b/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy @@ -55,7 +55,7 @@ class IntegrationTest implements MavenInvoke { // cut down on the unit test noise here Configurator.setLevel('org.apache.http.wire', Level.INFO) - if (!ANYPOINT_CLIENT_ID && !ANYPOINT_CLIENT_SECRET) { + if (!ANYPOINT_CONNECTED_APP_ID && !ANYPOINT_CONNECTED_APP_SECRET) { assert ANYPOINT_USERNAME: 'Did you forget -Danypoint.username?' assert ANYPOINT_PASSWORD: 'Did you forget -Danypoint.password?' } else { From bca29c71a21476d9840ffd9bccc8a5ede1f5f1ca Mon Sep 17 00:00:00 2001 From: Manik Magar Date: Fri, 4 Nov 2022 07:46:52 -0400 Subject: [PATCH 5/6] feat: support connected app credentials --- .gitignore | 1 + README.md | 12 +- cli/README.md | 14 +- .../mule/cli/DeployerCommandLine.groovy | 61 ++++++-- .../mule/cli/DeployerCommandLineTest.groovy | 23 ++- .../mule/deployment/api/Deployer.groovy | 14 +- .../deployment/api/DeployerFactory.groovy | 12 +- .../deployment/api/IDeployerFactory.groovy | 7 +- .../credentials/ConnectedAppCredential.groovy | 25 ++++ .../api/models/credentials/Credential.groovy | 9 ++ .../UsernamePasswordCredential.groovy | 26 ++++ .../internal/http/HttpClientWrapper.groovy | 45 +++--- .../mule/deployment/BaseTest.groovy | 13 +- .../http/HttpClientWrapperTest.groovy | 9 +- maven-plugin/pom.xml | 10 ++ .../mule/maven/MuleDeployMojo.groovy | 35 +++-- .../mule/maven/MuleDeployMojoTest.groovy | 139 +++++++++++++----- 17 files changed, 319 insertions(+), 136 deletions(-) create mode 100644 library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/ConnectedAppCredential.groovy create mode 100644 library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/Credential.groovy create mode 100644 library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/UsernamePasswordCredential.groovy diff --git a/.gitignore b/.gitignore index 8f3da6c2..18d6a756 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ schema.json evaluate.gdsl maven-plugin/stuff.groovy cli/stuff.groovy +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 69aa3dc7..73bc2919 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,11 @@ Both approaches 1 and 2 lean on using a Groovy DSL to supply your deployment spe ALL of these methods assume your CI/CD tool white lists secrets from output. If it does not, it's on YOU to deal with that. Jenkins and Azure DevOps should do this out of the box with no further configuration. +# Authentication +The tool supports two type of authentication methods - +1. Basic Anypoint Platform Credentials +2. Anypoint [Connected App Credentials](https://help.mulesoft.com/s/article/How-to-deploy-an-application-to-CloudHub-using-Connected-App-functionality) + # Maven plugin NOTE: The Maven plugin has 2 goals (deploy and validate). Regardless of whether you use it to actually perform the deployment, it's highly recommended you use the validate goal to ensure your DSL file is correct during the build pipeline. @@ -66,7 +71,12 @@ If you have no strong preference, then stick with the "with POM" approach which mvn clean deploy -DmuleDeploy.env=DEV -Danypoint.username=bob -Danypoint.password=asecret -DmuleDeploy.cryptoKey=hello -DmuleDeploy.autoDiscClientId=theId -DmuleDeploy.autoDiscClientSecret=theSecret ``` -See maven-plugin/README.md for more information. +To see all parameters, run help goal of the plugin: +```shell +mvn com.avioconsulting.mule:mule-deploy-maven-plugin:help -Ddetail=true +``` + +See [maven-plugin/README.md](./maven-plugin/README.md) for more information. # CLI diff --git a/cli/README.md b/cli/README.md index 7e4e0027..11587deb 100644 --- a/cli/README.md +++ b/cli/README.md @@ -38,9 +38,10 @@ You can get an idea of available options by running `./muleDeploy --help`: ``` Missing required parameter: -Usage: deploy [-V] [-m=] [-o=] - -p= -u= - [-a=]... +Usage: deploy ([-u= -p=] + [-caid= + -casec=]) [-V] [-m=] + [-o=] [-a=]... [-e=]... Will deploy using your Mule DSL The path to your DSL file @@ -57,11 +58,16 @@ Will deploy using your Mule DSL -o, --anypoint-org-name= The org/business group to use. If you do not specify it, the default for your user will be used + -V, --version print version info +Basic auth credentials of Anypoint Platform -p, --anypoint-password= -u, --anypoint-username= - -V, --version print version info +Connected App credentials + -caid, --anypoint-connected-app-id= + + -casec, --anypoint-connected-app-secret= ``` diff --git a/cli/src/main/java/com/avioconsulting/mule/cli/DeployerCommandLine.groovy b/cli/src/main/java/com/avioconsulting/mule/cli/DeployerCommandLine.groovy index 16dde83d..d67bfd3a 100644 --- a/cli/src/main/java/com/avioconsulting/mule/cli/DeployerCommandLine.groovy +++ b/cli/src/main/java/com/avioconsulting/mule/cli/DeployerCommandLine.groovy @@ -4,6 +4,9 @@ import com.avioconsulting.mule.deployment.api.DeployerFactory import com.avioconsulting.mule.deployment.api.DryRunMode import com.avioconsulting.mule.deployment.api.IDeployerFactory import com.avioconsulting.mule.deployment.api.ILogger +import com.avioconsulting.mule.deployment.api.models.credentials.ConnectedAppCredential +import com.avioconsulting.mule.deployment.api.models.credentials.Credential +import com.avioconsulting.mule.deployment.api.models.credentials.UsernamePasswordCredential import com.avioconsulting.mule.deployment.dsl.DeploymentPackage import com.avioconsulting.mule.deployment.dsl.MuleDeployContext import com.avioconsulting.mule.deployment.dsl.MuleDeployScript @@ -21,14 +24,10 @@ import java.util.concurrent.Callable class DeployerCommandLine implements Callable { @Parameters(index = '0', description = 'The path to your DSL file') private File groovyFile - @Option(names = ['-u', '--anypoint-username']) - private String anypointUsername - @Option(names = ['-p', '--anypoint-password']) - private String anypointPassword - @Option(names = ['-i', '--anypoint-connected-app-id']) - private String anypointConnectedAppId - @Option(names = ['-s', '--anypoint-connected-app-secret']) - private String anypointConnectedAppSecret + + @CommandLine.ArgGroup(exclusive = false, multiplicity = "1") + CredentialOptGroup credential; + @Option(names = ['-o', '--anypoint-org-name'], description = 'The org/business group to use. If you do not specify it, the default for your user will be used') private String anypointOrganizationName @@ -50,6 +49,44 @@ class DeployerCommandLine implements Callable { private static IDeployerFactory deployerFactory = new DeployerFactory() private static ILogger logger = new SimpleLogger() + static class CredentialOptGroup { + @CommandLine.ArgGroup(exclusive = false, multiplicity = "0..1", order = 1, heading = "Basic auth credentials of Anypoint Platform%n", headingKey = "Basic Credentials") + UsernamePasswordCredentialOptGroup usernamePasswordCredential + + @CommandLine.ArgGroup(exclusive = false, multiplicity = "0..1", order = 2, heading = "Connected App credentials%n", headingKey = "Connected App Credentials") + ConnectedAppCredentialOptGroup connectedAppCredential + + Credential getCoreCredential(){ + return usernamePasswordCredential != null ? usernamePasswordCredential.getCoreCredential() : connectedAppCredential.getCoreCredential() + } + } + + static abstract class BaseCredential { + abstract Credential getCoreCredential() + } + static class UsernamePasswordCredentialOptGroup extends BaseCredential { + @Option(names = ['-u', '--anypoint-username'], required = true, order = 1) + String anypointUsername + @Option(names = ['-p', '--anypoint-password'], required = true, order = 2) + String anypointPassword + + Credential getCoreCredential() { + return new UsernamePasswordCredential(anypointUsername,anypointPassword) + } + } + + static class ConnectedAppCredentialOptGroup { + @Option(names = ['-caid', '--anypoint-connected-app-id'], required = true, order = 1) + String anypointConnectedAppId + @Option(names = ['-casec', '--anypoint-connected-app-secret'], required = true, order = 2) + String anypointConnectedAppSecret + + Credential getCoreCredential() { + return new ConnectedAppCredential(anypointConnectedAppId,anypointConnectedAppSecret) + } + } + + static void main(String... args) { def commandLine = new CommandLine(new DeployerCommandLine()) if (commandLine.versionHelpRequested) { @@ -64,17 +101,11 @@ class DeployerCommandLine implements Callable { Integer call() throws Exception { def deploymentPackage = processDsl() logger.println "Successfully processed ${groovyFile} through DSL" - def deployer = deployerFactory.create(this.anypointUsername, - this.anypointPassword, - this.anypointConnectedAppId, - this.anypointConnectedAppSecret, + def deployer = deployerFactory.create(this.credential.getCoreCredential(), logger, this.dryRunMode, this.anypointOrganizationName, this.environmentsToDoDesignCenterDeploymentOn) - if (this.anypointUsername == null && this.anypointPassword == null && this.anypointConnectedAppId == null && this.anypointConnectedAppSecret == null) { - throw new Exception("Either --anypoint-username and --anypoint-password or --anypoint-connected-app-id and --anypoint-connected-app-secret must be defined.") - } if (this.dryRunMode == DryRunMode.OfflineValidate) { logger.println 'Offline validate was specified, so not deploying' return diff --git a/cli/src/test/java/com/avioconsulting/mule/cli/DeployerCommandLineTest.groovy b/cli/src/test/java/com/avioconsulting/mule/cli/DeployerCommandLineTest.groovy index 5454979a..de4b87fd 100644 --- a/cli/src/test/java/com/avioconsulting/mule/cli/DeployerCommandLineTest.groovy +++ b/cli/src/test/java/com/avioconsulting/mule/cli/DeployerCommandLineTest.groovy @@ -5,6 +5,7 @@ import com.avioconsulting.mule.deployment.api.IDeployer import com.avioconsulting.mule.deployment.api.IDeployerFactory import com.avioconsulting.mule.deployment.api.ILogger import com.avioconsulting.mule.deployment.api.models.* +import com.avioconsulting.mule.deployment.api.models.credentials.Credential import com.avioconsulting.mule.deployment.api.models.policies.Policy import org.apache.commons.io.FileUtils import org.junit.BeforeClass @@ -40,9 +41,9 @@ class DeployerCommandLineTest implements MavenInvoke { def args if (connId != null && connSecret != null) args = [ - '-i', + '-caid', "\"${connId}\"".toString(), - '-s', + '-casec', "\"${connSecret}\"".toString(), '-m', dryRunMode.name() @@ -91,8 +92,7 @@ class DeployerCommandLineTest implements MavenInvoke { } ] as IDeployer def mock = [ - create: { String username, - String password, + create: { Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, @@ -150,8 +150,7 @@ muleDeploy { } ] as IDeployer def mock = [ - create: { String connectedAppId, - String connectedAppSecret, + create: { Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, @@ -205,8 +204,7 @@ muleDeploy { } ] as IDeployer def mock = [ - create: { String username, - String password, + create: { Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, @@ -254,8 +252,7 @@ muleDeploy { } ] as IDeployer def mock = [ - create: { String username, - String password, + create: { Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, @@ -319,8 +316,7 @@ muleDeploy { } ] as IDeployer def mock = [ - create: { String username, - String password, + create: { Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, @@ -350,8 +346,7 @@ muleDeploy { } ] as IDeployer def mock = [ - create: { String username, - String password, + create: { Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/api/Deployer.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/api/Deployer.groovy index c2bf9f7e..5a08b2e4 100644 --- a/library/src/main/java/com/avioconsulting/mule/deployment/api/Deployer.groovy +++ b/library/src/main/java/com/avioconsulting/mule/deployment/api/Deployer.groovy @@ -5,6 +5,7 @@ import com.avioconsulting.mule.deployment.api.models.ApiSpecificationList import com.avioconsulting.mule.deployment.api.models.CloudhubDeploymentRequest import com.avioconsulting.mule.deployment.api.models.Features import com.avioconsulting.mule.deployment.api.models.FileBasedAppDeploymentRequest +import com.avioconsulting.mule.deployment.api.models.credentials.Credential import com.avioconsulting.mule.deployment.api.models.policies.Policy import com.avioconsulting.mule.deployment.internal.http.EnvironmentLocator import com.avioconsulting.mule.deployment.internal.http.HttpClientWrapper @@ -30,28 +31,21 @@ class Deployer implements IDeployer { /** * - * @param username anypoint creds to deploy with - * @param password anypoint creds to deploy with + * @param credential {@link Credential } to access anypoint platform. * @param logger all messages will be logged like this. This is Jenkins plugins friendly (or you can supply System.out) * @param dryRunMode Should we do a real run? * @param anypointOrganizationName Optional parameter. If null, the default organization/biz group for the user will be used. Otherwise supply name (NOT GUID) of the biz group or organization you want to use * @param baseUrl Base URL, optional * @param environmentsToDoDesignCenterDeploymentOn Normally workflow wise you'd only want to do this on DEV */ - Deployer(String username, - String password, - String connectedAppId, - String connectedAppSecret, + Deployer(Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName = null, String baseUrl = 'https://anypoint.mulesoft.com', List environmentsToDoDesignCenterDeploymentOn = ['DEV']) { this(new HttpClientWrapper(baseUrl, - username, - password, - connectedAppId, - connectedAppSecret, + credential, logger, anypointOrganizationName), dryRunMode, diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/api/DeployerFactory.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/api/DeployerFactory.groovy index e2c614f1..684f1d78 100644 --- a/library/src/main/java/com/avioconsulting/mule/deployment/api/DeployerFactory.groovy +++ b/library/src/main/java/com/avioconsulting/mule/deployment/api/DeployerFactory.groovy @@ -1,19 +1,15 @@ package com.avioconsulting.mule.deployment.api +import com.avioconsulting.mule.deployment.api.models.credentials.Credential + class DeployerFactory implements IDeployerFactory { @Override - IDeployer create(String username, - String password, - String connectedAppId, - String connectedAppSecret, + IDeployer create(Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, List environmentsToDoDesignCenterDeploymentOn) { - new Deployer(username, - password, - connectedAppId, - connectedAppSecret, + new Deployer(credential, logger, dryRunMode, anypointOrganizationName, diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/api/IDeployerFactory.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/api/IDeployerFactory.groovy index 2ddfda74..c894c2bd 100644 --- a/library/src/main/java/com/avioconsulting/mule/deployment/api/IDeployerFactory.groovy +++ b/library/src/main/java/com/avioconsulting/mule/deployment/api/IDeployerFactory.groovy @@ -1,10 +1,9 @@ package com.avioconsulting.mule.deployment.api +import com.avioconsulting.mule.deployment.api.models.credentials.Credential + interface IDeployerFactory { - IDeployer create(String username, - String password, - String connectedAppId, - String connectedAppSecret, + IDeployer create(Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/ConnectedAppCredential.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/ConnectedAppCredential.groovy new file mode 100644 index 00000000..ea67fc39 --- /dev/null +++ b/library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/ConnectedAppCredential.groovy @@ -0,0 +1,25 @@ +package com.avioconsulting.mule.deployment.api.models.credentials + +import groovy.transform.Immutable + +/** + * Connected App credentials for accessing Anypoint Platform. + */ +@Immutable +class ConnectedAppCredential extends Credential { + + /** + * Connected App Id + */ + final String id; + + /** + * Connected App Secret + */ + final String secret; + + @Override + String getPrincipal() { + return id + } +} diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/Credential.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/Credential.groovy new file mode 100644 index 00000000..60c240f0 --- /dev/null +++ b/library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/Credential.groovy @@ -0,0 +1,9 @@ +package com.avioconsulting.mule.deployment.api.models.credentials; + +/** + * Abstract Base class for defining anypoint platform access credentials + */ +abstract class Credential { + + abstract String getPrincipal() +} diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/UsernamePasswordCredential.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/UsernamePasswordCredential.groovy new file mode 100644 index 00000000..f5e1ea24 --- /dev/null +++ b/library/src/main/java/com/avioconsulting/mule/deployment/api/models/credentials/UsernamePasswordCredential.groovy @@ -0,0 +1,26 @@ +package com.avioconsulting.mule.deployment.api.models.credentials + +import groovy.transform.Immutable; + + +/** + * Basic username and password credentials for accessing Anypoint Platform. + */ +@Immutable +class UsernamePasswordCredential extends Credential { + + /** + * Anypoint Username + */ + final String username; + + /** + * Anypoint Password + */ + final String password; + + @Override + String getPrincipal() { + return username + } +} diff --git a/library/src/main/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapper.groovy b/library/src/main/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapper.groovy index 6b4429c3..c6654539 100644 --- a/library/src/main/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapper.groovy +++ b/library/src/main/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapper.groovy @@ -1,6 +1,9 @@ package com.avioconsulting.mule.deployment.internal.http import com.avioconsulting.mule.deployment.api.ILogger +import com.avioconsulting.mule.deployment.api.models.credentials.ConnectedAppCredential +import com.avioconsulting.mule.deployment.api.models.credentials.Credential +import com.avioconsulting.mule.deployment.api.models.credentials.UsernamePasswordCredential import groovy.json.JsonOutput import groovy.json.JsonSlurper import org.apache.http.HttpException @@ -16,10 +19,6 @@ import org.apache.http.impl.client.HttpClients import org.apache.http.protocol.HttpContext class HttpClientWrapper implements HttpRequestInterceptor { - private final String username - private final String password - private final String connectedAppId - private final String connectedAppSecret private String accessToken private String ownerGuid private final ILogger logger @@ -27,19 +26,14 @@ class HttpClientWrapper implements HttpRequestInterceptor { private final CloseableHttpClient httpClient private final String anypointOrganizationName private String anypointOrganizationId + private Credential credential HttpClientWrapper(String baseUrl, - String username, - String password, - String connectedAppId = null, - String connectedAppSecret = null, + Credential credential, ILogger logger, String anypointOrganizationName = null) { + this.credential = credential this.anypointOrganizationName = anypointOrganizationName - this.password = password - this.username = username - this.connectedAppId = connectedAppId - this.connectedAppSecret = connectedAppSecret this.logger = logger this.baseUrl = baseUrl this.httpClient = HttpClients.custom() @@ -56,10 +50,7 @@ class HttpClientWrapper implements HttpRequestInterceptor { private def authenticate() { if (!this.accessToken) { - if (username != null) - fetchAccessTokenAsUser() - else if (connectedAppId != null) - fetchAccessTokenWithConnectedApp() + fetchAccessToken(this.credential) fetchUserInfo() } } @@ -84,7 +75,7 @@ class HttpClientWrapper implements HttpRequestInterceptor { org.id == this.anypointOrganizationId }?.name if (name) { - logger.println("Using default organization for ${username} of '${name}'") + logger.println("Using default organization for ${this.credential.getPrincipal()} of '${name}'") } else { throw new Exception('No Anypoint org was specified and was unable to find a default one! This should not happen!') } @@ -100,11 +91,11 @@ class HttpClientWrapper implements HttpRequestInterceptor { } } - private def fetchAccessTokenAsUser() { - logger.println "Authenticating to Anypoint as user '${username}'" + private def fetchAccessToken(UsernamePasswordCredential cred) { + logger.println "Authenticating to Anypoint as user '${cred.username}'" def payload = [ - username: username, - password: password + username: cred.username, + password: cred.password ] def request = new HttpPost("${baseUrl}/accounts/login").with { setEntity(new StringEntity(JsonOutput.toJson(payload))) @@ -114,17 +105,17 @@ class HttpClientWrapper implements HttpRequestInterceptor { } httpClient.execute(request).with { response -> def result = assertSuccessfulResponseAndReturnJson(response, - "authenticate to Anypoint as '${username}'") + "authenticate to Anypoint as '${cred.username}'") logger.println 'Successfully authenticated' accessToken = result.access_token } } - private def fetchAccessTokenWithConnectedApp() { - logger.println "Authenticating to Anypoint with connected app '${connectedAppId}'" + private def fetchAccessToken(ConnectedAppCredential cred) { + logger.println "Authenticating to Anypoint with connected app '${cred.id}'" def payload = [ - client_id: connectedAppId, - client_secret: connectedAppSecret, + client_id: cred.id, + client_secret: cred.secret, grant_type: "client_credentials" ] def request = new HttpPost("${baseUrl}/accounts/api/v2/oauth2/token").with { @@ -135,7 +126,7 @@ class HttpClientWrapper implements HttpRequestInterceptor { } httpClient.execute(request).with { response -> def result = assertSuccessfulResponseAndReturnJson(response, - "authenticate to Anypoint with connected app '${connectedAppId}'") + "authenticate to Anypoint with connected app '${cred.id}'") logger.println 'Successfully authenticated' accessToken = result.access_token } diff --git a/library/src/test/java/com/avioconsulting/mule/deployment/BaseTest.groovy b/library/src/test/java/com/avioconsulting/mule/deployment/BaseTest.groovy index f3ab6697..3d152012 100644 --- a/library/src/test/java/com/avioconsulting/mule/deployment/BaseTest.groovy +++ b/library/src/test/java/com/avioconsulting/mule/deployment/BaseTest.groovy @@ -1,5 +1,7 @@ package com.avioconsulting.mule.deployment +import com.avioconsulting.mule.deployment.api.models.credentials.ConnectedAppCredential +import com.avioconsulting.mule.deployment.api.models.credentials.UsernamePasswordCredential import com.avioconsulting.mule.deployment.internal.http.EnvironmentLocator import com.avioconsulting.mule.deployment.internal.http.HttpClientWrapper import groovy.json.JsonOutput @@ -41,17 +43,14 @@ class BaseTest { httpServer = createServer() closure = null clientWrapper = new HttpClientWrapper("http://localhost:${httpServer.actualPort()}", - 'the user', - 'the password', - null, - null, + new UsernamePasswordCredential('the user', + 'the password'), new TestConsoleLogger(), 'the-org-name') clientWrapperConnectedApp = new HttpClientWrapper("http://localhost:${httpServer.actualPort()}", - null, - null, + new ConnectedAppCredential( 'the client', - 'the secret', + 'the secret'), new TestConsoleLogger(), 'the-org-name') environmentLocator = new EnvironmentLocator(clientWrapper, diff --git a/library/src/test/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapperTest.groovy b/library/src/test/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapperTest.groovy index 10e271d1..7e8c0aff 100644 --- a/library/src/test/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapperTest.groovy +++ b/library/src/test/java/com/avioconsulting/mule/deployment/internal/http/HttpClientWrapperTest.groovy @@ -2,6 +2,7 @@ package com.avioconsulting.mule.deployment.internal.http import com.avioconsulting.mule.deployment.BaseTest import com.avioconsulting.mule.deployment.TestConsoleLogger +import com.avioconsulting.mule.deployment.api.models.credentials.UsernamePasswordCredential import groovy.json.JsonOutput import groovy.json.JsonSlurper import io.vertx.core.http.HttpServerRequest @@ -270,8 +271,8 @@ class HttpClientWrapperTest extends BaseTest { void getAnypointOrganizationId_default_org_prefs_exist() { // arrange clientWrapper = new HttpClientWrapper("http://localhost:${httpServer.actualPort()}", - 'the user', - 'the password', + new UsernamePasswordCredential('the user', + 'the password'), new TestConsoleLogger()) withHttpServer { HttpServerRequest request -> request.response().with { @@ -319,8 +320,8 @@ class HttpClientWrapperTest extends BaseTest { void getAnypointOrganizationId_default_no_org_prefs_exist() { // arrange clientWrapper = new HttpClientWrapper("http://localhost:${httpServer.actualPort()}", - 'the user', - 'the password', + new UsernamePasswordCredential('the user', + 'the password'), new TestConsoleLogger()) withHttpServer { HttpServerRequest request -> request.response().with { diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml index cd00029d..41e8e6e0 100644 --- a/maven-plugin/pom.xml +++ b/maven-plugin/pom.xml @@ -36,6 +36,16 @@ org.apache.maven.plugins maven-plugin-plugin 3.6.0 + + + + generated-helpmojo + + helpmojo + + +