Browse Source

ISSUE-25 Delegation tokens api

German Osin 4 years ago
parent
commit
6c9b2ae2f8

+ 22 - 0
kafka-ui-api/src/main/java/com/provectus/kafka/ui/cluster/mapper/ClusterMapper.java

@@ -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());
+     }
+
 }

+ 28 - 0
kafka-ui-api/src/main/java/com/provectus/kafka/ui/cluster/service/ClusterService.java

@@ -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);
+    }
 }

+ 17 - 0
kafka-ui-api/src/main/java/com/provectus/kafka/ui/rest/MetricsRestController.java

@@ -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);
+    }
 }

+ 108 - 1
kafka-ui-contract/src/main/resources/swagger/kafka-ui-api.yaml

@@ -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'