Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/v4.0' into v4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Utkarsh Shukla committed Dec 20, 2024
2 parents 3539dd9 + 9d21971 commit 118627c
Show file tree
Hide file tree
Showing 18 changed files with 558 additions and 211 deletions.
2 changes: 1 addition & 1 deletion config/spinnaker.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
oes:
spinnakerVersion: "OES 1.30.1.20240300"
spinnakerVersion: "OES 1.30.1.20240300"
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import com.netflix.spinnaker.gate.services.OesAuthorizationService;
import com.netflix.spinnaker.gate.services.PermissionService;
import com.netflix.spinnaker.security.User;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -31,8 +32,6 @@
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

@Slf4j
Expand Down Expand Up @@ -84,7 +83,11 @@ public Authentication authenticate(Authentication authentication) throws Authent
} else {
grantedAuthorities.add(new SimpleGrantedAuthority("USER"));
}
UserDetails principal = new User(name, password, grantedAuthorities);
User principal = new User();
principal.setUsername(name);
principal.setEmail(name);
principal.setRoles(roles);

return new UsernamePasswordAuthenticationToken(principal, password, grantedAuthorities);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestParam


@FeignClient(name = "OES", url = '${services.platform.baseUrl}')
@FeignClient(name = "OES", url = '${services.platform.baseUrl}', dismiss404 = true)
interface OesAuthorizationService {

@PutMapping(value = "/platformservice/v2/usergroups/importAndCache", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

package com.opsmx.spinnaker.gate.security.saml;

import static org.springframework.security.saml2.core.Saml2ErrorCodes.INVALID_ASSERTION;
import static org.springframework.security.saml2.core.Saml2ErrorCodes.INVALID_IN_RESPONSE_TO;

import com.netflix.spectator.api.Registry;
import com.netflix.spinnaker.fiat.shared.FiatClientConfigurationProperties;
import com.netflix.spinnaker.gate.config.AuthConfig;
Expand All @@ -28,7 +31,9 @@
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.opensaml.saml.saml2.core.Assertion;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -46,6 +51,8 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.saml2.core.Saml2Error;
import org.springframework.security.saml2.core.Saml2ResponseValidatorResult;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
Expand Down Expand Up @@ -90,6 +97,9 @@ public class SamlSecurityConfiguration {
public static final String defaultFilterUrl =
"{baseUrl}" + Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;

@Value("${spring.security.saml2.validation.inresponseto:false}")
private boolean ignoreInResponseToValidation;

@Bean
public UserDetailsService userDetailsService() {
return username -> {
Expand All @@ -110,8 +120,15 @@ public RememberMeServices rememberMeServices(UserDetailsService userDetailsServi

@Bean
public OpenSaml4AuthenticationProvider authenticationProvider() {

var authProvider = new OpenSaml4AuthenticationProvider();
authProvider.setResponseAuthenticationConverter(extractUserDetails());
log.debug("ignoreInResponseToValidation :{}", ignoreInResponseToValidation);
if (ignoreInResponseToValidation) {
authProvider.setAssertionValidator(removeAssertionError());
authProvider.setResponseValidator(removeInResonseToError());
}

return authProvider;
}

Expand Down Expand Up @@ -208,7 +225,7 @@ public SecurityFilterChain samlFilterChain(

return responseToken -> {
List<String> roles = new ArrayList<>();
log.debug("responseToken : {}", responseToken);
log.debug("responseToken : {}", responseToken.getToken().getSaml2Response());
Saml2Authentication authentication = delegate.convert(responseToken);
Saml2AuthenticatedPrincipal principal =
(Saml2AuthenticatedPrincipal) authentication.getPrincipal();
Expand Down Expand Up @@ -276,6 +293,54 @@ public SecurityFilterChain samlFilterChain(
};
}

private Converter<OpenSaml4AuthenticationProvider.AssertionToken, Saml2ResponseValidatorResult>
removeAssertionError() {
log.debug("**remove assertion error from Saml2ResponseValidatorResult Errors**");
Converter<OpenSaml4AuthenticationProvider.AssertionToken, Saml2ResponseValidatorResult>
delegate = OpenSaml4AuthenticationProvider.createDefaultAssertionValidator();
return assertionToken -> {
log.debug("responseToken : {}", assertionToken.getToken().getSaml2Response());
Saml2ResponseValidatorResult result = delegate.convert(assertionToken);
result
.getErrors()
.forEach(
error ->
log.debug(
" error code :{} and description :{}",
error.getErrorCode(),
error.getDescription()));
Collection<Saml2Error> errors =
result.getErrors().stream()
.filter((error) -> !error.getErrorCode().equals(INVALID_ASSERTION))
.collect(Collectors.toList());
return Saml2ResponseValidatorResult.failure(errors);
};
}

private Converter<OpenSaml4AuthenticationProvider.ResponseToken, Saml2ResponseValidatorResult>
removeInResonseToError() {
log.debug("**remove InResonseTo error from Saml2ResponseValidatorResult Errors**");
Converter<OpenSaml4AuthenticationProvider.ResponseToken, Saml2ResponseValidatorResult>
delegate = OpenSaml4AuthenticationProvider.createDefaultResponseValidator();
return responseToken -> {
log.debug("responseToken : {}", responseToken.getToken().getSaml2Response());
Saml2ResponseValidatorResult result = delegate.convert(responseToken);
result
.getErrors()
.forEach(
error ->
log.debug(
" error code :{} and description :{}",
error.getErrorCode(),
error.getDescription()));
Collection<Saml2Error> errors =
result.getErrors().stream()
.filter((error) -> !error.getErrorCode().equals(INVALID_IN_RESPONSE_TO))
.collect(Collectors.toList());
return Saml2ResponseValidatorResult.failure(errors);
};
}

private void loginWithRoles(String username, List<String> roles) {

var id = registry.createId("fiat.login").withTag("type", "saml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ class GateConfig extends RedisHttpSessionConfiguration {
*/
@Bean
JedisPool jedis(@Value('${redis.connection:redis://localhost:6379}') String connection,
@Value('${redis.timeout:2000}') int timeout) {
@Value('${redis.timeout:2000}') int timeout,
@Value('${redis.certificate_location:#{null}}') String certFilePath) {
return new JedisPool(new URI(connection), timeout)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package com.netflix.spinnaker.gate.config;

import com.google.common.base.Splitter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.URI;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.List;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -26,6 +32,7 @@
*
* <p>This Redis pool is used for Spring Boot's session management, not for the rate limit storage.
*/
@Slf4j
@Component
public class PostConnectionConfiguringJedisConnectionFactory extends JedisConnectionFactory {

Expand All @@ -40,11 +47,15 @@ public class PostConnectionConfiguringJedisConnectionFactory extends JedisConnec

private volatile boolean ranConfigureRedisAction;

private String password = "keyStorePass";

@Autowired
public PostConnectionConfiguringJedisConnectionFactory(
@Value("${redis.connection:redis://localhost:6379}") String connectionUri,
@Value("${redis.timeout:2000}") int timeout,
@ConnectionPostProcessor Optional<ConfigureRedisAction> configureRedisAction) {
@Value(value = "${redis.certificate_location:#{null}}") String certFilePath,
@ConnectionPostProcessor Optional<ConfigureRedisAction> configureRedisAction)
throws Exception {

this.configureRedisAction =
configureRedisAction.orElse(new ConfigureNotifyKeyspaceEventsAction());
Expand All @@ -63,6 +74,39 @@ public PostConnectionConfiguringJedisConnectionFactory(

if (redisUri.getScheme().equals("rediss")) {
setUseSsl(true);
String jksFilePath = "/opsmx/conf/redis-truststore.jks";
String alias = "redis-truststore"; // An alias to identify the certificate in the keystore
char[] password = this.password.toCharArray(); // Keystore password

FileInputStream certInputStream = null;
FileOutputStream jksOutputStream = null;

/**
* If SSL is used then below steps add the certificate necessary for connection to redis as a
* java keystore and then add java keystore file's path as a system property for use in
* connection.
*/
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
certInputStream = new FileInputStream(certFilePath);
Certificate certificate = certificateFactory.generateCertificate(certInputStream);

KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, password);
keyStore.setCertificateEntry(alias, certificate);
jksOutputStream = new FileOutputStream(jksFilePath);
keyStore.store(jksOutputStream, password);

log.info("Certificate has been added to the KeyStore successfully.");
} catch (Exception e) {
throw e;
} finally {
certInputStream.close();
jksOutputStream.close();
}

System.setProperty("javax.net.ssl.trustStore", "/opsmx/conf/redis-truststore.jks");
System.setProperty("javax.net.ssl.trustStorePassword", this.password);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -551,4 +551,11 @@ class OpsmxOesController {
return ResponseEntity.ok().headers(headers).body(manifestFile)
}
}

@Operation(summary = "Rest api for fetching importing account environment mapping records")
@RequestMapping(value = "/acctEnvMapping/import", method = RequestMethod.POST)
Object importAccountsFromSpinnaker() {

return opsmxOesService.importAccountEnvironmentMappings();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ class PipelineController {
String resultStatus = result.get("status")

if (!"SUCCEEDED".equalsIgnoreCase(resultStatus)) {
log.debug("Pipeline save operation failed. Result: {}", result)

String exception = result.variables.find { it.key == "exception" }?.value?.details?.errors?.getAt(0)
throw new PipelineException(
exception ?: "Pipeline save operation did not succeed: ${result.get("id", "unknown task id")} (status: ${resultStatus})"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
*/
package com.netflix.spinnaker.gate.graphql.model;

/** @link */
/**
* @link
*/
public interface Node {

String getId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,7 @@ interface OpsmxOesService {
Object evaluateStaticPolicy(@Path('version') String version,
@Body Object data)

@POST("/oes/acctEnvMapping/import")
Object importAccountEnvironmentMappings()

}
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,22 @@ class OpsmxAuditClientServiceController {
return ResponseEntity.status(response.getStatus()).build()
}

@Operation(summary = "Rest api for fetching all account environment mapping records")
@RequestMapping(value = "/v3/acctEnvMapping", method = RequestMethod.GET)
Object getAllAcctEnvMappings() {
return opsmxAuditClientService.getAllAccountEnvironmentMappings();
}
@Operation(summary = "Rest api for fetching account environment mapping record with id")
@RequestMapping(value = "/v3/acctEnvMapping/{id}", method = RequestMethod.GET)
Object getAcctEnvMappingWithId(@PathVariable("id") Integer id) {
return opsmxAuditClientService.getAccountEnvironmentMappingWithId(id);
}
@Operation(summary = "Rest api for fetching all unique environment records")
@RequestMapping(value = "/v3/env", method = RequestMethod.GET)
Object getAllUniqueEnv() {

return opsmxAuditClientService.getAllUniqueEnvironments();
}


}
Loading

0 comments on commit 118627c

Please sign in to comment.