/*
* Copyright 2013-2017 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.reactor.uaa.tokens;
import io.netty.util.AsciiString;
import org.cloudfoundry.reactor.ConnectionContext;
import org.cloudfoundry.reactor.TokenProvider;
import org.cloudfoundry.reactor.uaa.AbstractUaaOperations;
import org.cloudfoundry.uaa.ResponseType;
import org.cloudfoundry.uaa.tokens.CheckTokenRequest;
import org.cloudfoundry.uaa.tokens.CheckTokenResponse;
import org.cloudfoundry.uaa.tokens.GetTokenByAuthorizationCodeRequest;
import org.cloudfoundry.uaa.tokens.GetTokenByAuthorizationCodeResponse;
import org.cloudfoundry.uaa.tokens.GetTokenByClientCredentialsRequest;
import org.cloudfoundry.uaa.tokens.GetTokenByClientCredentialsResponse;
import org.cloudfoundry.uaa.tokens.GetTokenByOneTimePasscodeRequest;
import org.cloudfoundry.uaa.tokens.GetTokenByOneTimePasscodeResponse;
import org.cloudfoundry.uaa.tokens.GetTokenByOpenIdRequest;
import org.cloudfoundry.uaa.tokens.GetTokenByOpenIdResponse;
import org.cloudfoundry.uaa.tokens.GetTokenByPasswordRequest;
import org.cloudfoundry.uaa.tokens.GetTokenByPasswordResponse;
import org.cloudfoundry.uaa.tokens.GetTokenKeyRequest;
import org.cloudfoundry.uaa.tokens.GetTokenKeyResponse;
import org.cloudfoundry.uaa.tokens.ListTokenKeysRequest;
import org.cloudfoundry.uaa.tokens.ListTokenKeysResponse;
import org.cloudfoundry.uaa.tokens.RefreshTokenRequest;
import org.cloudfoundry.uaa.tokens.RefreshTokenResponse;
import org.cloudfoundry.uaa.tokens.Tokens;
import reactor.core.publisher.Mono;
import reactor.ipc.netty.http.client.HttpClientRequest;
import java.util.Base64;
import static io.netty.handler.codec.http.HttpHeaderNames.AUTHORIZATION;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED;
import static org.cloudfoundry.uaa.tokens.GrantType.AUTHORIZATION_CODE;
import static org.cloudfoundry.uaa.tokens.GrantType.CLIENT_CREDENTIALS;
import static org.cloudfoundry.uaa.tokens.GrantType.PASSWORD;
import static org.cloudfoundry.uaa.tokens.GrantType.REFRESH_TOKEN;
public final class ReactorTokens extends AbstractUaaOperations implements Tokens {
private static final AsciiString BASIC_PREAMBLE = new AsciiString("Basic ");
/**
* Creates an instance
*
* @param connectionContext the {@link ConnectionContext} to use when communicating with the server
* @param root the root URI of the server. Typically something like {@code https://uaa.run.pivotal.io}.
* @param tokenProvider the {@link TokenProvider} to use when communicating with the server
*/
public ReactorTokens(ConnectionContext connectionContext, Mono<String> root, TokenProvider tokenProvider) {
super(connectionContext, root, tokenProvider);
}
@Override
public Mono<CheckTokenResponse> check(CheckTokenRequest request) {
return post(request, CheckTokenResponse.class, builder -> builder.pathSegment("check_token"),
outbound -> outbound
.map(r -> {
String encoded = Base64.getEncoder().encodeToString(new AsciiString(request.getClientId()).concat(":").concat(request.getClientSecret()).toByteArray());
r.requestHeaders().set(AUTHORIZATION, BASIC_PREAMBLE + encoded);
return r;
}))
.checkpoint();
}
@Override
public Mono<GetTokenByAuthorizationCodeResponse> getByAuthorizationCode(GetTokenByAuthorizationCodeRequest request) {
return post(request, GetTokenByAuthorizationCodeResponse.class,
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", AUTHORIZATION_CODE).queryParam("response_type", ResponseType.TOKEN),
outbound -> outbound
.map(ReactorTokens::removeAuthorization)
.map(ReactorTokens::setUrlEncoded))
.checkpoint();
}
@Override
public Mono<GetTokenByClientCredentialsResponse> getByClientCredentials(GetTokenByClientCredentialsRequest request) {
return post(request, GetTokenByClientCredentialsResponse.class,
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", CLIENT_CREDENTIALS).queryParam("response_type", ResponseType.TOKEN),
outbound -> outbound
.map(ReactorTokens::removeAuthorization)
.map(ReactorTokens::setUrlEncoded))
.checkpoint();
}
@Override
public Mono<GetTokenByOneTimePasscodeResponse> getByOneTimePasscode(GetTokenByOneTimePasscodeRequest request) {
return post(request, GetTokenByOneTimePasscodeResponse.class,
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", PASSWORD).queryParam("response_type", ResponseType.TOKEN),
outbound -> outbound
.map(ReactorTokens::removeAuthorization)
.map(ReactorTokens::setUrlEncoded))
.checkpoint();
}
@Override
public Mono<GetTokenByOpenIdResponse> getByOpenId(GetTokenByOpenIdRequest request) {
return post(request, GetTokenByOpenIdResponse.class,
builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", AUTHORIZATION_CODE).queryParam("response_type", ResponseType.ID_TOKEN),
outbound -> outbound
.map(ReactorTokens::removeAuthorization)
.map(ReactorTokens::setUrlEncoded))
.checkpoint();
}
@Override
public Mono<GetTokenByPasswordResponse> getByPassword(GetTokenByPasswordRequest request) {
return post(request, GetTokenByPasswordResponse.class, builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", PASSWORD).queryParam("response_type", ResponseType.TOKEN),
outbound -> outbound
.map(ReactorTokens::removeAuthorization)
.map(ReactorTokens::setUrlEncoded))
.checkpoint();
}
@Override
public Mono<GetTokenKeyResponse> getKey(GetTokenKeyRequest request) {
return get(request, GetTokenKeyResponse.class, builder -> builder.pathSegment("token_key"))
.checkpoint();
}
@Override
public Mono<ListTokenKeysResponse> listKeys(ListTokenKeysRequest request) {
return get(request, ListTokenKeysResponse.class, builder -> builder.pathSegment("token_keys"))
.checkpoint();
}
@Override
public Mono<RefreshTokenResponse> refresh(RefreshTokenRequest request) {
return post(request, RefreshTokenResponse.class, builder -> builder.pathSegment("oauth", "token").queryParam("grant_type", REFRESH_TOKEN),
outbound -> outbound
.map(ReactorTokens::removeAuthorization)
.map(ReactorTokens::setUrlEncoded))
.checkpoint();
}
private static HttpClientRequest removeAuthorization(HttpClientRequest request) {
request.requestHeaders().remove(AUTHORIZATION);
return request;
}
private static HttpClientRequest setUrlEncoded(HttpClientRequest request) {
return request
.chunkedTransfer(false)
.header(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED);
}
}