Skip to content
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

apiml/GH2062/add-x509-auth-source #2185

Merged
merged 20 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ef02cae
feat(authentication): introduce x509 authentication source
yelyzavetachebanova Feb 22, 2022
0038dd4
refactor: use dedicated origin of the authentication source instead o…
yelyzavetachebanova Mar 7, 2022
61dff2a
refactor: improve code coverage
yelyzavetachebanova Mar 7, 2022
dce428c
refactor: resolve licence issue
yelyzavetachebanova Mar 7, 2022
1d77b00
Merge branch 'master' into apiml/GH2062/refactoring-add-x509-origin-o…
yelyzavetachebanova Mar 7, 2022
3415b01
Merge remote-tracking branch 'origin/apiml/GH2062/refactoring-add-x50…
yelyzavetachebanova Mar 7, 2022
ea27c2f
feat: Add implementation of AuthSourceService interface to process cl…
yelyzavetachebanova Mar 9, 2022
5e130cb
Merge branch 'master' into apiml/GH2062/add-x509-auth-source
yelyzavetachebanova Mar 9, 2022
358c72b
feat: add JUnits
yelyzavetachebanova Mar 9, 2022
a261d11
Merge branch 'master' into apiml/GH2062/add-x509-auth-source
yelyzavetachebanova Mar 9, 2022
173824e
feat: return BAD REQUEST (400) when X509 certificate which cannot be …
yelyzavetachebanova Mar 11, 2022
35357a3
feat: fix error in acceptance test (ZosmfSchemeTest)
yelyzavetachebanova Mar 11, 2022
bc0da0d
Merge branch 'master' into apiml/GH2062/add-x509-auth-source
yelyzavetachebanova Mar 11, 2022
81319d1
feat: fix Sonar issues
yelyzavetachebanova Mar 11, 2022
25bf11b
Merge branch 'master' into apiml/GH2062/add-x509-auth-source
yelyzavetachebanova Mar 11, 2022
f3f3899
feat: define X509 authentication source service as bean in configuration
yelyzavetachebanova Mar 15, 2022
c06e1e6
Merge branch 'master' into apiml/GH2062/add-x509-auth-source
yelyzavetachebanova Mar 15, 2022
2c681a6
rerun
achmelo Mar 15, 2022
a107457
merge
achmelo Mar 15, 2022
0fb379d
Merge remote-tracking branch 'origin/master' into apiml/GH2062/add-x5…
achmelo Mar 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import org.zowe.apiml.gateway.security.service.schema.AuthenticationCommand;
import org.zowe.apiml.gateway.security.service.schema.source.AuthSource;
import org.zowe.apiml.gateway.security.service.schema.source.AuthSourceService;
import org.zowe.apiml.security.common.error.InvalidCertificateException;
import org.zowe.apiml.security.common.token.TokenExpireException;

import static org.apache.http.HttpStatus.SC_BAD_REQUEST;
import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.*;

