jhipster / jhipster-registry

JHipster Registry, based on Spring Cloud Netflix Eureka and Spring Cloud Config
Apache License 2.0
699 stars 657 forks source link

CORS problems since 5.0.0 on management and config endpoints #371

Open pdelaby opened 5 years ago

pdelaby commented 5 years ago
Overview of the issue

Since v5.0.0, the registry served behind a traefik proxy (or letsencrypt) on docker seems to have CORS issues on post methods ( authenticate, config/encrypt, management/loggers).

Sample configuration

jhipster-registry:
       image: jhipster/jhipster-registry:v5.0.2
       volumes:
           - jhipster-registry-data:/root/.ssh
       environment:
           - _JAVA_OPTIONS=-Xmx512m -Xms256m
           - SPRING_PROFILES_ACTIVE=prod
           ...
           - LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB_CORS=TRACE
           - JHIPSTER_CORS_ALLOWED-ORIGINS=https://my.domain.name
           - 'JHIPSTER_CORS_ALLOWED-METHODS=GET, PUT, POST, DELETE, OPTIONS'
           - 'JHIPSTER_CORS_ALLOWED-HEADERS="*"'
           - 'JHIPSTER_CORS_EXPOSED-HEADERS='
           - 'JHIPSTER_CORS_ALLOW-CREDENTIALS=true'
       networks:
          - "traefik-public"
       deploy:
          replicas: 1
          labels:
           - traefik.frontend.rule=Host:my.domain.name
           - traefik.enable=true
           - traefik.port=8761
           - traefik.tags=traefik-public
           - traefik.docker.network=traefik-public
           # Traefik service that listens to HTTP
           - traefik.redirectorservice.frontend.entryPoints=http
           - traefik.redirectorservice.frontend.redirect.entryPoint=https
           # Traefik service that listens to HTTPS
           - traefik.webservice.frontend.entryPoints=https
       depends_on:
          - traefik

The configuration worked in previous versions, but a http 403 'invalid cors request' is now returned.

I added

- JHIPSTER_CORS_ALLOWED-ORIGINS=https://my.domain.name
- 'JHIPSTER_CORS_ALLOWED-METHODS=GET, PUT, POST, DELETE, OPTIONS'
- 'JHIPSTER_CORS_ALLOWED-HEADERS="*"'
- 'JHIPSTER_CORS_EXPOSED-HEADERS='
- 'JHIPSTER_CORS_ALLOW-CREDENTIALS=true'

in the docker-compose, and it fixed the problem for /authenticate, but other POST on endpoints (like the encryption in configuration/encryption or changing the log levels) doesn't seems to share that configuration (they use the CORS configuration mapped to '/**' ).

Am I missing a property ?

Motivation for or Use Case

It worked in previous version ( before 5.0.0).

Reproduce the error

Start jhipster-registry on docker, served behind an https domain name.

JHipster Registry Version(s)

5.0.2

antaka1 commented 5 years ago

+1 here. Working before 5.0.0, now got invalid cors request at login

vishal423 commented 5 years ago

As per current implementation, configurable CORS settings are applied against following URI patterns in the WebConfigurer. If your URL path doesn't match against these patterns, then, the default CORS configuration is applied that deny the incoming request with 403.

            source.registerCorsConfiguration("/eureka/**", config);
            source.registerCorsConfiguration("/api/**", config);
            source.registerCorsConfiguration("/v2/api-docs", config);
            source.registerCorsConfiguration("/*/api/**", config);

To support customization, I think we can externalize these URL patterns like other CORS settings.

kavenr commented 5 years ago

To fix the loggers, I added the following cors filters to WebConfigurer.corsFilter():

            source.registerCorsConfiguration("/management/**", config);
            source.registerCorsConfiguration("/auth/**", config);
            source.registerCorsConfiguration("/services/*/api/**", config);
            source.registerCorsConfiguration("/services/**/management/**", config);
            source.registerCorsConfiguration("/*/management/**", config);
            source.registerCorsConfiguration("/*/oauth/**", config);

To fix the swagger ui when combined with the jhipster uaa, I also replaced the OAuth2ClientCredentialsService.retrieveNewAccessToken() implementation with:

    private void retrieveNewAccessToken() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        final String authString = jHipsterProperties.getSecurity().getClientAuthorization().getClientId() + ":" + jHipsterProperties.getSecurity().getClientAuthorization().getClientSecret();
        final String authorization = "Basic " + Base64.encodeBase64String(authString.getBytes());
        headers.add("Authorization", authorization);

        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("grant_type", "client_credentials");

        HttpEntity<?> requestEntity = new HttpEntity<>(map, headers);
        String tokenEndpoint = getTokenEndpoint();
        ResponseEntity<DefaultOAuth2AccessToken> responseEntity = this.restTemplate.exchange(tokenEndpoint, HttpMethod.POST, requestEntity, DefaultOAuth2AccessToken.class);

        if (!responseEntity.getStatusCode().is2xxSuccessful()) {
            logger.debug("Request failed for '{}'", Optional.ofNullable(requestEntity.getHeaders()).map(HttpHeaders::getLocation).map(URI::toString).orElse(""));
        }

        accessToken = Objects.requireNonNull(responseEntity.getBody()).getValue();
    }

    /**
     * Returns the configured OAuth2 token endpoint URI.
     *
     * @return the OAuth2 token endpoint URI.
     */
    private String getTokenEndpoint() {
        String tokenEndpointUrl = jHipsterProperties.getSecurity().getClientAuthorization().getAccessTokenUri();
        if (tokenEndpointUrl == null) {
            throw new InvalidClientException("no token endpoint configured in application properties");
        }
        return tokenEndpointUrl;
    }