diff --git a/build.gradle b/build.gradle index 720f676..72168ad 100644 --- a/build.gradle +++ b/build.gradle @@ -19,8 +19,8 @@ apply from: "https://raw.githubusercontent.com/gocd/gocd-plugin-gradle-task-help gocdPlugin { id = 'cd.go.authorization.github' - pluginVersion = '3.4.0' - goCdVersion = '20.9.0' + pluginVersion = '4.0.0' + goCdVersion = '22.1.0' name = 'GitHub OAuth authorization plugin' description = 'GitHub OAuth authorization plugin for GoCD' vendorName = 'Thoughtworks, Inc.' @@ -44,6 +44,11 @@ gocdPlugin { } } +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + group = 'cd.go' version = gocdPlugin.fullVersion(project) @@ -52,17 +57,6 @@ repositories { mavenLocal() } -java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 -} - -configurations.all { - resolutionStrategy.dependencySubstitution { - substitute module("junit:junit") using module("io.quarkus:quarkus-junit4-mock:3.0.0.Final") because "We don't want JUnit 4; but is an unneeded transitive of mockwebserver." - } -} - ext { deps = [ gocdPluginApi: 'cd.go.plugin:go-plugin-api:25.2.0', @@ -78,17 +72,18 @@ dependencies { implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.12.0' testImplementation project.deps.gocdPluginApi - testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.18.0' - testImplementation group: 'org.hamcrest', name: 'hamcrest', version: '3.0' - testImplementation group: 'org.skyscreamer', name: 'jsonassert', version: '1.5.3' - testImplementation group: 'org.jsoup', name: 'jsoup', version: '1.21.1' - testImplementation group: 'com.squareup.okhttp3', name: 'mockwebserver', version: '4.12.0' - testImplementation platform('org.junit:junit-bom:5.13.2') testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine' testRuntimeOnly group: 'org.junit.platform', name: 'junit-platform-launcher' + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.27.3' + testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.18.0' + testImplementation group: 'org.skyscreamer', name: 'jsonassert', version: '1.5.3' + testImplementation group: 'org.jsoup', name: 'jsoup', version: '1.21.1' + testImplementation group: 'com.squareup.okhttp3', name: 'mockwebserver3-junit5', version: '5.0.0-alpha.17' + + } test { diff --git a/src/main/java/cd/go/authorization/github/Constants.java b/src/main/java/cd/go/authorization/github/Constants.java index 4613a87..5777e72 100644 --- a/src/main/java/cd/go/authorization/github/Constants.java +++ b/src/main/java/cd/go/authorization/github/Constants.java @@ -31,4 +31,5 @@ public interface Constants { GoPluginIdentifier PLUGIN_IDENTIFIER = new GoPluginIdentifier(EXTENSION_TYPE, Collections.singletonList(API_VERSION)); String AUTH_SESSION_STATE = "oauth2_state"; + String AUTH_CODE_VERIFIER_ENCODED = "oauth2_code_verifier_encoded"; } diff --git a/src/main/java/cd/go/authorization/github/client/GitHubClientBuilder.java b/src/main/java/cd/go/authorization/github/client/GitHubClientBuilder.java index a85fc52..c7bdb28 100644 --- a/src/main/java/cd/go/authorization/github/client/GitHubClientBuilder.java +++ b/src/main/java/cd/go/authorization/github/client/GitHubClientBuilder.java @@ -18,11 +18,15 @@ import cd.go.authorization.github.models.AuthenticateWith; import cd.go.authorization.github.models.GitHubConfiguration; +import okhttp3.FormBody; +import okhttp3.HttpUrl; +import okhttp3.Request; import org.kohsuke.github.GitHub; import org.kohsuke.github.GitHubBuilder; import org.kohsuke.github.GitHubRateLimitHandler; import java.io.IOException; +import java.util.List; import static cd.go.authorization.github.GitHubPlugin.LOG; @@ -48,4 +52,44 @@ private GitHub clientFor(String personalAccessTokenOrUsersAccessToken, GitHubCon .build(); } } + + public List authorizationServerArgs(GitHubConfiguration config, String callbackUrl) { + String state = StateGenerator.generate(); + ProofKey proofKey = new ProofKey(); + String authorizationServerUrl = HttpUrl.parse(config.authenticateWith() == AuthenticateWith.GITHUB ? GitHubConfiguration.GITHUB_URL : config.gitHubEnterpriseUrl()) + .newBuilder() + .addPathSegment("login") + .addPathSegment("oauth") + .addPathSegment("authorize") + .addQueryParameter("client_id", config.clientId()) + .addQueryParameter("redirect_uri", callbackUrl) + .addQueryParameter("response_type", "code") + .addQueryParameter("scope", config.scope()) + .addQueryParameter("state", state) + .addQueryParameter("code_challenge_method", "S256") + .addEncodedQueryParameter("code_challenge", proofKey.codeChallengeEncoded()) + .build().toString(); + return List.of(authorizationServerUrl, state, proofKey.codeVerifierEncoded()); + } + + public Request accessTokenRequestFrom(GitHubConfiguration config, String authorizationCode, String codeVerifierEncoded) { + HttpUrl accessTokenUrl = HttpUrl.parse(config.authenticateWith() == AuthenticateWith.GITHUB ? GitHubConfiguration.GITHUB_URL : config.gitHubEnterpriseUrl()) + .newBuilder() + .addPathSegment("login") + .addPathSegment("oauth") + .addPathSegment("access_token") + .build(); + + return new Request.Builder() + .url(accessTokenUrl) + .addHeader("Accept", "application/json") + .post(new FormBody.Builder() + .add("client_id", config.clientId()) + .add("client_secret", config.clientSecret()) + .add("code", authorizationCode) + .add("code_verifier", codeVerifierEncoded) + .build()) + .build(); + } + } diff --git a/src/main/java/cd/go/authorization/github/client/ProofKey.java b/src/main/java/cd/go/authorization/github/client/ProofKey.java new file mode 100644 index 0000000..a0424c2 --- /dev/null +++ b/src/main/java/cd/go/authorization/github/client/ProofKey.java @@ -0,0 +1,61 @@ +/* + * Copyright 2025 ThoughtWorks, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cd.go.authorization.github.client; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Base64; + +public class ProofKey { + private static final SecureRandom RANDOM = new SecureRandom(); + + private final String codeVerifierEncoded; + private final String codeChallengeEncoded; + + public ProofKey() { + this.codeVerifierEncoded = generateCodeVerifier(); + this.codeChallengeEncoded = generateCodeChallenge(codeVerifierEncoded); + } + + private static String generateCodeVerifier() { + byte[] codeVerifier = new byte[32]; + RANDOM.nextBytes(codeVerifier); + return Base64.getUrlEncoder().withoutPadding().encodeToString(codeVerifier); + } + + private static String generateCodeChallenge(String codeVerifier) { + byte[] bytes = codeVerifier.getBytes(StandardCharsets.US_ASCII); + MessageDigest messageDigest; + try { + messageDigest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + messageDigest.update(bytes, 0, bytes.length); + return Base64.getUrlEncoder().withoutPadding().encodeToString(messageDigest.digest()); + } + + public String codeVerifierEncoded() { + return codeVerifierEncoded; + } + + public String codeChallengeEncoded() { + return codeChallengeEncoded; + } +} \ No newline at end of file diff --git a/src/main/java/cd/go/authorization/github/executors/FetchAccessTokenRequestExecutor.java b/src/main/java/cd/go/authorization/github/executors/FetchAccessTokenRequestExecutor.java index 4687a3b..6ce9232 100644 --- a/src/main/java/cd/go/authorization/github/executors/FetchAccessTokenRequestExecutor.java +++ b/src/main/java/cd/go/authorization/github/executors/FetchAccessTokenRequestExecutor.java @@ -16,15 +16,17 @@ package cd.go.authorization.github.executors; +import cd.go.authorization.github.client.GitHubClientBuilder; import cd.go.authorization.github.exceptions.AuthenticationException; import cd.go.authorization.github.exceptions.NoAuthorizationConfigurationException; import cd.go.authorization.github.models.AuthConfig; -import cd.go.authorization.github.models.GitHubConfiguration; import cd.go.authorization.github.models.TokenInfo; import cd.go.authorization.github.requests.FetchAccessTokenRequest; import com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse; import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse; -import okhttp3.*; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; import static cd.go.authorization.github.GitHubPlugin.LOG; import static java.text.MessageFormat.format; @@ -32,14 +34,16 @@ public class FetchAccessTokenRequestExecutor implements RequestExecutor { private final FetchAccessTokenRequest request; private final OkHttpClient httpClient; + private GitHubClientBuilder gitHubClientBuilder; public FetchAccessTokenRequestExecutor(FetchAccessTokenRequest request) { - this(request, new OkHttpClient()); + this(request, new OkHttpClient(), new GitHubClientBuilder()); } - FetchAccessTokenRequestExecutor(FetchAccessTokenRequest request, OkHttpClient httpClient) { + FetchAccessTokenRequestExecutor(FetchAccessTokenRequest request, OkHttpClient httpClient, GitHubClientBuilder gitHubClientBuilder) { this.request = request; this.httpClient = httpClient; + this.gitHubClientBuilder = gitHubClientBuilder; } public GoPluginApiResponse execute() throws Exception { @@ -47,18 +51,12 @@ public GoPluginApiResponse execute() throws Exception { throw new NoAuthorizationConfigurationException("[Get Access Token] No authorization configuration found."); } - if (!request.requestParameters().containsKey("code")) { - throw new IllegalArgumentException("Get Access Token] Expecting `code` in request params, but not received."); - } - request.validateState(); final AuthConfig authConfig = request.authConfigs().get(0); - final GitHubConfiguration gitHubConfiguration = authConfig.gitHubConfiguration(); - - final Request fetchAccessTokenRequest = accessTokenRequestFrom(gitHubConfiguration); - final Response response = httpClient.newCall(fetchAccessTokenRequest).execute(); + final Request request = gitHubClientBuilder.accessTokenRequestFrom(authConfig.gitHubConfiguration(), this.request.authorizationCode(), this.request.codeVerifierEncoded()); + final Response response = httpClient.newCall(request).execute(); if (response.isSuccessful()) { LOG.info("[Get Access Token] Access token fetched successfully."); final TokenInfo tokenInfo = TokenInfo.fromJSON(response.body().string()); @@ -68,24 +66,5 @@ public GoPluginApiResponse execute() throws Exception { throw new AuthenticationException(format("[Get Access Token] {0}", response.message())); } - private Request accessTokenRequestFrom(GitHubConfiguration gitHubConfiguration) { - return new Request.Builder() - .url(accessTokenUrl(gitHubConfiguration)) - .addHeader("Accept", "application/json") - .post(new FormBody.Builder() - .add("client_id", gitHubConfiguration.clientId()) - .add("client_secret", gitHubConfiguration.clientSecret()) - .add("code", request.requestParameters().get("code")) - .build()) - .build(); - } - private HttpUrl accessTokenUrl(GitHubConfiguration gitHubConfiguration) { - return HttpUrl.parse(gitHubConfiguration.apiUrl()) - .newBuilder() - .addPathSegment("login") - .addPathSegment("oauth") - .addPathSegment("access_token") - .build(); - } } diff --git a/src/main/java/cd/go/authorization/github/executors/GetAuthorizationServerUrlRequestExecutor.java b/src/main/java/cd/go/authorization/github/executors/GetAuthorizationServerUrlRequestExecutor.java index 6e823c9..f6f9264 100644 --- a/src/main/java/cd/go/authorization/github/executors/GetAuthorizationServerUrlRequestExecutor.java +++ b/src/main/java/cd/go/authorization/github/executors/GetAuthorizationServerUrlRequestExecutor.java @@ -17,15 +17,15 @@ package cd.go.authorization.github.executors; import cd.go.authorization.github.Constants; -import cd.go.authorization.github.client.StateGenerator; +import cd.go.authorization.github.client.GitHubClientBuilder; import cd.go.authorization.github.exceptions.NoAuthorizationConfigurationException; import cd.go.authorization.github.models.AuthConfig; import cd.go.authorization.github.models.GitHubConfiguration; import cd.go.authorization.github.requests.GetAuthorizationServerUrlRequest; import com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse; import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse; -import okhttp3.HttpUrl; +import java.util.List; import java.util.Map; import static cd.go.authorization.github.GitHubPlugin.LOG; @@ -33,9 +33,15 @@ public class GetAuthorizationServerUrlRequestExecutor implements RequestExecutor { private final GetAuthorizationServerUrlRequest request; + private final GitHubClientBuilder gitHubClientBuilder; public GetAuthorizationServerUrlRequestExecutor(GetAuthorizationServerUrlRequest request) { + this(request, new GitHubClientBuilder()); + } + + public GetAuthorizationServerUrlRequestExecutor(GetAuthorizationServerUrlRequest request, GitHubClientBuilder gitHubClientBuilder) { this.request = request; + this.gitHubClientBuilder = gitHubClientBuilder; } public GoPluginApiResponse execute() throws Exception { @@ -48,22 +54,11 @@ public GoPluginApiResponse execute() throws Exception { final AuthConfig authConfig = request.authConfigs().get(0); final GitHubConfiguration gitHubConfiguration = authConfig.gitHubConfiguration(); - String state = StateGenerator.generate(); - String authorizationServerUrl = HttpUrl.parse(gitHubConfiguration.apiUrl()) - .newBuilder() - .addPathSegment("login") - .addPathSegment("oauth") - .addPathSegment("authorize") - .addQueryParameter("client_id", gitHubConfiguration.clientId()) - .addQueryParameter("redirect_uri", request.callbackUrl()) - .addQueryParameter("scope", gitHubConfiguration.scope()) - .addQueryParameter("state", state) - .build().toString(); - + List result = gitHubClientBuilder.authorizationServerArgs(gitHubConfiguration, request.callbackUrl()); return DefaultGoPluginApiResponse.success(GSON.toJson(Map.of( - "authorization_server_url", authorizationServerUrl, - "auth_session", Map.of(Constants.AUTH_SESSION_STATE, state) + "authorization_server_url", result.get(0), + "auth_session", Map.of(Constants.AUTH_SESSION_STATE, result.get(1), Constants.AUTH_CODE_VERIFIER_ENCODED, result.get(2)) ))); } } diff --git a/src/main/java/cd/go/authorization/github/models/GitHubConfiguration.java b/src/main/java/cd/go/authorization/github/models/GitHubConfiguration.java index 99a90b1..a672bf9 100644 --- a/src/main/java/cd/go/authorization/github/models/GitHubConfiguration.java +++ b/src/main/java/cd/go/authorization/github/models/GitHubConfiguration.java @@ -29,7 +29,7 @@ import static cd.go.authorization.github.utils.Util.*; public class GitHubConfiguration implements Validatable { - private static final String GITHUB_URL = "https://github.com"; + public static final String GITHUB_URL = "https://github.com"; private static final String GITHUB_ENTERPRISE_API_SUFFIX = "/api/v3/"; @Expose @@ -102,10 +102,6 @@ public String gitHubEnterpriseApiUrl() { return gitHubEnterpriseUrl.concat(GITHUB_ENTERPRISE_API_SUFFIX); } - public String apiUrl() { - return authenticateWith == AuthenticateWith.GITHUB ? GITHUB_URL : gitHubEnterpriseUrl; - } - public String scope() { return "user:email"; } diff --git a/src/main/java/cd/go/authorization/github/requests/FetchAccessTokenRequest.java b/src/main/java/cd/go/authorization/github/requests/FetchAccessTokenRequest.java index 142d5d6..7720a2f 100644 --- a/src/main/java/cd/go/authorization/github/requests/FetchAccessTokenRequest.java +++ b/src/main/java/cd/go/authorization/github/requests/FetchAccessTokenRequest.java @@ -72,4 +72,12 @@ public void validateState() { throw new AuthenticationException("Redirected OAuth2 state from GitHub did not match previously generated state stored in session"); } } + + public String authorizationCode() { + return Objects.requireNonNullElseGet(requestParameters().get("code"), () -> { throw new IllegalArgumentException("[Get Access Token] Expecting `code` in request params, but not received."); }); + } + + public String codeVerifierEncoded() { + return Objects.requireNonNullElseGet(authSession.get(Constants.AUTH_CODE_VERIFIER_ENCODED), () -> { throw new IllegalArgumentException("[Get Access Token] OAuth2 code verifier is missing from session"); }); + } } diff --git a/src/main/java/cd/go/authorization/github/requests/UserAuthenticationRequest.java b/src/main/java/cd/go/authorization/github/requests/UserAuthenticationRequest.java index abbdc8f..864c4b2 100644 --- a/src/main/java/cd/go/authorization/github/requests/UserAuthenticationRequest.java +++ b/src/main/java/cd/go/authorization/github/requests/UserAuthenticationRequest.java @@ -31,14 +31,14 @@ public class UserAuthenticationRequest extends Request { @SerializedName("auth_configs") private List authConfigs; - @Expose - @SerializedName("role_configs") - private List roles; - @Expose @SerializedName("credentials") private TokenInfo tokenInfo; + @Expose + @SerializedName("role_configs") + private List roles; + public static UserAuthenticationRequest from(GoPluginApiRequest apiRequest) { return Request.from(apiRequest, UserAuthenticationRequest.class); } diff --git a/src/main/java/cd/go/authorization/github/utils/Util.java b/src/main/java/cd/go/authorization/github/utils/Util.java index 41b7762..67c0f59 100644 --- a/src/main/java/cd/go/authorization/github/utils/Util.java +++ b/src/main/java/cd/go/authorization/github/utils/Util.java @@ -24,7 +24,6 @@ import java.io.InputStream; import java.io.StringReader; import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Properties; @@ -69,14 +68,14 @@ public static List splitIntoLinesAndTrimSpaces(String lines) { if (isBlank(lines)) { return Collections.emptyList(); } - return Arrays.asList(lines.split("\\s*[\r\n]+\\s*")); + return List.of(lines.split("\\s*[\r\n]+\\s*")); } public static List listFromCommaSeparatedString(String str) { if (isBlank(str)) { return Collections.emptyList(); } - return Arrays.asList(str.trim().split("\\s*,\\s*")); + return List.of(str.trim().split("\\s*,\\s*")); } public static String toLowerCase(String str) { diff --git a/src/test/java/cd/go/authorization/github/GitHubAuthenticatorTest.java b/src/test/java/cd/go/authorization/github/GitHubAuthenticatorTest.java index 7a86e91..d1146d1 100644 --- a/src/test/java/cd/go/authorization/github/GitHubAuthenticatorTest.java +++ b/src/test/java/cd/go/authorization/github/GitHubAuthenticatorTest.java @@ -27,9 +27,7 @@ import java.util.Collections; import java.util.List; -import static java.util.Arrays.asList; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -69,13 +67,13 @@ public void shouldAuthenticateUser() throws Exception { final LoggedInUserInfo loggedInUserInfo = authenticator.authenticate(tokenInfo, authConfig); - assertThat(loggedInUserInfo.getUser(), is(new User("bford", "Bob", "bford@example.com"))); + assertThat(loggedInUserInfo.getUser()).isEqualTo(new User("bford", "Bob", "bford@example.com")); } @Test public void shouldAuthenticateUserWhenUserIsAMemberOfAtLeastOneOfTheAllowedOrganization() throws Exception { final GHMyself myself = mockUser("bford", "Bob"); - final List allowedOrganizations = asList("OrgA", "OrgB"); + final List allowedOrganizations = List.of("OrgA", "OrgB"); when(gitHub.getMyself()).thenReturn(myself); when(gitHubConfiguration.organizationsAllowed()).thenReturn(allowedOrganizations); @@ -83,13 +81,13 @@ public void shouldAuthenticateUserWhenUserIsAMemberOfAtLeastOneOfTheAllowedOrgan final LoggedInUserInfo loggedInUserInfo = authenticator.authenticate(tokenInfo, authConfig); - assertThat(loggedInUserInfo.getUser(), is(new User("bford", "Bob", "bford@example.com"))); + assertThat(loggedInUserInfo.getUser()).isEqualTo(new User("bford", "Bob", "bford@example.com")); } @Test public void shouldNotAuthenticateUserWhenUserIsNotAMemberOfAnyAllowedOrganization() throws Exception { final GHMyself myself = mockUser("bford", "Bob"); - final List allowedOrganizations = asList("OrgA", "OrgB"); + final List allowedOrganizations = List.of("OrgA", "OrgB"); when(gitHub.getMyself()).thenReturn(myself); when(gitHubConfiguration.organizationsAllowed()).thenReturn(allowedOrganizations); diff --git a/src/test/java/cd/go/authorization/github/GitHubAuthorizerTest.java b/src/test/java/cd/go/authorization/github/GitHubAuthorizerTest.java index 308f972..c46ca99 100644 --- a/src/test/java/cd/go/authorization/github/GitHubAuthorizerTest.java +++ b/src/test/java/cd/go/authorization/github/GitHubAuthorizerTest.java @@ -29,9 +29,7 @@ import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.hasSize; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; public class GitHubAuthorizerTest { @@ -55,7 +53,7 @@ public void setUp() { public void shouldReturnEmptyListIfNoRoleConfiguredForGivenAuthConfig() throws IOException { final List assignedRoles = authorizer.authorize(ghUser, authConfig, Collections.emptyList()); - assertThat(assignedRoles, hasSize(0)); + assertThat(assignedRoles).hasSize(0); verifyNoMoreInteractions(authConfig); verifyNoMoreInteractions(membershipChecker); } @@ -71,8 +69,8 @@ public void shouldAssignRoleWhenUsersListContainsUsernameOfTheGivenUser() throws final List assignedRoles = authorizer.authorize(ghUser, authConfig, singletonList(role)); - assertThat(assignedRoles, hasSize(1)); - assertThat(assignedRoles, contains("admin")); + assertThat(assignedRoles).hasSize(1); + assertThat(assignedRoles).contains("admin"); } @Test @@ -86,7 +84,7 @@ public void shouldNotAssignRoleWhenUsersListDoesNotContainsTheGivenUser() throws final List assignedRoles = authorizer.authorize(ghUser, authConfig, singletonList(role)); - assertThat(assignedRoles, hasSize(0)); + assertThat(assignedRoles).hasSize(0); } @Test @@ -101,8 +99,8 @@ public void shouldAssignRoleIfUserIsAMemberOfAtLeastOneOrganization() throws IOE final List assignedRoles = authorizer.authorize(ghUser, authConfig, singletonList(role)); - assertThat(assignedRoles, hasSize(1)); - assertThat(assignedRoles, contains("admin")); + assertThat(assignedRoles).hasSize(1); + assertThat(assignedRoles).contains("admin"); } @Test @@ -117,7 +115,7 @@ public void shouldNotAssignRoleIfUserIsNotMemberOfAnyOrganization() throws IOExc final List assignedRoles = authorizer.authorize(ghUser, authConfig, singletonList(role)); - assertThat(assignedRoles, hasSize(0)); + assertThat(assignedRoles).hasSize(0); } @Test @@ -132,8 +130,8 @@ public void shouldAssignRoleIfUserIsAMemberOfAtLeastOneOrganizationTeam() throws final List assignedRoles = authorizer.authorize(ghUser, authConfig, singletonList(role)); - assertThat(assignedRoles, hasSize(1)); - assertThat(assignedRoles, contains("admin")); + assertThat(assignedRoles).hasSize(1); + assertThat(assignedRoles).contains("admin"); } @Test @@ -148,6 +146,6 @@ public void shouldNotAssignRoleIfUserIsNotMemberOfAnyOrganizationTeam() throws I final List assignedRoles = authorizer.authorize(ghUser, authConfig, singletonList(role)); - assertThat(assignedRoles, hasSize(0)); + assertThat(assignedRoles).hasSize(0); } } diff --git a/src/test/java/cd/go/authorization/github/MembershipCheckerTest.java b/src/test/java/cd/go/authorization/github/MembershipCheckerTest.java index 3ceb389..854b025 100644 --- a/src/test/java/cd/go/authorization/github/MembershipCheckerTest.java +++ b/src/test/java/cd/go/authorization/github/MembershipCheckerTest.java @@ -28,8 +28,8 @@ import java.io.IOException; import java.util.HashMap; +import java.util.List; -import static java.util.Arrays.asList; import static java.util.Collections.singletonMap; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -66,7 +66,7 @@ public void shouldCheckUserMembershipUsingPersonalAccessToken_andReturnTrueIfUse when(gitHub.getOrganization("organization-foo")).thenReturn(organization); when(organization.hasMember(ghUser)).thenReturn(true); - final boolean aMemberOfAtLeastOneOrganization = membershipChecker.isAMemberOfAtLeastOneOrganization(ghUser, authConfig, asList("organization-foo", "organization-bar")); + final boolean aMemberOfAtLeastOneOrganization = membershipChecker.isAMemberOfAtLeastOneOrganization(ghUser, authConfig, List.of("organization-foo", "organization-bar")); assertTrue(aMemberOfAtLeastOneOrganization); } @@ -79,7 +79,7 @@ public void shouldCheckUserMembershipUsingPersonalAccessToken_andReturnFalseIfUs when(gitHub.getOrganization("organization-foo")).thenReturn(organization); when(organization.hasMember(ghUser)).thenReturn(false); - final boolean aMemberOfAtLeastOneOrganization = membershipChecker.isAMemberOfAtLeastOneOrganization(ghUser, authConfig, asList("organization-foo", "organization-bar")); + final boolean aMemberOfAtLeastOneOrganization = membershipChecker.isAMemberOfAtLeastOneOrganization(ghUser, authConfig, List.of("organization-foo", "organization-bar")); assertFalse(aMemberOfAtLeastOneOrganization); } @@ -95,7 +95,7 @@ public void shouldCheckUserMembershipUsingPersonalAccessToken_andReturnTrueIfUse when(gitHub.getOrganization("organization-foo")).thenReturn(organization); when(team.hasMember(ghUser)).thenReturn(true); - final boolean aMemberOfAtLeastOneTeamOfOrganization = membershipChecker.isAMemberOfAtLeastOneTeamOfOrganization(ghUser, authConfig, singletonMap("organization-foo", asList("teamx"))); + final boolean aMemberOfAtLeastOneTeamOfOrganization = membershipChecker.isAMemberOfAtLeastOneTeamOfOrganization(ghUser, authConfig, singletonMap("organization-foo", List.of("teamx"))); assertTrue(aMemberOfAtLeastOneTeamOfOrganization); } @@ -120,7 +120,7 @@ public void shouldCheckUserMembershipUsingPersonalAccessToken_andReturnFalseIfUs when(teamY.getName()).thenReturn("TeamY"); when(teamX.hasMember(ghUser)).thenReturn(true);; - final boolean aMemberOfAtLeastOneTeamOfOrganization = membershipChecker.isAMemberOfAtLeastOneTeamOfOrganization(ghUser, authConfig, singletonMap("organization-foo", asList("TeamX"))); + final boolean aMemberOfAtLeastOneTeamOfOrganization = membershipChecker.isAMemberOfAtLeastOneTeamOfOrganization(ghUser, authConfig, singletonMap("organization-foo", List.of("TeamX"))); assertFalse(aMemberOfAtLeastOneTeamOfOrganization); } diff --git a/src/test/java/cd/go/authorization/github/client/GitHubClientBuilderTest.java b/src/test/java/cd/go/authorization/github/client/GitHubClientBuilderTest.java new file mode 100644 index 0000000..750875f --- /dev/null +++ b/src/test/java/cd/go/authorization/github/client/GitHubClientBuilderTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2025 ThoughtWorks, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cd.go.authorization.github.client; + +import cd.go.authorization.github.models.AuthenticateWith; +import cd.go.authorization.github.models.GitHubConfiguration; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + +class GitHubClientBuilderTest { + + @Mock + private GitHubConfiguration gitHubConfiguration; + private GitHubClientBuilder builder; + + @BeforeEach + public void setUp() throws Exception { + openMocks(this); + + when(gitHubConfiguration.clientId()).thenReturn("client-id"); + when(gitHubConfiguration.clientSecret()).thenReturn("client-secret"); + when(gitHubConfiguration.authenticateWith()).thenReturn(AuthenticateWith.GITHUB); + when(gitHubConfiguration.scope()).thenReturn("api"); + + builder = new GitHubClientBuilder(); + } + + @Test + public void shouldReturnAuthorizationServerArgsForGitHub() { + final List authorizationServerArgs = builder.authorizationServerArgs(gitHubConfiguration, "call-back-url"); + + assertThat(authorizationServerArgs).satisfies(args -> { + assertThat(args).hasSize(3); + assertThat(args.get(0)).startsWith("https://github.com/login/oauth/authorize?client_id=client-id&redirect_uri=call-back-url&response_type=code&scope=api&state=" + URLEncoder.encode(args.get(1), StandardCharsets.UTF_8) + "&code_challenge_method=S256&code_challenge="); + }); + } + + @Test + public void shouldReturnAuthorizationServerArgsForGitHubEnterprise() { + when(gitHubConfiguration.authenticateWith()).thenReturn(AuthenticateWith.GITHUB_ENTERPRISE); + when(gitHubConfiguration.gitHubEnterpriseUrl()).thenReturn("http://enterprise.url"); + + final List authorizationServerArgs = builder.authorizationServerArgs(gitHubConfiguration, "call-back-url"); + + assertThat(authorizationServerArgs).satisfies(args -> { + assertThat(args).hasSize(3); + assertThat(args.get(0)).startsWith("http://enterprise.url/login/oauth/authorize?client_id=client-id&redirect_uri=call-back-url&response_type=code&scope=api&state=" + URLEncoder.encode(args.get(1), StandardCharsets.UTF_8) + "&code_challenge_method=S256&code_challenge="); + }); + } + + @Test + public void shouldReturnAuthorizationServerArgsForGitHubEnterpriseWithTrailingSlash() { + when(gitHubConfiguration.authenticateWith()).thenReturn(AuthenticateWith.GITHUB_ENTERPRISE); + when(gitHubConfiguration.gitHubEnterpriseUrl()).thenReturn("http://enterprise.url/"); + + final List authorizationServerArgs = builder.authorizationServerArgs(gitHubConfiguration, "call-back-url"); + + assertThat(authorizationServerArgs).satisfies(args -> { + assertThat(args).hasSize(3); + assertThat(args.get(0)).startsWith("http://enterprise.url/login/oauth/authorize?client_id=client-id&redirect_uri=call-back-url&response_type=code&scope=api&state=" + URLEncoder.encode(args.get(1), StandardCharsets.UTF_8) + "&code_challenge_method=S256&code_challenge="); + }); + } +} \ No newline at end of file diff --git a/src/test/java/cd/go/authorization/github/client/ProofKeyTest.java b/src/test/java/cd/go/authorization/github/client/ProofKeyTest.java new file mode 100644 index 0000000..a88085d --- /dev/null +++ b/src/test/java/cd/go/authorization/github/client/ProofKeyTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2025 ThoughtWorks, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cd.go.authorization.github.client; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class ProofKeyTest { + + @Test + public void shouldGenerate() { + ProofKey proofKey = new ProofKey(); + + assertThat(proofKey).satisfies(args -> { + assertThat(args.codeVerifierEncoded()).matches("[A-Za-z0-9_-]{43}"); + assertThat(args.codeChallengeEncoded()).matches("[A-Za-z0-9_-]{43}"); + assertThat(args.codeChallengeEncoded()).isNotEqualTo(args.codeVerifierEncoded()); + }); + } +} \ No newline at end of file diff --git a/src/test/java/cd/go/authorization/github/client/StateGeneratorTest.java b/src/test/java/cd/go/authorization/github/client/StateGeneratorTest.java index 003b794..47406b6 100644 --- a/src/test/java/cd/go/authorization/github/client/StateGeneratorTest.java +++ b/src/test/java/cd/go/authorization/github/client/StateGeneratorTest.java @@ -18,8 +18,7 @@ import org.junit.jupiter.api.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.matchesPattern; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; class StateGeneratorTest { @@ -27,6 +26,6 @@ class StateGeneratorTest { public void shouldGenerateState() { String state = StateGenerator.generate(); assertNotNull(state); - assertThat(state, matchesPattern("[A-Za-z0-9_-]{43}=")); + assertThat(state).matches("[A-Za-z0-9_-]{43}="); } } \ No newline at end of file diff --git a/src/test/java/cd/go/authorization/github/executors/FetchAccessTokenRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/FetchAccessTokenRequestExecutorTest.java index da300a8..1cc8340 100644 --- a/src/test/java/cd/go/authorization/github/executors/FetchAccessTokenRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/FetchAccessTokenRequestExecutorTest.java @@ -26,9 +26,9 @@ import cd.go.authorization.github.requests.FetchAccessTokenRequest; import com.thoughtworks.go.plugin.api.request.GoPluginApiRequest; import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; +import mockwebserver3.MockResponse; +import mockwebserver3.MockWebServer; +import mockwebserver3.RecordedRequest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -37,8 +37,7 @@ import java.util.Collections; import java.util.Map; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; @@ -66,7 +65,7 @@ public void setUp() throws Exception { @AfterEach public void tearDown() throws Exception { - mockWebServer.shutdown(); + mockWebServer.close(); } @Test @@ -83,13 +82,14 @@ public void shouldErrorOutIfAuthConfigIsNotProvided() { @Test public void shouldFetchAccessToken() throws Exception { - mockWebServer.enqueue(new MockResponse() - .setResponseCode(200) - .setBody(new TokenInfo("token-444248275346-5758603453985735", "bearer", "user:email,read:org").toJSON())); + mockWebServer.enqueue(new MockResponse.Builder() + .code(200) + .body(new TokenInfo("token-444248275346-5758603453985735", "bearer", "user:email,read:org").toJSON()) + .build()); when(fetchAccessTokenRequest.authConfigs()).thenReturn(Collections.singletonList(authConfig)); - when(fetchAccessTokenRequest.requestParameters()).thenReturn(Collections.singletonMap("code", "code-received-in-previous-step")); - + when(fetchAccessTokenRequest.authorizationCode()).thenReturn("code-received-in-previous-step"); + when(fetchAccessTokenRequest.codeVerifierEncoded()).thenReturn( "code-verifier"); final GoPluginApiResponse response = executor.execute(); @@ -99,27 +99,30 @@ public void shouldFetchAccessToken() throws Exception { " \"scope\": \"user:email,read:org\"\n" + "}"; - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); JSONAssert.assertEquals(expectedJSON, response.responseBody(), true); RecordedRequest recordedRequest = mockWebServer.takeRequest(); - assertThat(recordedRequest.getPath(), is("/login/oauth/access_token")); - assertThat(recordedRequest.getHeader("Content-Type"), is("application/x-www-form-urlencoded")); - assertThat(recordedRequest.getBody().readUtf8(), is("client_id=my-client&client_secret=my-secret&code=code-received-in-previous-step")); + assertThat(recordedRequest.getRequestLine()).isEqualTo("POST /login/oauth/access_token HTTP/1.1"); + assertThat(recordedRequest.getHeaders().get("Content-Type")).isEqualTo("application/x-www-form-urlencoded"); + assertThat(recordedRequest.getBody().utf8()).isEqualTo("client_id=my-client&client_secret=my-secret&code=code-received-in-previous-step&code_verifier=code-verifier"); verify(fetchAccessTokenRequest).validateState(); } @Test public void fetchAccessToken_shouldErrorOutIfResponseCodeIsNot200() throws Exception { - mockWebServer.enqueue(new MockResponse() - .setResponseCode(404)); + mockWebServer.enqueue(new MockResponse.Builder() + .code(404) + .build()); when(fetchAccessTokenRequest.authConfigs()).thenReturn(Collections.singletonList(authConfig)); - when(fetchAccessTokenRequest.requestParameters()).thenReturn(Collections.singletonMap("code", "code-received-in-previous-step")); + when(fetchAccessTokenRequest.authorizationCode()).thenReturn("code-received-in-previous-step"); + when(fetchAccessTokenRequest.codeVerifierEncoded()).thenReturn( "code-verifier"); + Exception exception = assertThrows(AuthenticationException.class, executor::execute); - assertThat(exception.getMessage(), is("[Get Access Token] Client Error")); + assertThat(exception.getMessage()).isEqualTo("[Get Access Token] Client Error"); verify(fetchAccessTokenRequest).validateState(); } @@ -128,10 +131,12 @@ public void fetchAccessToken_shouldErrorOutIfResponseCodeIsNot200() throws Excep public void fetchAccessToken_shouldErrorIfStateDoesNotMatch() throws Exception { when(fetchAccessTokenRequest.authConfigs()).thenReturn(Collections.singletonList(authConfig)); when(fetchAccessTokenRequest.authSession()).thenReturn(Map.of(Constants.AUTH_SESSION_STATE, "some-value")); - when(fetchAccessTokenRequest.requestParameters()).thenReturn(Collections.singletonMap("code", "code-received-in-previous-step")); + when(fetchAccessTokenRequest.authorizationCode()).thenReturn("code-received-in-previous-step"); + when(fetchAccessTokenRequest.codeVerifierEncoded()).thenReturn( "code-verifier"); + doThrow(new AuthenticationException("error validating state")).when(fetchAccessTokenRequest).validateState(); Exception exception = assertThrows(AuthenticationException.class, executor::execute); - assertThat(exception.getMessage(), is("error validating state")); + assertThat(exception.getMessage()).isEqualTo("error validating state"); } } diff --git a/src/test/java/cd/go/authorization/github/executors/GetAuthConfigMetadataRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/GetAuthConfigMetadataRequestExecutorTest.java index ef1022d..b496b40 100644 --- a/src/test/java/cd/go/authorization/github/executors/GetAuthConfigMetadataRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/GetAuthConfigMetadataRequestExecutorTest.java @@ -25,8 +25,7 @@ import java.util.List; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; public class GetAuthConfigMetadataRequestExecutorTest { @@ -42,7 +41,7 @@ public void shouldSerializeAllFields() throws Exception { public void assertJsonStructure() throws Exception { GoPluginApiResponse response = new GetAuthConfigMetadataRequestExecutor().execute(); - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); String expectedJSON = "[\n" + " {\n" + " \"key\": \"ClientId\",\n" + diff --git a/src/test/java/cd/go/authorization/github/executors/GetAuthConfigViewRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/GetAuthConfigViewRequestExecutorTest.java index e9efa67..dcd4487 100644 --- a/src/test/java/cd/go/authorization/github/executors/GetAuthConfigViewRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/GetAuthConfigViewRequestExecutorTest.java @@ -32,19 +32,18 @@ import java.util.Map; import static java.lang.String.format; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.assertj.core.api.Assertions.assertThat; public class GetAuthConfigViewRequestExecutorTest { @Test public void shouldRenderTheTemplateInJSON() { GoPluginApiResponse response = new GetAuthConfigViewRequestExecutor().execute(); - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); Type type = new TypeToken>() { }.getType(); Map hashSet = new Gson().fromJson(response.responseBody(), type); - assertThat(hashSet, hasEntry("template", Util.readResource("/auth-config.template.html"))); + assertThat(hashSet).containsEntry("template", Util.readResource("/auth-config.template.html")); } @Test @@ -53,19 +52,19 @@ public void allFieldsShouldBePresentInView() { final Document document = Jsoup.parse(template); - for (ProfileMetadata field : MetadataHelper.getMetadata(GitHubConfiguration.class)) { + for (ProfileMetadata field : MetadataHelper.getMetadata(GitHubConfiguration.class)) { final Elements inputFieldForKey = document.getElementsByAttributeValue("ng-model", field.getKey()); int elementCount = field.getKey().equalsIgnoreCase("AuthenticateWith") ? 2 : 1; - assertThat(format("Should have only one ng-model for %s", inputFieldForKey), inputFieldForKey, hasSize(elementCount)); + assertThat(inputFieldForKey).describedAs(format("Should have only one ng-model for %s", inputFieldForKey)).hasSize(elementCount); final Elements spanToShowError = document.getElementsByAttributeValue("ng-class", "{'is-visible': GOINPUTNAME[" + field.getKey() + "].$error.server}"); - assertThat(spanToShowError, hasSize(1)); - assertThat(spanToShowError.attr("ng-show"), is("GOINPUTNAME[" + field.getKey() + "].$error.server")); - assertThat(spanToShowError.text(), is("{{GOINPUTNAME[" + field.getKey() + "].$error.server}}")); + assertThat(spanToShowError).hasSize(1); + assertThat(spanToShowError.attr("ng-show")).isEqualTo("GOINPUTNAME[" + field.getKey() + "].$error.server"); + assertThat(spanToShowError.text()).isEqualTo("{{GOINPUTNAME[" + field.getKey() + "].$error.server}}"); } final Elements inputs = document.select("textarea,input,select"); //AuthenticateWith is coming twice as it is radio button - assertThat(inputs, hasSize(MetadataHelper.getMetadata(GitHubConfiguration.class).size() + 1)); + assertThat(inputs).hasSize(MetadataHelper.getMetadata(GitHubConfiguration.class).size() + 1); } } diff --git a/src/test/java/cd/go/authorization/github/executors/GetAuthorizationServerUrlRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/GetAuthorizationServerUrlRequestExecutorTest.java index cd3f210..9eb29f2 100644 --- a/src/test/java/cd/go/authorization/github/executors/GetAuthorizationServerUrlRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/GetAuthorizationServerUrlRequestExecutorTest.java @@ -16,28 +16,23 @@ package cd.go.authorization.github.executors; -import cd.go.authorization.github.Constants; -import cd.go.authorization.github.client.StateGenerator; +import cd.go.authorization.github.client.GitHubClientBuilder; import cd.go.authorization.github.exceptions.NoAuthorizationConfigurationException; import cd.go.authorization.github.models.AuthConfig; import cd.go.authorization.github.models.AuthenticateWith; import cd.go.authorization.github.models.GitHubConfiguration; import cd.go.authorization.github.requests.GetAuthorizationServerUrlRequest; import com.thoughtworks.go.plugin.api.response.GoPluginApiResponse; -import org.json.JSONObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; import java.util.Collections; -import java.util.Map; +import java.util.List; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; @@ -48,6 +43,8 @@ public class GetAuthorizationServerUrlRequestExecutorTest { private GetAuthorizationServerUrlRequest request; @Mock private AuthConfig authConfig; + @Mock + private GitHubClientBuilder gitHubClientBuilder; private GetAuthorizationServerUrlRequestExecutor executor; @@ -55,7 +52,7 @@ public class GetAuthorizationServerUrlRequestExecutorTest { public void setUp() throws Exception { openMocks(this); - executor = new GetAuthorizationServerUrlRequestExecutor(request); + executor = new GetAuthorizationServerUrlRequestExecutor(request, gitHubClientBuilder); } @Test @@ -67,68 +64,24 @@ public void shouldErrorOutIfAuthConfigIsNotProvided() throws Exception { @Test public void shouldReturnAuthorizationServerUrlForGitHub() throws Exception { - try (MockedStatic stateGenerator = Mockito.mockStatic(StateGenerator.class)) { - stateGenerator.when(StateGenerator::generate).thenReturn(DUMMY_STATE_VALUE); - GitHubConfiguration gitHubConfiguration = new GitHubConfiguration("client-id", "client-secret", AuthenticateWith.GITHUB, null, "example-1"); - - when(authConfig.gitHubConfiguration()).thenReturn(gitHubConfiguration); - when(request.authConfigs()).thenReturn(Collections.singletonList(authConfig)); - when(request.callbackUrl()).thenReturn("call-back-url"); - - final GoPluginApiResponse response = executor.execute(); - - assertThat(response.responseCode(), is(200)); - JSONObject expectedJSON = new JSONObject(Map.of( - "authorization_server_url", "https://github.com/login/oauth/authorize?client_id=client-id&redirect_uri=call-back-url&scope=user%3Aemail&state=some-state-value", - "auth_session", Map.of(Constants.AUTH_SESSION_STATE, DUMMY_STATE_VALUE) - )); - - JSONAssert.assertEquals(expectedJSON, new JSONObject(response.responseBody()), JSONCompareMode.LENIENT); - } - } - - @Test - public void shouldReturnAuthorizationServerUrlWithTrailingSlash() throws Exception { - try (MockedStatic stateGenerator = Mockito.mockStatic(StateGenerator.class)) { - stateGenerator.when(StateGenerator::generate).thenReturn(DUMMY_STATE_VALUE); - GitHubConfiguration gitHubConfiguration = new GitHubConfiguration("client-id", "client-secret", AuthenticateWith.GITHUB_ENTERPRISE, "http://enterprise.url/", "example-1"); - - when(authConfig.gitHubConfiguration()).thenReturn(gitHubConfiguration); - when(request.authConfigs()).thenReturn(Collections.singletonList(authConfig)); - when(request.callbackUrl()).thenReturn("call-back-url"); - - final GoPluginApiResponse response = executor.execute(); - - assertThat(response.responseCode(), is(200)); - JSONObject expectedJSON = new JSONObject(Map.of( - "authorization_server_url", "http://enterprise.url/login/oauth/authorize?client_id=client-id&redirect_uri=call-back-url&scope=user%3Aemail&state=some-state-value", - "auth_session", Map.of(Constants.AUTH_SESSION_STATE, DUMMY_STATE_VALUE) - )); - - JSONAssert.assertEquals(expectedJSON, new JSONObject(response.responseBody()), JSONCompareMode.LENIENT); - } - } - - @Test - public void shouldReturnAuthorizationServerUrlForGitHubEnterprise() throws Exception { - try (MockedStatic stateGenerator = Mockito.mockStatic(StateGenerator.class)) { - stateGenerator.when(StateGenerator::generate).thenReturn(DUMMY_STATE_VALUE); - GitHubConfiguration gitHubConfiguration = new GitHubConfiguration("client-id", "client-secret", AuthenticateWith.GITHUB_ENTERPRISE, "http://enterprise.url", "example-1"); - - when(authConfig.gitHubConfiguration()).thenReturn(gitHubConfiguration); - when(request.authConfigs()).thenReturn(Collections.singletonList(authConfig)); - when(request.callbackUrl()).thenReturn("call-back-url"); - - final GoPluginApiResponse response = executor.execute(); - - assertThat(response.responseCode(), is(200)); - - JSONObject expectedJSON = new JSONObject(Map.of( - "authorization_server_url", "http://enterprise.url/login/oauth/authorize?client_id=client-id&redirect_uri=call-back-url&scope=user%3Aemail&state=some-state-value", - "auth_session", Map.of(Constants.AUTH_SESSION_STATE, DUMMY_STATE_VALUE) - )); - - JSONAssert.assertEquals(expectedJSON, new JSONObject(response.responseBody()), JSONCompareMode.LENIENT); - } + GitHubConfiguration gitHubConfiguration = new GitHubConfiguration("client-id", "client-secret", AuthenticateWith.GITHUB, null, "example-1"); + + when(request.authConfigs()).thenReturn(Collections.singletonList(authConfig)); + when(authConfig.gitHubConfiguration()).thenReturn(gitHubConfiguration); + when(request.callbackUrl()).thenReturn("call-back-url"); + when(gitHubClientBuilder.authorizationServerArgs(gitHubConfiguration, "call-back-url")).thenReturn(List.of("foo-url", "foo-state", "foo-code-verifier")); + + final GoPluginApiResponse response = executor.execute(); + + assertThat(response.responseCode()).isEqualTo(200); + JSONAssert.assertEquals(""" + { + "authorization_server_url": "foo-url", + "auth_session" : { + "oauth2_state": "foo-state", + "oauth2_code_verifier_encoded": "foo-code-verifier" + } + } + """, response.responseBody(), JSONCompareMode.NON_EXTENSIBLE); } } diff --git a/src/test/java/cd/go/authorization/github/executors/GetPluginIconRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/GetPluginIconRequestExecutorTest.java index 0d5ec34..593b4fd 100644 --- a/src/test/java/cd/go/authorization/github/executors/GetPluginIconRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/GetPluginIconRequestExecutorTest.java @@ -24,8 +24,7 @@ import java.util.HashMap; import static cd.go.authorization.github.utils.Util.GSON; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class GetPluginIconRequestExecutorTest { @@ -33,8 +32,8 @@ public class GetPluginIconRequestExecutorTest { public void rendersIconInBase64() throws Exception { GoPluginApiResponse response = new GetPluginIconRequestExecutor().execute(); HashMap hashMap = GSON.fromJson(response.responseBody(), HashMap.class); - assertThat(hashMap.size(), is(2)); - assertThat(hashMap.get("content_type"), is("image/svg+xml")); - assertThat(Util.readResourceBytes("/logo.svg"), is(Base64.getDecoder().decode(hashMap.get("data")))); + assertThat(hashMap.size()).isEqualTo(2); + assertThat(hashMap.get("content_type")).isEqualTo("image/svg+xml"); + assertThat(Util.readResourceBytes("/logo.svg")).isEqualTo(Base64.getDecoder().decode(hashMap.get("data"))); } } diff --git a/src/test/java/cd/go/authorization/github/executors/GetRoleConfigMetadataRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/GetRoleConfigMetadataRequestExecutorTest.java index 1d30cea..1ac6d62 100644 --- a/src/test/java/cd/go/authorization/github/executors/GetRoleConfigMetadataRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/GetRoleConfigMetadataRequestExecutorTest.java @@ -20,8 +20,7 @@ import org.junit.jupiter.api.Test; import org.skyscreamer.jsonassert.JSONAssert; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; +import static org.assertj.core.api.Assertions.assertThat; public class GetRoleConfigMetadataRequestExecutorTest { @@ -29,7 +28,7 @@ public class GetRoleConfigMetadataRequestExecutorTest { public void shouldReturnRoleConfigMetadata() throws Exception { final GoPluginApiResponse response = new GetRoleConfigMetadataRequestExecutor().execute(); - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); final String expectedRoleConfigMetadata = "[\n" + " {\n" + diff --git a/src/test/java/cd/go/authorization/github/executors/GetRoleConfigViewRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/GetRoleConfigViewRequestExecutorTest.java index 8148456..eb98955 100644 --- a/src/test/java/cd/go/authorization/github/executors/GetRoleConfigViewRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/GetRoleConfigViewRequestExecutorTest.java @@ -31,8 +31,7 @@ import java.util.List; import java.util.Map; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.assertj.core.api.Assertions.assertThat; public class GetRoleConfigViewRequestExecutorTest { @@ -44,23 +43,25 @@ public void allFieldsShouldBePresentInView() throws Exception { final List metadataList = MetadataHelper.getMetadata(GitHubRoleConfiguration.class); for (ProfileMetadata field : metadataList) { final Elements inputFieldForKey = document.getElementsByAttributeValue("ng-model", field.getKey()); - assertThat(inputFieldForKey, hasSize(1)); + assertThat(inputFieldForKey).hasSize(1); final Elements spanToShowError = document.getElementsByAttributeValue("ng-class", "{'is-visible': GOINPUTNAME[" + field.getKey() + "].$error.server}"); - assertThat(spanToShowError, hasSize(1)); - assertThat(spanToShowError.attr("ng-show"), is("GOINPUTNAME[" + field.getKey() + "].$error.server")); - assertThat(spanToShowError.text(), is("{{GOINPUTNAME[" + field.getKey() + "].$error.server}}")); + assertThat(spanToShowError).hasSize(1); + assertThat(spanToShowError.attr("ng-show")).isEqualTo("GOINPUTNAME[" + field.getKey() + "].$error.server"); + assertThat(spanToShowError.text()).isEqualTo("{{GOINPUTNAME[" + field.getKey() + "].$error.server}}"); } final Elements inputs = document.select("textarea,input,select"); - assertThat("should contains only inputs that defined in GitHubRoleConfiguration.java",inputs, hasSize(metadataList.size())); + assertThat(inputs) + .describedAs("should contains only inputs that defined in GitHubRoleConfiguration.java") + .hasSize(metadataList.size()); } @Test public void shouldRenderTheTemplateInJSON() throws Exception { GoPluginApiResponse response = new GetRoleConfigViewRequestExecutor().execute(); - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); Map hashSet = new Gson().fromJson(response.responseBody(), HashMap.class); - assertThat(hashSet, hasEntry("template", Util.readResource("/role-config.template.html"))); + assertThat(hashSet).containsEntry("template", Util.readResource("/role-config.template.html")); } } diff --git a/src/test/java/cd/go/authorization/github/executors/GetRolesExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/GetRolesExecutorTest.java index 0b2a8f9..a43a001 100644 --- a/src/test/java/cd/go/authorization/github/executors/GetRolesExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/GetRolesExecutorTest.java @@ -36,8 +36,7 @@ import java.util.List; import java.util.stream.Collectors; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; public class GetRolesExecutorTest { @@ -66,7 +65,7 @@ public void shouldReturnEmptyResponseIfThereAreNoRolesProvidedFromRequest() thro GoPluginApiResponse response = executor.execute(); - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); JSONAssert.assertEquals("[]", response.responseBody(), true); verifyNoMoreInteractions(authorizer); verifyNoMoreInteractions(clientBuilder); @@ -84,7 +83,7 @@ public void shouldReturnSuccessResponseWithRoles() throws IOException, JSONExcep GoPluginApiResponse response = executor.execute(); - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); JSONAssert.assertEquals("[\"blackbird\",\"super-admin\"]", response.responseBody(), true); InOrder inOrder = inOrder(clientBuilder, gitHub, authorizer); @@ -103,7 +102,7 @@ public void shouldReturnErrorResponseWhenUserWithProvidedUsernameNotFound() thro GoPluginApiResponse response = executor.execute(); - assertThat(response.responseCode(), is(500)); + assertThat(response.responseCode()).isEqualTo(500); InOrder inOrder = inOrder(clientBuilder, gitHub); inOrder.verify(clientBuilder).from(request.getAuthConfig().gitHubConfiguration()); diff --git a/src/test/java/cd/go/authorization/github/executors/SearchUsersRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/SearchUsersRequestExecutorTest.java index 0475396..7b5be2c 100644 --- a/src/test/java/cd/go/authorization/github/executors/SearchUsersRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/SearchUsersRequestExecutorTest.java @@ -13,8 +13,7 @@ import java.util.Collections; import static java.util.Collections.singletonList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -60,7 +59,7 @@ public void shouldSearchForUsersThatMatchTheSearchTerm() throws Exception { GoPluginApiResponse response = executor.execute(); - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); JSONAssert.assertEquals("[{\"username\":\"tom01\", \"display_name\": \"Tom NoLastname\", \"email\": \"tom@gocd.org\"}]", response.responseBody(), true); } @@ -71,7 +70,7 @@ public void shouldNotPerformSearchIfAuthConfigsIsEmpty() throws Exception { GoPluginApiResponse response = executor.execute(); verify(clientBuilder, never()).from(any()); - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); JSONAssert.assertEquals("[]", response.responseBody(), false); } } diff --git a/src/test/java/cd/go/authorization/github/executors/UserAuthenticationRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/UserAuthenticationRequestExecutorTest.java index 560e07f..6e46d69 100644 --- a/src/test/java/cd/go/authorization/github/executors/UserAuthenticationRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/UserAuthenticationRequestExecutorTest.java @@ -30,8 +30,7 @@ import java.util.Collections; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; @@ -90,7 +89,7 @@ public void shouldAuthenticateUser() throws Exception { " }\n" + "}"; - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); JSONAssert.assertEquals(expectedJSON, response.responseBody(), true); } @@ -121,7 +120,7 @@ public void shouldAuthorizeUser() throws Exception { " }\n" + "}"; - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); JSONAssert.assertEquals(expectedJSON, response.responseBody(), true); } } diff --git a/src/test/java/cd/go/authorization/github/executors/ValidateUserRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/ValidateUserRequestExecutorTest.java index 8832618..9340a4f 100644 --- a/src/test/java/cd/go/authorization/github/executors/ValidateUserRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/ValidateUserRequestExecutorTest.java @@ -27,8 +27,7 @@ import org.kohsuke.github.GitHub; import org.skyscreamer.jsonassert.JSONAssert; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -60,7 +59,7 @@ public void shouldReturnSuccessResponseWhenUserIsAValidUser() throws Exception { GoPluginApiResponse response = executor.execute(); - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); JSONAssert.assertEquals("", response.responseBody(), true); } @@ -74,6 +73,6 @@ public void shouldReturnErrorResponseWhenUserIsNotAValidUser() throws Exception GoPluginApiResponse response = executor.execute(); - assertThat(response.responseCode(), is(500)); + assertThat(response.responseCode()).isEqualTo(500); } } diff --git a/src/test/java/cd/go/authorization/github/executors/VerifyConnectionRequestExecutorTest.java b/src/test/java/cd/go/authorization/github/executors/VerifyConnectionRequestExecutorTest.java index 21b0428..a34e24b 100644 --- a/src/test/java/cd/go/authorization/github/executors/VerifyConnectionRequestExecutorTest.java +++ b/src/test/java/cd/go/authorization/github/executors/VerifyConnectionRequestExecutorTest.java @@ -25,8 +25,7 @@ import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -68,7 +67,7 @@ public void shouldReturnValidationFailedStatusForInvalidAuthConfig() throws Exce " \"status\": \"validation-failed\"\n" + "}"; - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); JSONAssert.assertEquals(expectedJSON, response.responseBody(), JSONCompareMode.NON_EXTENSIBLE); } @@ -85,7 +84,7 @@ public void shouldReturnSuccessResponseOnSuccessfulVerification() throws Excepti " \"status\": \"success\"\n" + "}"; - assertThat(response.responseCode(), is(200)); + assertThat(response.responseCode()).isEqualTo(200); JSONAssert.assertEquals(expectedJSON, response.responseBody(), JSONCompareMode.NON_EXTENSIBLE); } } diff --git a/src/test/java/cd/go/authorization/github/models/GitHubConfigurationTest.java b/src/test/java/cd/go/authorization/github/models/GitHubConfigurationTest.java index a3618ec..6b09662 100644 --- a/src/test/java/cd/go/authorization/github/models/GitHubConfigurationTest.java +++ b/src/test/java/cd/go/authorization/github/models/GitHubConfigurationTest.java @@ -21,10 +21,7 @@ import java.util.Map; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.hasEntry; +import static org.assertj.core.api.Assertions.assertThat; public class GitHubConfigurationTest { @@ -40,12 +37,12 @@ public void shouldDeserializeGitHubConfiguration() { " \"AuthorizeUsing\": \"PersonalAccessToken\"\n" + "}"); - assertThat(gitHubConfiguration.clientId(), is("client-id")); - assertThat(gitHubConfiguration.clientSecret(), is("client-secret")); - assertThat(gitHubConfiguration.organizationsAllowed(), contains("example-1", "example-2")); - assertThat(gitHubConfiguration.gitHubEnterpriseUrl(), is("https://enterprise.url")); - assertThat(gitHubConfiguration.authenticateWith(), is(AuthenticateWith.GITHUB_ENTERPRISE)); - assertThat(gitHubConfiguration.personalAccessToken(), is("personal-access-token")); + assertThat(gitHubConfiguration.clientId()).isEqualTo("client-id"); + assertThat(gitHubConfiguration.clientSecret()).isEqualTo("client-secret"); + assertThat(gitHubConfiguration.organizationsAllowed()).contains("example-1", "example-2"); + assertThat(gitHubConfiguration.gitHubEnterpriseUrl()).isEqualTo("https://enterprise.url"); + assertThat(gitHubConfiguration.authenticateWith()).isEqualTo(AuthenticateWith.GITHUB_ENTERPRISE); + assertThat(gitHubConfiguration.personalAccessToken()).isEqualTo("personal-access-token"); } @Test @@ -71,10 +68,10 @@ public void shouldConvertConfigurationToProperties() { final Map properties = gitHubConfiguration.toProperties(); - assertThat(properties, hasEntry("ClientId", "client-id")); - assertThat(properties, hasEntry("ClientSecret", "client-secret")); - assertThat(properties, hasEntry("AllowedOrganizations", "example-1")); - assertThat(properties, hasEntry("AuthenticateWith", "GitHubEnterprise")); - assertThat(properties, hasEntry("GitHubEnterpriseUrl", "http://enterprise.url")); + assertThat(properties).containsEntry("ClientId", "client-id"); + assertThat(properties).containsEntry("ClientSecret", "client-secret"); + assertThat(properties).containsEntry("AllowedOrganizations", "example-1"); + assertThat(properties).containsEntry("AuthenticateWith", "GitHubEnterprise"); + assertThat(properties).containsEntry("GitHubEnterpriseUrl", "http://enterprise.url"); } } diff --git a/src/test/java/cd/go/authorization/github/models/GitHubRoleConfigurationTest.java b/src/test/java/cd/go/authorization/github/models/GitHubRoleConfigurationTest.java index 5f3b20a..ff78840 100644 --- a/src/test/java/cd/go/authorization/github/models/GitHubRoleConfigurationTest.java +++ b/src/test/java/cd/go/authorization/github/models/GitHubRoleConfigurationTest.java @@ -18,10 +18,9 @@ import org.junit.jupiter.api.Test; -import static java.util.Arrays.asList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.hasEntry; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; public class GitHubRoleConfigurationTest { @@ -35,9 +34,9 @@ public void shouldDeserializeFromJson() throws Exception { final GitHubRoleConfiguration gitHubRoleConfiguration = GitHubRoleConfiguration.fromJSON(json); - assertThat(gitHubRoleConfiguration.users(), contains("bob", "alice")); - assertThat(gitHubRoleConfiguration.organizations(), contains("organizationfoo", "organizationbar")); - assertThat(gitHubRoleConfiguration.teams(), hasEntry("organizationfoo", asList("teamx", "teamy"))); - assertThat(gitHubRoleConfiguration.teams(), hasEntry("organizationbar", asList("teama", "teamb"))); + assertThat(gitHubRoleConfiguration.users()).contains("bob", "alice"); + assertThat(gitHubRoleConfiguration.organizations()).contains("organizationfoo", "organizationbar"); + assertThat(gitHubRoleConfiguration.teams()).containsEntry("organizationfoo", List.of("teamx", "teamy")); + assertThat(gitHubRoleConfiguration.teams()).containsEntry("organizationbar", List.of("teama", "teamb")); } } diff --git a/src/test/java/cd/go/authorization/github/requests/AuthConfigValidateRequestTest.java b/src/test/java/cd/go/authorization/github/requests/AuthConfigValidateRequestTest.java index 1d84c1b..6cffb58 100644 --- a/src/test/java/cd/go/authorization/github/requests/AuthConfigValidateRequestTest.java +++ b/src/test/java/cd/go/authorization/github/requests/AuthConfigValidateRequestTest.java @@ -22,8 +22,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; @@ -49,7 +48,7 @@ public void shouldDeserializeGoPluginApiRequestToAuthConfigValidateRequest() thr final AuthConfigValidateRequest request = AuthConfigValidateRequest.from(apiRequest); final GitHubConfiguration gitHubConfiguration = request.githubConfiguration(); - assertThat(gitHubConfiguration.clientId(), is("client-id")); - assertThat(gitHubConfiguration.clientSecret(), is("client-secret")); + assertThat(gitHubConfiguration.clientId()).isEqualTo("client-id"); + assertThat(gitHubConfiguration.clientSecret()).isEqualTo("client-secret"); } } diff --git a/src/test/java/cd/go/authorization/github/requests/FetchAccessTokenRequestTest.java b/src/test/java/cd/go/authorization/github/requests/FetchAccessTokenRequestTest.java index f816c94..81c0664 100644 --- a/src/test/java/cd/go/authorization/github/requests/FetchAccessTokenRequestTest.java +++ b/src/test/java/cd/go/authorization/github/requests/FetchAccessTokenRequestTest.java @@ -24,82 +24,86 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; +import java.util.Collections; import java.util.Map; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.nullValue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; public class FetchAccessTokenRequestTest { - public static final String WITH_NO_SESSION_RESPONSE = "{\n" + - " \"auth_configs\": [\n" + - " {\n" + - " \"id\": \"github-auth-config\",\n" + - " \"configuration\": {\n" + - " \"GoServerUrl\": \"https://your.go.server.url\",\n" + - " \"ClientId\": \"client-id\",\n" + - " \"ClientSecret\": \"client-secret\"\n" + - " }\n" + - " }\n" + - " ]\n" + - "}"; - - public static final String WITH_SESSION_STATE_RESPONSE = "{\n" + - " \"auth_configs\": [\n" + - " {\n" + - " \"id\": \"github-auth-config\",\n" + - " \"configuration\": {\n" + - " \"GoServerUrl\": \"https://your.go.server.url\",\n" + - " \"ClientId\": \"client-id\",\n" + - " \"ClientSecret\": \"client-secret\"\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"auth_session\": {" + - " \"oauth2_state\": \"some-state-value\"" + - " }\n" + - "}"; - public static final String WITH_EMPTY_SESSION_RESPONSE = "{\n" + - " \"auth_configs\": [\n" + - " {\n" + - " \"id\": \"github-auth-config\",\n" + - " \"configuration\": {\n" + - " \"GoServerUrl\": \"https://your.go.server.url\",\n" + - " \"ClientId\": \"client-id\",\n" + - " \"ClientSecret\": \"client-secret\"\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"auth_session\": {}\n" + - "}"; + public static final String WITH_NO_SESSION_RESPONSE = """ + { + "auth_configs": [ + { + "id": "github-auth-config", + "configuration": { + "GoServerUrl": "https://your.go.server.url", + "ClientId": "client-id", + "ClientSecret": "client-secret" + } + } + ] + }"""; + + public static final String WITH_SESSION_STATE_RESPONSE = """ + { + "auth_configs": [ + { + "id": "github-auth-config", + "configuration": { + "GoServerUrl": "https://your.go.server.url", + "ClientId": "client-id", + "ClientSecret": "client-secret" + } + } + ], + "auth_session": {\ + "oauth2_state": "some-state-value"\ + } + }"""; + public static final String WITH_NORMAL_SESSION_RESPONSE = """ + { + "auth_configs": [ + { + "id": "github-auth-config", + "configuration": { + "GoServerUrl": "https://your.go.server.url", + "ClientId": "client-id", + "ClientSecret": "client-secret" + } + } + ], + "auth_session": { + "oauth2_code_verifier_encoded": "code-verifier" + } + }"""; @Mock private GoPluginApiRequest apiRequest; @BeforeEach public void setUp() throws Exception { openMocks(this); + when(apiRequest.requestParameters()).thenReturn(Map.of("code", "authorization-code")); + when(apiRequest.requestBody()).thenReturn(WITH_NORMAL_SESSION_RESPONSE); } @Test public void shouldDeserializeGoPluginApiRequestToFetchAccessTokenRequest() throws Exception { - when(apiRequest.requestBody()).thenReturn(WITH_NO_SESSION_RESPONSE); - final FetchAccessTokenRequest request = FetchAccessTokenRequest.from(apiRequest); - assertThat(request.authConfigs(), hasSize(1)); - assertThat(request.authSession(), nullValue()); - assertThat(request.executor(), instanceOf(FetchAccessTokenRequestExecutor.class)); + assertThat(request.authConfigs()).hasSize(1); + assertThat(request.executor()).isInstanceOf(FetchAccessTokenRequestExecutor.class); final AuthConfig authConfig = request.authConfigs().get(0); - assertThat(authConfig.getId(), is("github-auth-config")); - assertThat(authConfig.gitHubConfiguration().clientId(), is("client-id")); - assertThat(authConfig.gitHubConfiguration().clientSecret(), is("client-secret")); + assertThat(authConfig.getId()).isEqualTo("github-auth-config"); + assertThat(authConfig.gitHubConfiguration().clientId()).isEqualTo("client-id"); + assertThat(authConfig.gitHubConfiguration().clientSecret()).isEqualTo("client-secret"); + + assertThat(request.authSession()).containsOnly(Map.entry("oauth2_code_verifier_encoded", "code-verifier")); } @Test @@ -123,17 +127,17 @@ public void validateStateShouldFailWhenRequestParamIsMissing() { final FetchAccessTokenRequest request = FetchAccessTokenRequest.from(apiRequest); Exception error = assertThrows(NullPointerException.class, request::validateState); - assertThat(error.getMessage(), is("OAuth2 state is missing from redirect")); + assertThat(error.getMessage()).isEqualTo("OAuth2 state is missing from redirect"); } @Test public void validateStateShouldFailWhenSessionStateIsMissing() { - when(apiRequest.requestBody()).thenReturn(WITH_EMPTY_SESSION_RESPONSE); + when(apiRequest.requestBody()).thenReturn(WITH_NORMAL_SESSION_RESPONSE); when(apiRequest.requestParameters()).thenReturn(Map.of("state", "some-state-value")); final FetchAccessTokenRequest request = FetchAccessTokenRequest.from(apiRequest); Exception error = assertThrows(NullPointerException.class, request::validateState); - assertThat(error.getMessage(), is("OAuth2 state is missing from session")); + assertThat(error.getMessage()).isEqualTo("OAuth2 state is missing from session"); } @Test @@ -143,6 +147,48 @@ public void validateStateShouldFailWhenSessionStateDoesntMatch() { final FetchAccessTokenRequest request = FetchAccessTokenRequest.from(apiRequest); Exception error = assertThrows(AuthenticationException.class, request::validateState); - assertThat(error.getMessage(), is("Redirected OAuth2 state from GitHub did not match previously generated state stored in session")); + assertThat(error.getMessage()).isEqualTo("Redirected OAuth2 state from GitHub did not match previously generated state stored in session"); + } + + @Test + public void shouldValidateAuthorizationCode() { + assertThat(FetchAccessTokenRequest.from(apiRequest).authorizationCode()) + .isEqualTo("authorization-code"); + } + + @Test + public void shouldFailIfAuthorizationNotFound() { + when(apiRequest.requestParameters()).thenReturn(Collections.emptyMap()); + assertThatThrownBy(() -> FetchAccessTokenRequest.from(apiRequest).authorizationCode()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[Get Access Token] Expecting `code` in request params, but not received."); + } + + @Test + public void shouldValidateCodeVerifier() { + assertThat(FetchAccessTokenRequest.from(apiRequest).codeVerifierEncoded()) + .isEqualTo("code-verifier"); + } + + @Test + public void shouldFailIfCodeVerifierNotFound() { + when(apiRequest.requestBody()).thenReturn(""" + { + "auth_configs": [ + { + "id": "github-auth-config", + "configuration": { + "GoServerUrl": "https://your.go.server.url", + "ClientId": "client-id", + "ClientSecret": "client-secret" + } + } + ], + "auth_session": {} + } + """); + assertThatThrownBy(() -> FetchAccessTokenRequest.from(apiRequest).codeVerifierEncoded()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[Get Access Token] OAuth2 code verifier is missing from session"); } } diff --git a/src/test/java/cd/go/authorization/github/requests/GetAuthorizationServerUrlRequestTest.java b/src/test/java/cd/go/authorization/github/requests/GetAuthorizationServerUrlRequestTest.java index a88f3ea..1cf8801 100644 --- a/src/test/java/cd/go/authorization/github/requests/GetAuthorizationServerUrlRequestTest.java +++ b/src/test/java/cd/go/authorization/github/requests/GetAuthorizationServerUrlRequestTest.java @@ -23,10 +23,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; @@ -59,16 +56,16 @@ public void shouldDeserializeGoPluginApiRequestToGetAuthorizationServerUrlReques final GetAuthorizationServerUrlRequest request = GetAuthorizationServerUrlRequest.from(apiRequest); - assertThat(request.authConfigs(), hasSize(1)); - assertThat(request.executor(), instanceOf(GetAuthorizationServerUrlRequestExecutor.class)); + assertThat(request.authConfigs()).hasSize(1); + assertThat(request.executor()).isInstanceOf(GetAuthorizationServerUrlRequestExecutor.class); final AuthConfig authConfig = request.authConfigs().get(0); - assertThat(request.callbackUrl(), is("https://redirect.url")); + assertThat(request.callbackUrl()).isEqualTo("https://redirect.url"); - assertThat(authConfig.getId(), is("github-config")); - assertThat(authConfig.gitHubConfiguration().clientId(), is("client-id")); - assertThat(authConfig.gitHubConfiguration().clientSecret(), is("client-secret")); + assertThat(authConfig.getId()).isEqualTo("github-config"); + assertThat(authConfig.gitHubConfiguration().clientId()).isEqualTo("client-id"); + assertThat(authConfig.gitHubConfiguration().clientSecret()).isEqualTo("client-secret"); } } diff --git a/src/test/java/cd/go/authorization/github/requests/GetRolesRequestTest.java b/src/test/java/cd/go/authorization/github/requests/GetRolesRequestTest.java index 28db019..2ff19f9 100644 --- a/src/test/java/cd/go/authorization/github/requests/GetRolesRequestTest.java +++ b/src/test/java/cd/go/authorization/github/requests/GetRolesRequestTest.java @@ -20,9 +20,7 @@ import com.thoughtworks.go.plugin.api.request.GoPluginApiRequest; import org.junit.jupiter.api.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -49,9 +47,9 @@ public void shouldParseRequest() { GetRolesRequest request = (GetRolesRequest) GetRolesRequest.from(apiRequest); - assertThat(request.getUsername(), is("bob")); - assertThat(request.getAuthConfig().getId(), is("GitHub")); - assertThat(request.getRoles(), hasSize(0)); + assertThat(request.getUsername()).isEqualTo("bob"); + assertThat(request.getAuthConfig().getId()).isEqualTo("GitHub"); + assertThat(request.getRoles()).hasSize(0); } @Test @@ -75,6 +73,6 @@ public void shouldReturnValidExecutor() { "}"); GetRolesRequest request = (GetRolesRequest) GetRolesRequest.from(apiRequest); - assertThat(request.executor() instanceof GetRolesExecutor, is(true)); + assertThat(request.executor() instanceof GetRolesExecutor).isEqualTo(true); } } \ No newline at end of file diff --git a/src/test/java/cd/go/authorization/github/requests/RoleConfigValidateRequestTest.java b/src/test/java/cd/go/authorization/github/requests/RoleConfigValidateRequestTest.java index 6e4c160..b2d49ba 100644 --- a/src/test/java/cd/go/authorization/github/requests/RoleConfigValidateRequestTest.java +++ b/src/test/java/cd/go/authorization/github/requests/RoleConfigValidateRequestTest.java @@ -22,10 +22,9 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; -import static java.util.Arrays.asList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.hasEntry; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; @@ -52,8 +51,8 @@ public void shouldDeserializeGoPluginApiRequestToRoleConfigValidateRequest() thr final RoleConfigValidateRequest request = RoleConfigValidateRequest.from(apiRequest); final GitHubRoleConfiguration gitHubRoleConfiguration = request.gitHubRoleConfiguration(); - assertThat(gitHubRoleConfiguration.organizations(), contains("org1", "org2", "org3")); - assertThat(gitHubRoleConfiguration.teams(), hasEntry("org4", asList("team-1", "team-2"))); - assertThat(gitHubRoleConfiguration.users(), contains("bob", "alice")); + assertThat(gitHubRoleConfiguration.organizations()).contains("org1", "org2", "org3"); + assertThat(gitHubRoleConfiguration.teams()).containsEntry("org4", List.of("team-1", "team-2")); + assertThat(gitHubRoleConfiguration.users()).contains("bob", "alice"); } } diff --git a/src/test/java/cd/go/authorization/github/requests/SearchUsersRequestTest.java b/src/test/java/cd/go/authorization/github/requests/SearchUsersRequestTest.java index c578437..48a7c15 100644 --- a/src/test/java/cd/go/authorization/github/requests/SearchUsersRequestTest.java +++ b/src/test/java/cd/go/authorization/github/requests/SearchUsersRequestTest.java @@ -20,9 +20,7 @@ import com.thoughtworks.go.plugin.api.request.GoPluginApiRequest; import org.junit.jupiter.api.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -47,8 +45,8 @@ public void shouldParseRequest() { SearchUsersRequest searchUsersRequest = SearchUsersRequest.from(request); - assertThat(searchUsersRequest.getSearchTerm(), is("tom")); - assertThat(searchUsersRequest.getAuthConfigs(), hasSize(1)); + assertThat(searchUsersRequest.getSearchTerm()).isEqualTo("tom"); + assertThat(searchUsersRequest.getAuthConfigs()).hasSize(1); } @Test @@ -58,6 +56,6 @@ public void shouldReturnSearchUsersRequestExecutor() { Request request = SearchUsersRequest.from(apiRequest); - assertThat(request.executor() instanceof SearchUsersRequestExecutor, is(true)); + assertThat(request.executor()).isInstanceOf(SearchUsersRequestExecutor.class); } } \ No newline at end of file diff --git a/src/test/java/cd/go/authorization/github/requests/UserAuthenticationRequestTest.java b/src/test/java/cd/go/authorization/github/requests/UserAuthenticationRequestTest.java index d3a79f0..1690247 100644 --- a/src/test/java/cd/go/authorization/github/requests/UserAuthenticationRequestTest.java +++ b/src/test/java/cd/go/authorization/github/requests/UserAuthenticationRequestTest.java @@ -25,11 +25,9 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; -import static java.util.Arrays.asList; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; @@ -79,9 +77,9 @@ public void shouldDeserializeGoPluginApiRequestToUserAuthenticationRequest() thr final UserAuthenticationRequest request = UserAuthenticationRequest.from(this.request); - assertThat(request.authConfigs(), hasSize(1)); - assertThat(request.roles(), hasSize(1)); - assertThat(request.executor(), instanceOf(UserAuthenticationRequestExecutor.class)); + assertThat(request.authConfigs()).hasSize(1); + assertThat(request.roles()).hasSize(1); + assertThat(request.executor()).isInstanceOf(UserAuthenticationRequestExecutor.class); assertAuthConfig(request.authConfigs().get(0)); assertTokenInfo(request.tokenInfo()); @@ -89,24 +87,24 @@ public void shouldDeserializeGoPluginApiRequestToUserAuthenticationRequest() thr } private void assertRole(Role role) { - assertThat(role.name(), is("admin")); - assertThat(role.authConfigId(), is("github-config")); - assertThat(role.roleConfiguration().users(), contains("bob", "alice")); - assertThat(role.roleConfiguration().organizations(), contains("organizationfoo", "organizationbar")); - assertThat(role.roleConfiguration().teams(), hasEntry("organizationfoo", asList("teamx", "teamy"))); - assertThat(role.roleConfiguration().teams(), hasEntry("organizationbar", asList("teama", "teamb"))); + assertThat(role.name()).isEqualTo("admin"); + assertThat(role.authConfigId()).isEqualTo("github-config"); + assertThat(role.roleConfiguration().users()).contains("bob", "alice"); + assertThat(role.roleConfiguration().organizations()).contains("organizationfoo", "organizationbar"); + assertThat(role.roleConfiguration().teams()).containsEntry("organizationfoo", List.of("teamx", "teamy")); + assertThat(role.roleConfiguration().teams()).containsEntry("organizationbar", List.of("teama", "teamb")); } private void assertTokenInfo(TokenInfo tokenInfo) { - assertThat(tokenInfo.accessToken(), is("access-token")); - assertThat(tokenInfo.tokenType(), is("token")); - assertThat(tokenInfo.scope(), is("profile")); + assertThat(tokenInfo.accessToken()).isEqualTo("access-token"); + assertThat(tokenInfo.tokenType()).isEqualTo("token"); + assertThat(tokenInfo.scope()).isEqualTo("profile"); } private void assertAuthConfig(AuthConfig authConfig) { - assertThat(authConfig.getId(), is("github-config")); - assertThat(authConfig.gitHubConfiguration().clientId(), is("client-id")); - assertThat(authConfig.gitHubConfiguration().clientSecret(), is("client-secret")); - assertThat(authConfig.gitHubConfiguration().organizationsAllowed(), contains("org1","org2")); + assertThat(authConfig.getId()).isEqualTo("github-config"); + assertThat(authConfig.gitHubConfiguration().clientId()).isEqualTo("client-id"); + assertThat(authConfig.gitHubConfiguration().clientSecret()).isEqualTo("client-secret"); + assertThat(authConfig.gitHubConfiguration().organizationsAllowed()).contains("org1","org2"); } } diff --git a/src/test/java/cd/go/authorization/github/requests/ValidateUserRequestTest.java b/src/test/java/cd/go/authorization/github/requests/ValidateUserRequestTest.java index 645d199..97e5008 100644 --- a/src/test/java/cd/go/authorization/github/requests/ValidateUserRequestTest.java +++ b/src/test/java/cd/go/authorization/github/requests/ValidateUserRequestTest.java @@ -20,8 +20,7 @@ import com.thoughtworks.go.plugin.api.request.GoPluginApiRequest; import org.junit.jupiter.api.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -48,8 +47,8 @@ public void shouldParseGoAPIRequestToValidateUserRequest() { ValidateUserRequest request = (ValidateUserRequest) ValidateUserRequest.from(apiRequest); - assertThat(request.getUsername(), is("bob")); - assertThat(request.getAuthConfig().getId(), is("GitHub")); + assertThat(request.getUsername()).isEqualTo("bob"); + assertThat(request.getAuthConfig().getId()).isEqualTo("GitHub"); } @Test @@ -73,6 +72,6 @@ public void shouldReturnValidateUserExecutor() { Request request = ValidateUserRequest.from(apiRequest); - assertThat(request.executor() instanceof ValidateUserRequestExecutor, is(true)); + assertThat(request.executor() instanceof ValidateUserRequestExecutor).isEqualTo(true); } } \ No newline at end of file diff --git a/src/test/java/cd/go/authorization/github/requests/VerifyConnectionRequestTest.java b/src/test/java/cd/go/authorization/github/requests/VerifyConnectionRequestTest.java index f8e464a..abebda1 100644 --- a/src/test/java/cd/go/authorization/github/requests/VerifyConnectionRequestTest.java +++ b/src/test/java/cd/go/authorization/github/requests/VerifyConnectionRequestTest.java @@ -24,10 +24,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; @@ -55,13 +52,13 @@ public void shouldDeserializeGoPluginApiRequestToVerifyConnectionRequest() throw final VerifyConnectionRequest request = VerifyConnectionRequest.from(apiRequest); final GitHubConfiguration gitHubConfiguration = request.githubConfiguration(); - assertThat(request.executor(), instanceOf(VerifyConnectionRequestExecutor.class)); + assertThat(request.executor()).isInstanceOf(VerifyConnectionRequestExecutor.class); - assertThat(gitHubConfiguration.clientId(), is("client-id")); - assertThat(gitHubConfiguration.clientSecret(), is("client-secret")); - assertThat(gitHubConfiguration.authenticateWith(), is(AuthenticateWith.GITHUB_ENTERPRISE)); - assertThat(gitHubConfiguration.gitHubEnterpriseUrl(), is("my-enterprise-url")); - assertThat(gitHubConfiguration.organizationsAllowed(), contains("foo", "bar")); + assertThat(gitHubConfiguration.clientId()).isEqualTo("client-id"); + assertThat(gitHubConfiguration.clientSecret()).isEqualTo("client-secret"); + assertThat(gitHubConfiguration.authenticateWith()).isEqualTo(AuthenticateWith.GITHUB_ENTERPRISE); + assertThat(gitHubConfiguration.gitHubEnterpriseUrl()).isEqualTo("my-enterprise-url"); + assertThat(gitHubConfiguration.organizationsAllowed()).contains("foo", "bar"); } }