Expand Down Expand Up @@ -54,6 +56,7 @@ public Object run() {
final RequestContext context = RequestContext.getCurrentContext();

boolean rejected = false;
boolean badRequest = false;
AuthenticationCommand cmd = null;

final String serviceId = (String) context.get(SERVICE_ID_KEY);
Expand All @@ -67,6 +70,9 @@ public Object run() {
}
} catch (TokenExpireException tee) {
cmd = null;
} catch (InvalidCertificateException ice) {
rejected = true;
badRequest = true;
} catch (AuthenticationException ae) {
rejected = true;
} catch (Exception e) {
Expand All @@ -77,7 +83,7 @@ public Object run() {

if (rejected) {
context.setSendZuulResponse(false);
context.setResponseStatusCode(SC_UNAUTHORIZED);
context.setResponseStatusCode(badRequest ? SC_BAD_REQUEST : SC_UNAUTHORIZED);
} else if (cmd != null) {
try {
// Update ZUUL context by authentication schema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
*/
package org.zowe.apiml.gateway.security.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.zowe.apiml.gateway.security.login.Providers;
import org.zowe.apiml.gateway.security.login.x509.X509AbstractMapper;
import org.zowe.apiml.gateway.security.service.schema.source.X509AuthSourceService;
import org.zowe.apiml.gateway.security.service.zosmf.ZosmfService;
import org.zowe.apiml.passticket.PassTicketService;
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
Expand Down Expand Up @@ -57,4 +61,13 @@ public Providers loginProviders(
return new Providers(discoveryClient, authConfigurationProperties, compoundAuthProvider, zosmfService);
}

/**
* Implementation of AuthSourceService interface which uses client certificate as an authentication source.
* This bean performs the mapping between common name from the client certificate and the mainframe user ID.
*/
@Bean
@Qualifier("x509MFAuthSourceService")
public X509AuthSourceService getX509MFAuthSourceService(@Autowired X509AbstractMapper mapper) {
return new X509AuthSourceService(mapper);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,32 @@
* Interface defines general source of authentication. Keeps original source of authentication (JWT token, client certificate etc.).
*/
public interface AuthSource extends Serializable {

/**
* Returns the original (unparsed) source of the authentication - JWT token, client certificate etc.
* @return original source of authentication
*/
Object getRawSource();

/**
* Return type of the authentication source
* @return {@link AuthSourceType}
*/
AuthSourceType getType();

/**
* Interface defines general parsed form of the authentication source.
*/
interface Parsed {
String getUserId();
Date getCreation();
Date getExpiration();
Origin getOrigin();
}

/**
* Defines supported origins of the authentication
*/
enum Origin {

// JWT token is generated by Zowe (including ie. LTPA token from z/OSMF)
Expand Down Expand Up @@ -55,4 +72,12 @@ public static Origin valueByIssuer(String issuer) {
throw new TokenNotValidException("Unknown authentication source type : " + issuer);
}
}

/**
* Defines supported type of authentication source - JWT token and client certificate
*/
enum AuthSourceType {
JWT,
CLIENT_CERT
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/
package org.zowe.apiml.gateway.security.service.schema.source;

import java.util.EnumMap;
import java.util.Map;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;
import org.zowe.apiml.gateway.security.service.schema.source.AuthSource.AuthSourceType;
import org.zowe.apiml.gateway.security.service.schema.source.AuthSource.Parsed;

/**
* Main implementation of AuthSourceService, supports two types of authentication source - JWT token and client certificate.
* <p>
* Service keeps a map of the specific implementations of {@link AuthSourceService} which are responsible to perform operations defined by an interface
* for a particular authentication source. {@link JwtAuthSourceService} is responsible for processing of the authentication source based on JWT token;
* @Qualifier("x509MFAuthSourceService") {@link X509AuthSourceService} is responsible for processing of the authentication source based on client certificate.
* The key for the map is {@link AuthSourceType}.
*/
@Slf4j
@Service
@Primary
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DefaultAuthSourceService implements AuthSourceService {
private final Map<AuthSourceType, AuthSourceService> map = new EnumMap<>(AuthSourceType.class);

/**
* Build the map of the specific implementations of {@link AuthSourceService} for processing of different type of authentications
* @param jwtAuthSourceService {@link JwtAuthSourceService} service which process authentication source of type JWT
* @param x509AuthSourceService {@link X509AuthSourceService} service which process authentication source of type client certificate
*/
public DefaultAuthSourceService(@Autowired JwtAuthSourceService jwtAuthSourceService, @Autowired @Qualifier("x509MFAuthSourceService") X509AuthSourceService x509AuthSourceService) {
map.put(AuthSourceType.JWT, jwtAuthSourceService);
map.put(AuthSourceType.CLIENT_CERT, x509AuthSourceService);
}

/**
* Core method of the interface. Gets source of authentication from request.
* <p>
* In case if more than one source is present in request the precedence is the following:
* 1) JWT token
* 2) Client certificate
* <p>
* @return Optional<AuthSource> which hold original source of authentication (JWT token, client certificate etc.)
* or Optional.empty() when no authentication source found.
*/
@Override
public Optional<AuthSource> getAuthSourceFromRequest() {
AuthSourceService service = getService(AuthSourceType.JWT);
Optional<AuthSource> authSource = service.getAuthSourceFromRequest();
if (!authSource.isPresent()) {
service = getService(AuthSourceType.CLIENT_CERT);
authSource = service.getAuthSourceFromRequest();
}
return authSource;
}

/**
* Delegates the validation of the authentication source to a corresponding service.
* @param authSource {@link AuthSource} object which hold original source of authentication (JWT token, client certificate etc.)
* @return true is authentication source is valid, false otherwise
*/
@Override
public boolean isValid(AuthSource authSource) {
AuthSourceService service = getService(authSource);
return service != null && service.isValid(authSource);
}

/**
* Delegates the parsing of the authentication source to a corresponding service.
* @param authSource {@link AuthSource} object which hold original source of authentication (JWT token, client certificate etc.)
* @return authentication source in parsed form
*/
@Override
public Parsed parse(AuthSource authSource) {
AuthSourceService service = getService(authSource);
return service != null ? service.parse(authSource) : null;
}

/**
* Delegates the generation of the LTPA token based on the authentication source to a corresponding service.
* @param authSource {@link AuthSource} object which hold original source of authentication (JWT token, client certificate etc.)
* @return LPTA token
*/
@Override
public String getLtpaToken(AuthSource authSource) {
AuthSourceService service = getService(authSource);
return service != null ? service.getLtpaToken(authSource) : null;
}

/**
* Choose a service to process authentication source from the map of available services.
* @param authSource {@link AuthSource} object which hold original source of authentication (JWT token, client certificate etc.)
* @return implementation of {@link AuthSourceService} or null if service not found
*/
private AuthSourceService getService(AuthSource authSource) {
return authSource != null ? getService(authSource.getType()) : null;
}

/**
* Choose a service to process authentication source of specific type from the map of available services.
* @param authSourceType {@link AuthSourceType} type of the authentication source
* @return implementation of {@link AuthSourceService} or null if service not found
*/
private AuthSourceService getService(AuthSourceType authSourceType) {
final AuthSourceService service = map.get(authSourceType);
if (service == null) {
throw new IllegalArgumentException("Unknown authentication source");
}
return service;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
@Getter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class JwtAuthSource implements AuthSource {
public static final AuthSourceType type = AuthSourceType.JWT;

/**
* JWT token
*/
Expand All @@ -32,6 +34,11 @@ public String getRawSource() {
return source;
}

@Override
public AuthSourceType getType() {
return type;
}

@RequiredArgsConstructor
@Getter
@EqualsAndHashCode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,34 @@
import org.zowe.apiml.security.common.token.QueryResponse;

/**
* Main implementation of AuthSourceService, currently support only on type of authentication source - JWT token.
* Implementation of AuthSourceService which supports JWT token as authentication source.
*/
@Slf4j
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AuthSourceServiceImpl implements AuthSourceService {
public class JwtAuthSourceService implements AuthSourceService {
@Autowired
private AuthenticationService authenticationService;

/**
* Core method of the interface. Gets source of authentication (JWT token) from request.
* <p>
* @return Optional<AuthSource> which hold original source of authentication (JWT token)
* or Optional.empty() when no authentication source found.
*/
public Optional<AuthSource> getAuthSourceFromRequest() {
final RequestContext context = RequestContext.getCurrentContext();

Optional<String> jwtToken = authenticationService.getJwtTokenFromRequest(context.getRequest());
return jwtToken.map(JwtAuthSource::new);
}

/**
* Validates authentication source (JWT token) using method of {@link AuthenticationService}
* @param authSource {@link AuthSource} object which hold original source of authentication (JWT token)
* @return true if token is valid, false otherwise
*/
public boolean isValid(AuthSource authSource) {
if (authSource instanceof JwtAuthSource) {
String jwtToken = ((JwtAuthSource)authSource).getRawSource();
Expand All @@ -47,6 +58,11 @@ public boolean isValid(AuthSource authSource) {
return false;
}

/**
* Validates authentication source (JWT token) using method of {@link AuthenticationService}
* @param authSource {@link AuthSource} object which hold original source of authentication (JWT token)
* @return authentication source in parsed form
*/
public AuthSource.Parsed parse(AuthSource authSource) {
if (authSource instanceof JwtAuthSource) {
String jwtToken = ((JwtAuthSource)authSource).getRawSource();
Expand All @@ -57,6 +73,11 @@ public AuthSource.Parsed parse(AuthSource authSource) {
return null;
}

/**
* Generates LTPA token from current source of authentication (JWT token) using method of {@link AuthenticationService}
* @param authSource {@link AuthSource} object which hold original source of authentication (JWT token)
* @return LTPA token
*/
public String getLtpaToken(AuthSource authSource) {
if (authSource instanceof JwtAuthSource) {
String jwtToken = ((JwtAuthSource)authSource).getRawSource();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/
package org.zowe.apiml.gateway.security.service.schema.source;

import java.security.cert.X509Certificate;
import java.util.Date;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

/**
* Implementation of source of authentication based on client certificate.
*/
@RequiredArgsConstructor
@Getter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class X509AuthSource implements AuthSource {
public static final AuthSourceType type = AuthSourceType.CLIENT_CERT;
/**
* X509 client certificate
*/
@EqualsAndHashCode.Include
private final X509Certificate source;

@Override
public X509Certificate getRawSource() {
return source;
}

@Override
public AuthSourceType getType() {
return AuthSourceType.CLIENT_CERT;
}

@RequiredArgsConstructor
@Getter
@EqualsAndHashCode
public static class Parsed implements AuthSource.Parsed, X509Parsed {
private final String userId;
private final Date creation;
private final Date expiration;
private final Origin origin;
private final String publicKey;
private final String distinguishedName;

public String getCommonName() {
return userId;
}
}

public interface X509Parsed {
String getCommonName();
String getPublicKey();
String getDistinguishedName();
}
}
Loading