From 54597f6f6ed00d0fdc3cc451803251abc500b391 Mon Sep 17 00:00:00 2001 From: Ramya Ravi <72739416+ramyaravi-opsmx@users.noreply.github.com> Date: Fri, 12 Aug 2022 23:51:26 +0530 Subject: [PATCH] Op 17001 audit tail exec v4.0 phase2 (#239) * OP-15420: filterBy in gate v4.0 * OP-14834: invalidate session and throw SAMLAuthenticationException (#209) (#216) * OP-14834: Check and update the token. * OP-14834: Updated logs and packages. * OP-14834: Only validates ExpiringUsernameAuthenticationToken. * OP-14834: Only validates ExpiringUsernameAuthenticationToken. * OP-14834: Added SamlAuthTokenUpdateFilter to the filter chain. * OP-14834: Added SamlAuthTokenUpdateFilter to the filter chain after basic auth filter. * OP-14834: redirect to logout * OP-14834: throw AccessDeniedException * OP-14834:redirect to /saml/login. * OP-14834:redirect to /saml/sso. * OP-14834: send html response * OP-14834: clear context * OP-14834: invalidate session * OP-14834: and then logout * OP-14834: clear context, invalidate session and redirect to log out url. * OP-14834: invalidate session and throw SAMLAuthenticationException * OP-14834: removed commented code. * OP-14834: Updated the log message. * OP-14834: Updated the certificate * OP-16308: OP-16308: Wait and retry if connection fails. Also, css file missing fix. (#217) (#218) * op-16538 op-16537 - Added autocomplete off in login page * OP-16545: jquery upgraded to 3.6.0. (#224) * OP-16992 : Added parameter for PipelineAndPolicy summarycount (#229) (#230) * OP-16541: Added contentSecurityPolicy header. * OP-16541: config change contentSecurityPolicy. * OP-17106: Added SamlSsoEventPublishConfig to update ApplicationEventPublisher. * OP-17106: get FilterChainProxy and then go for SAMLProcessingFilter to update ApplicationEventPublisher. * OP-17106: code correction. * OP-17106: added logs. * OP-17106: Handling InteractiveAuthenticationSuccessEvent events also. * OP-17106: Handling serialization issues. * OP-17106: set details to null to remove some json serialization issues. * OP-17106: Added AuditData. * OP-17106: Setting name. * OP-17106: Setting name via source. * OP-17106: Refactored code. * OP-17106: Added some comments. * OP-17106: Refactored configs. * OP-17106: Appending roles configs. * OP-17106: Logs and comments. * OP-17001: audit trail execution (#237) * OP-17001: audit trail execution * OP-17001: audit trail execution phase 2 * OP-17001: audit trail execution phase 3 Co-authored-by: arunkumaropsmx Co-authored-by: Rahul Chekuri <89373036+rahul-chekuri@users.noreply.github.com> Co-authored-by: Sahiti Tarigoppula Co-authored-by: Luthan95 <66585454+Luthan95@users.noreply.github.com> Co-authored-by: Sudhakar Guvvalapall Co-authored-by: rahul-chekuri Co-authored-by: Sri Harsha Kancharla --- .../saml/SamlSsoEventPublishConfig.java | 56 +++++++++++++++++++ .../audit/AuthenticationAuditListener.java | 31 +++++++++- .../opsmx/spinnaker/gate/model/AuditData.java | 49 ++++++++++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 gate-saml/src/main/java/com/opsmx/spinnaker/gate/security/saml/SamlSsoEventPublishConfig.java create mode 100644 gate-web/src/main/java/com/opsmx/spinnaker/gate/model/AuditData.java diff --git a/gate-saml/src/main/java/com/opsmx/spinnaker/gate/security/saml/SamlSsoEventPublishConfig.java b/gate-saml/src/main/java/com/opsmx/spinnaker/gate/security/saml/SamlSsoEventPublishConfig.java new file mode 100644 index 0000000000..9e0b85a591 --- /dev/null +++ b/gate-saml/src/main/java/com/opsmx/spinnaker/gate/security/saml/SamlSsoEventPublishConfig.java @@ -0,0 +1,56 @@ +package com.opsmx.spinnaker.gate.security.saml; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.Filter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.security.saml.SAMLProcessingFilter; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.security.web.SecurityFilterChain; + +@ConditionalOnExpression("${saml.enabled:false}") +@Configuration +@Slf4j +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SamlSsoEventPublishConfig { + + private ApplicationEventPublisher applicationEventPublisher; + + @Autowired + @Qualifier("springSecurityFilterChain") + private Filter springSecurityFilterChain; + + @Autowired + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } + + @Bean + public FilterChainProxy getFilters() { + FilterChainProxy filterChainProxy = (FilterChainProxy) springSecurityFilterChain; + List list = filterChainProxy.getFilterChains(); + + list.stream() + .flatMap(chain -> chain.getFilters().stream()) + .filter(filter -> filter.getClass() == FilterChainProxy.class) + .findAny() + .map(FilterChainProxy.class::cast) + .map(FilterChainProxy::getFilterChains) + .orElse(new ArrayList<>()) + .stream() + .flatMap(chin -> chin.getFilters().stream()) + .filter(filter -> filter.getClass() == SAMLProcessingFilter.class) + .findAny() + .map(SAMLProcessingFilter.class::cast) + .ifPresent(filter -> filter.setApplicationEventPublisher(applicationEventPublisher)); + return filterChainProxy; + } +} diff --git a/gate-web/src/main/java/com/opsmx/spinnaker/gate/audit/AuthenticationAuditListener.java b/gate-web/src/main/java/com/opsmx/spinnaker/gate/audit/AuthenticationAuditListener.java index d2c3da76bb..26f3b02649 100644 --- a/gate-web/src/main/java/com/opsmx/spinnaker/gate/audit/AuthenticationAuditListener.java +++ b/gate-web/src/main/java/com/opsmx/spinnaker/gate/audit/AuthenticationAuditListener.java @@ -17,12 +17,19 @@ package com.opsmx.spinnaker.gate.audit; import com.opsmx.spinnaker.gate.enums.AuditEventType; +import com.opsmx.spinnaker.gate.model.AuditData; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.security.AbstractAuthenticationAuditListener; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.event.*; +import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Component; @Slf4j @@ -38,15 +45,24 @@ public void onApplicationEvent(AbstractAuthenticationEvent event) { try { log.debug("Authentication audit events received : {}", event); + // OP-17106: looks like a saml event fetch name and roles to publish + if (event.getAuthentication().isAuthenticated() + && event instanceof InteractiveAuthenticationSuccessEvent) { + log.debug("publishEvent InteractiveAuthenticationSuccessEvent"); + handleInteractiveAuthenticationSuccessEvent(event); + return; + } + if (event.getAuthentication().isAuthenticated() && event instanceof AuthenticationSuccessEvent) { + log.debug("publishEvent AuthenticationSuccessEvent"); auditHandler.publishEvent(AuditEventType.AUTHENTICATION_SUCCESSFUL_AUDIT, event); - } else if (!event.getAuthentication().isAuthenticated() && event instanceof AbstractAuthenticationFailureEvent) { + log.debug("publishEvent AbstractAuthenticationFailureEvent"); auditHandler.publishEvent(AuditEventType.AUTHENTICATION_FAILURE_AUDIT, event); - } else if (event instanceof LogoutSuccessEvent) { + log.debug("publishEvent LogoutSuccessEvent"); auditHandler.publishEvent(AuditEventType.SUCCESSFUL_USER_LOGOUT_AUDIT, event); } @@ -54,4 +70,15 @@ public void onApplicationEvent(AbstractAuthenticationEvent event) { log.error("Exception occured while capturing audit events : {}", e); } } + + private void handleInteractiveAuthenticationSuccessEvent(AbstractAuthenticationEvent event) { + AbstractAuthenticationToken auth = (AbstractAuthenticationToken) event.getAuthentication(); + String name = auth.getName(); + List roles = + Optional.ofNullable(auth.getAuthorities()).orElse(new ArrayList<>()).stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.toList()); + AuditData data = new AuditData(name, roles); + auditHandler.publishEvent(AuditEventType.AUTHENTICATION_SUCCESSFUL_AUDIT, data); + } } diff --git a/gate-web/src/main/java/com/opsmx/spinnaker/gate/model/AuditData.java b/gate-web/src/main/java/com/opsmx/spinnaker/gate/model/AuditData.java new file mode 100644 index 0000000000..5951c05f9a --- /dev/null +++ b/gate-web/src/main/java/com/opsmx/spinnaker/gate/model/AuditData.java @@ -0,0 +1,49 @@ +/* + * Copyright 2022 OpsMx + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.opsmx.spinnaker.gate.model; + +import java.util.List; +import lombok.Data; + +@Data +public class AuditData { + private Source source; + + public AuditData(String name, List roles) { + this.source = new Source(name, roles); + } + + @Data + public class Source { + private String name; + private Principal principal; + + public Source(String name, List roles) { + this.name = name; + this.principal = new Principal(roles); + } + } + + @Data + public class Principal { + private List roles; + + public Principal(List roles) { + this.roles = roles; + } + } +}