Skip to content

Commit 1a76824

Browse files
author
Steve Powell
committed
Merge 120471467-auth-code-grant to master
[Completes #120471467]
2 parents 4f0f484 + 0bcbe51 commit 1a76824

File tree

15 files changed

+219
-37
lines changed

15 files changed

+219
-37
lines changed

cloudfoundry-client-spring/src/main/java/org/cloudfoundry/reactor/client/QueryBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public static void augment(UriComponentsBuilder builder, Object instance) {
5858
if (value instanceof Collection) {
5959
builder.queryParam(queryParameter.value(), ((Collection<?>) value).stream()
6060
.map(Object::toString)
61-
.collect(Collectors.joining(",")));
61+
.collect(Collectors.joining(queryParameter.delimiter())));
6262
} else {
6363
builder.queryParam(queryParameter.value(), value);
6464
}

cloudfoundry-client-spring/src/main/java/org/cloudfoundry/reactor/uaa/authorizations/ReactorAuthorizations.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,17 @@
2020
import io.netty.util.AsciiString;
2121
import org.cloudfoundry.reactor.uaa.AbstractUaaOperations;
2222
import org.cloudfoundry.reactor.util.AuthorizationProvider;
23+
import org.cloudfoundry.uaa.ResponseType;
2324
import org.cloudfoundry.uaa.authorizations.Authorizations;
2425
import org.cloudfoundry.uaa.authorizations.AuthorizeByAuthorizationCodeGrantApiRequest;
26+
import org.cloudfoundry.uaa.authorizations.AuthorizeByAuthorizationCodeGrantBrowserRequest;
27+
import org.cloudfoundry.util.ExceptionUtils;
2528
import org.springframework.web.util.UriComponentsBuilder;
2629
import reactor.core.publisher.Mono;
2730
import reactor.io.netty.http.HttpClient;
2831

32+
import java.util.Optional;
33+
2934
/**
3035
* The Reactor-based implementation of {@link Authorizations}
3136
*/
@@ -47,9 +52,21 @@ public ReactorAuthorizations(AuthorizationProvider authorizationProvider, HttpCl
4752

4853
@Override
4954
public Mono<String> authorizeByAuthorizationCodeGrantApi(AuthorizeByAuthorizationCodeGrantApiRequest request) {
50-
return get(request, builder -> builder.pathSegment("oauth", "authorize"))
55+
return get(request, builder -> builder.pathSegment("oauth", "authorize").queryParam("response_type", ResponseType.CODE))
56+
.map(inbound -> inbound.responseHeaders().get(LOCATION))
57+
.then(location -> extractParameterFromLocation(location, "code"));
58+
}
59+
60+
@Override
61+
public Mono<String> authorizeByAuthorizationCodeGrantBrowser(AuthorizeByAuthorizationCodeGrantBrowserRequest request) {
62+
return get(request, builder -> builder.pathSegment("oauth", "authorize").queryParam("response_type", ResponseType.CODE))
5163
.map(inbound -> inbound.responseHeaders().get(LOCATION))
52-
.map(location -> UriComponentsBuilder.fromUriString(location).build().getQueryParams().getFirst("code"));
64+
.then(location -> extractParameterFromLocation(location, "code"));
5365
}
5466

67+
private static Mono<String> extractParameterFromLocation(String location, String parameter) {
68+
return Optional.ofNullable(UriComponentsBuilder.fromUriString(location).build().getQueryParams().getFirst(parameter))
69+
.map(parameterValue -> Mono.just(parameterValue))
70+
.orElse(ExceptionUtils.illegalState(String.format("Parameter %s not found in location", parameter)));
71+
}
5572
}

cloudfoundry-client-spring/src/main/java/org/cloudfoundry/reactor/uaa/tokens/ReactorTokens.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.fasterxml.jackson.databind.ObjectMapper;
2020
import org.cloudfoundry.reactor.uaa.AbstractUaaOperations;
2121
import org.cloudfoundry.reactor.util.AuthorizationProvider;
22+
import org.cloudfoundry.uaa.ResponseType;
2223
import org.cloudfoundry.uaa.tokens.CheckTokenRequest;
2324
import org.cloudfoundry.uaa.tokens.CheckTokenResponse;
2425
import org.cloudfoundry.uaa.tokens.GetTokenByAuthorizationCodeRequest;
@@ -64,29 +65,31 @@ public Mono<CheckTokenResponse> check(CheckTokenRequest request) {
6465
@Override
6566
public Mono<GetTokenByAuthorizationCodeResponse> getByAuthorizationCode(GetTokenByAuthorizationCodeRequest request) {
6667
return postForm(request, GetTokenByAuthorizationCodeResponse.class,
67-
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", "authorization_code").queryParam("response_type", "token"));
68+
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", "authorization_code").queryParam("response_type", ResponseType.TOKEN));
6869
}
6970

7071
@Override
7172
public Mono<GetTokenByClientCredentialsResponse> getByClientCredentials(GetTokenByClientCredentialsRequest request) {
7273
return postForm(request, GetTokenByClientCredentialsResponse.class,
73-
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", "client_credentials").queryParam("response_type", "token"));
74+
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", "client_credentials").queryParam("response_type", ResponseType.TOKEN));
7475
}
7576

7677
@Override
7778
public Mono<GetTokenByOneTimePasscodeResponse> getByOneTimePasscode(GetTokenByOneTimePasscodeRequest request) {
78-
return postForm(request, GetTokenByOneTimePasscodeResponse.class, builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", "password").queryParam("response_type", "token"));
79+
return postForm(request, GetTokenByOneTimePasscodeResponse.class,
80+
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", "password").queryParam("response_type", ResponseType.TOKEN));
7981
}
8082

8183
@Override
8284
public Mono<GetTokenByOpenIdResponse> getByOpenId(GetTokenByOpenIdRequest request) {
8385
return postForm(request, GetTokenByOpenIdResponse.class,
84-
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", "authorization_code").queryParam("response_type", "id_token"));
86+
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", "authorization_code").queryParam("response_type", ResponseType.ID_TOKEN));
8587
}
8688

8789
@Override
8890
public Mono<GetTokenByPasswordResponse> getByPassword(GetTokenByPasswordRequest request) {
89-
return postForm(request, GetTokenByPasswordResponse.class, builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", "password").queryParam("response_type", "token"));
91+
return postForm(request, GetTokenByPasswordResponse.class,
92+
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", "password").queryParam("response_type", ResponseType.TOKEN));
9093
}
9194

9295
@Override

cloudfoundry-client-spring/src/test/java/org/cloudfoundry/reactor/client/QueryBuilderTest.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
import org.springframework.util.MultiValueMap;
2222
import org.springframework.web.util.UriComponentsBuilder;
2323

24+
import java.util.Arrays;
25+
import java.util.List;
26+
2427
import static org.junit.Assert.assertEquals;
2528

2629
public final class QueryBuilderTest {
@@ -32,9 +35,10 @@ public void test() {
3235
QueryBuilder.augment(builder, new StubQueryParamsSubClass());
3336

3437
MultiValueMap<String, String> queryParams = builder.build().getQueryParams();
35-
assertEquals(2, queryParams.size());
36-
assertEquals("test-value-1", queryParams.getFirst("test-parameter-1"));
38+
assertEquals(3, queryParams.size());
39+
assertEquals("test-value-1,test-value-2", queryParams.getFirst("test-parameter-1"));
3740
assertEquals("test-value-3", queryParams.getFirst("test-parameter-3"));
41+
assertEquals("test-value-4 test-value-5", queryParams.getFirst("test-parameter-4"));
3842
}
3943

4044
private static abstract class StubQueryParams {
@@ -45,8 +49,8 @@ final String getNull() {
4549
}
4650

4751
@QueryParameter("test-parameter-1")
48-
final String getParameter1() {
49-
return "test-value-1";
52+
final List<String> getParameter1() {
53+
return Arrays.asList("test-value-1", "test-value-2");
5054
}
5155

5256
}
@@ -58,6 +62,10 @@ String getParameter2() {
5862
return "test-value-3";
5963
}
6064

65+
@QueryParameter(value = "test-parameter-4", delimiter = " ")
66+
List<String> getParameter4() {
67+
return Arrays.asList("test-value-4", "test-value-5");
68+
}
6169
}
6270

6371
}

cloudfoundry-client-spring/src/test/java/org/cloudfoundry/reactor/uaa/authorizations/ReactorAuthorizationsTest.java

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import org.cloudfoundry.reactor.TestResponse;
2222
import org.cloudfoundry.reactor.uaa.AbstractUaaApiTest;
2323
import org.cloudfoundry.uaa.authorizations.AuthorizeByAuthorizationCodeGrantApiRequest;
24-
import org.cloudfoundry.uaa.authorizations.ResponseType;
24+
import org.cloudfoundry.uaa.authorizations.AuthorizeByAuthorizationCodeGrantBrowserRequest;
2525
import reactor.core.publisher.Mono;
2626

2727
import static io.netty.handler.codec.http.HttpMethod.GET;
@@ -37,7 +37,7 @@ public static final class AuthorizeByAuthorizationCodeGrantApi extends AbstractU
3737
protected InteractionContext getInteractionContext() {
3838
return InteractionContext.builder()
3939
.request(TestRequest.builder()
40-
.method(GET).path("/oauth/authorize?client_id=login&redirect_uri=https://uaa.cloudfoundry.com/redirect/cf&response_type=code&state=v4LpFF")
40+
.method(GET).path("/oauth/authorize?client_id=login&redirect_uri=https://uaa.cloudfoundry.com/redirect/cf&state=v4LpFF&response_type=code")
4141
.build())
4242
.response(TestResponse.builder()
4343
.status(FOUND)
@@ -54,7 +54,6 @@ protected String getResponse() {
5454
@Override
5555
protected AuthorizeByAuthorizationCodeGrantApiRequest getValidRequest() throws Exception {
5656
return AuthorizeByAuthorizationCodeGrantApiRequest.builder()
57-
.responseType(ResponseType.CODE)
5857
.clientId("login")
5958
.redirectUri("https://uaa.cloudfoundry.com/redirect/cf")
6059
.state("v4LpFF")
@@ -67,4 +66,42 @@ protected Mono<String> invoke(AuthorizeByAuthorizationCodeGrantApiRequest reques
6766
}
6867
}
6968

69+
public static final class AuthorizeByAuthorizationCodeGrantBrowser extends AbstractUaaApiTest<AuthorizeByAuthorizationCodeGrantBrowserRequest, String> {
70+
71+
private final ReactorAuthorizations authorizations = new ReactorAuthorizations(AUTHORIZATION_PROVIDER, HTTP_CLIENT, OBJECT_MAPPER, this.root);
72+
73+
@Override
74+
protected InteractionContext getInteractionContext() {
75+
return InteractionContext.builder()
76+
.request(TestRequest.builder()
77+
.method(GET).path("/oauth/authorize?client_id=login&redirect_uri=https://uaa.cloudfoundry.com/redirect/cf&scope=openid%20oauth.approvals&response_type=code")
78+
.build())
79+
.response(TestResponse.builder()
80+
.status(FOUND)
81+
.header("Location", "http://redirect.to/app?code=JuEj0D")
82+
.build())
83+
.build();
84+
}
85+
86+
@Override
87+
protected String getResponse() {
88+
return "JuEj0D";
89+
}
90+
91+
@Override
92+
protected AuthorizeByAuthorizationCodeGrantBrowserRequest getValidRequest() throws Exception {
93+
return AuthorizeByAuthorizationCodeGrantBrowserRequest.builder()
94+
.clientId("login")
95+
.redirectUri("https://uaa.cloudfoundry.com/redirect/cf")
96+
.scope("openid")
97+
.scope("oauth.approvals")
98+
.build();
99+
}
100+
101+
@Override
102+
protected Mono<String> invoke(AuthorizeByAuthorizationCodeGrantBrowserRequest request) {
103+
return this.authorizations.authorizeByAuthorizationCodeGrantBrowser(request);
104+
}
105+
}
106+
70107
}

cloudfoundry-client/src/main/java/org/cloudfoundry/QueryParameter.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,17 @@
3333
@JacksonAnnotationsInside
3434
public @interface QueryParameter {
3535

36+
/**
37+
* Returns the delimiter to use between Iterable elements
38+
*
39+
* @return the delimiter to use between Iterable elements
40+
*/
41+
String delimiter() default ",";
42+
3643
/**
3744
* Returns the name of the query parameter
3845
*
3946
* @return the name of the query parameter
4047
*/
4148
String value();
42-
4349
}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.cloudfoundry.uaa.authorizations;
17+
package org.cloudfoundry.uaa;
1818

1919
import com.fasterxml.jackson.annotation.JsonValue;
2020

@@ -23,7 +23,11 @@
2323
*/
2424
public enum ResponseType {
2525

26-
CODE("code");
26+
CODE("code"),
27+
28+
TOKEN("token"),
29+
30+
ID_TOKEN("id_token");
2731

2832
private final String value;
2933

cloudfoundry-client/src/main/java/org/cloudfoundry/uaa/authorizations/Authorizations.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,16 @@ public interface Authorizations {
2727
* Makes the <a href="http://docs.cloudfoundry.com/uaa/#api-flow">Authorize By Authorization Code Grant (API)</a> request
2828
*
2929
* @param request Authorize By Authorization Code Grant (API) request
30-
* @return the redirect URI
30+
* @return the authorization code
3131
*/
3232
Mono<String> authorizeByAuthorizationCodeGrantApi(AuthorizeByAuthorizationCodeGrantApiRequest request);
3333

34+
/**
35+
* Makes the <a href="http://docs.cloudfoundry.com/uaa/#browser-flow">Authorize By Authorization Code Grant (Browser)</a> request
36+
*
37+
* @param request Authorize By Authorization Code Grant (Browser) request
38+
* @return the authorization code
39+
*/
40+
Mono<String> authorizeByAuthorizationCodeGrantBrowser(AuthorizeByAuthorizationCodeGrantBrowserRequest request);
41+
3442
}

cloudfoundry-client/src/main/java/org/cloudfoundry/uaa/authorizations/_AuthorizeByAuthorizationCodeGrantApiRequest.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,6 @@ abstract class _AuthorizeByAuthorizationCodeGrantApiRequest {
3939
@QueryParameter("redirect_uri")
4040
abstract String getRedirectUri();
4141

42-
/**
43-
* {@code code} for requesting an authorization code for an access token, as per OAuth spec
44-
*/
45-
@QueryParameter("response_type")
46-
abstract ResponseType getResponseType();
47-
4842
/**
4943
* Any random string to be returned in the Location header as a query parameter, used to achieve per-request customization
5044
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2013-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.cloudfoundry.uaa.authorizations;
18+
19+
import org.cloudfoundry.Nullable;
20+
import org.cloudfoundry.QueryParameter;
21+
import org.immutables.value.Value;
22+
23+
import java.util.List;
24+
25+
/**
26+
* The request payload for authorization with an authorization code grant operation
27+
*/
28+
@Value.Immutable
29+
abstract class _AuthorizeByAuthorizationCodeGrantBrowserRequest {
30+
31+
/**
32+
* A unique string representing the registration information provided by the client
33+
*/
34+
@QueryParameter("client_id")
35+
abstract String getClientId();
36+
37+
/**
38+
* Redirection URI to which the authorization server will send the user-agent back once access is granted (or denied), optional if pre-registered by the client
39+
*/
40+
@Nullable
41+
@QueryParameter("redirect_uri")
42+
abstract String getRedirectUri();
43+
44+
/**
45+
* requested scopes, space-delimited
46+
*/
47+
@Nullable
48+
@QueryParameter(value = "scope", delimiter = " ")
49+
abstract List<String> getScopes();
50+
51+
}

0 commit comments

Comments
 (0)