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 ab1e05c0..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,10 +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'], required = true) - private String anypointUsername - @Option(names = ['-p', '--anypoint-password'], required = true) - private String anypointPassword + + @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 @@ -46,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) { @@ -60,8 +101,7 @@ 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, + def deployer = deployerFactory.create(this.credential.getCoreCredential(), logger, this.dryRunMode, this.anypointOrganizationName, 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..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 @@ -25,6 +26,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 +38,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 = [ + '-caid', + "\"${connId}\"".toString(), + '-casec', + "\"${connSecret}\"".toString(), + '-m', + dryRunMode.name() + ] + else + args = [ + '-u', + "\"${user}\"".toString(), + '-p', + "\"${pass}\"".toString(), + '-m', + dryRunMode.name(), + ] if (orgName) { args += [ '-o', @@ -78,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, @@ -120,6 +133,64 @@ 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: { Credential credential, + 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 @@ -133,8 +204,7 @@ muleDeploy { } ] as IDeployer def mock = [ - create: { String username, - String password, + create: { Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, @@ -160,6 +230,8 @@ muleDeploy { dslText, 'user', 'pass', + null, + null, DryRunMode.OfflineValidate) // assert @@ -180,8 +252,7 @@ muleDeploy { } ] as IDeployer def mock = [ - create: { String username, - String password, + create: { Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, @@ -216,6 +287,8 @@ muleDeploy { dslText, 'our user', 'our pass', + null, + null, DryRunMode.Run, null, [ @@ -243,8 +316,7 @@ muleDeploy { } ] as IDeployer def mock = [ - create: { String username, - String password, + create: { Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, @@ -274,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 1c4cdf83..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,24 +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, + 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, + 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 03b4621b..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,15 +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, + IDeployer create(Credential credential, ILogger logger, DryRunMode dryRunMode, String anypointOrganizationName, List environmentsToDoDesignCenterDeploymentOn) { - new Deployer(username, - password, + 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 c369c142..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,8 +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, + 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 3e6f69b7..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,8 +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 String accessToken private String ownerGuid private final ILogger logger @@ -25,15 +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, + Credential credential, ILogger logger, String anypointOrganizationName = null) { + this.credential = credential this.anypointOrganizationName = anypointOrganizationName - this.password = password - this.username = username this.logger = logger this.baseUrl = baseUrl this.httpClient = HttpClients.custom() @@ -50,7 +50,7 @@ class HttpClientWrapper implements HttpRequestInterceptor { private def authenticate() { if (!this.accessToken) { - fetchAccessToken() + fetchAccessToken(this.credential) fetchUserInfo() } } @@ -75,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!') } @@ -91,11 +91,11 @@ class HttpClientWrapper implements HttpRequestInterceptor { } } - private def fetchAccessToken() { - 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))) @@ -105,7 +105,28 @@ 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 fetchAccessToken(ConnectedAppCredential cred) { + logger.println "Authenticating to Anypoint with connected app '${cred.id}'" + def payload = [ + client_id: cred.id, + client_secret: cred.secret, + 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 '${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 bc7477a4..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 @@ -19,6 +21,7 @@ import java.util.concurrent.CompletableFuture class BaseTest { protected HttpServer httpServer protected HttpClientWrapper clientWrapper + protected HttpClientWrapper clientWrapperConnectedApp protected EnvironmentLocator environmentLocator protected Handler closure @@ -40,10 +43,16 @@ class BaseTest { httpServer = createServer() closure = null clientWrapper = new HttpClientWrapper("http://localhost:${httpServer.actualPort()}", - 'the user', - 'the password', + new UsernamePasswordCredential('the user', + 'the password'), new TestConsoleLogger(), 'the-org-name') + clientWrapperConnectedApp = new HttpClientWrapper("http://localhost:${httpServer.actualPort()}", + new ConnectedAppCredential( + '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..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 @@ -76,6 +77,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 @@ -206,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 { @@ -255,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/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy b/library/src/test/java/com/avioconsulting/mule/integrationtest/IntegrationTest.groovy index cf24f764..2fef523a 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_CONNECTED_APP_ID && !ANYPOINT_CONNECTED_APP_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/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 + + +