Migrate from SAML extensions to SAML service provider and spring security

26 Views Asked by At

I am trying to upgrade my project to spring boot 3. For this, I need to upgrade the whole security part of my project, since the old code is not compatible with spring security 6 and therefore spring boot 3. I am no expert in spring security and have a lot of difficulties to migrate my code, more so since I find the documentation a bit light.

Enough talk, here is my new code I already migrated :

@Configuration
@EnableWebSecurity
@Slf4j
@Order(3)
@ConditionalOnProperty(name = SamlSettings.ENABLE_PROPERTY, havingValue = "true")
@NoLazy
@AllArgsConstructor
public class SamlSecurityConfiguration {

    @Nonnull
    private final SamlSettings samlSettings;

    @Bean
    SecurityFilterChain app(HttpSecurity http) throws Exception {
        if (samlSettings.isDisableSSLVerification()) {
            disableSSLVerification();
        }
        OpenSaml4AuthenticationProvider authenticationProvider =
            new OpenSaml4AuthenticationProvider();
        authenticationProvider.setAssertionValidator(
            OpenSaml4AuthenticationProvider.createDefaultAssertionValidatorWithParameters(p -> {
                p.put(CLOCK_SKEW, samlSettings.getResponseSkew());
                p.put(LIFETIME, samlSettings.getMaxAuthenticationAge());
                p.put(SIGNATURE_REQUIRED, false);
            })
        );

        http
            .authorizeHttpRequests(authorize -> authorize
                .anyRequest().authenticated()
            )
            .saml2Login(withDefaults());

        return http.build();
    }

    @Bean
    RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
        var credentials = loadCredential();
        RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations
            .fromMetadataLocation(samlSettings.getIdpMetadataLocation())
            .entityId(samlSettings.getEntityId())
            .registrationId(samlSettings.getEntityId())
            .decryptionX509Credentials(c -> c.add(credentials))
            .signingX509Credentials(c -> c.add(credentials))
            .assertingPartyDetails(details ->
                details
                    .singleSignOnServiceLocation(
                        samlSettings.getBaseEndPoint() + samlSettings.getAcs() + "/**"
                    )
            )
            .build();

        return new InMemoryRelyingPartyRegistrationRepository(relyingPartyRegistration);
    }

    private Saml2X509Credential loadCredential() {
        try {
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(
                new FileSystemResource(samlSettings.getKeystoreFile()).getInputStream(),
                samlSettings.getStorePass().toCharArray()
            );
            PrivateKey privateKey = (PrivateKey) keyStore.getKey(
                samlSettings.getKeyAlias(),
                samlSettings.getKeyPass().toCharArray()
            );
            X509Certificate certificate = (X509Certificate) keyStore.getCertificate(
                samlSettings.getKeyAlias()
            );
            return new Saml2X509Credential(privateKey, certificate, DECRYPTION, SIGNING);
        } catch (Exception e) {
            log.error("Could not load credentials for SAML", e);
            throw new RuntimeException(e);
        }
    }

    public static void disableSSLVerification() {
        try {
            // Create a trust manager that does not validate certificate chains
            TrustManager[] trustAllCerts = new TrustManager[] {
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    }
                }
            };

            // Install the all-trusting trust manager
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAllCerts, new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The last part for SSL disabling I had to add since for testing purposes my shibboleth uses a self signed certificat which seems not valid. If there is something to do about this that's more appropriate I would like to know, but this is not my main concern.

The problem I have is that prior to the migration, this code redirects to /saml/login :

function getRedirectUrl(webParams) {
    var suffix =
        'sourceUrl=' +
        (webParams.samlEnabled
            ? encodeURIComponent('/' + $window.location.hash)
            : encodeURIComponent(
                    $window.location.pathname + $window.location.hash
                ))

    return webParams.ssoLoginUrl.match(/\?/)
        ? webParams.ssoLoginUrl + '&' + suffix
        : webParams.ssoLoginUrl + '?' + suffix
}

It worked perfectly before, the redirection to /saml/login redirected to the shibboleth login page and I could connect without any trouble.

Now the problem is that I get a 404 NOT FOUND on /saml/login. The redirection to shibboleth does not work. I am stuck here.

I suspect that this class did the redirection to shibboleth and the rest of the saml authentication process but I do not know if that's the case, and if it is, how to migrate it :

@Slf4j
public class TargetUrlSamlEntryPoint
   implements AuthenticationEntryPoint {

   private String sourceUrl;

   private boolean expectsJson(HttpServletRequest request) {
       return request.getHeader("accept").contains("application/json");
   }

   @Override
   public void commence(
       HttpServletRequest request,
       HttpServletResponse response,
       AuthenticationException e
   ) throws IOException, ServletException {
       this.sourceUrl = request.getParameter("sourceUrl");
       if (expectsJson(request) && e instanceof InsufficientAuthenticationException) {
           response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
       }
   }

   @Override
   protected void initializeSSO(SAMLMessageContext context, AuthenticationException e)
       throws MetadataProviderException, SAMLException, MessageEncodingException {
       WebSSOProfileOptions options = this.getProfileOptions(context, e);
       options.setRelayState(this.sourceUrl);
       if (log.isDebugEnabled()) {
           log.debug("Set relay state to redirect the user to {}", this.sourceUrl);
       }
       this.setDefaultProfileOptions(options);
       super.initializeSSO(context, e);
   }
}

It seems that SAMLEntrypoint is no longer available. I don't know if spring security is supposed to do the job itself or if I have to add more code.

0

There are 0 best solutions below