Skip to content

Commit

Permalink
Gate with autopilot changes draft
Browse files Browse the repository at this point in the history
  • Loading branch information
mscr06 committed Jul 29, 2020
1 parent 51576ac commit ac15116
Show file tree
Hide file tree
Showing 16 changed files with 594 additions and 7 deletions.
5 changes: 5 additions & 0 deletions docker_build/gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ services:
opsmx:
baseUrl: http://oes-api:8085
enabled: true
autopilot:
baseUrl: http://localhost:8090
enabled: true
security:
basic:
enabled: true
Expand All @@ -12,6 +15,8 @@ ldap:
enabled: true
url: ldap://localhost:389/DC=example,DC=com
userDnPattern: uid={0},ou=people
login:
mode: session
google: {}
logging:
level:
Expand Down
1 change: 1 addition & 0 deletions gate-core/gate-core.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies {
implementation "com.github.ben-manes.caffeine:guava"
implementation "com.netflix.hystrix:hystrix-core"
implementation "org.apache.commons:commons-lang3"
implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1'
}

sourceSets {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler
import org.springframework.stereotype.Component
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.Filter
import javax.servlet.ServletException
Expand Down Expand Up @@ -67,6 +69,12 @@ class AuthConfig {
@Autowired
RequestMatcherProvider requestMatcherProvider

@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint

@Autowired
private JwtRequestFilter jwtRequestFilter;

@Value('${security.debug:false}')
boolean securityDebug

Expand Down Expand Up @@ -106,6 +114,28 @@ class AuthConfig {
// @formatter:on
}

void jwtconfigure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.cors()
.disable()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/auth/login").permitAll()
.antMatchers('/**/favicon.ico').permitAll()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers(PermissionRevokingLogoutSuccessHandler.LOGGED_OUT_URL).permitAll()
.antMatchers('/plugins/deck/**').permitAll()
.antMatchers(HttpMethod.POST, '/webhooks/**').permitAll()
.antMatchers(HttpMethod.POST, '/notifications/callbacks/**').permitAll()
.antMatchers('/health').permitAll()
.anyRequest().authenticated()
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}

void configure(WebSecurity web) throws Exception {
web.debug(securityDebug)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class DefaultProviderLookupService implements ProviderLookupService, AccountLook
this.clouddriverService = clouddriverService
}

@Scheduled(fixedDelay = 30000L)
//@Scheduled(fixedDelay = 30000L)
void refreshCache() {
try {
def accounts = AuthenticatedRequest.allowAnonymous { clouddriverService.getAccountDetails() }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.netflix.spinnaker.gate.config;

import java.io.Serializable;

public class AuthenticationRequest implements Serializable {

private String username;
private String password;

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public AuthenticationRequest() {}

public AuthenticationRequest(String username, String password) {
this.setUsername(username);
this.setPassword(password);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.netflix.spinnaker.gate.config;

import java.io.Serializable;

public class AuthenticationResponse implements Serializable {

private final String jwt;

public AuthenticationResponse(String jwt) {
this.jwt = jwt;
}

public String getJwt() {
return jwt;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.netflix.spinnaker.gate.config;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AuthenticationException e)
throws IOException, ServletException {
httpServletResponse.sendError(
HttpServletResponse.SC_UNAUTHORIZED,
"Sorry, You're not authorized to access this resource.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.netflix.spinnaker.gate.config;

import com.netflix.spinnaker.gate.services.UserDataService;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

@Autowired private JwtUtil jwtUtil;
@Autowired UserDataService userDetailsService;

@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {

final String authorizationHeader = request.getHeader("Authorization");

String username = null;
String jwt = null;

if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
try {
username = jwtUtil.extractUsername(jwt);
} catch (SignatureException ex) {
logger.error("Invalid JWT signature");
} catch (MalformedJwtException ex) {
logger.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
logger.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
logger.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
logger.error("JWT claims string is empty.");
}
}

if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
// Pass request down the chain, except for OPTIONS
if (!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
chain.doFilter(request, response);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.netflix.spinnaker.gate.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

@Component
public class JwtUtil {

private String SECRET_KEY = "secret";

public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}

public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}

public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}

private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}

private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}

public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}

private String createToken(Map<String, Object> claims, String subject) {

return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}

public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.netflix.spinnaker.gate.services

import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.stereotype.Service

@Service
class UserDataService implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new User(s, s, new ArrayList<GrantedAuthority>())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,21 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression
import org.springframework.boot.autoconfigure.security.SecurityProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.ldap.core.DirContextAdapter
import org.springframework.ldap.core.DirContextOperations
import org.springframework.security.authentication.AuthenticationManager
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.builders.WebSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
Expand Down Expand Up @@ -63,6 +68,29 @@ class LdapSsoConfig extends WebSecurityConfigurerAdapter {
@Autowired
DefaultCookieSerializer defaultCookieSerializer

@Autowired
private UserDetailsService userDataService

@Autowired
LoginProps loginProps


@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDataService).passwordEncoder(passwordEncoder());
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {

Expand Down Expand Up @@ -92,11 +120,18 @@ class LdapSsoConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
defaultCookieSerializer.setSameSite(null)
http.formLogin()
authConfig.configure(http)
http.addFilterBefore(new BasicAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter)
}
if (loginProps == null || loginProps.mode.equalsIgnoreCase("session"))
{
defaultCookieSerializer.setSameSite(null)
http.formLogin()
authConfig.configure(http)
http.addFilterBefore(new BasicAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter)
}
else if (loginProps !=null && loginProps.mode.equalsIgnoreCase("token")) {
authConfig.jwtconfigure(http)
}

}

@Override
void configure(WebSecurity web) throws Exception {
Expand Down Expand Up @@ -147,4 +182,10 @@ class LdapSsoConfig extends WebSecurityConfigurerAdapter {
String userSearchBase
String userSearchFilter
}

@Component
@ConfigurationProperties("login")
static class LoginProps {
String mode
}
}
Loading

0 comments on commit ac15116

Please sign in to comment.