aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/JakartaToJavaxRequestWrapper.java409
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/JakartaToJavaxResponseWrapper.java226
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/RedirectToUrlProvider.java2
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/RelyingPartyRegistrationRepositoryProvider.java8
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlCertificateConverter.java31
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlModule.java2
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlPrivateKeyConverter.java32
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlResponseAuthenticator.java4
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepository.java94
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationResolver.java4
-rw-r--r--server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeSaml2ResponseValidator.java2
-rw-r--r--server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/JakartaToJavaxRequestWrapperTest.java216
-rw-r--r--server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/JakartaToJavaxResponseWrapperTest.java95
-rw-r--r--server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/RelyingPartyRegistrationRepositoryProviderTest.java58
-rw-r--r--server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlModuleTest.java2
-rw-r--r--server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepositoryTest.java185
16 files changed, 348 insertions, 1022 deletions
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/JakartaToJavaxRequestWrapper.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/JakartaToJavaxRequestWrapper.java
deleted file mode 100644
index 89ede705811..00000000000
--- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/JakartaToJavaxRequestWrapper.java
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.auth.saml;
-
-import jakarta.servlet.http.HttpServletRequest;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.security.Principal;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.Locale;
-import java.util.Map;
-import javax.servlet.AsyncContext;
-import javax.servlet.DispatcherType;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import javax.servlet.http.HttpUpgradeHandler;
-import javax.servlet.http.Part;
-
-/**
- * This class is needed only due to the fact that OneLogin Java SAML needs javax HttpServletRequest.
- * It wraps a jakarta.servlet.http.HttpServletRequest and adapts it to javax.servlet.http.HttpServletRequest.
- */
-class JakartaToJavaxRequestWrapper implements javax.servlet.http.HttpServletRequest {
- public static final String NOT_IMPLEMENTED = "Not implemented";
- private final HttpServletRequest delegate;
-
- public JakartaToJavaxRequestWrapper(HttpServletRequest delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public String getAuthType() {
- return delegate.getAuthType();
- }
-
- @Override
- public Cookie[] getCookies() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public long getDateHeader(String s) {
- return delegate.getDateHeader(s);
- }
-
- @Override
- public String getHeader(String s) {
- return delegate.getHeader(s);
- }
-
- @Override
- public Enumeration<String> getHeaders(String s) {
- return delegate.getHeaders(s);
- }
-
- @Override
- public Enumeration<String> getHeaderNames() {
- return delegate.getHeaderNames();
- }
-
- @Override
- public int getIntHeader(String s) {
- return delegate.getIntHeader(s);
- }
-
- @Override
- public String getMethod() {
- return delegate.getMethod();
- }
-
- @Override
- public String getPathInfo() {
- return delegate.getPathInfo();
- }
-
- @Override
- public String getPathTranslated() {
- return delegate.getPathTranslated();
- }
-
- @Override
- public String getContextPath() {
- return delegate.getContextPath();
- }
-
- @Override
- public String getQueryString() {
- return delegate.getQueryString();
- }
-
- @Override
- public String getRemoteUser() {
- return delegate.getRemoteUser();
- }
-
- @Override
- public boolean isUserInRole(String s) {
- return delegate.isUserInRole(s);
- }
-
- @Override
- public Principal getUserPrincipal() {
- return delegate.getUserPrincipal();
- }
-
- @Override
- public String getRequestedSessionId() {
- return delegate.getSession().getId();
- }
-
- @Override
- public String getRequestURI() {
- return delegate.getRequestURI();
- }
-
- @Override
- public StringBuffer getRequestURL() {
- return delegate.getRequestURL();
- }
-
- @Override
- public String getServletPath() {
- return delegate.getServletPath();
- }
-
- @Override
- public HttpSession getSession(boolean b) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public HttpSession getSession() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public String changeSessionId() {
- return delegate.changeSessionId();
- }
-
- @Override
- public boolean isRequestedSessionIdValid() {
- return delegate.isRequestedSessionIdValid();
- }
-
- @Override
- public boolean isRequestedSessionIdFromCookie() {
- return delegate.isRequestedSessionIdFromCookie();
- }
-
- @Override
- public boolean isRequestedSessionIdFromURL() {
- return delegate.isRequestedSessionIdFromURL();
- }
-
- @Override
- public boolean isRequestedSessionIdFromUrl() {
- return delegate.isRequestedSessionIdFromURL();
- }
-
- @Override
- public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void login(String s, String s1) throws ServletException {
- try {
- delegate.login(s, s1);
- } catch (jakarta.servlet.ServletException e) {
- throw new ServletException(e);
- }
- }
-
- @Override
- public void logout() throws ServletException {
- try {
- delegate.logout();
- } catch (jakarta.servlet.ServletException e) {
- throw new ServletException(e);
- }
- }
-
- @Override
- public Collection<Part> getParts() throws IOException, ServletException {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public Part getPart(String s) throws IOException, ServletException {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public <T extends HttpUpgradeHandler> T upgrade(Class<T> aClass) throws IOException, ServletException {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public Object getAttribute(String s) {
- return delegate.getAttribute(s);
- }
-
- @Override
- public Enumeration<String> getAttributeNames() {
- return delegate.getAttributeNames();
- }
-
- @Override
- public String getCharacterEncoding() {
- return delegate.getCharacterEncoding();
- }
-
- @Override
- public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
- delegate.setCharacterEncoding(s);
- }
-
- @Override
- public int getContentLength() {
- return delegate.getContentLength();
- }
-
- @Override
- public long getContentLengthLong() {
- return delegate.getContentLengthLong();
- }
-
- @Override
- public String getContentType() {
- return delegate.getContentType();
- }
-
- @Override
- public ServletInputStream getInputStream() throws IOException {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public String getParameter(String s) {
- return delegate.getParameter(s);
- }
-
- @Override
- public Enumeration<String> getParameterNames() {
- return delegate.getParameterNames();
- }
-
- @Override
- public String[] getParameterValues(String s) {
- return delegate.getParameterValues(s);
- }
-
- @Override
- public Map<String, String[]> getParameterMap() {
- return delegate.getParameterMap();
- }
-
- @Override
- public String getProtocol() {
- return delegate.getProtocol();
- }
-
- @Override
- public String getScheme() {
- return delegate.getScheme();
- }
-
- @Override
- public String getServerName() {
- return delegate.getServerName();
- }
-
- @Override
- public int getServerPort() {
- return delegate.getServerPort();
- }
-
- @Override
- public BufferedReader getReader() throws IOException {
- return delegate.getReader();
- }
-
- @Override
- public String getRemoteAddr() {
- return delegate.getRemoteAddr();
- }
-
- @Override
- public String getRemoteHost() {
- return delegate.getRemoteHost();
- }
-
- @Override
- public void setAttribute(String s, Object o) {
- delegate.setAttribute(s, o);
- }
-
- @Override
- public void removeAttribute(String s) {
- delegate.removeAttribute(s);
- }
-
- @Override
- public Locale getLocale() {
- return delegate.getLocale();
- }
-
- @Override
- public Enumeration<Locale> getLocales() {
- return delegate.getLocales();
- }
-
- @Override
- public boolean isSecure() {
- return delegate.isSecure();
- }
-
- @Override
- public RequestDispatcher getRequestDispatcher(String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public String getRealPath(String s) {
- return delegate.getServletContext().getRealPath(s);
- }
-
- @Override
- public int getRemotePort() {
- return delegate.getRemotePort();
- }
-
- @Override
- public String getLocalName() {
- return delegate.getLocalName();
- }
-
- @Override
- public String getLocalAddr() {
- return delegate.getLocalAddr();
- }
-
- @Override
- public int getLocalPort() {
- return delegate.getLocalPort();
- }
-
- @Override
- public ServletContext getServletContext() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public AsyncContext startAsync() throws IllegalStateException {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public boolean isAsyncStarted() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public boolean isAsyncSupported() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public AsyncContext getAsyncContext() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public DispatcherType getDispatcherType() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-}
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/JakartaToJavaxResponseWrapper.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/JakartaToJavaxResponseWrapper.java
deleted file mode 100644
index 65f1ffe2249..00000000000
--- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/JakartaToJavaxResponseWrapper.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.auth.saml;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Collection;
-import java.util.Locale;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.Cookie;
-
-/**
- * This class is needed only due to the fact that OneLogin Java SAML needs javax HttpServletResponse.
- * It wraps a jakarta.servlet.http.HttpServletResponse and adapts it to javax.servlet.http.HttpServletResponse.
- */
-class JakartaToJavaxResponseWrapper implements javax.servlet.http.HttpServletResponse {
- public static final String NOT_IMPLEMENTED = "Not implemented";
- private final HttpServletResponse delegate;
-
- public JakartaToJavaxResponseWrapper(HttpServletResponse delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public void addCookie(Cookie cookie) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public boolean containsHeader(String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public String encodeURL(String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public String encodeRedirectURL(String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public String encodeUrl(String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public String encodeRedirectUrl(String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void sendError(int i, String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void sendError(int i) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void sendRedirect(String s) throws IOException {
- delegate.sendRedirect(s);
- }
-
- @Override
- public void setDateHeader(String s, long l) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void addDateHeader(String s, long l) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void setHeader(String s, String s1) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void addHeader(String s, String s1) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void setIntHeader(String s, int i) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void addIntHeader(String s, int i) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void setStatus(int i) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void setStatus(int i, String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public int getStatus() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public String getHeader(String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public Collection<String> getHeaders(String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public Collection<String> getHeaderNames() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public String getCharacterEncoding() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public String getContentType() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public ServletOutputStream getOutputStream() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public PrintWriter getWriter() throws IOException {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void setCharacterEncoding(String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void setContentLength(int i) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void setContentLengthLong(long l) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void setContentType(String s) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void setBufferSize(int i) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public int getBufferSize() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void flushBuffer() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void resetBuffer() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public boolean isCommitted() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void reset() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public void setLocale(Locale locale) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-
- @Override
- public Locale getLocale() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED);
- }
-}
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/RedirectToUrlProvider.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/RedirectToUrlProvider.java
index c3ce97a7a0f..7121c595163 100644
--- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/RedirectToUrlProvider.java
+++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/RedirectToUrlProvider.java
@@ -35,7 +35,7 @@ public class RedirectToUrlProvider {
private final RelyingPartyRegistrationRepositoryProvider relyingPartyRegistrationRepositoryProvider;
- public RedirectToUrlProvider(RelyingPartyRegistrationRepositoryProvider relyingPartyRegistrationRepositoryProvider) {
+ RedirectToUrlProvider(RelyingPartyRegistrationRepositoryProvider relyingPartyRegistrationRepositoryProvider) {
this.relyingPartyRegistrationRepositoryProvider = relyingPartyRegistrationRepositoryProvider;
}
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/RelyingPartyRegistrationRepositoryProvider.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/RelyingPartyRegistrationRepositoryProvider.java
index 2b7d917454c..e04d342ed50 100644
--- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/RelyingPartyRegistrationRepositoryProvider.java
+++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/RelyingPartyRegistrationRepositoryProvider.java
@@ -27,13 +27,17 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
public class RelyingPartyRegistrationRepositoryProvider {
private final SamlSettings samlSettings;
+ private final SamlCertificateConverter samlCertificateConverter;
+ private final SamlPrivateKeyConverter samlPrivateKeyConverter;
- public RelyingPartyRegistrationRepositoryProvider(SamlSettings samlSettings) {
+ public RelyingPartyRegistrationRepositoryProvider(SamlSettings samlSettings, SamlCertificateConverter samlCertificateConverter, SamlPrivateKeyConverter samlPrivateKeyConverter) {
this.samlSettings = samlSettings;
+ this.samlCertificateConverter = samlCertificateConverter;
+ this.samlPrivateKeyConverter = samlPrivateKeyConverter;
}
RelyingPartyRegistrationRepository provide(@Nullable String callbackUrl) {
- return new SonarqubeRelyingPartyRegistrationRepository(samlSettings, callbackUrl);
+ return new SonarqubeRelyingPartyRegistrationRepository(samlSettings, samlCertificateConverter, samlPrivateKeyConverter, callbackUrl);
}
}
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlCertificateConverter.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlCertificateConverter.java
new file mode 100644
index 00000000000..bcc4d768027
--- /dev/null
+++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlCertificateConverter.java
@@ -0,0 +1,31 @@
+package org.sonar.auth.saml;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+class SamlCertificateConverter {
+
+ X509Certificate toX509Certificate(String certificateString) {
+ String cleanedCertificateString = sanitizeCertificateString(certificateString);
+
+ byte[] decoded = Base64.getDecoder().decode(cleanedCertificateString);
+ try {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(decoded));
+ } catch (CertificateException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String sanitizeCertificateString(String certificateString) {
+ return certificateString
+ .replace("-----BEGIN CERTIFICATE-----", "")
+ .replace("-----END CERTIFICATE-----", "")
+ .replaceAll("\\s+", "");
+ }
+}
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlModule.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlModule.java
index 1690e9a2447..81c5e38dcab 100644
--- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlModule.java
+++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlModule.java
@@ -32,8 +32,10 @@ public class SamlModule extends Module {
RelyingPartyRegistrationRepositoryProvider.class,
SamlAuthenticator.class,
SamlConfiguration.class,
+ SamlCertificateConverter.class,
SamlIdentityProvider.class,
SamlMessageIdChecker.class,
+ SamlPrivateKeyConverter.class,
SamlResponseAuthenticator.class,
SamlSettings.class,
RedirectToUrlProvider.class,
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlPrivateKeyConverter.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlPrivateKeyConverter.java
new file mode 100644
index 00000000000..50ccb152e96
--- /dev/null
+++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlPrivateKeyConverter.java
@@ -0,0 +1,32 @@
+package org.sonar.auth.saml;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+class SamlPrivateKeyConverter {
+ PrivateKey toPrivateKey(String privateKeyString) {
+ String cleanedPrivateKeyString = sanitizePrivateKeyString(privateKeyString);
+
+ byte[] decoded = Base64.getDecoder().decode(cleanedPrivateKeyString);
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return keyFactory.generatePrivate(keySpec);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String sanitizePrivateKeyString(String privateKeyString) {
+ return privateKeyString
+ .replace("-----BEGIN PRIVATE KEY-----", "")
+ .replace("-----END PRIVATE KEY-----", "")
+ .replaceAll("\\s+", "");
+ }
+}
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlResponseAuthenticator.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlResponseAuthenticator.java
index 284baf1d777..a20e41080df 100644
--- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlResponseAuthenticator.java
+++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SamlResponseAuthenticator.java
@@ -30,12 +30,12 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2A
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
@ServerSide
-public class SamlResponseAuthenticator {
+class SamlResponseAuthenticator {
private final OpenSaml4AuthenticationProvider openSaml4AuthenticationProvider;
private final RelyingPartyRegistrationRepositoryProvider relyingPartyRegistrationRepositoryProvider;
- public SamlResponseAuthenticator(OpenSaml4AuthenticationProvider openSaml4AuthenticationProvider,
+ SamlResponseAuthenticator(OpenSaml4AuthenticationProvider openSaml4AuthenticationProvider,
RelyingPartyRegistrationRepositoryProvider relyingPartyRegistrationRepositoryProvider) {
this.openSaml4AuthenticationProvider = openSaml4AuthenticationProvider;
this.relyingPartyRegistrationRepositoryProvider = relyingPartyRegistrationRepositoryProvider;
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepository.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepository.java
index 85295ef6eed..f37321903ee 100644
--- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepository.java
+++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepository.java
@@ -19,16 +19,9 @@
*/
package org.sonar.auth.saml;
-import java.io.ByteArrayInputStream;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
+import com.google.common.annotations.VisibleForTesting;
import java.security.PrivateKey;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.Base64;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.springframework.security.saml2.core.Saml2X509Credential;
@@ -41,17 +34,23 @@ public class SonarqubeRelyingPartyRegistrationRepository implements RelyingParty
private static final String ANY_URL = "http://anyurl";
private final SamlSettings samlSettings;
+ private final SamlCertificateConverter samlCertificateConverter;
+ private final SamlPrivateKeyConverter samlPrivateKeyConverter;
@CheckForNull
private final String callbackUrl;
- public SonarqubeRelyingPartyRegistrationRepository(SamlSettings samlSettings, @Nullable String callbackUrl) {
+ SonarqubeRelyingPartyRegistrationRepository(SamlSettings samlSettings, SamlCertificateConverter samlCertificateConverter, SamlPrivateKeyConverter samlPrivateKeyConverter,
+ @Nullable String callbackUrl) {
this.samlSettings = samlSettings;
+ this.samlCertificateConverter = samlCertificateConverter;
+ this.samlPrivateKeyConverter = samlPrivateKeyConverter;
this.callbackUrl = callbackUrl;
}
@Override
public RelyingPartyRegistration findByRegistrationId(String registrationId) {
+ X509Certificate x509Certificate = samlCertificateConverter.toX509Certificate(samlSettings.getCertificate());
RelyingPartyRegistration.Builder builder = RelyingPartyRegistration.withRegistrationId("saml")
.assertionConsumerServiceLocation(callbackUrl != null ? callbackUrl : ANY_URL)
.assertionConsumerServiceBinding(Saml2MessageBinding.POST)
@@ -59,72 +58,33 @@ public class SonarqubeRelyingPartyRegistrationRepository implements RelyingParty
.assertingPartyMetadata(metadata -> metadata
.entityId(samlSettings.getProviderId())
.singleSignOnServiceLocation(samlSettings.getLoginUrl())
- .verificationX509Credentials(c -> c.add(convertStringToSaml2X509Credential(samlSettings.getCertificate())))
+ .verificationX509Credentials(c -> c.add(Saml2X509Credential.verification(x509Certificate)))
.wantAuthnRequestsSigned(samlSettings.isSignRequestsEnabled())
);
-
- if(samlSettings.isSignRequestsEnabled()) {
- builder
- .signingX509Credentials(c -> c.add(convertStringToSaml2X509Credential(samlSettings.getServiceProviderCertificate(),
- samlSettings.getServiceProviderPrivateKey().get(), Saml2X509Credential.Saml2X509CredentialType.SIGNING)))
- .decryptionX509Credentials(c -> c.add(convertStringToSaml2X509Credential(samlSettings.getServiceProviderCertificate(),
- samlSettings.getServiceProviderPrivateKey().get(), Saml2X509Credential.Saml2X509CredentialType.DECRYPTION)));
- }
+ addSignRequestFieldsIfNecessary(builder);
return builder.build();
}
- public Saml2X509Credential convertStringToSaml2X509Credential(String certificateString, String privateKey, Saml2X509Credential.Saml2X509CredentialType type){
- return new Saml2X509Credential(convertStringToPrivateKey(privateKey), getX509Certificate(certificateString), type);
- }
-
- public Saml2X509Credential convertStringToSaml2X509Credential(String certificateString){
- X509Certificate certificate = getX509Certificate(certificateString);
-
- // Create and return the Saml2X509Credential
- return Saml2X509Credential.verification(certificate);
- }
-
-
- public static PrivateKey convertStringToPrivateKey(String privateKeyString){
- // Remove the "BEGIN" and "END" lines and any whitespace
- String cleanedPrivateKeyString = privateKeyString
- .replace("-----BEGIN PRIVATE KEY-----", "")
- .replace("-----END PRIVATE KEY-----", "")
- .replaceAll("\\s+", "");
-
- // Decode the base64 encoded string
- byte[] decoded = Base64.getDecoder().decode(cleanedPrivateKeyString);
-
- // Create a PrivateKey from the decoded bytes
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
- KeyFactory keyFactory = null;
- try {
- keyFactory = KeyFactory.getInstance("RSA");
- return keyFactory.generatePrivate(keySpec);
- } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
- throw new RuntimeException(e);
+ private void addSignRequestFieldsIfNecessary(RelyingPartyRegistration.Builder builder) {
+ if (!samlSettings.isSignRequestsEnabled()) {
+ return;
}
+ String privateKeyString = samlSettings.getServiceProviderPrivateKey().orElseThrow(() -> new IllegalStateException("Sign requests is enabled but private key is missing"));
+ String serviceProviderCertificateString = samlSettings.getServiceProviderCertificate();
+ PrivateKey privateKey = samlPrivateKeyConverter.toPrivateKey(privateKeyString);
+ X509Certificate spX509Certificate = samlCertificateConverter.toX509Certificate(serviceProviderCertificateString);
+ builder
+ .signingX509Credentials(c -> c.add(Saml2X509Credential.signing(privateKey, spX509Certificate)))
+ .decryptionX509Credentials(c -> c.add(Saml2X509Credential.decryption(privateKey, spX509Certificate)));
}
+ @VisibleForTesting
+ SamlSettings getSamlSettings() {
+ return samlSettings;
+ }
- private static X509Certificate getX509Certificate(String certificateString) {
- String cleanedCertificateString = certificateString
- .replace("-----BEGIN CERTIFICATE-----", "")
- .replace("-----END CERTIFICATE-----", "")
- .replaceAll("\\s+", "");
-
- // Decode the base64 encoded string
- byte[] decoded = Base64.getDecoder().decode(cleanedCertificateString);
-
- // Create an X509Certificate from the decoded bytes
- CertificateFactory factory;
- X509Certificate certificate;
- try {
- factory = CertificateFactory.getInstance("X.509");
- certificate = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(decoded));
- } catch (CertificateException e) {
- throw new RuntimeException(e);
- }
- return certificate;
+ @VisibleForTesting
+ String getCallbackUrl() {
+ return callbackUrl;
}
}
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationResolver.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationResolver.java
index 61e2812bfd7..1c7a776e85f 100644
--- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationResolver.java
+++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationResolver.java
@@ -26,13 +26,13 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
-public class SonarqubeRelyingPartyRegistrationResolver implements RelyingPartyRegistrationResolver {
+class SonarqubeRelyingPartyRegistrationResolver implements RelyingPartyRegistrationResolver {
private final RelyingPartyRegistrationRepositoryProvider relyingPartyRegistrationRepositoryProvider;
@Nullable
private final String callbackUrl;
- public SonarqubeRelyingPartyRegistrationResolver(RelyingPartyRegistrationRepositoryProvider relyingPartyRegistrationRepositoryProvider,
+ SonarqubeRelyingPartyRegistrationResolver(RelyingPartyRegistrationRepositoryProvider relyingPartyRegistrationRepositoryProvider,
@Nullable String callbackUrl) {
this.relyingPartyRegistrationRepositoryProvider = relyingPartyRegistrationRepositoryProvider;
this.callbackUrl = callbackUrl;
diff --git a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeSaml2ResponseValidator.java b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeSaml2ResponseValidator.java
index d24c2a1428b..98b4d5697c5 100644
--- a/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeSaml2ResponseValidator.java
+++ b/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeSaml2ResponseValidator.java
@@ -29,7 +29,7 @@ import static org.springframework.security.saml2.core.Saml2ErrorCodes.INVALID_IN
import static org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.ResponseToken;
import static org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.createDefaultResponseValidator;
-public class SonarqubeSaml2ResponseValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {
+class SonarqubeSaml2ResponseValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {
private final Converter<ResponseToken, Saml2ResponseValidatorResult> delegate = createDefaultResponseValidator();
diff --git a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/JakartaToJavaxRequestWrapperTest.java b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/JakartaToJavaxRequestWrapperTest.java
deleted file mode 100644
index ae12a2cd612..00000000000
--- a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/JakartaToJavaxRequestWrapperTest.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.auth.saml;
-
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpSession;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.security.Principal;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import javax.servlet.ServletException;
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.assertThatException;
-import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.when;
-
-class JakartaToJavaxRequestWrapperTest {
-
- @Test
- void delegate_methods() throws IOException, ServletException, jakarta.servlet.ServletException {
-
- HttpServletRequest delegateRequest = getDelegateRequest();
-
- JakartaToJavaxRequestWrapper underTest = new JakartaToJavaxRequestWrapper(delegateRequest);
-
- assertThat(underTest.getServerPort()).isEqualTo(80);
- assertThat(underTest.getScheme()).isEqualTo("https");
- assertThat(underTest.getServerName()).isEqualTo("hostname");
- assertThat(underTest.getRequestURL()).hasToString("https://hostname:80/path");
- assertThat(underTest.getRequestURI()).isEqualTo("/request-uri");
- assertThat(underTest.getQueryString()).isEqualTo("param1=value1");
- assertThat(underTest.getContextPath()).isEqualTo("/context-path");
- assertThat(underTest.getMethod()).isEqualTo("POST");
- assertThat(underTest.getParameter("param1")).isEqualTo("value1");
- assertThat(underTest.getParameterValues("param1")).containsExactly("value1");
- assertThat(underTest.getHeader("header1")).isEqualTo("hvalue1");
- assertThat(underTest.getHeaders("header1")).isEqualTo(delegateRequest.getHeaders("header1"));
- assertThat(underTest.getHeaderNames()).isEqualTo(delegateRequest.getHeaderNames());
- assertThat(underTest.getRemoteAddr()).isEqualTo("192.168.0.1");
- assertThat(underTest.getRemoteHost()).isEqualTo("remoteHost");
- assertThat(underTest.getRemotePort()).isEqualTo(80);
- assertThat(underTest.getServletPath()).isEqualTo("/servlet-path");
- assertThat(underTest.getReader()).isEqualTo(delegateRequest.getReader());
- assertThat(underTest.getAuthType()).isEqualTo("authType");
- assertThat(underTest.getDateHeader("header1")).isEqualTo(1L);
- assertThat(underTest.getIntHeader("header1")).isEqualTo(1);
- assertThat(underTest.getPathInfo()).isEqualTo("/path-info");
- assertThat(underTest.getPathTranslated()).isEqualTo("/path-translated");
- assertThat(underTest.getRemoteUser()).isEqualTo("remoteUser");
- assertThat(underTest.isUserInRole("role")).isFalse();
- assertThat(underTest.getRequestedSessionId()).isEqualTo("sessionId");
- assertThat(underTest.getProtocol()).isEqualTo("protocol");
- assertThat(underTest.getContentType()).isEqualTo("content-type");
- assertThat(underTest.getContentLength()).isEqualTo(1);
- assertThat(underTest.getContentLengthLong()).isEqualTo(1L);
- assertThat(underTest.getParameterNames()).isEqualTo(delegateRequest.getParameterNames());
- assertThat(underTest.getLocale()).isEqualTo(Locale.ENGLISH);
- assertThat(underTest.getLocales()).isEqualTo(delegateRequest.getLocales());
- assertThat(underTest.getUserPrincipal()).isEqualTo(delegateRequest.getUserPrincipal());
- assertThat(underTest.getLocalName()).isEqualTo("localName");
- assertThat(underTest.getLocalAddr()).isEqualTo("localAddress");
- assertThat(underTest.getLocalPort()).isEqualTo(80);
- assertThat(underTest.getParameterMap()).isEqualTo(delegateRequest.getParameterMap());
- assertThat(underTest.getAttribute("key")).isEqualTo("value");
- assertThat(underTest.getAttributeNames()).isEqualTo(delegateRequest.getAttributeNames());
- assertThat(underTest.getCharacterEncoding()).isEqualTo("encoding");
-
- assertTrue(underTest.isRequestedSessionIdValid());
- assertTrue(underTest.isRequestedSessionIdFromCookie());
- assertTrue(underTest.isRequestedSessionIdFromURL());
- assertTrue(underTest.isRequestedSessionIdFromUrl());
- assertTrue(underTest.isSecure());
-
- underTest.changeSessionId();
- verify(delegateRequest).changeSessionId();
-
- underTest.setAttribute("name", "value");
- verify(delegateRequest).setAttribute("name", "value");
-
- underTest.setCharacterEncoding("encoding");
- verify(delegateRequest).setCharacterEncoding("encoding");
-
- underTest.removeAttribute("name");
- verify(delegateRequest).removeAttribute("name");
-
- underTest.login("name", "password");
- verify(delegateRequest).login("name", "password");
-
- underTest.logout();
- verify(delegateRequest).logout();
- }
-
- @Test
- void methodsNotImplemented_throwUnsupportedOperationException() throws IOException {
- HttpServletRequest delegateRequest = getDelegateRequest();
-
- JakartaToJavaxRequestWrapper underTest = new JakartaToJavaxRequestWrapper(delegateRequest);
-
- assertThatException().isThrownBy(underTest::getInputStream).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getParts).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getCookies).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getSession).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getAsyncContext).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getDispatcherType).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::isAsyncSupported).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::isAsyncStarted).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::startAsync).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getServletContext).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getInputStream).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.upgrade(null)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.getSession(false)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.authenticate(null)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.getPart(null)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.getRequestDispatcher(null)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.getRequestDispatcher(null)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.startAsync(null, null)).isInstanceOf(UnsupportedOperationException.class);
-
- verifyNoInteractions(delegateRequest);
- }
-
- HttpServletRequest getDelegateRequest() throws IOException {
- HttpServletRequest delegateRequest = mock(HttpServletRequest.class);
- Enumeration<String> stringEnumeration = Collections.enumeration(Collections.emptySet());
- Enumeration<Locale> localeEnumeration = Collections.enumeration(Collections.emptySet());
-
- Map<String, String[]> paramterMap = new HashMap<>();
- when(delegateRequest.getParameterMap()).thenReturn(paramterMap);
-
- HttpSession httpSession = mock(HttpSession.class);
- when(httpSession.getId()).thenReturn("sessionId");
-
- Principal principal = mock(Principal.class);
- when(principal.getName()).thenReturn("name");
- when(delegateRequest.getUserPrincipal()).thenReturn(principal);
-
- when(delegateRequest.getSession()).thenReturn(httpSession);
-
- when(delegateRequest.getAuthType()).thenReturn("authType");
- when(delegateRequest.getCookies()).thenReturn(new jakarta.servlet.http.Cookie[0]);
- when(delegateRequest.getDateHeader("header1")).thenReturn(1L);
- when(delegateRequest.getHeader("header1")).thenReturn("hvalue1");
- when(delegateRequest.getHeaders("header1")).thenReturn(stringEnumeration);
- when(delegateRequest.getHeaderNames()).thenReturn(stringEnumeration);
- when(delegateRequest.getIntHeader("header1")).thenReturn(1);
- when(delegateRequest.getMethod()).thenReturn("POST");
- when(delegateRequest.getPathInfo()).thenReturn("/path-info");
- when(delegateRequest.getPathTranslated()).thenReturn("/path-translated");
- when(delegateRequest.getContextPath()).thenReturn("/context-path");
- when(delegateRequest.getQueryString()).thenReturn("param1=value1");
- when(delegateRequest.getRemoteUser()).thenReturn("remoteUser");
- when(delegateRequest.getRequestURI()).thenReturn("/request-uri");
- when(delegateRequest.getRequestURL()).thenReturn(new StringBuffer("https://hostname:80/path"));
- when(delegateRequest.getServletPath()).thenReturn("/servlet-path");
- when(delegateRequest.getServerName()).thenReturn("hostname");
- when(delegateRequest.getServerPort()).thenReturn(80);
- when(delegateRequest.isSecure()).thenReturn(true);
- when(delegateRequest.getRemoteHost()).thenReturn("remoteHost");
- when(delegateRequest.getLocale()).thenReturn(Locale.ENGLISH);
- when(delegateRequest.getLocales()).thenReturn(localeEnumeration);
- when(delegateRequest.getRemoteAddr()).thenReturn("192.168.0.1");
- when(delegateRequest.getRemotePort()).thenReturn(80);
- BufferedReader bufferedReader = mock(BufferedReader.class);
- when(delegateRequest.getReader()).thenReturn(bufferedReader);
- jakarta.servlet.http.Cookie[] cookies = new jakarta.servlet.http.Cookie[0];
- when(delegateRequest.getCookies()).thenReturn(cookies);
- when(delegateRequest.getScheme()).thenReturn("https");
- when(delegateRequest.getParameter("param1")).thenReturn("value1");
- when(delegateRequest.getParameterValues("param1")).thenReturn(new String[]{"value1"});
- when(delegateRequest.getHeader("header1")).thenReturn("hvalue1");
- Enumeration<String> headers = mock(Enumeration.class);
- when(delegateRequest.getHeaders("header1")).thenReturn(headers);
- when(delegateRequest.isRequestedSessionIdValid()).thenReturn(true);
- when(delegateRequest.isRequestedSessionIdFromCookie()).thenReturn(true);
- when(delegateRequest.isRequestedSessionIdFromURL()).thenReturn(true);
- when(delegateRequest.getProtocol()).thenReturn("protocol");
- when(delegateRequest.getContentType()).thenReturn("content-type");
- when(delegateRequest.getContentLength()).thenReturn(1);
- when(delegateRequest.getContentLengthLong()).thenReturn(1L);
- when(delegateRequest.getParameterNames()).thenReturn(stringEnumeration);
- when(delegateRequest.getLocalName()).thenReturn("localName");
- when(delegateRequest.getLocalAddr()).thenReturn("localAddress");
- when(delegateRequest.getLocalPort()).thenReturn(80);
- when(delegateRequest.getAttribute("key")).thenReturn("value");
- when(delegateRequest.getAttributeNames()).thenReturn(stringEnumeration);
- when(delegateRequest.getCharacterEncoding()).thenReturn("encoding");
-
- return delegateRequest;
- }
-
-}
diff --git a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/JakartaToJavaxResponseWrapperTest.java b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/JakartaToJavaxResponseWrapperTest.java
deleted file mode 100644
index d07bf0c70bf..00000000000
--- a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/JakartaToJavaxResponseWrapperTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.auth.saml;
-
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.assertThatException;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.when;
-
-class JakartaToJavaxResponseWrapperTest {
-
- private final HttpServletResponse delegate = getResponse();
-
- private HttpServletResponse getResponse() {
- HttpServletResponse mockedResponse = mock(HttpServletResponse.class);
- when(mockedResponse.containsHeader(anyString())).thenReturn(false);
- return mockedResponse;
- }
-
- private final JakartaToJavaxResponseWrapper underTest = new JakartaToJavaxResponseWrapper(delegate);
-
- @Test
- void sendRedirectIsDelegated() throws IOException {
- underTest.sendRedirect("redirectUrl");
-
- verify(delegate).sendRedirect("redirectUrl");
- }
-
- @Test
- void methodsNotImplemented_throwUnsupportedOperationException() {
- assertThatException().isThrownBy(() -> underTest.setBufferSize(0)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::resetBuffer).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::flushBuffer).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.setLocale(null)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.setContentType("type")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.setContentLengthLong(0L)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.setContentLength(0)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.setCharacterEncoding(null)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.setStatus(0, "")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.setStatus(0)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.addIntHeader("",0)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.setIntHeader("",0)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.addDateHeader("",0l)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.setDateHeader("",0l)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.addHeader("","")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.setHeader("","")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.sendError(0)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.sendError(0, "")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.addCookie(null)).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.encodeURL("")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.encodeRedirectURL("")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.encodeUrl("")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.encodeRedirectUrl("")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getCharacterEncoding).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getContentType).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getStatus).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.getHeader("")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getBufferSize).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getOutputStream).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getWriter).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getLocale).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.getHeaders("")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::getHeaderNames).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(() -> underTest.containsHeader("")).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::isCommitted).isInstanceOf(UnsupportedOperationException.class);
- assertThatException().isThrownBy(underTest::reset).isInstanceOf(UnsupportedOperationException.class);
-
- verifyNoInteractions(delegate);
- }
-
-
-}
diff --git a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/RelyingPartyRegistrationRepositoryProviderTest.java b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/RelyingPartyRegistrationRepositoryProviderTest.java
new file mode 100644
index 00000000000..9172f53d328
--- /dev/null
+++ b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/RelyingPartyRegistrationRepositoryProviderTest.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.auth.saml;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@ExtendWith(MockitoExtension.class)
+class RelyingPartyRegistrationRepositoryProviderTest {
+
+ @Mock
+ private SamlSettings samlSettings;
+
+ @InjectMocks
+ private RelyingPartyRegistrationRepositoryProvider relyingPartyRegistrationRepositoryProvider;
+
+ @Test
+ void provide_whenNullCallback_returnsRelyingPartyRegistrationRepository() {
+ SonarqubeRelyingPartyRegistrationRepository relyingPartyRegistrationRepository = (SonarqubeRelyingPartyRegistrationRepository) relyingPartyRegistrationRepositoryProvider.provide(null);
+
+ assertThat(relyingPartyRegistrationRepository).isNotNull();
+ assertThat(relyingPartyRegistrationRepository.getSamlSettings()).isEqualTo(samlSettings);
+ assertThat(relyingPartyRegistrationRepository.getCallbackUrl()).isNull();
+ }
+
+ @Test
+ void provide_whenCallbackSet_returnsRelyingPartyRegistrationRepository() {
+ SonarqubeRelyingPartyRegistrationRepository relyingPartyRegistrationRepository =
+ (SonarqubeRelyingPartyRegistrationRepository) relyingPartyRegistrationRepositoryProvider.provide("callback");
+
+ assertThat(relyingPartyRegistrationRepository).isNotNull();
+ assertThat(relyingPartyRegistrationRepository.getSamlSettings()).isEqualTo(samlSettings);
+ assertThat(relyingPartyRegistrationRepository.getCallbackUrl()).isEqualTo("callback");
+ }
+
+}
diff --git a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlModuleTest.java b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlModuleTest.java
index e40790d85ac..fc432a50648 100644
--- a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlModuleTest.java
+++ b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SamlModuleTest.java
@@ -30,6 +30,6 @@ public class SamlModuleTest {
public void verify_count_of_added_components() {
ListContainer container = new ListContainer();
new SamlModule().configure(container);
- assertThat(container.getAddedObjects()).hasSize(17);
+ assertThat(container.getAddedObjects()).hasSize(25);
}
}
diff --git a/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepositoryTest.java b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepositoryTest.java
new file mode 100644
index 00000000000..4af992cc598
--- /dev/null
+++ b/server/sonar-auth-saml/src/test/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepositoryTest.java
@@ -0,0 +1,185 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.auth.saml;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.saml2.core.Saml2X509Credential;
+import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
+import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class SonarqubeRelyingPartyRegistrationRepositoryTest {
+
+ private static final String VALID_CERTIFICATE_STRING = """
+ -----BEGIN CERTIFICATE-----
+ MIIDqDCCApCgAwIBAgIGAYcJtZATMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYDVQQGEwJVUzETMBEG
+ A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
+ MBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi05ODIxMjUzNzEcMBoGCSqGSIb3DQEJ
+ ARYNaW5mb0Bva3RhLmNvbTAeFw0yMzAzMjIxNDI0MDZaFw0zMzAzMjIxNDI1MDZaMIGUMQswCQYD
+ VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsG
+ A1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi05ODIxMjUzNzEc
+ MBoGCSqGSIb3DQEJARYNaW5mb0Bva3RhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ ggEBALNloIuL4r7mUDkZcD7hdYrUw335hlRGWFjMV1OjRFhI/hMw2NUq+KgQjyte6zpE7a9nMlWw
+ lRqkEVAuCW7ZcjO/Wk1TECiKwS2nYN3InPuuF6TCk0/gJSFZuiKXdtUUDod5viNJyEXb0Ol8rtIl
+ TRffbSRiaWPvPykhtDZVObS0QDpBo4wVK1C+G+3e0/P/YCD6g4+zJWFYT4sbY6Ee97xhVwcdO6ZS
+ jfba6lYtmUCUwRPRLQPkM9xAjKinVu5mmNPY8sXuxIRs/yEvhxnhTOnbvnU5oNU5DWI28vAiMOlD
+ SpQTUQZjqLDa9AHyvkWT/j0WU5AI1IFgLqB5gg6dY8UCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+ DRAEvjil9vPgCMJSYl3x5i83is4JlZ6SeN8mXxJfj35pQb+sLa+XrnITnAk6fnYX4NYYEwDGD+Vq
+ AnSIRsJEeMYTnQWMGLp5er88IDltDlfIMSs8WgVxWkJ6R66BVGFQRVo9IJQRVuBXrPTahL43ZBn1
+ SynJxMl9tceAb6Q18ncyK9DpsLfrgpkerPcLjhjWiCl9iEpfUEzGEeLzin9OyfSwTtMWcPLrqgUb
+ nWiSEIvNnGzGVQunZaUF4cLxlstgWJzsWLcuzr0cdSO7eIsAtAMVDqXY1ESpewRYqzDeXmj+eKso
+ k5X4rDjQIGfE0XskScXfKyY7CVklfmW1dCuzdw==
+ -----END CERTIFICATE-----
+ """;
+ public static final String APPLICATION_ID = "applicationId";
+ public static final String PROVIDER_ID = "providerId";
+ public static final String SSO_URL = "ssoUrl";
+ public static final String CERTIF_STRING = "certifString";
+ private static final String CERTIF_SP_STRING = "certifSpString";
+ public static final String PRIVATE_KEY = "privateKey";
+ public static final String CALLBACK_URL = "callback/";
+
+ @Mock
+ private SamlSettings samlSettings;
+ @Mock
+ private SamlCertificateConverter samlCertificateConverter;
+ @Mock
+ private SamlPrivateKeyConverter samlPrivateKeyConverter;
+
+ @Test
+ void findByRegistrationId_whenSignRequestsIsDisabledAndAllFieldSet_succeeds() {
+ when(samlSettings.getApplicationId()).thenReturn(APPLICATION_ID);
+ when(samlSettings.getProviderId()).thenReturn(PROVIDER_ID);
+ when(samlSettings.getLoginUrl()).thenReturn(SSO_URL);
+ when(samlSettings.getCertificate()).thenReturn(CERTIF_STRING);
+
+ X509Certificate mockedCertificate = mockCertificate();
+
+ RelyingPartyRegistration registration = findRegistration(CALLBACK_URL);
+
+ assertCommonFields(registration, mockedCertificate, CALLBACK_URL);
+
+ assertThat(registration.getAssertingPartyMetadata().getWantAuthnRequestsSigned()).isFalse();
+ assertThat(registration.getSigningX509Credentials()).isEmpty();
+ assertThat(registration.getDecryptionX509Credentials()).isEmpty();
+
+ verifyNoInteractions(samlPrivateKeyConverter);
+ }
+
+ @Test
+ void findByRegistrationId_whenCallbackUrlIsNull_succeeds() {
+ when(samlSettings.getApplicationId()).thenReturn(APPLICATION_ID);
+ when(samlSettings.getProviderId()).thenReturn(PROVIDER_ID);
+ when(samlSettings.getLoginUrl()).thenReturn(SSO_URL);
+ when(samlSettings.getCertificate()).thenReturn(CERTIF_STRING);
+
+ X509Certificate mockedCertificate = mockCertificate();
+
+ RelyingPartyRegistration registration = findRegistration(null);
+
+ assertCommonFields(registration, mockedCertificate, "http://anyurl");
+ }
+
+ @Test
+ void findByRegistrationId_whenSignRequestIsEnabledAndAllFieldSet_succeeds() {
+ when(samlSettings.getApplicationId()).thenReturn(APPLICATION_ID);
+ when(samlSettings.getProviderId()).thenReturn(PROVIDER_ID);
+ when(samlSettings.getLoginUrl()).thenReturn(SSO_URL);
+ when(samlSettings.getCertificate()).thenReturn(CERTIF_STRING);
+ when(samlSettings.isSignRequestsEnabled()).thenReturn(true);
+ when(samlSettings.getServiceProviderPrivateKey()).thenReturn(Optional.of(PRIVATE_KEY));
+ when(samlSettings.getServiceProviderCertificate()).thenReturn(CERTIF_SP_STRING);
+
+ X509Certificate certificate = mockCertificate();
+ X509Certificate serviceProviderCertificate = mockServiceProviderCertificate();
+ PrivateKey privateKey = mockPrivateKey();
+
+ RelyingPartyRegistration registration = findRegistration(CALLBACK_URL);
+
+ assertCommonFields(registration, certificate, CALLBACK_URL);
+
+ assertThat(registration.getAssertingPartyMetadata().getWantAuthnRequestsSigned()).isTrue();
+ assertThat(registration.getSigningX509Credentials()).containsExactly(Saml2X509Credential.signing(privateKey, serviceProviderCertificate));
+ assertThat(registration.getDecryptionX509Credentials()).containsExactly(Saml2X509Credential.decryption(privateKey, serviceProviderCertificate));
+ }
+
+ @Test
+ void findByRegistrationId_whenSignRequestIsEnabledAndPrivateKeyEmpty_throws() {
+ when(samlSettings.getApplicationId()).thenReturn(APPLICATION_ID);
+ when(samlSettings.getProviderId()).thenReturn(PROVIDER_ID);
+ when(samlSettings.getLoginUrl()).thenReturn(SSO_URL);
+ when(samlSettings.getCertificate()).thenReturn(CERTIF_STRING);
+ when(samlSettings.isSignRequestsEnabled()).thenReturn(true);
+ when(samlSettings.getServiceProviderPrivateKey()).thenReturn(Optional.empty());
+ mockCertificate();
+
+ assertThatIllegalStateException()
+ .isThrownBy(() -> findRegistration(CALLBACK_URL))
+ .withMessage("Sign requests is enabled but private key is missing");
+ }
+
+ private static void assertCommonFields(RelyingPartyRegistration registration, X509Certificate certificate, String callbackUrl) {
+ assertThat(registration.getRegistrationId()).isEqualTo("saml");
+ assertThat(registration.getAssertionConsumerServiceLocation()).isEqualTo(callbackUrl);
+ assertThat(registration.getAssertionConsumerServiceBinding()).isEqualTo(Saml2MessageBinding.POST);
+ assertThat(registration.getEntityId()).isEqualTo(APPLICATION_ID);
+ assertThat(registration.getAssertingPartyMetadata().getEntityId()).isEqualTo(PROVIDER_ID);
+ assertThat(registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation()).isEqualTo(SSO_URL);
+ assertThat(registration.getAssertingPartyMetadata().getVerificationX509Credentials()).containsExactly(Saml2X509Credential.verification(certificate));
+ }
+
+ private X509Certificate mockCertificate() {
+ X509Certificate mockedCertificate = mock();
+ when(samlCertificateConverter.toX509Certificate(CERTIF_STRING)).thenReturn(mockedCertificate);
+ return mockedCertificate;
+ }
+
+ private X509Certificate mockServiceProviderCertificate() {
+ X509Certificate mockedCertificate = mock();
+ when(samlCertificateConverter.toX509Certificate(CERTIF_SP_STRING)).thenReturn(mockedCertificate);
+ return mockedCertificate;
+ }
+
+ private PrivateKey mockPrivateKey() {
+ PrivateKey privateKey = mock();
+ when(samlPrivateKeyConverter.toPrivateKey(PRIVATE_KEY)).thenReturn(privateKey);
+ return privateKey;
+ }
+
+ private RelyingPartyRegistration findRegistration(@Nullable String callbackUrl) {
+ SonarqubeRelyingPartyRegistrationRepository relyingPartyRegistrationRepository = new SonarqubeRelyingPartyRegistrationRepository(samlSettings, samlCertificateConverter,
+ samlPrivateKeyConverter, callbackUrl);
+ return relyingPartyRegistrationRepository.findByRegistrationId("registrationId");
+ }
+
+}