Skip to content

Commit

Permalink
Merge pull request #101 from ConsumerDataStandardsAustralia/feature/8…
Browse files Browse the repository at this point in the history
…7-web-based-tokens-in-client-cli

Web Login integration with Client CLI
  • Loading branch information
vadkor authored Jan 31, 2020
2 parents f825ab3 + 6f0a950 commit 23f3907
Show file tree
Hide file tree
Showing 35 changed files with 21,283 additions and 57 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# java-artefacts
[![Build Status](https://travis-ci.com/ConsumerDataStandardsAustralia/java-artefacts.svg?branch=master)](https://travis-ci.com/ConsumerDataStandardsAustralia/java-artefacts)
[![Build Status](https://travis-ci.org/ConsumerDataStandardsAustralia/java-artefacts.svg?branch=master)](https://travis-ci.org/ConsumerDataStandardsAustralia/java-artefacts)
[![license](https://img.shields.io/github/license/ConsumerDataStandardsAustralia/java-artefacts)](https://github.com/ConsumerDataStandardsAustralia/java-artefacts/blob/master/LICENSE)
[![version](https://img.shields.io/github/v/tag/ConsumerDataStandardsAustralia/java-artefacts.svg)](https://github.com/ConsumerDataStandardsAustralia/java-artefacts/releases/latest)
[![issues](https://img.shields.io/github/issues/ConsumerDataStandardsAustralia/java-artefacts)](https://github.com/ConsumerDataStandardsAustralia/java-artefacts/issues)
Expand Down
4 changes: 3 additions & 1 deletion client-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ CDS Client (Data Recipient) CLI

### Get started

Open a terminal and execute command
Open a terminal, cd to the `java-artefacts/client-cli` directory and execute command

mvn spring-boot:run

Expand All @@ -36,6 +36,8 @@ Auth- and security-related Functions
jwks-path: Set JWKS keystore file path (Property: jwks.path)
refresh-token: Set refresh token. The access token, if set and valid, takes precedence. (Property: refresh.token)
setup-mtls: Setup client certificate and CA to enable MTLS connection to the server
unset-access-token: Unset access token
unset-refresh-token: Unset refresh token
verifying-ssl: Set verifyingSsl, e.g. true, false

BankingAccounts
Expand Down
17 changes: 17 additions & 0 deletions client-cli/keystore/keystore.jwks
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"keys": [
{
"p": "yPPnr_O1OM68k6PrrNWKHewqmGMgXzT1ApgxI9IvR4F3bucJuo4LijUTjt4vqmwbeaFd_0uQQg2SukmpmZZwItD-f8WbP495hiCPikYQvF7wXvO_IjCTWubXW8iwcQQ_4EnQ7Xn8VFhAbSPPR2f7Kn92NG8sVF54VdRF5OcwFGE",
"kty": "RSA",
"q": "taDLDWgmiqKMpIV7oSjoVuj5ERZ2thBYYlwh2Wr4g9TQZnYjgJwiA6xVw9Os7rUebAZpNH3R5J38VSrc7NpWKszeeozrtWrVTlBFOlW8cJi_AQZtKsLQ6Lg7MUApgBc6_PXWTbMIKi_Q3s0cOrE9MCafob9vFazGB7n6QuyL5Z0",
"d": "SFNC2W20F4srgwUu0l3y378Vl2FQZCE1hts7JIdqGnpOOiU1fFJD8Plyi4Z8WQyGjAt6UbejLh65Goc2X0Jn0b1Ojr2tbUu7gdAQZdZLDMBYHp7N4MSnaiEV4MLYrOx7i3Z8nFf8pXuBEKQa0CZ62pjALlP-STT-H9VGYyfo8Gd4WyLnfXFioHfDVohHvfoAZZ6L-B5hVozL7WDt_wDsPAP0n6OzhS891yjGtPOf44hMzbIB8HjlrunhLYUBGuEYTSqeMbgfVQDB5cvmjWN-tg_JI-KprE1VOg5x5__ZIP3wFQuU0LKct_C3-10ZIzJEE7jweETYDZVR_xRQ-r4bAQ",
"e": "AQAB",
"kid": "rsa1",
"qi": "scUOO7I18o9_vqQl2Hg5bq-UVjgoi9zg-0zgH2n79_jbxH9Q3Sa3ASzRuJukcAkdRYFdXnWwRNrD61lnrI8gRoH_eiTK0DYOhztql-DWNzGvYdP8rJGQpuYlsKlnTnlNwJsiM4ujsWfX3QAgNzljJod5WHd_Wa6eLMmmfvrlNy4",
"dp": "EHmWrCFB4UdpxHzy7HeRXESpdVbjJ4sS70SE-tfXkng1z3zXmljc71bMpLDonYNBeWZWa4DJcfDk76rsYbAov2H9C4Kq6Lodj3aJ7h8ybg8mi9JWADqVSiWU0GySsqFTj1Ld8ypGGrAlkA0YiFUpDWHn0gKvUrdwDDFcDZk6ouE",
"alg": "RS256",
"dq": "EthsOSflV90l2qw-96nW8a2aqjs6dgmcIhiNKpH-1y7tsLPxWxbYMKlg_MCp4NrKHUtM_zVO4fnChNO9uJWu-yHms7CLhHdhSo7pn_PlaYlvyBtymE0-f-cdvS57H99JL5YSdl1tK3NqqLN_ild5kdVwGshSLl2O7p5NaURB_8E",
"n": "jpKqpCQkE8SzjdujeKMJo2W6x20UM3MRmmTzBAIlQKtRpAZXlALGKnQ89kpSNBc4ejuWNXRLgYwHhma6La70zhicrvsG6Cdeld9172NqhFCsedbapp7XMfJ6gjM7jScyjxW6_1emqJLI8eMLC0SCLAq9crAPv5cWcdWyapaN9n3XvPbkG9iPis6xVX3mKrC5DZ6hcOwmgsgTw92qH2i3XrixL3iQKoXKbCCKOfEWTB4aH9SBYJcpBmMALGRDogbHNFTpK0I7uGE1mxaPnEttsRf7u9yZsrfKwTZnE323w05F6r-fzcr0PZpAWGNH02vRBAkGaOxEv2Btg3UWR2JEfQ"
}
]
}
39 changes: 39 additions & 0 deletions client-cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,52 @@
<description>CDS client side command line tool</description>
<properties>
<spring-shell.version>2.0.1.RELEASE</spring-shell.version>
<spring-security.version>4.2.11.RELEASE</spring-security.version>
<mitreid-version>1.3.3</mitreid-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring-security.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mitre</groupId>
<artifactId>openid-connect-client</artifactId>
<version>${mitreid-version}</version>
</dependency>
<dependency>
<groupId>au.org.consumerdatastandards</groupId>
<artifactId>client</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,50 @@
*/
package au.org.consumerdatastandards.client.cli;

import au.org.consumerdatastandards.client.cli.support.ApiUtil;
import ch.qos.logback.classic.Logger;
import org.apache.commons.lang3.StringUtils;
import org.mitre.jose.keystore.JWKSetKeyStore;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.client.service.impl.StaticClientConfigurationService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.shell.standard.ShellCommandGroup;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

import javax.annotation.PostConstruct;
import java.util.Iterator;
import java.util.Map;

@ShellComponent
@ShellCommandGroup("Auth- and security-related Functions")
public class Auth extends ApiCliBase {

private static final Logger LOGGER = (Logger) LoggerFactory.getLogger(Auth.class);

@Autowired
StaticClientConfigurationService clientConfig;

@Autowired
JWKSetKeyStore jwksKeyStore;

@PostConstruct
// Needs to be called after component initialised to init auth server and client id in case they come from application.properties
public void init() {
String authServer = apiClientOptions.getAuthServer();
if (StringUtils.isNotBlank(authServer)) {
Map<String, RegisteredClient> clients = clientConfig.getClients();
Iterator<RegisteredClient> it = clients.values().iterator();
RegisteredClient clientSettings = it.next();
it.remove();
clientSettings.setClientId(apiClientOptions.getClientId());
clients.put(authServer, clientSettings);
}
}

@ShellMethod("Set verifyingSsl, e.g. true, false")
public void verifyingSsl(@ShellOption String verifyingSsl) {
apiClientOptions.setVerifyingSsl(Boolean.getBoolean(verifyingSsl));
Expand Down Expand Up @@ -61,6 +92,12 @@ public void accessToken(@ShellOption String jwt) {
apiClientOptions.setAccessToken(jwt);
}

@ShellMethod("Unset access token")
public void unsetAccessToken() {
ApiUtil.accessTokenExpiresAt = 0;
apiClientOptions.setAccessToken(null);
}

@ShellMethod("Get currently set access token. The refresh token flow can update access token.")
public String getAccessToken() {
return apiClientOptions.getAccessToken();
Expand All @@ -69,6 +106,7 @@ public String getAccessToken() {
@ShellMethod("Set the base URL of the OIDC Server (Property: auth.server)")
public void authServer(@ShellOption String authServer) {
apiClientOptions.setAuthServer(authServer);
init();
}

@ShellMethod("Get configured OIDC Server base URL")
Expand All @@ -81,6 +119,11 @@ public void refreshToken(@ShellOption String refreshToken) {
apiClientOptions.setRefreshToken(refreshToken);
}

@ShellMethod("Unset refresh token")
public void unsetRefreshToken() {
apiClientOptions.setRefreshToken(null);
}

@ShellMethod("Get refresh token")
public String getRefreshToken() {
return apiClientOptions.getRefreshToken();
Expand All @@ -89,6 +132,7 @@ public String getRefreshToken() {
@ShellMethod("Set client ID registered on the Auth Server (Property: client.id)")
public void clientId(@ShellOption String clientId) {
apiClientOptions.setClientId(clientId);
init();
}

@ShellMethod("Get client ID")
Expand All @@ -99,6 +143,7 @@ public String getClientId() {
@ShellMethod("Set JWKS keystore file path (Property: jwks.path)")
public void jwksPath(@ShellOption String jwksPath) {
apiClientOptions.setJwksPath(jwksPath);
jwksKeyStore.setLocation(new FileSystemResource(jwksPath));
}

@ShellMethod("Get JWKS keystore file path")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public String getProductDetail(@ShellOption(defaultValue = ShellOption.NULL) Boo
LOGGER.info("Get product detail CLI initiated with productId: {}",
productId);

api.setApiClient(ApiUtil.createApiClient(apiClientOptions));
api.setApiClient(ApiUtil.createApiClient(apiClientOptions, false));
ResponseBankingProductById response = api.getProductDetail(productId);
if (apiClientOptions.isValidationEnabled() || (check != null && check)) {
LOGGER.info("Payload validation is enabled");
Expand Down Expand Up @@ -74,7 +74,7 @@ public String listProducts(@ShellOption(defaultValue = ShellOption.NULL) Boolean
productCategory,
updatedSince);

api.setApiClient(ApiUtil.createApiClient(apiClientOptions));
api.setApiClient(ApiUtil.createApiClient(apiClientOptions, false));
ResponseBankingProductList response = api.listProducts(brand, effective, page, pageSize, productCategory, updatedSince);
if (apiClientOptions.isValidationEnabled() || (check != null && check)) {
LOGGER.info("Payload validation is enabled");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.jline.PromptProvider;

import java.security.Security;

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
public class CdsClientShell {
public class CdsClientShell extends SpringBootServletInitializer {

public static void main(String[] args) {
Security.addProvider(new BouncyCastleProvider());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public String getOutages(@ShellOption(defaultValue = ShellOption.NULL) Boolean c

LOGGER.info("Get outages CLI initiated");

api.setApiClient(ApiUtil.createApiClient(apiClientOptions));
api.setApiClient(ApiUtil.createApiClient(apiClientOptions, false));
ResponseDiscoveryOutagesList response = api.getOutages();
if (apiClientOptions.isValidationEnabled() || (check != null && check)) {
LOGGER.info("Payload validation is enabled");
Expand All @@ -57,7 +57,7 @@ public String getStatus(@ShellOption(defaultValue = ShellOption.NULL) Boolean ch

LOGGER.info("Get status CLI initiated");

api.setApiClient(ApiUtil.createApiClient(apiClientOptions));
api.setApiClient(ApiUtil.createApiClient(apiClientOptions, false));
CommonDiscoveryStatus response = api.getStatus();
if (apiClientOptions.isValidationEnabled() || (check != null && check)) {
LOGGER.info("Payload validation is enabled");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package au.org.consumerdatastandards.client.cli.auth;

import au.org.consumerdatastandards.client.cli.support.ApiClientOptions;
import org.mitre.jose.keystore.JWKSetKeyStore;
import org.mitre.openid.connect.client.keypublisher.JwkViewResolver;
import org.mitre.openid.connect.view.JWKSetView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.core.io.FileSystemResource;

@Configuration
@ImportResource({"classpath*:servlet-context.xml"})
public class AuthConfig {
@Autowired
protected ApiClientOptions apiClientOptions;

@Bean
public JwkViewResolver jwkViewResolver() {
JwkViewResolver res = new JwkViewResolver();
res.setJwkViewName(JWKSetView.VIEWNAME);
res.setJwk(jwkSetView());
return res;
}

@Bean
public JWKSetView jwkSetView() {
return new JWKSetView();
}

@Bean
public JWKSetKeyStore keyStore() {
JWKSetKeyStore jwkSetKeyStore = new JWKSetKeyStore();
jwkSetKeyStore.setLocation(new FileSystemResource(apiClientOptions.getJwksPath()));
return jwkSetKeyStore;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package au.org.consumerdatastandards.client.cli.auth;

import au.org.consumerdatastandards.client.cli.support.ApiClientOptions;
import au.org.consumerdatastandards.client.cli.support.ApiUtil;
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class AuthController {
@Autowired
private ApiClientOptions clientOptions;

@GetMapping("/auth")
@PreAuthorize("hasRole('ROLE_USER')")
public String auth() throws InterruptedException {
OIDCAuthenticationToken auth = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
clientOptions.setAccessToken(auth.getAccessTokenValue());
clientOptions.setRefreshToken(auth.getRefreshTokenValue());
ApiUtil.browserMutex.put(this);
return "auth";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package au.org.consumerdatastandards.client.cli.auth;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class ClientWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package au.org.consumerdatastandards.client.cli.auth;

import com.nimbusds.jose.jwk.JWK;
import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
import org.mitre.openid.connect.view.JWKSetView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.Map;

@Controller
public class JwkController {
@Autowired
private JWTSigningAndValidationService signingAndValidationService;

@GetMapping("/jwk")
public ModelAndView jwk() {
Map<String, JWK> keys = signingAndValidationService.getAllPublicKeys();
return new ModelAndView(JWKSetView.VIEWNAME, "keys", keys);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package au.org.consumerdatastandards.client.cli.auth;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {
@GetMapping("/login")
public String auth() {
return "login";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package au.org.consumerdatastandards.client.cli.auth;

import au.org.consumerdatastandards.client.cli.support.ApiUtil;
import org.mitre.openid.connect.client.OIDCAuthenticationFilter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

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

public class UnblockingOIDCAuthenticationFilter extends OIDCAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
try {
return super.attemptAuthentication(request, response);
} catch (Exception e) {
try {
ApiUtil.browserMutex.put(e);
} catch (InterruptedException ex) {
// Safe to ignore
}
throw e;
}
}
}
Loading

0 comments on commit 23f3907

Please sign in to comment.