diff --git a/pom.xml b/pom.xml index 39a8245..fa76479 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.vaulttec.sonarqube.auth.oidc sonar-auth-oidc-plugin - 2.1.2-SNAPSHOT + 3.0.0-SNAPSHOT sonar-plugin OpenID Connect Authentication for SonarQube OpenID Connect Authentication for SonarQube @@ -19,11 +19,13 @@ - 1.8 + 11 + 11 + 11 UTF-8 org.vaulttec.sonarqube.auth.oidc.AuthOidcPlugin authoidc - 7.4 + 11.0.0.2664 10.14.2 9.31 @@ -128,8 +130,9 @@ + - org.sonarsource.sonarqube + org.sonarsource.api.plugin sonar-plugin-api ${sonar-plugin-api.version} provided diff --git a/src/main/java/org/vaulttec/sonarqube/auth/oidc/AutoLoginFilter.java b/src/main/java/org/vaulttec/sonarqube/auth/oidc/AutoLoginFilter.java index d8ef6f8..e4f3894 100644 --- a/src/main/java/org/vaulttec/sonarqube/auth/oidc/AutoLoginFilter.java +++ b/src/main/java/org/vaulttec/sonarqube/auth/oidc/AutoLoginFilter.java @@ -19,7 +19,6 @@ import java.io.IOException; -import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; @@ -28,12 +27,16 @@ import javax.servlet.http.HttpServletResponse; import org.sonar.api.server.ServerSide; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; +import org.sonar.api.web.UrlPattern; @ServerSide -public class AutoLoginFilter extends ServletFilter { +public class AutoLoginFilter extends HttpFilter { private static final Logger LOGGER = Loggers.get(AutoLoginFilter.class); @@ -53,17 +56,16 @@ public UrlPattern doGetPattern() { } @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { - if (config.isEnabled() && config.isAutoLogin() && request instanceof HttpServletRequest) { - String referrer = ((HttpServletRequest) request).getHeader("referer"); + public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws IOException { + if (config.isEnabled() && config.isAutoLogin()) { + String referrer = request.getHeader("referer"); LOGGER.debug("Referrer: {}", referrer); // Skip if disabled via request parameter if (referrer == null || !referrer.endsWith(SKIP_REQUEST_PARAM)) { String loginPageUrl = config.getBaseUrl() + OIDC_URL + config.getContextPath() + "/projects"; LOGGER.debug("Redirecting to OIDC login page: {}", loginPageUrl); - ((HttpServletResponse) response).sendRedirect(loginPageUrl); + response.sendRedirect(loginPageUrl); return; } } @@ -71,10 +73,9 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha } @Override - public void init(FilterConfig filterConfig) throws ServletException { + public void init(){ // Not needed here } - @Override public void destroy() { // Not needed here diff --git a/src/main/java/org/vaulttec/sonarqube/auth/oidc/OidcClient.java b/src/main/java/org/vaulttec/sonarqube/auth/oidc/OidcClient.java index 6bc96f5..a860779 100644 --- a/src/main/java/org/vaulttec/sonarqube/auth/oidc/OidcClient.java +++ b/src/main/java/org/vaulttec/sonarqube/auth/oidc/OidcClient.java @@ -18,10 +18,12 @@ package org.vaulttec.sonarqube.auth.oidc; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; +import java.io.UnsupportedEncodingException; +import java.net.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import javax.servlet.http.HttpServletRequest; @@ -67,6 +69,7 @@ import com.nimbusds.openid.connect.sdk.validators.IDTokenValidator; import org.sonar.api.server.ServerSide; +import org.sonar.api.server.http.HttpRequest; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -97,20 +100,36 @@ public AuthenticationRequest createAuthenticationRequest(String callbackUrl, Str return request; } - public AuthorizationCode getAuthorizationCode(HttpServletRequest callbackRequest) { + public AuthorizationCode getAuthorizationCode(HttpRequest callbackRequest) { LOGGER.debug("Retrieving authorization code from callback request's query parameters: {}", callbackRequest.getQueryString()); AuthenticationResponse authResponse; try { - HTTPRequest request = ServletUtils.createHTTPRequest(callbackRequest); - authResponse = AuthenticationResponseParser.parse(request.getURL().toURI(), request.getQueryParameters()); - } catch (ParseException | URISyntaxException | IOException e) { - throw new IllegalStateException("Error while parsing callback request", e); + URI uri = new URI(callbackRequest.getRequestURL()); + + Map> queryParams = new HashMap<>(); + String queryString = callbackRequest.getQueryString(); + if (queryString != null && !queryString.isEmpty() ) { + String[] pairs = queryString.split("&"); + for (String pair: pairs) { + int idx = pair.indexOf("="); + if (idx > 0) { + String key = URLDecoder.decode(pair.substring(0,idx), "UTF-8"); + String value = URLDecoder.decode(pair.substring(idx + 1), "UTF-8"); + queryParams.computeIfAbsent(key, k -> new ArrayList<>()).add(value); + } + } + } + authResponse = AuthenticationResponseParser.parse(uri, queryParams); + } catch (ParseException | URISyntaxException | UnsupportedEncodingException e) { + throw new IllegalStateException("Error while processing callback request", e); } + if (authResponse instanceof AuthenticationErrorResponse) { ErrorObject error = ((AuthenticationErrorResponse) authResponse).getErrorObject(); throw new IllegalStateException("Authentication request failed: " + error.toJSONObject()); } + AuthorizationCode authorizationCode = ((AuthenticationSuccessResponse) authResponse).getAuthorizationCode(); LOGGER.debug("Authorization code: {}", authorizationCode.getValue()); return authorizationCode; diff --git a/src/main/java/org/vaulttec/sonarqube/auth/oidc/OidcIdentityProvider.java b/src/main/java/org/vaulttec/sonarqube/auth/oidc/OidcIdentityProvider.java index 81fcbe7..dbd2e2e 100644 --- a/src/main/java/org/vaulttec/sonarqube/auth/oidc/OidcIdentityProvider.java +++ b/src/main/java/org/vaulttec/sonarqube/auth/oidc/OidcIdentityProvider.java @@ -85,7 +85,7 @@ public void init(InitContext context) { public void callback(CallbackContext context) { LOGGER.debug("Handling authentication response"); context.verifyCsrfState(); - AuthorizationCode authorizationCode = client.getAuthorizationCode(context.getRequest()); + AuthorizationCode authorizationCode = client.getAuthorizationCode(context.getHttpRequest()); UserInfo userInfo = client.getUserInfo(authorizationCode, context.getCallbackUrl()); UserIdentity userIdentity = userIdentityFactory.create(userInfo); LOGGER.debug("Authenticating user '{}' with groups {}", userIdentity.getProviderLogin(), userIdentity.getGroups()); diff --git a/src/test/java/org/vaulttec/sonarqube/auth/oidc/AbstractOidcTest.java b/src/test/java/org/vaulttec/sonarqube/auth/oidc/AbstractOidcTest.java index 0bea833..f4630b1 100644 --- a/src/test/java/org/vaulttec/sonarqube/auth/oidc/AbstractOidcTest.java +++ b/src/test/java/org/vaulttec/sonarqube/auth/oidc/AbstractOidcTest.java @@ -18,16 +18,18 @@ package org.vaulttec.sonarqube.auth.oidc; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.*; import static org.vaulttec.sonarqube.auth.oidc.OidcConfiguration.LOGIN_STRATEGY_DEFAULT_VALUE; import com.nimbusds.oauth2.sdk.ParseException; import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata; import com.nimbusds.openid.connect.sdk.validators.IDTokenValidator; +import org.sonar.api.config.Configuration; -import org.sonar.api.config.internal.MapSettings; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; public abstract class AbstractOidcTest { @@ -36,27 +38,41 @@ public abstract class AbstractOidcTest { public static final String STATE = "state"; public static final String VALID_CODE = "valid_code"; - protected MapSettings settings = new MapSettings(); - protected OidcConfiguration config = new OidcConfiguration(settings.asConfig()); + protected OidcConfiguration oidcConfig; + protected Configuration config; + protected void setSettings(boolean enabled) { setSettings(enabled, ISSUER_URI); } protected void setSettings(boolean enabled, String issuerUri) { + Map settings = new HashMap<>(); if (enabled) { - settings.setProperty(OidcConfiguration.ENABLED, true); - settings.setProperty(OidcConfiguration.ISSUER_URI, issuerUri); - settings.setProperty(OidcConfiguration.CLIENT_ID, "id"); - settings.setProperty(OidcConfiguration.CLIENT_SECRET, "secret"); - settings.setProperty(OidcConfiguration.ID_TOKEN_SIG_ALG, "RS256"); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, LOGIN_STRATEGY_DEFAULT_VALUE); - settings.setProperty(OidcConfiguration.GROUPS_SYNC, true); - settings.setProperty(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME, "myGroups"); - settings.setProperty(OidcConfiguration.SCOPES, "openid email profile"); + settings.put(property(OidcConfiguration.ENABLED), "true"); + settings.put(property(OidcConfiguration.ISSUER_URI), issuerUri); + settings.put(property(OidcConfiguration.CLIENT_ID), "id"); + settings.put(property(OidcConfiguration.CLIENT_SECRET), "secret"); + settings.put(property(OidcConfiguration.ID_TOKEN_SIG_ALG), "RS256"); + settings.put(property(OidcConfiguration.LOGIN_STRATEGY), LOGIN_STRATEGY_DEFAULT_VALUE); + settings.put(property(OidcConfiguration.GROUPS_SYNC), "true"); + settings.put(property(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME), "myGroups"); + settings.put(property(OidcConfiguration.SCOPES), "openid email profile"); } else { - settings.setProperty(OidcConfiguration.ENABLED, false); + settings.put(property(OidcConfiguration.ENABLED), "false"); + } + + config = mock(Configuration.class); + for(Map.Entry entry: settings.entrySet()){ + when(config.get(entry.getKey())).thenReturn(Optional.of(entry.getValue())); + when(config.getBoolean(entry.getKey())).thenReturn(Optional.of(Boolean.parseBoolean(entry.getValue()))); } + + this.oidcConfig = new OidcConfiguration(config); + } + + protected static String property(String suffix){ + return "sonar.auth" + OidcIdentityProvider.KEY + "." + suffix; } protected OIDCProviderMetadata getProviderMetadata(String issuerUri) { @@ -84,8 +100,8 @@ protected OIDCProviderMetadata getProviderMetadata(String issuerUri) { } protected OidcClient createSpyOidcClient() { - OidcClient client = spy(new OidcClient(config)); - doReturn(getProviderMetadata(config.issuerUri())).when(client).getProviderMetadata(); + OidcClient client = spy(new OidcClient(oidcConfig)); + doReturn(getProviderMetadata(oidcConfig.issuerUri())).when(client).getProviderMetadata(); doReturn(mock(IDTokenValidator.class)).when(client).createValidator(any(), any()); return client; } diff --git a/src/test/java/org/vaulttec/sonarqube/auth/oidc/AuthOidcPluginTest.java b/src/test/java/org/vaulttec/sonarqube/auth/oidc/AuthOidcPluginTest.java index be66ada..5a9ddb7 100644 --- a/src/test/java/org/vaulttec/sonarqube/auth/oidc/AuthOidcPluginTest.java +++ b/src/test/java/org/vaulttec/sonarqube/auth/oidc/AuthOidcPluginTest.java @@ -17,36 +17,58 @@ */ package org.vaulttec.sonarqube.auth.oidc; -import static org.assertj.core.api.Assertions.assertThat; - import org.junit.Test; -import org.sonar.api.Plugin; -import org.sonar.api.SonarQubeSide; -import org.sonar.api.SonarRuntime; -import org.sonar.api.internal.PluginContextImpl; -import org.sonar.api.internal.SonarRuntimeImpl; +import org.sonar.api.*; import org.sonar.api.utils.Version; +import static org.assertj.core.api.Assertions.assertThat; + public class AuthOidcPluginTest { AuthOidcPlugin underTest = new AuthOidcPlugin(); + + @Test public void test_server_side_extensions() throws Exception { - SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.create(7, 6), SonarQubeSide.SERVER); - Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build(); + Plugin.Context context = setupContext(SonarQubeSide.SERVER); underTest.define(context); - assertThat(context.getExtensions()).hasSize(20); } @Test public void test_scnner_side_extensions() throws Exception { - SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.create(7, 6), SonarQubeSide.SCANNER); - Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build(); + Plugin.Context context = setupContext(SonarQubeSide.SCANNER); underTest.define(context); - assertThat(context.getExtensions()).isEmpty(); } + + private Plugin.Context setupContext(SonarQubeSide side){ + + SonarRuntime runtime = new SonarRuntime() { + @Override + public Version getApiVersion() { + return Version.create(9, 9); + } + + @Override + public SonarProduct getProduct() { + return SonarProduct.SONARQUBE; + } + + @Override + public SonarQubeSide getSonarQubeSide() { + return side; + } + + @Override + public SonarEdition getEdition() { + return SonarEdition.COMMUNITY; + } + }; + + return new Plugin.Context(runtime); + } + } diff --git a/src/test/java/org/vaulttec/sonarqube/auth/oidc/AutoLoginFilterTest.java b/src/test/java/org/vaulttec/sonarqube/auth/oidc/AutoLoginFilterTest.java index e1b5149..26fbec6 100644 --- a/src/test/java/org/vaulttec/sonarqube/auth/oidc/AutoLoginFilterTest.java +++ b/src/test/java/org/vaulttec/sonarqube/auth/oidc/AutoLoginFilterTest.java @@ -17,24 +17,18 @@ */ package org.vaulttec.sonarqube.auth.oidc; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Optional; - -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.junit.Test; import org.sonar.api.CoreProperties; import org.sonar.api.config.Configuration; -import org.sonar.api.web.ServletFilter; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; +import org.sonar.api.web.FilterChain; +import org.sonar.api.web.HttpFilter; + +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; public class AutoLoginFilterTest { @@ -42,11 +36,6 @@ public class AutoLoginFilterTest { @Test public void testFilter() throws Exception { - ServletContext servletContext = mock(ServletContext.class); - when(servletContext.getContextPath()).thenReturn("/sonar"); - FilterConfig filterConfig = mock(FilterConfig.class); - when(filterConfig.getServletContext()).thenReturn(servletContext); - Configuration configurationMock = mock(Configuration.class); when(configurationMock.getBoolean("sonar.auth." + OidcIdentityProvider.KEY + ".enabled")) .thenReturn(Optional.of(true)); @@ -58,16 +47,15 @@ public void testFilter() throws Exception { .thenReturn(Optional.of(true)); when(configurationMock.get(CoreProperties.SERVER_BASE_URL)).thenReturn(Optional.of(SONAR_URL)); - ServletFilter filter = new AutoLoginFilter(new OidcConfiguration(configurationMock)); - filter.init(filterConfig); + HttpFilter filter = new AutoLoginFilter(new OidcConfiguration(configurationMock)); + filter.init(); filter.doGetPattern(); - HttpServletRequest request = mock(HttpServletRequest.class); - when(request.getRequestURL()).thenReturn(new StringBuffer(SONAR_URL + "/sessions/new")); + HttpRequest request = mock(HttpRequest.class); + when(request.getRequestURI()).thenReturn(String.valueOf(SONAR_URL + "/sessions/new")); when(request.getServerName()).thenReturn("acme.com"); - HttpServletResponse response = mock(HttpServletResponse.class); - + HttpResponse response = mock(HttpResponse.class); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, response, chain); @@ -78,11 +66,6 @@ public void testFilter() throws Exception { @Test public void testFilterDisbled() throws Exception { - ServletContext servletContext = mock(ServletContext.class); - when(servletContext.getContextPath()).thenReturn("/sonar"); - FilterConfig filterConfig = mock(FilterConfig.class); - when(filterConfig.getServletContext()).thenReturn(servletContext); - Configuration configurationMock = mock(Configuration.class); when(configurationMock.getBoolean("sonar.auth." + OidcIdentityProvider.KEY + ".enabled")) .thenReturn(Optional.of(true)); @@ -94,15 +77,15 @@ public void testFilterDisbled() throws Exception { .thenReturn(Optional.of(false)); when(configurationMock.get(CoreProperties.SERVER_BASE_URL)).thenReturn(Optional.of(SONAR_URL)); - ServletFilter filter = new AutoLoginFilter(new OidcConfiguration(configurationMock)); - filter.init(filterConfig); + HttpFilter filter = new AutoLoginFilter(new OidcConfiguration(configurationMock)); + filter.init(); filter.doGetPattern(); - HttpServletRequest request = mock(HttpServletRequest.class); - when(request.getRequestURL()).thenReturn(new StringBuffer(SONAR_URL + "/sessions/new")); + HttpRequest request = mock(HttpRequest.class); + when(request.getRequestURL()).thenReturn(String.valueOf(SONAR_URL + "/sessions/new")); when(request.getServerName()).thenReturn("acme.com"); - HttpServletResponse response = mock(HttpServletResponse.class); + HttpResponse response = mock(HttpResponse.class); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, response, chain); @@ -114,11 +97,6 @@ public void testFilterDisbled() throws Exception { @Test public void testFilterTemporarilyDisbled() throws Exception { - ServletContext servletContext = mock(ServletContext.class); - when(servletContext.getContextPath()).thenReturn("/sonar"); - FilterConfig filterConfig = mock(FilterConfig.class); - when(filterConfig.getServletContext()).thenReturn(servletContext); - Configuration configurationMock = mock(Configuration.class); when(configurationMock.getBoolean(OidcConfiguration.ENABLED)).thenReturn(Optional.of(true)); when(configurationMock.get(OidcConfiguration.ISSUER_URI)).thenReturn(Optional.of("http://idp.com")); @@ -126,16 +104,16 @@ public void testFilterTemporarilyDisbled() throws Exception { when(configurationMock.getBoolean(OidcConfiguration.AUTO_LOGIN)).thenReturn(Optional.of(true)); when(configurationMock.get(CoreProperties.SERVER_BASE_URL)).thenReturn(Optional.of(SONAR_URL)); - ServletFilter filter = new AutoLoginFilter(new OidcConfiguration(configurationMock)); - filter.init(filterConfig); + HttpFilter filter = new AutoLoginFilter(new OidcConfiguration(configurationMock)); + filter.init(); filter.doGetPattern(); - HttpServletRequest request = mock(HttpServletRequest.class); - when(request.getRequestURL()).thenReturn(new StringBuffer(SONAR_URL + "/sessions/new")); + HttpRequest request = mock(HttpRequest.class); + when(request.getRequestURL()).thenReturn(String.valueOf(SONAR_URL + "/sessions/new")); when(request.getServerName()).thenReturn("acme.com"); when(request.getHeader("referer")).thenReturn(SONAR_URL + "/?auto-login=false"); - HttpServletResponse response = mock(HttpServletResponse.class); + HttpResponse response = mock(HttpResponse.class); FilterChain chain = mock(FilterChain.class); filter.doFilter(request, response, chain); diff --git a/src/test/java/org/vaulttec/sonarqube/auth/oidc/IntegrationTest.java b/src/test/java/org/vaulttec/sonarqube/auth/oidc/IntegrationTest.java index e53dd97..00a9cf5 100644 --- a/src/test/java/org/vaulttec/sonarqube/auth/oidc/IntegrationTest.java +++ b/src/test/java/org/vaulttec/sonarqube/auth/oidc/IntegrationTest.java @@ -17,33 +17,30 @@ */ package org.vaulttec.sonarqube.auth.oidc; -import static java.lang.String.format; -import static java.net.URLEncoder.encode; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import com.nimbusds.common.contenttype.ContentType; - +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.sonar.api.server.authentication.OAuth2IdentityProvider; import org.sonar.api.server.authentication.UserIdentity; +import org.sonar.api.server.http.HttpRequest; +import org.sonar.api.server.http.HttpResponse; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicBoolean; + +import static java.lang.String.format; +import static java.net.URLEncoder.encode; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class IntegrationTest extends AbstractOidcTest { @@ -59,8 +56,8 @@ public class IntegrationTest extends AbstractOidcTest { public void init() { setSettings(true, idpUri); oidcClient = createSpyOidcClient(); - userIdentityFactory = new UserIdentityFactory(config); - underTest = new OidcIdentityProvider(config, oidcClient, userIdentityFactory); + userIdentityFactory = new UserIdentityFactory(oidcConfig); + underTest = new OidcIdentityProvider(oidcConfig, oidcClient, userIdentityFactory); } /** @@ -84,7 +81,7 @@ public void redirect_browser_to_oidc_authentication_form() throws Exception { @Test public void callback_on_successful_authentication() throws IOException, InterruptedException { idp.enqueue(newSuccessfulAccessTokenResponse()); - HttpServletRequest request = newAuthenticationRequest(); + HttpRequest request = newAuthenticationRequest(); DumbCallbackContext callbackContext = new DumbCallbackContext(request); underTest.callback(callbackContext); @@ -112,7 +109,7 @@ public void callback_on_successful_authentication_with_additional_user_info_requ throws IOException, InterruptedException { idp.enqueue(newSuccessfulAccessTokenResponseWithoutUserInfo()); idp.enqueue(newUserInfoResponse()); - HttpServletRequest request = newAuthenticationRequest(); + HttpRequest request = newAuthenticationRequest(); DumbCallbackContext callbackContext = new DumbCallbackContext(request); underTest.callback(callbackContext); @@ -140,7 +137,7 @@ public void callback_on_successful_authentication_with_additional_user_info_requ throws IOException, InterruptedException { idp.enqueue(newSuccessfulAccessTokenResponseWithoutGroupsClaim()); idp.enqueue(newUserInfoResponse()); - HttpServletRequest request = newAuthenticationRequest(); + HttpRequest request = newAuthenticationRequest(); DumbCallbackContext callbackContext = new DumbCallbackContext(request); underTest.callback(callbackContext); @@ -179,8 +176,8 @@ public void callback_throws_ISE_if_error_when_requesting_id_token() throws Inter assertThat(accessTokenRequest.getPath()).startsWith("/protocol/openid-connect/token"); } - private static HttpServletRequest newAuthenticationRequest() { - HttpServletRequest request = mock(HttpServletRequest.class); + private static HttpRequest newAuthenticationRequest() { + HttpRequest request = mock(HttpRequest.class); when(request.getMethod()).thenReturn("GET"); when(request.getHeaderNames()).thenReturn(Collections.emptyEnumeration()); when(request.getQueryString()).thenReturn("state=" + STATE + "&code=" + VALID_CODE); @@ -221,12 +218,12 @@ private static MockResponse newUserInfoResponse() { } private static class DumbCallbackContext implements OAuth2IdentityProvider.CallbackContext { - final HttpServletRequest request; + final HttpRequest request; final AtomicBoolean csrfStateVerified = new AtomicBoolean(false); final AtomicBoolean redirectedToRequestedPage = new AtomicBoolean(false); UserIdentity userIdentity = null; - public DumbCallbackContext(HttpServletRequest request) { + public DumbCallbackContext(HttpRequest request) { this.request = request; } @@ -251,12 +248,12 @@ public String getCallbackUrl() { } @Override - public HttpServletRequest getRequest() { + public HttpRequest getHttpRequest() { return request; } @Override - public HttpServletResponse getResponse() { + public HttpResponse getHttpResponse() { throw new UnsupportedOperationException("not used"); } @@ -289,12 +286,12 @@ public String getCallbackUrl() { } @Override - public HttpServletRequest getRequest() { + public HttpRequest getHttpRequest() { return null; } @Override - public HttpServletResponse getResponse() { + public HttpResponse getHttpResponse() { return null; } } diff --git a/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcClientTest.java b/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcClientTest.java index cd58124..ed35f10 100644 --- a/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcClientTest.java +++ b/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcClientTest.java @@ -50,6 +50,7 @@ import org.junit.Test; import net.minidev.json.JSONObject; +import org.sonar.api.server.http.HttpRequest; public class OidcClientTest extends AbstractOidcTest { @@ -84,7 +85,7 @@ public void invalidAuthenticationRequestUri() { @Test public void getAuthorizationCode() { OidcClient underTest = newSpyOidcClient(); - HttpServletRequest request = mock(HttpServletRequest.class); + HttpRequest request = mock(HttpRequest.class); when(request.getMethod()).thenReturn("GET"); when(request.getHeaderNames()).thenReturn(Collections.emptyEnumeration()); when(request.getQueryString()).thenReturn("state=" + STATE + "&code=" + VALID_CODE); @@ -96,9 +97,9 @@ public void getAuthorizationCode() { @Test public void invalidAuthenticationResponseUri() { OidcClient underTest = newSpyOidcClient(); - HttpServletRequest request = mock(HttpServletRequest.class); + HttpRequest request = mock(HttpRequest.class); when(request.getMethod()).thenReturn("GET"); - when(request.getLocalAddr()).thenReturn("invalid . com"); + when(request.getRemoteAddr()).thenReturn("invalid . com"); when(request.getHeaderNames()).thenReturn(Collections.emptyEnumeration()); try { underTest.getAuthorizationCode(request); @@ -111,7 +112,7 @@ public void invalidAuthenticationResponseUri() { @Test public void authenticationErrorResponse() { OidcClient underTest = newSpyOidcClient(); - HttpServletRequest request = mock(HttpServletRequest.class); + HttpRequest request = mock(HttpRequest.class); when(request.getMethod()).thenReturn("GET"); when(request.getHeaderNames()).thenReturn(Collections.emptyEnumeration()); when(request.getQueryString()) diff --git a/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcConfigurationTest.java b/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcConfigurationTest.java index 8b39a61..c5372bf 100644 --- a/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcConfigurationTest.java +++ b/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcConfigurationTest.java @@ -17,82 +17,107 @@ */ package org.vaulttec.sonarqube.auth.oidc; -import static org.assertj.core.api.Assertions.assertThat; -import static org.vaulttec.sonarqube.auth.oidc.OidcConfiguration.ID_TOKEN_SIG_ALG_RSA; -import static org.vaulttec.sonarqube.auth.oidc.OidcConfiguration.LOGIN_STRATEGY_PREFERRED_USERNAME; -import static org.vaulttec.sonarqube.auth.oidc.OidcConfiguration.LOGIN_STRATEGY_PROVIDER_ID; - +import org.junit.Before; import org.junit.Test; import org.sonar.api.CoreProperties; -import org.sonar.api.config.PropertyDefinitions; -import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.config.Configuration; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.vaulttec.sonarqube.auth.oidc.OidcConfiguration.*; public class OidcConfigurationTest { private static final String SONAR_URL = "https://sonar.acme.com"; private static final String AUTH_URL = "https://auth.acme.com"; - private MapSettings settings = new MapSettings(new PropertyDefinitions(OidcConfiguration.definitions())); - private OidcConfiguration underTest = new OidcConfiguration(settings.asConfig()); + + protected static String property(String suffix){ + return "sonar.auth" + OidcIdentityProvider.KEY + "." + suffix; + } + + private void mockConfigValue(String key, String value){ + when(config.get(property(key))).thenReturn(Optional.of(value)); + + if("true".equals(value) || "false".equals(value)){ + when(config.getBoolean(property(key))).thenReturn(Optional.of(Boolean.parseBoolean(value))); + } + } + + + private Configuration config; + private OidcConfiguration underTest; + + @Before + public void setUp(){ + config = mock(Configuration.class); + when(config.get(any())).thenReturn(Optional.empty()); + when(config.getBoolean(any())).thenReturn(Optional.empty()); + underTest = new OidcConfiguration(config); + } @Test public void is_enabled() { - settings.setProperty(OidcConfiguration.ENABLED, true); - settings.setProperty(OidcConfiguration.ISSUER_URI, AUTH_URL); - settings.setProperty(OidcConfiguration.CLIENT_ID, "id"); + mockConfigValue(OidcConfiguration.ENABLED, "true"); + mockConfigValue(OidcConfiguration.ISSUER_URI, AUTH_URL); + mockConfigValue(OidcConfiguration.CLIENT_ID, "id"); assertThat(underTest.isEnabled()).isTrue(); - settings.setProperty(OidcConfiguration.ENABLED, false); + mockConfigValue(OidcConfiguration.ENABLED, "false"); assertThat(underTest.isEnabled()).isFalse(); } @Test public void is_enabled_always_return_false_when_issuer_uri_is_null() { - settings.setProperty(OidcConfiguration.ENABLED, true); - settings.setProperty(OidcConfiguration.ISSUER_URI, (String) null); - settings.setProperty(OidcConfiguration.CLIENT_ID, "id"); + mockConfigValue(OidcConfiguration.ENABLED, "true"); + mockConfigValue(OidcConfiguration.ISSUER_URI, (String) null); + mockConfigValue(OidcConfiguration.CLIENT_ID, "id"); assertThat(underTest.isEnabled()).isFalse(); } @Test public void is_enabled_always_return_false_when_client_id_is_null() { - settings.setProperty(OidcConfiguration.ENABLED, true); - settings.setProperty(OidcConfiguration.ISSUER_URI, AUTH_URL); - settings.setProperty(OidcConfiguration.CLIENT_ID, (String) null); + mockConfigValue(OidcConfiguration.ENABLED, "true"); + mockConfigValue(OidcConfiguration.ISSUER_URI, AUTH_URL); + mockConfigValue(OidcConfiguration.CLIENT_ID, (String) null); assertThat(underTest.isEnabled()).isFalse(); } @Test public void is_auto_login() { - settings.setProperty(OidcConfiguration.AUTO_LOGIN, true); + mockConfigValue(OidcConfiguration.AUTO_LOGIN, "true"); assertThat(underTest.isAutoLogin()).isTrue(); } @Test public void configure_issuer_uri() throws Exception { - settings.setProperty(OidcConfiguration.ISSUER_URI, AUTH_URL); + mockConfigValue(OidcConfiguration.ISSUER_URI, AUTH_URL); assertThat(underTest.issuerUri()).isEqualTo(AUTH_URL); } @Test public void return_client_id() { - settings.setProperty(OidcConfiguration.CLIENT_ID, "id"); + mockConfigValue(OidcConfiguration.CLIENT_ID, "id"); assertThat(underTest.clientId()).isEqualTo("id"); } @Test public void return_client_secret() { - settings.setProperty(OidcConfiguration.CLIENT_SECRET, "secret"); + mockConfigValue(OidcConfiguration.CLIENT_SECRET, "secret"); assertThat(underTest.clientSecret()).isEqualTo("secret"); } @Test public void return_id_token_sign_algorithm() { - settings.setProperty(OidcConfiguration.ID_TOKEN_SIG_ALG, ID_TOKEN_SIG_ALG_RSA); + mockConfigValue(OidcConfiguration.ID_TOKEN_SIG_ALG, ID_TOKEN_SIG_ALG_RSA); assertThat(underTest.idTokenSignAlgorithm()).isEqualTo(ID_TOKEN_SIG_ALG_RSA); } @@ -103,7 +128,7 @@ public void default_id_token_sign_algorithm() { @Test public void return_login_strategy() { - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, LOGIN_STRATEGY_PROVIDER_ID); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, LOGIN_STRATEGY_PROVIDER_ID); assertThat(underTest.loginStrategy()).isEqualTo(LOGIN_STRATEGY_PROVIDER_ID); } @@ -114,50 +139,50 @@ public void default_login_strategy_is_preferred_username() { @Test public void allow_users_to_sign_up() { - settings.setProperty(OidcConfiguration.ALLOW_USERS_TO_SIGN_UP, "true"); + mockConfigValue(OidcConfiguration.ALLOW_USERS_TO_SIGN_UP, "true"); assertThat(underTest.allowUsersToSignUp()).isTrue(); - settings.setProperty(OidcConfiguration.ALLOW_USERS_TO_SIGN_UP, "false"); + mockConfigValue(OidcConfiguration.ALLOW_USERS_TO_SIGN_UP, "false"); assertThat(underTest.allowUsersToSignUp()).isFalse(); } @Test public void group_sync() { - settings.setProperty(OidcConfiguration.GROUPS_SYNC, "true"); + mockConfigValue(OidcConfiguration.GROUPS_SYNC, "true"); assertThat(underTest.syncGroups()).isTrue(); - settings.setProperty(OidcConfiguration.GROUPS_SYNC, "false"); + mockConfigValue(OidcConfiguration.GROUPS_SYNC, "false"); assertThat(underTest.syncGroups()).isFalse(); } @Test public void group_sync_claim_name() { assertThat(underTest.syncGroupsClaimName()).isEqualTo("groups"); - settings.setProperty(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME, "test"); + mockConfigValue(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME, "test"); assertThat(underTest.syncGroupsClaimName()).isEqualTo("test"); } @Test public void scopes() { - settings.setProperty(OidcConfiguration.SCOPES, "openid"); + mockConfigValue(OidcConfiguration.SCOPES, "openid"); assertThat(underTest.scopes()).isEqualTo("openid"); } @Test public void icon_path() { - settings.setProperty(OidcConfiguration.ICON_PATH, "http://mydomain.com/myicon.png"); + mockConfigValue(OidcConfiguration.ICON_PATH, "http://mydomain.com/myicon.png"); assertThat(underTest.iconPath()).isEqualTo("http://mydomain.com/myicon.png"); } @Test public void background_color() { - settings.setProperty(OidcConfiguration.BACKGROUND_COLOR, "#123456"); + mockConfigValue(OidcConfiguration.BACKGROUND_COLOR, "#123456"); assertThat(underTest.backgroundColor()).isEqualTo("#123456"); } @Test public void login_button_text() { - settings.setProperty(OidcConfiguration.LOGIN_BUTTON_TEXT, "My Company Single-Sign-On"); + mockConfigValue(OidcConfiguration.LOGIN_BUTTON_TEXT, "My Company Single-Sign-On"); assertThat(underTest.loginButtonText()).isEqualTo("My Company Single-Sign-On"); } @@ -168,19 +193,19 @@ public void definitions() { @Test public void with_base_url() { - settings.setProperty(CoreProperties.SERVER_BASE_URL, SONAR_URL); + mockConfigValue(CoreProperties.SERVER_BASE_URL, SONAR_URL); assertThat(underTest.getBaseUrl()).isEqualTo(SONAR_URL); } @Test public void without_base_url() { - settings.setProperty(CoreProperties.SERVER_BASE_URL, (String) null); + mockConfigValue(CoreProperties.SERVER_BASE_URL, (String) null); assertThat(underTest.getBaseUrl()).isEmpty(); } @Test public void with_context() { - settings.setProperty("sonar.web.context", "sonar"); + mockConfigValue("sonar.web.context", "sonar"); assertThat(underTest.getContextPath()).isEqualTo("sonar"); } diff --git a/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcIdentityProviderTest.java b/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcIdentityProviderTest.java index 6f7f9a5..04fd04d 100644 --- a/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcIdentityProviderTest.java +++ b/src/test/java/org/vaulttec/sonarqube/auth/oidc/OidcIdentityProviderTest.java @@ -20,16 +20,20 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.net.URI; import java.net.URISyntaxException; +import java.util.Optional; import com.nimbusds.openid.connect.sdk.AuthenticationRequest; +import org.junit.Before; import org.junit.Test; +import org.sonar.api.config.Configuration; import org.sonar.api.server.authentication.Display; import org.sonar.api.server.authentication.OAuth2IdentityProvider; @@ -38,7 +42,23 @@ public class OidcIdentityProviderTest extends AbstractOidcTest { private UserIdentityFactory userIdentityFactory = mock(UserIdentityFactory.class); private OidcClient client = newMockClient(); - private OidcIdentityProvider underTest = new OidcIdentityProvider(config, client, userIdentityFactory); + private OidcIdentityProvider underTest = new OidcIdentityProvider(oidcConfig, client, userIdentityFactory); + + private void mockConfigValue(String key, String value){ + when(config.get(property(key))).thenReturn(Optional.of(value)); + + if("true".equals(value) || "false".equals(value)){ + when(config.getBoolean(property(key))).thenReturn(Optional.of(Boolean.parseBoolean(value))); + } + } + @Before + public void setup(){ + config = mock(Configuration.class); + when(config.get(any())).thenReturn(Optional.empty()); + when(config.getBoolean(any())).thenReturn(Optional.empty()); + oidcConfig = new OidcConfiguration(config); + underTest = new OidcIdentityProvider(oidcConfig, client, userIdentityFactory); + } @Test public void check_fields() throws Exception { @@ -47,18 +67,18 @@ public void check_fields() throws Exception { @Test public void custom_name() throws Exception { - settings.setProperty(OidcConfiguration.LOGIN_BUTTON_TEXT, "My text"); + mockConfigValue(OidcConfiguration.LOGIN_BUTTON_TEXT, "My text"); assertThat(underTest.getName()).isEqualTo("My text"); } @Test public void is_enabled() throws Exception { - settings.setProperty(OidcConfiguration.ENABLED, true); - settings.setProperty(OidcConfiguration.ISSUER_URI, ISSUER_URI); - settings.setProperty(OidcConfiguration.CLIENT_ID, "id"); + mockConfigValue(OidcConfiguration.ENABLED, "true"); + mockConfigValue(OidcConfiguration.ISSUER_URI, ISSUER_URI); + mockConfigValue(OidcConfiguration.CLIENT_ID, "id"); assertThat(underTest.isEnabled()).isTrue(); - settings.setProperty(OidcConfiguration.ENABLED, false); + mockConfigValue(OidcConfiguration.ENABLED, "false"); assertThat(underTest.isEnabled()).isFalse(); } @@ -66,7 +86,7 @@ public void is_enabled() throws Exception { public void should_allow_users_to_signup() { assertThat(underTest.allowsUsersToSignUp()).as("default").isFalse(); - settings.setProperty(OidcConfiguration.ALLOW_USERS_TO_SIGN_UP, true); + mockConfigValue(OidcConfiguration.ALLOW_USERS_TO_SIGN_UP, "true"); assertThat(underTest.allowsUsersToSignUp()).isTrue(); } @@ -76,7 +96,7 @@ public void init() throws Exception { OAuth2IdentityProvider.InitContext context = mock(OAuth2IdentityProvider.InitContext.class); when(context.generateCsrfState()).thenReturn(STATE); when(context.getCallbackUrl()).thenReturn(CALLBACK_URL); - settings.setProperty(OidcConfiguration.ISSUER_URI, ISSUER_URI); + mockConfigValue(OidcConfiguration.ISSUER_URI, ISSUER_URI); underTest.init(context); @@ -95,8 +115,8 @@ public void fail_to_init_when_disabled() throws Exception { @Test public void display() { - settings.setProperty(OidcConfiguration.ICON_PATH, "my_path"); - settings.setProperty(OidcConfiguration.BACKGROUND_COLOR, "#123456"); + mockConfigValue(OidcConfiguration.ICON_PATH, "my_path"); + mockConfigValue(OidcConfiguration.BACKGROUND_COLOR, "#123456"); Display display = underTest.getDisplay(); assertThat(display).isNotNull(); diff --git a/src/test/java/org/vaulttec/sonarqube/auth/oidc/UserIdentityFactoryTest.java b/src/test/java/org/vaulttec/sonarqube/auth/oidc/UserIdentityFactoryTest.java index 87a9574..03a488c 100644 --- a/src/test/java/org/vaulttec/sonarqube/auth/oidc/UserIdentityFactoryTest.java +++ b/src/test/java/org/vaulttec/sonarqube/auth/oidc/UserIdentityFactoryTest.java @@ -19,26 +19,57 @@ import com.nimbusds.oauth2.sdk.ParseException; import com.nimbusds.openid.connect.sdk.claims.UserInfo; +import org.junit.Before; import org.junit.Test; -import org.sonar.api.config.PropertyDefinitions; -import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.config.Configuration; import org.sonar.api.server.authentication.UserIdentity; import java.util.Arrays; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class UserIdentityFactoryTest { - MapSettings settings = new MapSettings(new PropertyDefinitions(OidcConfiguration.definitions())); - UserIdentityFactory underTest = new UserIdentityFactory(new OidcConfiguration(settings.asConfig())); + private Configuration config; + private OidcConfiguration oidcConfig; + private UserIdentityFactory underTest; + + protected static String property(String suffix){ + return "sonar.auth" + OidcIdentityProvider.KEY + "." + suffix; + } + private void mockConfigValue(String key, String value){ + String fullKey = property(key); + System.out.println("Mocking config key: " + fullKey + " with value " + value); + when(config.get(property(key))).thenReturn(Optional.of(value)); + + if("true".equals(value) || "false".equals(value)){ + when(config.getBoolean(property(key))).thenReturn(Optional.of(Boolean.parseBoolean(value))); + } + } + @Before + public void setup(){ + config = mock(Configuration.class); + when(config.get(any())).thenReturn(Optional.empty()); + when(config.getBoolean(any())).thenReturn(Optional.empty()); + + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY_CUSTOM_CLAIM_NAME, "upn"); + mockConfigValue(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME, "groups"); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_PREFERRED_USERNAME); + + oidcConfig = new OidcConfiguration(config); + underTest = new UserIdentityFactory(oidcConfig); + } @Test public void create_for_provider_strategy() { UserInfo userInfo = newUserInfo(false, false); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_PROVIDER_ID); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_PROVIDER_ID); UserIdentity identity = underTest.create(userInfo); assertThat(identity.getProviderLogin()).isEqualTo("8f63a486-6699-4f25-beef-118dd240bef8"); @@ -49,7 +80,7 @@ public void create_for_provider_strategy() { @Test public void create_for_unique_login_strategy() { UserInfo userInfo = newUserInfo(false, false); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_UNIQUE); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_UNIQUE); UserIdentity identity = underTest.create(userInfo); assertThat(identity.getProviderLogin()).isEqualTo("8f63a486-6699-4f25-beef-118dd240bef8@oidc"); @@ -60,7 +91,7 @@ public void create_for_unique_login_strategy() { @Test public void create_for_preferred_username_login_strategy() { UserInfo userInfo = newUserInfo(false, false); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_PREFERRED_USERNAME); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_PREFERRED_USERNAME); UserIdentity identity = underTest.create(userInfo); assertThat(identity.getProviderLogin()).isEqualTo("jdoo"); @@ -71,7 +102,7 @@ public void create_for_preferred_username_login_strategy() { @Test public void create_for_email_login_strategy() { UserInfo userInfo = newUserInfo(false, false); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_EMAIL); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_EMAIL); UserIdentity identity = underTest.create(userInfo); assertThat(identity.getProviderLogin()).isEqualTo(identity.getEmail()); @@ -83,7 +114,7 @@ public void create_for_email_login_strategy() { public void create_for_custom_claim_strategy() { UserInfo userInfo = newUserInfo(false, false); userInfo.setClaim("upn", "johndoo"); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_CUSTOM_CLAIM); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_CUSTOM_CLAIM); UserIdentity identity = underTest.create(userInfo); assertThat(identity.getProviderLogin()).isEqualTo(userInfo.getClaim("upn")); @@ -95,7 +126,7 @@ public void create_for_custom_claim_strategy() { public void no_email() { UserInfo userInfo = newUserInfo(false, false); userInfo.setEmailAddress(null); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_PROVIDER_ID); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_PROVIDER_ID); UserIdentity identity = underTest.create(userInfo); assertThat(identity.getProviderLogin()).isEqualTo("8f63a486-6699-4f25-beef-118dd240bef8"); @@ -115,7 +146,7 @@ public void null_name_is_replaced_by_preferred_username() { @Test public void throw_ISE_if_strategy_is_not_supported() { UserInfo userInfo = newUserInfo(false, false); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, "xxx"); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, "xxx"); IllegalStateException exception = assertThrows(IllegalStateException.class, () -> underTest.create(userInfo)); assertTrue(exception.getMessage().contains("Login strategy not supported: xxx")); @@ -125,7 +156,7 @@ public void throw_ISE_if_strategy_is_not_supported() { public void throw_ISE_if_missing_preferred_username() { UserInfo userInfo = newUserInfo(false, false); userInfo.setPreferredUsername(null); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_PREFERRED_USERNAME); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_PREFERRED_USERNAME); IllegalStateException exception = assertThrows(IllegalStateException.class, () -> underTest.create(userInfo)); assertTrue(exception.getMessage().startsWith("Claim 'preferred_username' is missing in user info")); @@ -135,7 +166,7 @@ public void throw_ISE_if_missing_preferred_username() { public void throw_ISE_if_missing_email() { UserInfo userInfo = newUserInfo(false, false); userInfo.setEmailAddress(null); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_EMAIL); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_EMAIL); IllegalStateException exception = assertThrows(IllegalStateException.class, () -> underTest.create(userInfo)); assertTrue(exception.getMessage().startsWith("Claim 'email' is missing in user info")); @@ -146,7 +177,7 @@ public void throw_ISE_if_missing_name_and_preferred_username() { UserInfo userInfo = newUserInfo(false, false); userInfo.setName(null); userInfo.setPreferredUsername(null); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_UNIQUE); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_UNIQUE); IllegalStateException exception = assertThrows(IllegalStateException.class, () -> underTest.create(userInfo)); assertTrue(exception.getMessage().startsWith("Claims 'name' and 'preferred_username' are missing in user info")); @@ -155,7 +186,12 @@ public void throw_ISE_if_missing_name_and_preferred_username() { @Test public void throw_ISE_if_missing_custom_claim() { UserInfo userInfo = newUserInfo(false, false); - settings.setProperty(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_CUSTOM_CLAIM); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY, OidcConfiguration.LOGIN_STRATEGY_CUSTOM_CLAIM); + mockConfigValue(OidcConfiguration.LOGIN_STRATEGY_CUSTOM_CLAIM_NAME, "upn"); + + System.out.println("Login Strategy from config: " + oidcConfig.loginStrategy()); + System.out.println("Custom Claim Name from config: " + oidcConfig.loginStrategyCustomClaimName()); + // System.out.println("UserInfo claims: " + userInfo.getClaim()); IllegalStateException exception = assertThrows(IllegalStateException.class, () -> underTest.create(userInfo)); assertTrue(exception.getMessage().startsWith("Custom claim 'upn' is missing in user info")); @@ -164,7 +200,7 @@ public void throw_ISE_if_missing_custom_claim() { @Test public void create_with_synched_multiple_groups_as_list() { UserInfo userInfo = newUserInfo(false, false); - settings.setProperty(OidcConfiguration.GROUPS_SYNC, true); + mockConfigValue(OidcConfiguration.GROUPS_SYNC, "true"); UserIdentity identity = underTest.create(userInfo); assertThat(identity.getGroups()).containsAll(Arrays.asList("admins", "internal")); @@ -173,7 +209,7 @@ public void create_with_synched_multiple_groups_as_list() { @Test public void create_with_synched_multiple_groups_as_string() { UserInfo userInfo = newUserInfo(false, true); - settings.setProperty(OidcConfiguration.GROUPS_SYNC, true); + mockConfigValue(OidcConfiguration.GROUPS_SYNC, "true"); UserIdentity identity = underTest.create(userInfo); assertThat(identity.getGroups()).containsAll(Arrays.asList("admins", "internal")); @@ -182,8 +218,8 @@ public void create_with_synched_multiple_groups_as_string() { @Test public void create_with_synched_single_group_as_list() { UserInfo userInfo = newUserInfo(true, false); - settings.setProperty(OidcConfiguration.GROUPS_SYNC, true); - settings.setProperty(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME, "group"); + mockConfigValue(OidcConfiguration.GROUPS_SYNC, "true"); + mockConfigValue(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME, "group"); UserIdentity identity = underTest.create(userInfo); assertThat(identity.getGroups()).containsExactly("admins"); @@ -192,8 +228,8 @@ public void create_with_synched_single_group_as_list() { @Test public void create_with_synched_single_group_as_string() { UserInfo userInfo = newUserInfo(true, true); - settings.setProperty(OidcConfiguration.GROUPS_SYNC, true); - settings.setProperty(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME, "group"); + mockConfigValue(OidcConfiguration.GROUPS_SYNC, "true"); + mockConfigValue(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME, "group"); UserIdentity identity = underTest.create(userInfo); assertThat(identity.getGroups()).containsExactly("admins"); @@ -202,8 +238,8 @@ public void create_with_synched_single_group_as_string() { @Test public void create_with_synched_groups_invalid_groups_claim_name() { UserInfo userInfo = newUserInfo(false, false); - settings.setProperty(OidcConfiguration.GROUPS_SYNC, true); - settings.setProperty(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME, "invalid"); + mockConfigValue(OidcConfiguration.GROUPS_SYNC, "true"); + mockConfigValue(OidcConfiguration.GROUPS_SYNC_CLAIM_NAME, "invalid"); IllegalStateException exception = assertThrows(IllegalStateException.class, () -> underTest.create(userInfo)); assertTrue(exception.getMessage().startsWith("Groups claim 'invalid' is missing in user info")); diff --git a/target/sonar-auth-oidc-plugin-3.0.0-SNAPSHOT.jar b/target/sonar-auth-oidc-plugin-3.0.0-SNAPSHOT.jar new file mode 100644 index 0000000..2285266 Binary files /dev/null and b/target/sonar-auth-oidc-plugin-3.0.0-SNAPSHOT.jar differ