From e7fd624b1180ddf33a9db7390f8e4423dc507a1b Mon Sep 17 00:00:00 2001 From: sudhakaropsmx <sudhakar@opsmx.io> Date: Fri, 22 Mar 2024 19:33:38 +0530 Subject: [PATCH] OP-21911: Bugfix sapor gate basic authentication --- .../gate/security/basic/BasicAuthConfig.java | 50 ++++++++++++------- .../security/basic/BasicAuthProvider.java | 26 +++++----- .../gate/controllers/RootController.groovy | 15 ++++-- .../spinnaker/gate/cache/OesCacheManager.java | 20 -------- .../gate/cache/OesPlatformCacheManager.java | 48 ++++++++++++++++++ .../gate/service/AdminAuthService.java | 6 +-- 6 files changed, 110 insertions(+), 55 deletions(-) create mode 100644 gate-web/src/main/java/com/opsmx/spinnaker/gate/cache/OesPlatformCacheManager.java diff --git a/gate-basic/src/main/java/com/netflix/spinnaker/gate/security/basic/BasicAuthConfig.java b/gate-basic/src/main/java/com/netflix/spinnaker/gate/security/basic/BasicAuthConfig.java index ba98fc123f..45428ad5a2 100644 --- a/gate-basic/src/main/java/com/netflix/spinnaker/gate/security/basic/BasicAuthConfig.java +++ b/gate-basic/src/main/java/com/netflix/spinnaker/gate/security/basic/BasicAuthConfig.java @@ -18,8 +18,6 @@ import com.netflix.spinnaker.gate.config.AuthConfig; import com.netflix.spinnaker.gate.security.SpinnakerAuthConfig; -import com.netflix.spinnaker.gate.services.OesAuthorizationService; -import com.netflix.spinnaker.gate.services.PermissionService; import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; @@ -28,10 +26,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.session.web.http.DefaultCookieSerializer; @@ -43,9 +45,8 @@ @Slf4j public class BasicAuthConfig { - private final AuthConfig authConfig; - - private final BasicAuthProvider authProvider; + @Autowired private final AuthConfig authConfig; + @Autowired private final BasicAuthProvider authProvider; @Autowired DefaultCookieSerializer defaultCookieSerializer; @@ -58,19 +59,16 @@ public class BasicAuthConfig { @Value("${security.user.password:}") String password; - @Autowired PermissionService permissionService; - @Autowired - public BasicAuthConfig( - AuthConfig authConfig, - PermissionService permissionService, - OesAuthorizationService oesAuthorizationService) { + public BasicAuthConfig(AuthConfig authConfig, BasicAuthProvider authProvider) { this.authConfig = authConfig; - this.authProvider = new BasicAuthProvider(permissionService, oesAuthorizationService); + this.authProvider = authProvider; } - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + @Bean + public AuthenticationManager authManager(HttpSecurity http) throws Exception { + AuthenticationManagerBuilder authenticationManagerBuilder = + http.getSharedObject(AuthenticationManagerBuilder.class); if (name == null || name.isEmpty() || password == null || password.isEmpty()) { throw new AuthenticationServiceException( "User credentials are not configured properly. Please check username and password are properly configured"); @@ -86,13 +84,15 @@ public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception authProvider.setName(this.name); authProvider.setPassword(this.password); - - auth.authenticationProvider(authProvider); + authenticationManagerBuilder.authenticationProvider(authProvider); + authenticationManagerBuilder.eraseCredentials(false); + return authenticationManagerBuilder.build(); } @Bean - public SecurityFilterChain configure(HttpSecurity http) throws Exception { + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { defaultCookieSerializer.setSameSite(null); + http.csrf().disable(); http.formLogin() .and() .httpBasic() @@ -100,4 +100,20 @@ public SecurityFilterChain configure(HttpSecurity http) throws Exception { authConfig.configure(http); return http.build(); } + + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return (web) -> { + try { + authConfig.configure(web); + } catch (Exception e) { + throw new RuntimeException(e); + } + }; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } } diff --git a/gate-basic/src/main/java/com/netflix/spinnaker/gate/security/basic/BasicAuthProvider.java b/gate-basic/src/main/java/com/netflix/spinnaker/gate/security/basic/BasicAuthProvider.java index 5c609ca47f..296b3dae4a 100644 --- a/gate-basic/src/main/java/com/netflix/spinnaker/gate/security/basic/BasicAuthProvider.java +++ b/gate-basic/src/main/java/com/netflix/spinnaker/gate/security/basic/BasicAuthProvider.java @@ -17,13 +17,12 @@ 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.Collections; import java.util.List; import java.util.stream.Collectors; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; @@ -32,8 +31,12 @@ 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 +@Component public class BasicAuthProvider implements AuthenticationProvider { private final PermissionService permissionService; @@ -46,6 +49,7 @@ public class BasicAuthProvider implements AuthenticationProvider { @Setter private String name; @Setter private String password; + @Autowired public BasicAuthProvider( PermissionService permissionService, OesAuthorizationService oesAuthorizationService) { this.permissionService = permissionService; @@ -61,19 +65,15 @@ public Authentication authenticate(Authentication authentication) throws Authent if (!this.name.equals(name) || !this.password.equals(password)) { throw new BadCredentialsException("Invalid username/password combination"); } - log.debug("roles configured for user: {} are roles: {}", name, roles); - User user = new User(); - user.setEmail(name); - user.setUsername(name); - user.setRoles(Collections.singletonList("USER")); List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); if (roles != null && !roles.isEmpty() && permissionService != null) { - user.setRoles(roles); - grantedAuthorities = - roles.stream().map(role -> new SimpleGrantedAuthority(role)).collect(Collectors.toList()); + grantedAuthorities.addAll( + roles.stream() + .map(role -> new SimpleGrantedAuthority(role)) + .collect(Collectors.toList())); // Updating roles in fiat service permissionService.loginWithRoles(name, roles); log.info("Platform service enabled value :{}", isPlatformEnabled); @@ -81,9 +81,11 @@ public Authentication authenticate(Authentication authentication) throws Authent if (isPlatformEnabled) { oesAuthorizationService.cacheUserGroups(roles, name); } + } else { + grantedAuthorities.add(new SimpleGrantedAuthority("USER")); } - - return new UsernamePasswordAuthenticationToken(user, password, grantedAuthorities); + UserDetails principal = new User(name, password, grantedAuthorities); + return new UsernamePasswordAuthenticationToken(principal, password, grantedAuthorities); } @Override diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/RootController.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/RootController.groovy index afa09bc0a7..6e37c8ac3b 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/RootController.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/RootController.groovy @@ -26,13 +26,22 @@ import jakarta.servlet.http.HttpServletResponse @Slf4j @RestController class RootController { - + @Value('${services.deck.base-url:}') + URL deckBaseUrl @Value('${services.oesui.externalUrl:}') String uiBaseUrl + @Value('${services.gate:oes-gate}') + String gateType @RequestMapping("/") void root(HttpServletResponse response) { - log.info("uiBaseUrl : {}", uiBaseUrl) - response.sendRedirect(uiBaseUrl + "/application") + if(gateType.equalsIgnoreCase("oes-gate")){ + log.info("uiBaseUrl : {}", uiBaseUrl) + response.sendRedirect(uiBaseUrl + "/application") + } else { + log.info("deckBaseUrl : {}", deckBaseUrl) + response.sendRedirect("/applications") + } + } } diff --git a/gate-web/src/main/java/com/opsmx/spinnaker/gate/cache/OesCacheManager.java b/gate-web/src/main/java/com/opsmx/spinnaker/gate/cache/OesCacheManager.java index 9b9190326d..3d1dbfeb38 100644 --- a/gate-web/src/main/java/com/opsmx/spinnaker/gate/cache/OesCacheManager.java +++ b/gate-web/src/main/java/com/opsmx/spinnaker/gate/cache/OesCacheManager.java @@ -16,13 +16,9 @@ package com.opsmx.spinnaker.gate.cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import java.util.concurrent.TimeUnit; import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.cache.CacheManager; -import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -34,26 +30,10 @@ public class OesCacheManager { @Getter private CacheManager concurrentMapCacheManager; - @Getter private CaffeineCacheManager caffeineCacheManager; - - @Value("${cache.expiryTime:600000}") - private String cacheExpiryTimeout; - @Primary @Bean(name = "concurrentMapCacheManager") public CacheManager concurrentMapCacheManager() { concurrentMapCacheManager = new ConcurrentMapCacheManager("datasource"); return concurrentMapCacheManager; } - - @Bean(name = "caffeineCacheManager") - public CacheManager cacheManager() { - - CaffeineCacheManager cacheManager = new CaffeineCacheManager("adminAuth"); - cacheManager.setCaffeine( - Caffeine.newBuilder() - .expireAfterWrite(Long.parseLong(cacheExpiryTimeout), TimeUnit.MILLISECONDS)); - caffeineCacheManager = cacheManager; - return cacheManager; - } } diff --git a/gate-web/src/main/java/com/opsmx/spinnaker/gate/cache/OesPlatformCacheManager.java b/gate-web/src/main/java/com/opsmx/spinnaker/gate/cache/OesPlatformCacheManager.java new file mode 100644 index 0000000000..be3885a615 --- /dev/null +++ b/gate-web/src/main/java/com/opsmx/spinnaker/gate/cache/OesPlatformCacheManager.java @@ -0,0 +1,48 @@ +/* + * Copyright 2024 Netflix, Inc. + * + * 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.cache; + +import com.github.benmanes.caffeine.cache.Caffeine; +import java.util.concurrent.TimeUnit; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.cache.CacheManager; +import org.springframework.cache.caffeine.CaffeineCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnExpression("${services.platform.enabled:false}") +public class OesPlatformCacheManager { + + @Getter private CaffeineCacheManager caffeineCacheManager; + + @Value("${cache.expiryTime:600000}") + private String cacheExpiryTimeout; + + @Bean(name = "caffeineCacheManager") + public CacheManager cacheManager() { + + CaffeineCacheManager cacheManager = new CaffeineCacheManager("adminAuth"); + cacheManager.setCaffeine( + Caffeine.newBuilder() + .expireAfterWrite(Long.parseLong(cacheExpiryTimeout), TimeUnit.MILLISECONDS)); + caffeineCacheManager = cacheManager; + return cacheManager; + } +} diff --git a/gate-web/src/main/java/com/opsmx/spinnaker/gate/service/AdminAuthService.java b/gate-web/src/main/java/com/opsmx/spinnaker/gate/service/AdminAuthService.java index 9fc28ada94..bbdcfa158d 100644 --- a/gate-web/src/main/java/com/opsmx/spinnaker/gate/service/AdminAuthService.java +++ b/gate-web/src/main/java/com/opsmx/spinnaker/gate/service/AdminAuthService.java @@ -17,7 +17,7 @@ package com.opsmx.spinnaker.gate.service; import com.google.gson.Gson; -import com.opsmx.spinnaker.gate.cache.OesCacheManager; +import com.opsmx.spinnaker.gate.cache.OesPlatformCacheManager; import com.opsmx.spinnaker.gate.cache.platform.AuthorizationCaching; import java.util.Map; import java.util.Set; @@ -35,7 +35,7 @@ public class AdminAuthService implements PlatformCachingService { private Gson gson = new Gson(); - @Autowired private OesCacheManager oesCacheManager; + @Autowired private OesPlatformCacheManager oesPlatformCacheManager; @Autowired private AuthorizationCaching authorizationCaching; @@ -50,7 +50,7 @@ public void cacheResponse(Object response, String userName) { @Override public boolean isCacheNotEmpty(String userName) { - CacheManager cacheManager = oesCacheManager.getCaffeineCacheManager(); + CacheManager cacheManager = oesPlatformCacheManager.getCaffeineCacheManager(); CaffeineCache caffeineCache = (CaffeineCache) cacheManager.getCache("adminAuth"); Set<Object> keySet = caffeineCache.getNativeCache().asMap().keySet(); return keySet.stream()