Skip to content
This repository has been archived by the owner on Oct 2, 2023. It is now read-only.

Commit

Permalink
openid auth implementation (#200)
Browse files Browse the repository at this point in the history
  • Loading branch information
nameisaravind authored Mar 29, 2021
1 parent 12f0eb0 commit b0474c7
Show file tree
Hide file tree
Showing 8 changed files with 391 additions and 3 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<artifactId>api</artifactId>
<packaging>jar</packaging>
<name>${project.groupId}:${project.artifactId}</name>
<version>3.4.9-SNAPSHOT</version>
<version>3.4.10-SNAPSHOT</version>
<description>Hygieia Rest API Layer</description>
<url>https://github.com/Hygieia/api</url>

Expand Down Expand Up @@ -59,7 +59,7 @@

<properties>
<!-- Dependencies -->
<com.capitalone.dashboard.core.version>3.14.10</com.capitalone.dashboard.core.version>
<com.capitalone.dashboard.core.version>3.14.11</com.capitalone.dashboard.core.version>
<spring-security.version>4.2.18.RELEASE</spring-security.version>
<tomcat.version>8.5.57</tomcat.version>
<commons-beanutils.version>1.9.4</commons-beanutils.version>
Expand Down
55 changes: 55 additions & 0 deletions src/main/java/com/capitalone/dashboard/auth/AuthProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ public class AuthProperties {
private String ldapServerUrl;
private List<AuthType> authenticationProviders = Lists.newArrayList();

private String openIdClientId;
private String openIdClientSecret;
private String openIdServerHost;
private String openIdRedirectUri;
private String openIdGrantType;
private String openIdScope;

private String adDomain;
private String adRootDn;
private String adUserRootDn;
Expand Down Expand Up @@ -260,4 +267,52 @@ public void setUserDisplayName(String userDisplayName) {
this.userDisplayName = userDisplayName;
}

public String getOpenIdServerHost() {
return openIdServerHost;
}

public void setOpenIdServerHost(String openIdServerHost) {
this.openIdServerHost = openIdServerHost;
}

public String getOpenIdClientId() {
return openIdClientId;
}

public void setOpenIdClientId(String openIdClientId) {
this.openIdClientId = openIdClientId;
}

public String getOpenIdClientSecret() {
return openIdClientSecret;
}

public void setOpenIdClientSecret(String openIdClientSecret) {
this.openIdClientSecret = openIdClientSecret;
}

public String getOpenIdRedirectUri() {
return openIdRedirectUri;
}

public void setOpenIdRedirectUri(String openIdRedirectUri) {
this.openIdRedirectUri = openIdRedirectUri;
}

public String getOpenIdGrantType() {
return openIdGrantType;
}

public void setOpenIdGrantType(String openIdGrantType) {
this.openIdGrantType = openIdGrantType;
}

public String getOpenIdScope() {
return openIdScope;
}

public void setOpenIdScope(String openIdScope) {
this.openIdScope = openIdScope;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void handle(HttpServletResponse response, Authentication authentication)
authenticationWithAuthorities = new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), authorities);
authenticationWithAuthorities.setDetails(authentication.getDetails());

if(authType == AuthType.LDAP){
if(authType == AuthType.LDAP || authType == AuthType.SSO){
busCompOwnerService.assignOwnerToDashboards(firstName, middleName, lastName, authentication);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.capitalone.dashboard.auth.openid;

import com.capitalone.dashboard.auth.AuthenticationResultHandler;
import com.capitalone.dashboard.client.RestClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class OpenIdAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

@Autowired
private OpenIdAuthenticationService openIdAuthenticationService;
protected RestClient restClient;

@Autowired
public OpenIdAuthenticationFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager,
AuthenticationResultHandler authenticationResultHandler, RestClient restClient) {
super(defaultFilterProcessesUrl);
setAuthenticationManager(authenticationManager);
setAuthenticationSuccessHandler(authenticationResultHandler);
this.restClient = restClient;
}

@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException {
return openIdAuthenticationService.getAuthentication(httpServletRequest);
}

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) {
SecurityContextHolder.getContext().setAuthentication(authResult);
openIdAuthenticationService.addAuthentication(response, authResult);
}

@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "OpenId SSO Authentication Failed");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.capitalone.dashboard.auth.openid;

