Skip to content

Commit

Permalink
Merge pull request #5 from jgwest/cft-1.0.0-java-client-1.1.4-patch
Browse files Browse the repository at this point in the history
Cft 1.0.0 java client 1.1.4 patch
  • Loading branch information
nierajsingh committed Jun 16, 2016
2 parents 2044b2f + d0f7003 commit eaeda65
Show file tree
Hide file tree
Showing 5 changed files with 365 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2012 the original author or authors.
* Copyright 2009-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -39,6 +39,8 @@ public class CloudCredentials {

private String proxyUser;

private String passcode;

/**
* Create credentials using email and password.
*
Expand Down Expand Up @@ -86,6 +88,17 @@ public CloudCredentials(OAuth2AccessToken token) {
this.token = token;
}

/**
* Create credentials using a token and passcode
*
* @param token token to use for authorization
*/
public CloudCredentials(String passcode, OAuth2AccessToken token) {
this.passcode = passcode;
this.token = token;
}


/**
* Create credentials using a token and indicates if the token is
* refreshable or not.
Expand Down Expand Up @@ -136,6 +149,15 @@ public CloudCredentials(CloudCredentials cloudCredentials, String proxyForUser)
this.proxyUser = proxyForUser;
}

/**
* Create credentials using OTP passcode.
*
* @param passcode passcode to authenticate with
*/
public CloudCredentials(String passcode) {
this.passcode = passcode;
}

/**
* Get the email.
*
Expand Down Expand Up @@ -221,4 +243,20 @@ public CloudCredentials proxyForUser(String user) {
public boolean isRefreshable() {
return refreshable;
}

/**
* Get the OTP passcode
*
* @return the passcode
*/
public String getPasscode() { return passcode; }

/**
* Is this a authentication attempt with OTP passcode?
*
* @return whether a passcode is set
*/
public boolean isPasscodeSet() {
return passcode != null;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2012 the original author or authors.
* Copyright 2009-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,7 @@
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
Expand Down Expand Up @@ -67,8 +68,7 @@ public void init(CloudCredentials credentials) {
if (credentials.getToken() != null) {
this.token = credentials.getToken();
} else {
this.token = createToken(credentials.getEmail(), credentials.getPassword(),
credentials.getClientId(), credentials.getClientSecret());
this.token = createToken(credentials);
}
}
}
Expand All @@ -85,8 +85,7 @@ public OAuth2AccessToken getToken() {

if(this.credentials.isRefreshable()) {
if (token.getExpiresIn() < 50) { // 50 seconds before expiration? Then refresh it.
token = refreshToken(token, credentials.getEmail(), credentials.getPassword(),
credentials.getClientId(), credentials.getClientSecret());
token = refreshToken(token, credentials.getClientId(), credentials.getClientSecret());
}
}

Expand All @@ -101,11 +100,23 @@ public String getAuthorizationHeader() {
return null;
}

private OAuth2AccessToken createToken(String username, String password, String clientId, String clientSecret) {
OAuth2ProtectedResourceDetails resource = getResourceDetails(username, password, clientId, clientSecret);
AccessTokenRequest request = createAccessTokenRequest(username, password);
private OAuth2AccessToken createToken(CloudCredentials credentials) {
OAuth2ProtectedResourceDetails resource = getResourceDetails(credentials);
AccessTokenRequest request = createAccessTokenRequest();

ResourceOwnerPasswordAccessTokenProvider provider;
if(credentials.isPasscodeSet()) {
// if we're using OTP passcode for auth, make sure the OAuth client is really only called once
// since on the second login attempt, the passcode is invalid and a 403 is thrown.
// some parts of cf-java-client seem to login the oauth client multiple times, so we add this protection.
if(token != null) {
return token;
}
provider = createResourceOwnerPasscodeAccessTokenProvider();
} else {
provider = createResourceOwnerPasswordAccessTokenProvider();
}

ResourceOwnerPasswordAccessTokenProvider provider = createResourceOwnerPasswordAccessTokenProvider();
try {
return provider.obtainAccessToken(resource, request);
}
Expand All @@ -117,9 +128,9 @@ private OAuth2AccessToken createToken(String username, String password, String c
}
}

private OAuth2AccessToken refreshToken(OAuth2AccessToken currentToken, String username, String password, String clientId, String clientSecret) {
OAuth2ProtectedResourceDetails resource = getResourceDetails(username, password, clientId, clientSecret);
AccessTokenRequest request = createAccessTokenRequest(username, password);
private OAuth2AccessToken refreshToken(OAuth2AccessToken currentToken, String clientId, String clientSecret) {
OAuth2ProtectedResourceDetails resource = getResourceDetails(new CloudCredentials(currentToken, clientId, clientSecret));
AccessTokenRequest request = createAccessTokenRequest();

ResourceOwnerPasswordAccessTokenProvider provider = createResourceOwnerPasswordAccessTokenProvider();

Expand Down Expand Up @@ -148,22 +159,37 @@ protected ResourceOwnerPasswordAccessTokenProvider createResourceOwnerPasswordAc
return resourceOwnerPasswordAccessTokenProvider;
}

private AccessTokenRequest createAccessTokenRequest(String username, String password) {
private AccessTokenRequest createAccessTokenRequest() {
AccessTokenRequest request = new DefaultAccessTokenRequest();
return request;
}

private OAuth2ProtectedResourceDetails getResourceDetails(String username, String password, String clientId, String clientSecret) {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setUsername(username);
resource.setPassword(password);
private OAuth2ProtectedResourceDetails getResourceDetails(CloudCredentials credentials) {
BaseOAuth2ProtectedResourceDetails resource;
if(credentials.isPasscodeSet()) {
resource = new ResourceOwnerPasscodeResourceDetails();
((ResourceOwnerPasscodeResourceDetails)resource).setPasscode(credentials.getPasscode());
}
else {
resource = new ResourceOwnerPasswordResourceDetails();
((ResourceOwnerPasswordResourceDetails)resource).setUsername(credentials.getEmail());
((ResourceOwnerPasswordResourceDetails)resource).setPassword(credentials.getPassword());
}

resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setId(clientId);
resource.setClientId(credentials.getClientId());
resource.setClientSecret(credentials.getClientSecret());
resource.setId(credentials.getClientId());
resource.setClientAuthenticationScheme(AuthenticationScheme.header);
resource.setAccessTokenUri(authorizationUrl + "/oauth/token");

return resource;
}

protected ResourceOwnerPasscodeAccessTokenProvider createResourceOwnerPasscodeAccessTokenProvider() {
ResourceOwnerPasscodeAccessTokenProvider resourceOwnerPasscodeAccessTokenProvider = new ResourceOwnerPasscodeAccessTokenProvider();
resourceOwnerPasscodeAccessTokenProvider.setRequestFactory(restTemplate.getRequestFactory()); //copy the http proxy along
return resourceOwnerPasscodeAccessTokenProvider;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.cloudfoundry.client.lib.oauth2;

import java.util.Iterator;
import java.util.List;

import org.springframework.http.HttpHeaders;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
* This class extends the Spring Security OAuth ResourceOwnerPasswordAccessTokenProvider
* to pass a passcode param instead of a username and password param.
*
* This is needed because CloudFoundry uses this passcode submission which is not part of the OAuth2 standard
* and thus it's not provided by Spring Security Oauth.
*
* It is used in SSO scenarios, similar to what the CF CLI with <code>cf login --sso</code> does.
*
* @author Matthias Winzeler <matthias.winzeler@gmail.com>
*/

public class ResourceOwnerPasscodeAccessTokenProvider extends ResourceOwnerPasswordAccessTokenProvider {
// copied from ResourceOwnerPasswordAccessTokenProvider and modified to use
// ResourceOwnerPasscodeResourceDetails
@Override
public boolean supportsResource(OAuth2ProtectedResourceDetails resource) {
return resource instanceof ResourceOwnerPasscodeResourceDetails && "password".equals(resource.getGrantType());
}

@Override
public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)
throws UserRedirectRequiredException, AccessDeniedException, OAuth2AccessDeniedException {

ResourceOwnerPasscodeResourceDetails resource = (ResourceOwnerPasscodeResourceDetails) details;
return retrieveToken(request, resource, getParametersForTokenRequest(resource, request), new HttpHeaders());
}

// copied from ResourceOwnerPasswordAccessTokenProvider and modified to send passcode instead of user/pw
private MultiValueMap<String, String> getParametersForTokenRequest(ResourceOwnerPasscodeResourceDetails resource, AccessTokenRequest request) {
MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
form.set("grant_type", "password");

form.set("passcode", resource.getPasscode());
form.putAll(request);

if (resource.isScoped()) {

StringBuilder builder = new StringBuilder();
List<String> scope = resource.getScope();

if (scope != null) {
Iterator<String> scopeIt = scope.iterator();
while (scopeIt.hasNext()) {
builder.append(scopeIt.next());
if (scopeIt.hasNext()) {
builder.append(' ');
}
}
}

form.set("scope", builder.toString());
}

return form;

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.cloudfoundry.client.lib.oauth2;

import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;

/**
* Resource details for passcode auth.
*
* @see ResourceOwnerPasscodeAccessTokenProvider
*
* @author Matthias Winzeler <matthias.winzeler@gmail.com>
*/
public class ResourceOwnerPasscodeResourceDetails extends BaseOAuth2ProtectedResourceDetails {
private String passcode;

public ResourceOwnerPasscodeResourceDetails() {
setGrantType("password");
}

public String getPasscode() {
return passcode;
}

public void setPasscode(String passcode) {
this.passcode = passcode;
}
}

0 comments on commit eaeda65

Please sign in to comment.