ISSUE-25 Delegation tokens api

This commit is contained in:
German Osin 2020-12-18 11:58:44 +03:00
parent 4bd5f7d9da
commit 6c9b2ae2f8
4 changed files with 175 additions and 1 deletions

View file

@ -3,7 +3,9 @@ package com.provectus.kafka.ui.cluster.mapper;
import com.provectus.kafka.ui.cluster.config.ClustersProperties;
import com.provectus.kafka.ui.cluster.model.*;
import com.provectus.kafka.ui.model.*;
import java.util.Base64;
import java.util.Properties;
import org.apache.kafka.common.security.token.delegation.TokenInformation;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@ -83,4 +85,24 @@ public interface ClusterMapper {
return copy;
}
default DelegationToken mapToken(
org.apache.kafka.common.security.token.delegation.DelegationToken t) {
final byte[] encoded = Base64.getEncoder().encode(t.hmac());
final TokenInformation ti = t.tokenInfo();
return new DelegationToken()
.expiryTimestamp(ti.expiryTimestamp())
.issueTimestamp(ti.issueTimestamp())
.maxTimestamp(ti.maxTimestamp())
.tokenId(ti.tokenId())
.token(new String(encoded))
.principal(mapPricipal(ti.owner()))
.renewers(ti.renewers().stream().map(this::mapPricipal).collect(Collectors.toList()));
}
default KafkaPrincipal mapPricipal(org.apache.kafka.common.security.auth.KafkaPrincipal principal) {
return new KafkaPrincipal()
.name(principal.getName())
.type(principal.getPrincipalType());
}
}

View file

@ -15,6 +15,7 @@ import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -180,4 +181,31 @@ public class ClusterService {
.orElse(Flux.empty());
}
public Mono<Long> expireToken(String clusterName, String tokenId) {
final byte[] decodedId = Base64.getDecoder().decode(tokenId);
return kafkaService.getOrCreateAdminClient(
clustersStorage.getClusterByName(clusterName).orElseThrow()
).flatMap(cl -> ClusterUtil.toMono(
cl.getAdminClient().expireDelegationToken(decodedId).expiryTimestamp()
));
}
public Mono<Long> renewToken(String clusterName, String id) {
final byte[] decodedId = Base64.getDecoder().decode(id);
return kafkaService.getOrCreateAdminClient(
clustersStorage.getClusterByName(clusterName).orElseThrow()
).flatMap(cl -> ClusterUtil.toMono(
cl.getAdminClient().renewDelegationToken(decodedId).expiryTimestamp()
));
}
public Flux<DelegationToken> getTokens(String clusterName) {
return kafkaService.getOrCreateAdminClient(
clustersStorage.getClusterByName(clusterName).orElseThrow()
).flatMap(cl -> ClusterUtil.toMono(
cl.getAdminClient().describeDelegationToken().delegationTokens()
)).map(list ->
list.stream().map(clusterMapper::mapToken).collect(Collectors.toList())
).flatMapMany(Flux::fromIterable);
}
}

View file

@ -126,4 +126,21 @@ public class MetricsRestController implements ApiClustersApi {
.collectMap(Pair::getKey, Pair::getValue)
.map(positions -> new ConsumerPosition(seekType != null ? seekType : SeekType.BEGINNING, positions));
}
@Override
public Mono<ResponseEntity<Long>> expireToken(String clusterName, String id, ServerWebExchange exchange) {
return clusterService.expireToken(clusterName, id).map(ResponseEntity::ok);
}
@Override
public Mono<ResponseEntity<Flux<DelegationToken>>> getTokens(String clusterName,
ServerWebExchange exchange) {
return Mono.just(ResponseEntity.ok(clusterService.getTokens(clusterName)));
}
@Override
public Mono<ResponseEntity<Long>> renewToken(String clusterName, String id,
ServerWebExchange exchange) {
return clusterService.renewToken(clusterName, id).map(ResponseEntity::ok);
}
}

View file

@ -92,6 +92,65 @@ paths:
schema:
$ref: '#/components/schemas/ClusterStats'
/api/clusters/{clusterName}/tokens:
get:
tags:
- api/clusters
operationId: getTokens
responses:
200:
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/DelegationTokens'
parameters:
- name: clusterName
in: path
required: true
schema:
type: string
/api/clusters/{clusterName}/tokens/{id}:
patch:
tags:
- api/clusters
operationId: renewToken
responses:
200:
description: OK
content:
application/json:
schema:
type: integer
format: int64
delete:
tags:
- api/clusters
operationId: expireToken
responses:
200:
description: OK
content:
application/json:
schema:
type: integer
format: int64
parameters:
- name: clusterName
in: path
required: true
schema:
type: string
- name: id
in: path
required: true
schema:
type: string
/api/clusters/{clusterName}/brokers/{id}/metrics:
get:
tags:
@ -657,4 +716,52 @@ components:
value:
type: string
additionalProperties:
type: number
type: number
KafkaPrincipal:
type: object
properties:
type:
type: string
name:
type: string
CreateDelegationToken:
type: object
properties:
maxLifeTime:
type: integer
format: int64
renewers:
type: array
items:
$ref: "#/components/schemas/KafkaPrincipal"
DelegationToken:
type: object
properties:
token:
type: string
description: base64 encoded token
principal:
$ref: "#/components/schemas/KafkaPrincipal"
renewers:
type: array
items:
$ref: "#/components/schemas/KafkaPrincipal"
issueTimestamp:
type: integer
format: int64
maxTimestamp:
type: integer
format: int64
expiryTimestamp:
type: integer
format: int64
tokenId:
type: string
DelegationTokens:
type: array
items:
$ref: '#/components/schemas/DelegationToken'