import org.springframework.security.core.Authentication;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface OpenIdAuthenticationService {
void addAuthentication(HttpServletResponse response, Authentication authentication);
Authentication getAuthentication(HttpServletRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.capitalone.dashboard.auth.openid;

import com.capitalone.dashboard.auth.AuthProperties;
import com.capitalone.dashboard.auth.ldap.CustomUserDetails;
import com.capitalone.dashboard.client.RestClient;
import com.capitalone.dashboard.model.AuthType;
import com.capitalone.dashboard.model.UserRole;
import com.google.common.collect.Sets;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.MalformedJwtException;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Date;

@Component
public class OpenIdAuthenticationServiceImpl implements OpenIdAuthenticationService {

private static final String AUTHORIZATION = "Authorization";
private static final String AUTH_PREFIX_SSO_W_SPACE = "ssoCode ";
private static final String AUTH_RESPONSE_HEADER = "X-Authentication-Token";
private static final String ROLES_CLAIM = "roles";
private static final String DETAILS_CLAIM = "details";
private static final String ACCESS_TOKEN = "access_token";
private static final String TOKEN_ENDPOINT = "/as/token.oauth2";
private static final String USER_INFO_ENDPOINT = "/idp/userinfo.openid";

private AuthProperties authProperties;
private RestClient restClient;

public OpenIdAuthenticationServiceImpl() {}

@Autowired
public OpenIdAuthenticationServiceImpl(AuthProperties authProperties, RestClient restClient) {
this.authProperties = authProperties;
this.restClient = restClient;
}

@Override
public void addAuthentication(HttpServletResponse response, Authentication authentication) {
String jwt = Jwts.builder().setSubject(authentication.getName())
.claim(DETAILS_CLAIM, authentication.getDetails())
.claim(ROLES_CLAIM, getRoles(authentication.getAuthorities()))
.setExpiration(new Date(System.currentTimeMillis() + authProperties.getExpirationTime()))
.signWith(SignatureAlgorithm.HS512, authProperties.getSecret()).compact();
response.addHeader(AUTH_RESPONSE_HEADER, jwt);

}

@Override
public Authentication getAuthentication(HttpServletRequest request) {
String authHeader = request.getHeader(AUTHORIZATION);
if (!isValid(authHeader)) return null;

String authCode = getAuthCode(authHeader);
try {
JSONObject tokenObj = getOpenIdAccessTokenObject(authCode);
String accessToken = (String) tokenObj.get(ACCESS_TOKEN);
if (Objects.isNull(accessToken)) return null;

JSONObject userInfoObj = getOpenIdUserInfo(accessToken);
if (Objects.isNull(userInfoObj)) return null;

CustomUserDetails principle = getUserDetails(userInfoObj);
Collection<? extends GrantedAuthority> authorities = getAuthorities(Collections.singletonList(UserRole.ROLE_USER.name()));
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(principle, null, authorities);
authentication.setDetails(AuthType.SSO);
return authentication;

} catch (ExpiredJwtException | SignatureException | MalformedJwtException | ParseException e) {
return null;
}
}

private CustomUserDetails getUserDetails(JSONObject userInfoObj) {
CustomUserDetails customUserDetails = new CustomUserDetails();
customUserDetails.setUsername((String) userInfoObj.get("sub"));
customUserDetails.setFirstName((String) userInfoObj.get("FirstName"));
customUserDetails.setLastName((String) userInfoObj.get("LastName"));
customUserDetails.setEmailAddress((String) userInfoObj.get("Email"));
return customUserDetails;
}

private boolean isValid(String authHeader) {
return StringUtils.isNotBlank(authHeader) && StringUtils.startsWithIgnoreCase(authHeader, AUTH_PREFIX_SSO_W_SPACE)
&& StringUtils.isNotBlank(getAuthCode(authHeader));
}

private JSONObject getOpenIdUserInfo(String accessToken) throws ParseException {
String userInfoEndPoint = authProperties.getOpenIdServerHost() + USER_INFO_ENDPOINT;
ResponseEntity<String> userInfoResponse = restClient.makeRestCallPost(userInfoEndPoint, getUserInfoHeaders(accessToken), "");
return restClient.parseAsObject(userInfoResponse);
}

private HttpHeaders getUserInfoHeaders(String accessToken) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(AUTHORIZATION, "Bearer "+ accessToken);
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
return httpHeaders;
}

private JSONObject getOpenIdAccessTokenObject(String authCode) throws ParseException {
String tokenHostEndPoint = authProperties.getOpenIdServerHost() + TOKEN_ENDPOINT;
String grantType = authProperties.getOpenIdGrantType();
String redirectUri = authProperties.getOpenIdRedirectUri();
String scope = authProperties.getOpenIdScope();
String tokenUrl = tokenHostEndPoint + "?code=" + authCode + "&grant_type=" + grantType + "&redirect_uri=" + redirectUri + "&scope="+ scope;
ResponseEntity<String> tokenResponse = restClient.makeRestCallPost(tokenUrl, getHeaders(), "");
return restClient.parseAsObject(tokenResponse);
}

private String getAuthCode(String authHeader) {
return StringUtils.removeStart(authHeader, AUTH_PREFIX_SSO_W_SPACE);
}

private HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
String clientAuth = Base64.getEncoder().encodeToString((authProperties.getOpenIdClientId()
+ ":" + authProperties.getOpenIdClientSecret()).getBytes());
httpHeaders.add(AUTHORIZATION, "Basic" + " " + clientAuth);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return httpHeaders;
}

private Collection<String> getRoles(Collection<? extends GrantedAuthority> authorities) {
Collection<String> roles = Sets.newHashSet();
authorities.forEach(authority -> {
roles.add(authority.getAuthority());
});
return roles;
}

private Collection<? extends GrantedAuthority> getAuthorities(Collection<String> roles) {
Collection<GrantedAuthority> authorities = Sets.newHashSet();
roles.forEach(role -> authorities.add(new SimpleGrantedAuthority(role)));
return authorities;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
import com.capitalone.dashboard.auth.ldap.CustomUserDetailsContextMapper;
import com.capitalone.dashboard.auth.ldap.LdapLoginRequestFilter;
import com.capitalone.dashboard.auth.sso.SsoAuthenticationFilter;
import com.capitalone.dashboard.auth.openid.OpenIdAuthenticationFilter;
import com.capitalone.dashboard.auth.standard.StandardLoginRequestFilter;
import com.capitalone.dashboard.auth.token.JwtAuthenticationFilter;
import com.capitalone.dashboard.auth.webhook.github.GithubWebHookAuthService;
import com.capitalone.dashboard.auth.webhook.github.GithubWebHookRequestFilter;
import com.capitalone.dashboard.client.RestClient;
import com.capitalone.dashboard.model.AuthType;
import com.capitalone.dashboard.settings.ApiSettings;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -31,6 +33,7 @@
import org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.client.RestTemplate;

import java.util.List;

Expand Down Expand Up @@ -99,6 +102,7 @@ protected void configure(HttpSecurity http) throws Exception {
.addFilterBefore(ssoAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(ldapLoginRequestFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(apiTokenRequestFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(openIdAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(githubWebhookRequestFilter(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(new Http401AuthenticationEntryPoint("Authorization"));
Expand Down Expand Up @@ -178,6 +182,11 @@ protected SsoAuthenticationFilter ssoAuthenticationFilter() throws Exception {
return new SsoAuthenticationFilter("/findUser", authenticationManager(), authenticationResultHandler);
}

@Bean
protected OpenIdAuthenticationFilter openIdAuthenticationFilter() throws Exception {
return new OpenIdAuthenticationFilter("/login/openid", authenticationManager(), authenticationResultHandler, restClient());
}

@Bean
protected LdapLoginRequestFilter ldapLoginRequestFilter() throws Exception {
return new LdapLoginRequestFilter("/login/ldap", authenticationManager(), authenticationResultHandler);
Expand All @@ -200,4 +209,9 @@ protected ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthentic
return provider;
}

@Bean
public RestClient restClient() {
return new RestClient(RestTemplate::new);
}

}
Loading

0 comments on commit b0474c7

Please sign in to comment.