Skip to content

Commit

Permalink
fix(concourse): update auth for v6.5+ (#852)
Browse files Browse the repository at this point in the history
another concourse minor release has broken token authentication (this is a private API, so I expect this to continue to occur until they expose a public one).
  • Loading branch information
jaredstehler committed Oct 7, 2020
1 parent 0bbc7dc commit 37ff59b
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 10 deletions.
Expand Up @@ -43,6 +43,7 @@ public class ConcourseClient {
private final SkyServiceV2 skyServiceV2;
private final TokenService tokenServiceV1;
private final TokenServiceV2 tokenServiceV2;
private final TokenServiceV3 tokenServiceV3;

@Getter private ClusterInfoService clusterInfoService;

Expand Down Expand Up @@ -107,24 +108,40 @@ public ConcourseClient(String host, String user, String password) {

ClusterInfo clusterInfo = this.clusterInfoService.clusterInfo();

Semver ver = new Semver("6.1.0");
if (ver.isGreaterThan(clusterInfo.getVersion())) {
Semver clusterVer = new Semver(clusterInfo.getVersion());

if (clusterVer.isLowerThan("6.1.0")) {
this.tokenServiceV1 =
tokenRestBuilder
.setLog(new Slf4jRetrofitLogger(TokenService.class))
.build()
.create(TokenService.class);
this.tokenServiceV2 = null;
this.tokenServiceV3 = null;

this.skyServiceV1 = createService(SkyService.class);
this.skyServiceV2 = null;
} else {

} else if (clusterVer.isLowerThan("6.5.0")) {
this.tokenServiceV1 = null;
this.tokenServiceV2 =
tokenRestBuilder
.setLog(new Slf4jRetrofitLogger(TokenServiceV2.class))
.build()
.create(TokenServiceV2.class);
this.tokenServiceV3 = null;

this.skyServiceV1 = null;
this.skyServiceV2 = createService(SkyServiceV2.class);

} else {
this.tokenServiceV1 = null;
this.tokenServiceV2 = null;
this.tokenServiceV3 =
tokenRestBuilder
.setLog(new Slf4jRetrofitLogger(TokenServiceV3.class))
.build()
.create(TokenServiceV3.class);

this.skyServiceV1 = null;
this.skyServiceV2 = createService(SkyServiceV2.class);
Expand All @@ -145,12 +162,21 @@ private void refreshTokenIfNecessary() {
}

private Token refreshToken() {
token =
tokenServiceV1 != null
? tokenServiceV1.passwordToken(
"password", user, password, "openid profile email federated:id groups")
: tokenServiceV2.passwordToken(
"password", user, password, "openid profile email federated:id groups");
if (tokenServiceV1 != null) {
token =
tokenServiceV1.passwordToken(
"password", user, password, "openid profile email federated:id groups");

} else if (tokenServiceV2 != null) {
token =
tokenServiceV2.passwordToken(
"password", user, password, "openid profile email federated:id groups");

} else if (tokenServiceV3 != null) {
token =
tokenServiceV3.passwordToken(
"password", user, password, "openid profile email federated:id groups");
}

tokenExpiration = token.getExpiry();
return token;
Expand Down
@@ -0,0 +1,32 @@
/*
* Copyright 2019 Pivotal, Inc.
*
* 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 com.netflix.spinnaker.igor.concourse.client;

import com.netflix.spinnaker.igor.concourse.client.model.TokenV3;
import retrofit.http.Field;
import retrofit.http.FormUrlEncoded;
import retrofit.http.POST;

public interface TokenServiceV3 {
@FormUrlEncoded
@POST("/sky/issuer/token")
TokenV3 passwordToken(
@Field("grant_type") String grantType,
@Field("username") String username,
@Field("password") String password,
@Field("scope") String scope);
}
@@ -0,0 +1,32 @@
/*
* Copyright 2019 Pivotal, Inc.
*
* 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 com.netflix.spinnaker.igor.concourse.client.model;

import java.time.ZonedDateTime;
import lombok.Data;

@Data
public class TokenV3 extends Token {
private String idToken;
private Long expiresIn;
private String tokenType;

@Override
public ZonedDateTime getExpiry() {
return ZonedDateTime.now().plusSeconds(expiresIn);
}
}
Expand Up @@ -67,7 +67,7 @@ class ConcourseClientSpec extends Specification {
resp.status == 200
}

def "it uses v2 auth for concourse versions >= 6.1.0"() {
def "it uses v2 auth for concourse versions < 6.5.0"() {
given:
setResponse '''{
"cluster_name": "mycluster",
Expand Down Expand Up @@ -100,6 +100,42 @@ class ConcourseClientSpec extends Specification {
resp.status == 200
}

def "it uses v3 auth for concourse versions >= 6.5.0"() {
given:
setResponse '''{
"cluster_name": "mycluster",
"external_url": "https://mycluster.example.com",
"version": "6.5.0",
"worker_version": "2.2"
}''',
"""{
"access_token": "my_access_token",
"expires_in": 86399,
"id_token": "my_id_token",
"token_type": "bearer"
}""",
'''{
"name": "jsmith"
}'''

when:
Response resp = client.userInfo()
RecordedRequest req1 = server.takeRequest()
RecordedRequest req2 = server.takeRequest()
RecordedRequest req3 = server.takeRequest()

then:
req1.path == '/api/v1/info'

req2.path == '/sky/issuer/token'
req2.getHeader('Authorization') == "Basic Zmx5OlpteDU="

req3.path == '/api/v1/user'
req3.getHeader('Authorization') == "bearer my_access_token"

resp.status == 200
}

private void setResponse(String... body) {
for(int i = 0; i < body.length; i++) {
server.enqueue(
Expand Down

0 comments on commit 37ff59b

Please sign in to comment.