Expand Up
@@ -12,6 +12,7 @@
import static org .junit .Assert .assertThat ;
import static org .junit .Assert .assertTrue ;
import static org .junit .Assert .fail ;
import static org .junit .Assume .assumeNotNull ;
import static org .junit .Assume .assumeTrue ;
import java .io .File ;
Expand All
@@ -34,6 +35,8 @@
import java .util .Set ;
import java .util .UUID ;
import java .util .concurrent .atomic .AtomicInteger ;
import java .util .regex .Matcher ;
import java .util .regex .Pattern ;
import org .apache .commons .io .IOUtils ;
import org .cloudfoundry .client .lib .domain .ApplicationLog ;
Expand All
@@ -55,6 +58,7 @@
import org .cloudfoundry .client .lib .domain .CloudServicePlan ;
import org .cloudfoundry .client .lib .domain .CloudSpace ;
import org .cloudfoundry .client .lib .domain .CloudStack ;
import org .cloudfoundry .client .lib .domain .CloudUser ;
import org .cloudfoundry .client .lib .domain .CrashInfo ;
import org .cloudfoundry .client .lib .domain .CrashesInfo ;
import org .cloudfoundry .client .lib .domain .InstanceInfo ;
Expand All
@@ -63,7 +67,6 @@
import org .cloudfoundry .client .lib .domain .InstancesInfo ;
import org .cloudfoundry .client .lib .domain .SecurityGroupRule ;
import org .cloudfoundry .client .lib .domain .Staging ;
import org .cloudfoundry .client .lib .domain .CloudUser ;
import org .cloudfoundry .client .lib .oauth2 .OauthClient ;
import org .cloudfoundry .client .lib .rest .CloudControllerClient ;
import org .cloudfoundry .client .lib .rest .CloudControllerClientFactory ;
Expand Down
Expand Up
@@ -91,14 +94,15 @@
import org .junit .runner .Description ;
import org .junit .runner .RunWith ;
import org .springframework .core .io .ClassPathResource ;
import org .springframework .http .HttpMethod ;
import org .springframework .http .HttpStatus ;
import org .springframework .http .*;
import org .springframework .http .client .ClientHttpRequestFactory ;
import org .springframework .http .client .ClientHttpResponse ;
import org .springframework .http .client .HttpComponentsClientHttpRequestFactory ;
import org .springframework .http .client .SimpleClientHttpRequestFactory ;
import org .springframework .security .oauth2 .common .DefaultOAuth2AccessToken ;
import org .springframework .security .oauth2 .common .OAuth2AccessToken ;
import org .springframework .util .LinkedMultiValueMap ;
import org .springframework .util .MultiValueMap ;
import org .springframework .web .client .HttpClientErrorException ;
import org .springframework .web .client .HttpServerErrorException ;
import org .springframework .web .client .ResponseExtractor ;
Expand Down
Expand Up
@@ -157,6 +161,8 @@ public class CloudFoundryClientTest {
public static final int STARTUP_TIMEOUT = Integer .getInteger ("ccng.startup.timeout" , 60000 );
private static final boolean SSO_ENABLED = "true" .equalsIgnoreCase (System .getProperty ("ccng.sso.enabled" , "false" ).trim ());
private static String defaultDomainName = null ;
private static HttpProxyConfiguration httpProxyConfiguration ;
Expand Down
Expand Up
@@ -2248,6 +2254,145 @@ private void deleteAnyOrphanedTestSecurityGroups(){
}
}
@ Test
public void loginWithPasscode () throws Exception {
assumeTrue (SSO_ENABLED );
String passcode = getOtpPasscodeForTest ();
assumeNotNull (passcode );
CloudCredentials credentials = new CloudCredentials (passcode );
URL cloudControllerUrl = new URL (CCNG_API_URL );
CloudFoundryClient clientForPasscodeLogin =
new CloudFoundryClient (credentials , cloudControllerUrl , httpProxyConfiguration );
// setting up client triggers login - so no need to login() again
assertTrue ("orgs should be retrieved (login successful)" , !clientForPasscodeLogin .getOrganizations ().isEmpty ());
}
@ Test
public void loginWithPasscodeHandlesMultipleCallsToLogin () throws Exception {
assumeTrue (SSO_ENABLED );
String passcode = getOtpPasscodeForTest ();
assumeNotNull (passcode );
CloudCredentials credentials = new CloudCredentials (passcode );
URL cloudControllerUrl = new URL (CCNG_API_URL );
CloudFoundryClient clientForPasscodeLogin =
new CloudFoundryClient (credentials , cloudControllerUrl , httpProxyConfiguration );
clientForPasscodeLogin .login ();
clientForPasscodeLogin .login ();
clientForPasscodeLogin .login ();
assertTrue ("orgs should be retrieved (login successful)" , !clientForPasscodeLogin .getOrganizations ().isEmpty ());
}
@ Test
public void refreshTokenOnExpirationWithPasscodeLogin () throws Exception {
assumeTrue (SSO_ENABLED );
String passcode = getOtpPasscodeForTest ();
assumeNotNull (passcode );
CloudCredentials credentials = new CloudCredentials (passcode );
URL cloudControllerUrl = new URL (CCNG_API_URL );
CloudControllerClientFactory factory =
new CloudControllerClientFactory (httpProxyConfiguration , CCNG_API_SSL );
CloudControllerClient client = factory .newCloudController (cloudControllerUrl , credentials , CCNG_USER_ORG , CCNG_USER_SPACE );
client .login ();
validateClientAccess (client );
OauthClient oauthClient = factory .getOauthClient ();
OAuth2AccessToken token = oauthClient .getToken ();
if (token instanceof DefaultOAuth2AccessToken ) {
// set the token expiration to "now", forcing the access token to be refreshed
((DefaultOAuth2AccessToken ) token ).setExpiration (new Date ());
validateClientAccess (client );
} else {
fail ("Error forcing expiration of access token" );
}
}
@ Test
public void obtainPasscodeForLoggedInUser () throws Exception {
assumeTrue (SSO_ENABLED );
String passcodePattern = "[A-Za-z0-9]{6}" ;
String passcode1 = getOtpPasscodeForTest ();
assumeNotNull (passcode1 );
assertTrue ("Passcode is retrieved and looks valid" , passcode1 .matches (passcodePattern ));
String passcode2 = getOtpPasscodeForTest ();
assertTrue ("Can obtain multiple passcodes" , !passcode2 .equals (passcode1 ));
assertTrue ("Can obtain multiple passcodes" , passcode2 .matches (passcodePattern ));
}
private String getOtpPasscodeForTest () throws Exception {
try {
CloudInfo info = connectedClient .getCloudInfo ();
List <String > cookies = doFormAuthOnLoginServer (info .getAuthorizationEndpoint ());
return retrieveOtpPasscodeFromLoginServerSession (info .getAuthorizationEndpoint (), cookies );
}
catch (Exception e ) {
System .err .println ("WARNING: Skipping some tests since obtaining passcodes failed: " + e .getMessage ());
e .printStackTrace ();
return null ;
}
}
private List <String > doFormAuthOnLoginServer (String loginServerUrl ) {
String loginEndpointUrl = loginServerUrl + "/login.do" ;
RestUtil restUtil = new RestUtil ();
RestTemplate restTemplate = restUtil .createRestTemplate (httpProxyConfiguration , CCNG_API_SSL );
MultiValueMap <String , String > loginRequestBody = new LinkedMultiValueMap <>();
loginRequestBody .add ("username" , CCNG_USER_EMAIL );
loginRequestBody .add ("password" , CCNG_USER_PASS );
HttpHeaders loginRequestHeaders = new HttpHeaders ();
loginRequestHeaders .setContentType (MediaType .APPLICATION_FORM_URLENCODED );
HttpEntity <MultiValueMap <String , String >> loginRequest = new HttpEntity <>(loginRequestBody , loginRequestHeaders );
HttpEntity <String > response = restTemplate .exchange (loginEndpointUrl , HttpMethod .POST , loginRequest , String .class );
HttpHeaders headers = response .getHeaders ();
List <String > cookies = headers .get ("Set-Cookie" );
if (cookies == null || cookies .isEmpty ()) {
throw new RuntimeException ("No cookies set when logging in on login server for passcodes: " + response );
}
return cookies ;
}
private String retrieveOtpPasscodeFromLoginServerSession (String loginServerUrl , List <String > sessionCookies ) {
String passcodeUrl = loginServerUrl + "/passcode" ;
RestUtil restUtil = new RestUtil ();
RestTemplate restTemplate = restUtil .createRestTemplate (httpProxyConfiguration , CCNG_API_SSL );
HttpHeaders requestHeaders = new HttpHeaders ();
for (String cookie : sessionCookies ) {
requestHeaders .add ("Cookie" , cookie );
}
requestHeaders .add ("Accept" , "*/*" );
HttpEntity requestEntity = new HttpEntity (null , requestHeaders );
ResponseEntity passcodeResponse = restTemplate .exchange (
passcodeUrl ,
HttpMethod .GET ,
requestEntity ,
String .class );
return parseOtpPasscodeFromHtml ((String ) passcodeResponse .getBody ());
}
private String parseOtpPasscodeFromHtml (String html ) {
String passcodeRegex = "<h1>Temporary Authentication Code</h1>.*<h2>([A-Za-z0-9]{6})</h2>" ;
Pattern passcodePattern = Pattern .compile (passcodeRegex , Pattern .DOTALL );
Matcher passcodeMatcher = passcodePattern .matcher (html );
assertTrue (passcodeMatcher .find ());
return passcodeMatcher .group (1 );
}
//
// Shared test methods
//
Expand Down