-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extract out environment verification logic and add optional Spring Security component #8
Merged
Merged
Changes from 3 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
e45a463
fix test packages
dcrissman 5f1593e
move over spring classes
dcrissman 3f4b62c
refactor out comon environment logic
dcrissman 9a38566
requested changes
dcrissman 5b74c79
fix NPE
dcrissman 5f68663
refactor and add testing for CertEnvironmentVerificationFilter
dcrissman 3e2a90b
refactors
dcrissman 8a6a0e8
clean up
dcrissman 70afbdb
rename variable
dcrissman c24e963
merge in upstream
dcrissman f570478
Merge pull request #1 from dcrissman/spring-iterate
dcrissman f7fd8c9
extract out multiple modules
dcrissman b73c857
remove files
dcrissman 810a249
move blank check to common
dcrissman bee312a
unit test fixes
dcrissman 8231c25
requested changes
dcrissman 42664c8
READMEs
dcrissman 917fd69
add unit test
dcrissman f146d7b
requested changes:
dcrissman 830a10c
rename common module
dcrissman 6b10ba9
naming fixes
dcrissman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/main/java/org/esbtools/auth/spring/CertEnvironmentAuthenticationException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.esbtools.auth.spring; | ||
|
||
import org.springframework.security.core.AuthenticationException; | ||
|
||
public class CertEnvironmentAuthenticationException extends AuthenticationException { | ||
|
||
private static final long serialVersionUID = -1102864286332227011L; | ||
|
||
public CertEnvironmentAuthenticationException(String msg) { | ||
super(msg); | ||
} | ||
|
||
public CertEnvironmentAuthenticationException(String msg, Throwable t) { | ||
super(msg, t); | ||
} | ||
|
||
} |
80 changes: 80 additions & 0 deletions
80
src/main/java/org/esbtools/auth/spring/CertEnvironmentVerificationFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package org.esbtools.auth.spring; | ||
|
||
import java.io.IOException; | ||
import java.security.cert.X509Certificate; | ||
|
||
import javax.naming.NamingException; | ||
import javax.servlet.FilterChain; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
import org.esbtools.auth.util.EnvironmentUtils; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.web.WebAttributes; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
public class CertEnvironmentVerificationFilter extends OncePerRequestFilter { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(CertEnvironmentVerificationFilter.class); | ||
|
||
private final EnvironmentUtils envUtils; | ||
|
||
public CertEnvironmentVerificationFilter(String environment) { | ||
envUtils = (null == environment) ? null : new EnvironmentUtils(environment); | ||
|
||
LOGGER.info("Cert Environment: " + ((environment == null) ? "Not Set" : environment)); | ||
} | ||
|
||
@Override | ||
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { | ||
if (null != envUtils) { | ||
LOGGER.debug("Attempting Environment Cert verification"); | ||
X509Certificate certChain[] = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); | ||
|
||
String dn = certChain[0].getSubjectDN().getName(); | ||
|
||
if ((null != certChain) && (certChain.length > 0)) { | ||
LOGGER.debug("Verifying environment on cert"); | ||
try { | ||
envUtils.validateEnvironment(dn); | ||
} | ||
catch (NamingException e) { | ||
unsuccessfulAuthentication(request, response, | ||
new CertEnvironmentAuthenticationException(e.getMessage())); | ||
return; //end the chain | ||
} | ||
} | ||
else { | ||
LOGGER.debug("Cert not found. Skipping Environment Cert verification."); | ||
} | ||
} | ||
else { | ||
LOGGER.debug("No environment configured. Skipping Environment Cert verification."); | ||
} | ||
|
||
chain.doFilter(request, response); | ||
} | ||
|
||
/** | ||
* Ensures the authentication object in the secure context is set to null when | ||
* authentication fails. | ||
* <p> | ||
* Caches the failure exception as a request attribute | ||
*/ | ||
protected void unsuccessfulAuthentication(HttpServletRequest request, | ||
HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { | ||
SecurityContextHolder.clearContext(); | ||
|
||
if (LOGGER.isDebugEnabled()) { | ||
LOGGER.debug("Cleared security context due to exception", failed); | ||
} | ||
request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, failed); | ||
|
||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, failed.getMessage()); | ||
} | ||
|
||
} |
67 changes: 67 additions & 0 deletions
67
src/main/java/org/esbtools/auth/spring/LdapUserDetailsService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package org.esbtools.auth.spring; | ||
|
||
import java.util.stream.Collectors; | ||
|
||
import org.esbtools.auth.ldap.LdapConfiguration; | ||
import org.esbtools.auth.ldap.LdapRolesProvider; | ||
import org.esbtools.auth.util.CachedRolesProvider; | ||
import org.esbtools.auth.util.RolesCache; | ||
import org.esbtools.auth.util.RolesProvider; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; | ||
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.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; | ||
|
||
public class LdapUserDetailsService implements UserDetailsService, AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(LdapUserDetailsService.class); | ||
|
||
private final RolesProvider rolesProvider; | ||
|
||
public LdapUserDetailsService(String searchBase, LdapConfiguration ldapConfiguration, int rolesCacheExpiryMS) throws Exception { | ||
this(new LdapRolesProvider(searchBase, ldapConfiguration), rolesCacheExpiryMS); | ||
} | ||
|
||
public LdapUserDetailsService(String searchBase, LdapConfiguration ldapConfiguration) throws Exception { | ||
this(new LdapRolesProvider(searchBase, ldapConfiguration)); | ||
} | ||
|
||
public LdapUserDetailsService(LdapRolesProvider rolesProvider, int rolesCacheExpiryMS) { | ||
this(new CachedRolesProvider(rolesProvider, new RolesCache(rolesCacheExpiryMS))); | ||
} | ||
|
||
public LdapUserDetailsService(RolesProvider rolesProvider) { | ||
this.rolesProvider = rolesProvider; | ||
} | ||
|
||
@Override | ||
public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) throws UsernameNotFoundException { | ||
return loadUserByUsername(token.getName()); | ||
} | ||
|
||
@Override | ||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | ||
LOGGER.debug("Using LdapUserDetailsService with principal: " + username); | ||
|
||
try { | ||
return new User( | ||
username, | ||
"no-password", | ||
rolesProvider.getUserRoles(username).stream() | ||
.map(n -> new SimpleGrantedAuthority(n)) | ||
.collect(Collectors.toList()) | ||
); | ||
} | ||
catch (Exception e) { | ||
LOGGER.error("Unable to check ldap for unknown reason.", e); | ||
|
||
throw new UsernameNotFoundException(username + " could not be authorized"); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also want to check if what is configured is not an empty string (i.e. ""). In the past an empty environment signified you could skip environment validation. I think that the way this is currently written, an empty environment config would still be validated (because it is not null).
We could also choose to interpret null for the environment configuration as "doesn't need to be validated", we just need to be consistent and allow for that in CertLoginLdapLoginModule (currently requires environment to be present in config) and whatever else uses this.
We could instead a method to the Environment class called requiresValidation() that returns false when the value is null or empty string, and true otherwise (I think I like this option the best). We could also check the allAccessOu value in that method too since that is another factor in determining whether or not to do validation logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to handle blank spaces also? eg. " "
That is exactly what CertEnvironmentVerificationFilter does, it just handles it at that layer instead of in Environment. I did it at that level because I didn't have enough context to make changes to the logic flow of CertLdapLoginModule.
If we want this to be default behaviour, then it can definitely be moved to the Environment / requiresValidation.