Skip to content

Commit

Permalink
fix: Fix responses of security provider including expired account (#2969
Browse files Browse the repository at this point in the history
)

Signed-off-by: Pavel Jareš <[email protected]>
  • Loading branch information
pj892031 authored Jul 4, 2023
1 parent 368145c commit c4dc217
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ private void warnForDummyProvider(String defaultProviderName) {
}

private AuthenticationProvider getConfiguredLoginAuthProvider() {
return authProvidersMap.get(loginProvider.getAuthProviderBeanName());
String providerBeanName = loginProvider.getAuthProviderBeanName();
AuthenticationProvider authenticationProvider = authProvidersMap.get(providerBeanName);
if (authenticationProvider == null) {
log.warn("Login provider {} is not available.", providerBeanName);
}
return authenticationProvider;
}

public synchronized String getLoginAuthProviderName() {
Expand Down Expand Up @@ -95,7 +100,10 @@ public synchronized void setLoginAuthProvider(String provider) {
@Override
public Authentication authenticate(Authentication authentication) {
AuthenticationProvider configuredLoginAuthProvider = getConfiguredLoginAuthProvider();
return configuredLoginAuthProvider.authenticate(authentication);
if (configuredLoginAuthProvider != null) {
return configuredLoginAuthProvider.authenticate(authentication);
}
return null;
}

/**
Expand All @@ -121,6 +129,9 @@ public Authentication authenticate(Authentication authentication) {
@Override
public boolean supports(Class<?> authentication) {
AuthenticationProvider configuredLoginAuthProvider = getConfiguredLoginAuthProvider();
return configuredLoginAuthProvider.supports(authentication);
if (configuredLoginAuthProvider != null) {
return configuredLoginAuthProvider.supports(authentication);
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
Expand All @@ -41,8 +43,8 @@
import org.zowe.apiml.gateway.controllers.CacheServiceController;
import org.zowe.apiml.gateway.controllers.SafResourceAccessController;
import org.zowe.apiml.gateway.error.controllers.InternalServerErrorController;
import org.zowe.apiml.gateway.security.login.SuccessfulAccessTokenHandler;
import org.zowe.apiml.gateway.security.login.FailedAccessTokenHandler;
import org.zowe.apiml.gateway.security.login.SuccessfulAccessTokenHandler;
import org.zowe.apiml.gateway.security.login.x509.X509AuthenticationProvider;
import org.zowe.apiml.gateway.security.query.QueryFilter;
import org.zowe.apiml.gateway.security.query.SuccessfulQueryHandler;
Expand All @@ -63,6 +65,7 @@
import org.zowe.apiml.security.common.handler.FailedAuthenticationHandler;
import org.zowe.apiml.security.common.login.*;

import java.util.Collections;
import java.util.Set;

/**
Expand Down Expand Up @@ -132,7 +135,7 @@ public SecurityFilterChain authenticationFunctionalityFilterChain(HttpSecurity h
.anyRequest().permitAll()
.and()

.x509()
.x509().userDetailsService(x509UserDetailsService())

.and()
.logout()
Expand Down Expand Up @@ -204,7 +207,7 @@ public SecurityFilterChain accessTokenFilterChain(HttpSecurity http) throws Exce
.authorizeRequests()
.anyRequest().permitAll()
.and()
.x509()
.x509().userDetailsService(x509UserDetailsService())
.and()
.authenticationProvider(compoundAuthProvider) // for authenticating credentials
.authenticationProvider(tokenAuthenticationProvider)
Expand Down Expand Up @@ -259,7 +262,7 @@ public SecurityFilterChain authProtectedEndpointsFilterChain(HttpSecurity http)
.authorizeRequests()
.anyRequest().authenticated()
.and()
.x509()
.x509().userDetailsService(x509UserDetailsService())
.and()
.authenticationProvider(compoundAuthProvider) // for authenticating credentials
.apply(new CustomSecurityFilters());
Expand All @@ -273,23 +276,24 @@ public void configure(HttpSecurity http) {
.addFilterBefore(loginFilter(http), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class)
.addFilterAfter(x509AuthenticationFilter(), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class);
}
}

private NonCompulsoryAuthenticationProcessingFilter loginFilter(HttpSecurity http) {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
return new BasicAuthFilter("/**",
handlerInitializer.getAuthenticationFailureHandler(),
securityObjectMapper,
authenticationManager,
handlerInitializer.getResourceAccessExceptionHandler());
}
private NonCompulsoryAuthenticationProcessingFilter loginFilter(HttpSecurity http) {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
return new BasicAuthFilter("/**",
handlerInitializer.getAuthenticationFailureHandler(),
securityObjectMapper,
authenticationManager,
handlerInitializer.getResourceAccessExceptionHandler());
}

private X509AuthenticationFilter x509AuthenticationFilter() {
return new X509AuthAwareFilter("/**",
handlerInitializer.getAuthenticationFailureHandler(),
x509AuthenticationProvider);
}

private X509AuthenticationFilter x509AuthenticationFilter() {
return new X509AuthAwareFilter("/**",
handlerInitializer.getAuthenticationFailureHandler(),
x509AuthenticationProvider);
}

}


Expand Down Expand Up @@ -498,7 +502,7 @@ public SecurityFilterChain certificateOrAuthEndpointsFilterChain(HttpSecurity ht
// filter out API ML certificate
.addFilterBefore(reversedCategorizeCertFilter(), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class);
} else {
http.x509(); // default x509 filter, authenticates trusted cert
http.x509().userDetailsService(x509UserDetailsService()); // default x509 filter, authenticates trusted cert
}

return http.authenticationProvider(compoundAuthProvider) // for authenticating credentials
Expand Down Expand Up @@ -633,4 +637,9 @@ protected HttpSecurity baseConfigure(HttpSecurity http) throws Exception {
.exceptionHandling().authenticationEntryPoint(handlerInitializer.getBasicAuthUnauthorizedHandler())
.and();
}

private UserDetailsService x509UserDetailsService() {
return username -> new User(username, "", Collections.emptyList());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package org.zowe.apiml.gateway.security.login.dummy;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
Expand All @@ -32,6 +33,7 @@
* Allows Gateway to run without mainframe (z/OSMF service)
*/
@Component
@ConditionalOnProperty(value = "apiml.security.auth.provider", havingValue = "dummy")
public class DummyAuthenticationProvider extends DaoAuthenticationProvider {
private static final String DUMMY_PROVIDER = "Dummy provider";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
Expand All @@ -30,6 +31,7 @@
*/
@Component
@Qualifier("dummyService")
@ConditionalOnProperty(value = "apiml.security.auth.provider", havingValue = "dummy")
public class InMemoryUserDetailsService implements UserDetailsService {
private final BCryptPasswordEncoder passwordEncoder;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
Expand All @@ -26,6 +27,7 @@

@Component
@Slf4j
@ConditionalOnExpression("#{('${apiml.security.auth.provider:zosmf}' == 'zosmf') or ('${apiml.security.auth.provider:zosmf}' == 'dummy') or ('${apiml.security.auth.provider:zosmf}' == 'saf')}")
public class ZosAuthenticationProvider implements AuthenticationProvider, InitializingBean {
private PlatformUser platformUser = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
Expand All @@ -32,6 +33,7 @@
*/
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(value = "apiml.security.auth.provider", havingValue = "zosmf", matchIfMissing = true)
public class ZosmfAuthenticationProvider implements AuthenticationProvider {

private final AuthenticationService authenticationService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@
import org.zowe.apiml.security.common.error.AuthenticationTokenException;
import org.zowe.apiml.security.common.token.TokenAuthentication;

import java.util.Optional;

@RequiredArgsConstructor
@Service
@Slf4j
public class TokenCreationService {
private final Providers providers;
private final ZosmfAuthenticationProvider zosmfAuthenticationProvider;
private final Optional<ZosmfAuthenticationProvider> zosmfAuthenticationProvider;
private final PassTicketService passTicketService;
private final AuthenticationService authenticationService;

Expand Down Expand Up @@ -56,7 +58,9 @@ public String createJwtTokenWithoutCredentials(String user) {
log.debug("Generating PassTicket for user: {} and ZOSMF applid: {}", user, zosmfApplId);
String passTicket = passTicketService.generate(user, zosmfApplId);
log.debug("Generated passticket: {}", passTicket);
return ((TokenAuthentication) zosmfAuthenticationProvider.authenticate(new UsernamePasswordAuthenticationToken(user, passTicket)))
return ((TokenAuthentication) zosmfAuthenticationProvider
.orElseThrow(() -> new IllegalStateException("The z/OSMF is not configured. The config value `apiml.security.auth.provider` should be set to `zosmf`."))
.authenticate(new UsernamePasswordAuthenticationToken(user, passTicket)))
.getCredentials();
} catch (IRRPassTicketGenerationException e) {
throw new AuthenticationTokenException("Problem with generating PassTicket");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.zowe.apiml.security.common.error.AuthenticationTokenException;
import org.zowe.apiml.security.common.token.TokenAuthentication;

import java.util.Optional;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertThrows;
Expand Down Expand Up @@ -48,7 +50,7 @@ void setUp() {
providers = mock(Providers.class);
authenticationService = mock(AuthenticationService.class);

underTest = new TokenCreationService(providers, zosmfAuthenticationProvider, passTicketService, authenticationService);
underTest = new TokenCreationService(providers, Optional.of(zosmfAuthenticationProvider), passTicketService, authenticationService);
underTest.zosmfApplId = "IZUDFLT";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,12 @@ void givenNotAuthorizedCall_thenDontAllowToRevokeTokensForUser() {
given().contentType(ContentType.JSON).body(bodyContent).when()
.post(VALIDATE_ENDPOINT)
.then().statusCode(200);
// revoke all tokens fro USERNAME
// revoke all tokens for USERNAME
Map<String, String> requestBody = new HashMap<>();
requestBody.put("userId", SecurityUtils.USERNAME);
given().contentType(ContentType.JSON).config(SslContext.clientCertApiml).body(requestBody)
.when().delete(REVOKE_FOR_USER_ENDPOINT)
.then().statusCode(401);
.then().statusCode(403);
// validate after revocation rule
given().contentType(ContentType.JSON).body(bodyContent).when()
.post(VALIDATE_ENDPOINT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,28 @@
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHeaders;
import org.json.JSONObject;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.http.HttpStatus;
import org.zowe.apiml.security.common.login.LoginRequest;
import org.zowe.apiml.util.TestWithStartedInstances;
import org.zowe.apiml.util.categories.*;
import org.zowe.apiml.util.config.*;
import org.zowe.apiml.util.categories.GeneralAuthenticationTest;
import org.zowe.apiml.util.categories.SAFAuthTest;
import org.zowe.apiml.util.categories.zOSMFAuthTest;
import org.zowe.apiml.util.config.ConfigReader;
import org.zowe.apiml.util.config.ItSslConfigFactory;
import org.zowe.apiml.util.config.SslContext;
import org.zowe.apiml.util.http.HttpRequestUtils;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Optional;
import java.util.stream.Stream;

Expand Down Expand Up @@ -111,7 +120,7 @@ void givenValidCredentialsInBody(URI loginUrl) {
@MethodSource("org.zowe.apiml.integration.authentication.providers.LoginTest#loginUrlsSource")
void givenValidCredentialsInHeader(URI loginUrl) {
String token = given()
.auth().preemptive().basic(getUsername(), new String(getPassword()))
.auth().preemptive().basic(getUsername(), getPassword())
.contentType(JSON)
.when()
.post(loginUrl)
Expand Down Expand Up @@ -155,11 +164,11 @@ void givenInvalidCredentialsInBody(URI loginUrl) {
void givenInvalidCredentialsInHeader(URI loginUrl) {
String expectedMessage = "Invalid username or password for URL '" + getPath(loginUrl) + "'";

LoginRequest loginRequest = new LoginRequest(INVALID_USERNAME, INVALID_PASSWORD.toCharArray());
String headerValue = "Basic " + Base64.getEncoder().encodeToString((INVALID_USERNAME + ":" + INVALID_PASSWORD).getBytes(StandardCharsets.UTF_8));

given()
.contentType(JSON)
.body(loginRequest)
.header(HttpHeaders.AUTHORIZATION, headerValue)
.when()
.post(loginUrl)
.then()
Expand All @@ -168,6 +177,7 @@ void givenInvalidCredentialsInHeader(URI loginUrl) {
"messages.find { it.messageNumber == 'ZWEAG120E' }.messageContent", equalTo(expectedMessage)
);
}

}

@Nested
Expand Down
Loading

0 comments on commit c4dc217

Please sign in to comment.