From 9b95355cd84351d01631ab459ae4c8a3bfcb6545 Mon Sep 17 00:00:00 2001 From: Ankit Rathod Date: Thu, 12 Feb 2026 14:15:18 +0530 Subject: [PATCH] feat: 'fcli aviator entitlement': Add 'list-sast'/'list-dast' commands and deprecate 'list' --- .../cli/aviator/grpc/AviatorGrpcClient.java | 12 ++ .../fortify/cli/aviator/util/Constants.java | 1 + .../src/main/proto/dast_entitlement.proto | 116 ++++++++++++++++++ .../cli/mixin/AviatorOutputHelperMixins.java | 34 +++++ ...ractAviatorSastEntitlementListCommand.java | 83 +++++++++++++ .../cli/cmd/AviatorEntitlementCommands.java | 4 +- .../cmd/AviatorEntitlementListCommand.java | 77 +----------- .../AviatorEntitlementListDastCommand.java | 85 +++++++++++++ .../AviatorEntitlementListSastCommand.java | 24 ++++ .../aviator/i18n/AviatorMessages.properties | 17 ++- 10 files changed, 376 insertions(+), 77 deletions(-) create mode 100644 fcli-core/fcli-aviator-common/src/main/proto/dast_entitlement.proto create mode 100644 fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/_common/output/cli/mixin/AviatorOutputHelperMixins.java create mode 100644 fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AbstractAviatorSastEntitlementListCommand.java create mode 100644 fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListDastCommand.java create mode 100644 fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListSastCommand.java diff --git a/fcli-core/fcli-aviator-common/src/main/java/com/fortify/cli/aviator/grpc/AviatorGrpcClient.java b/fcli-core/fcli-aviator-common/src/main/java/com/fortify/cli/aviator/grpc/AviatorGrpcClient.java index d5302e6e423..cb52328783d 100644 --- a/fcli-core/fcli-aviator-common/src/main/java/com/fortify/cli/aviator/grpc/AviatorGrpcClient.java +++ b/fcli-core/fcli-aviator-common/src/main/java/com/fortify/cli/aviator/grpc/AviatorGrpcClient.java @@ -33,6 +33,10 @@ import com.fortify.aviator.application.ApplicationServiceGrpc; import com.fortify.aviator.application.CreateApplicationRequest; import com.fortify.aviator.application.UpdateApplicationRequest; +import com.fortify.aviator.dastentitlement.DastEntitlement; +import com.fortify.aviator.dastentitlement.DastEntitlementServiceGrpc; +import com.fortify.aviator.dastentitlement.ListDastEntitlementsByTenantRequest; +import com.fortify.aviator.dastentitlement.ListDastEntitlementsByTenantResponse; import com.fortify.aviator.entitlement.Entitlement; import com.fortify.aviator.entitlement.EntitlementServiceGrpc; import com.fortify.aviator.entitlement.ListEntitlementsByTenantRequest; @@ -72,6 +76,7 @@ public class AviatorGrpcClient implements AutoCloseable { private final ApplicationServiceGrpc.ApplicationServiceBlockingStub blockingStub; private final TokenServiceGrpc.TokenServiceBlockingStub tokenServiceBlockingStub; private final EntitlementServiceGrpc.EntitlementServiceBlockingStub entitlementServiceBlockingStub; + private final DastEntitlementServiceGrpc.DastEntitlementServiceBlockingStub dastEntitlementServiceBlockingStub; private final long defaultTimeoutSeconds; private final java.util.concurrent.ExecutorService processingExecutor; private final long pingIntervalSeconds; @@ -86,6 +91,7 @@ public AviatorGrpcClient(ManagedChannel channel, long defaultTimeoutSeconds, IAv this.blockingStub = ApplicationServiceGrpc.newBlockingStub(channel).withCompression("gzip").withMaxInboundMessageSize(Constants.MAX_MESSAGE_SIZE).withMaxOutboundMessageSize(Constants.MAX_MESSAGE_SIZE).withWaitForReady(); this.tokenServiceBlockingStub = TokenServiceGrpc.newBlockingStub(channel).withCompression("gzip").withMaxInboundMessageSize(Constants.MAX_MESSAGE_SIZE).withMaxOutboundMessageSize(Constants.MAX_MESSAGE_SIZE).withWaitForReady(); this.entitlementServiceBlockingStub = EntitlementServiceGrpc.newBlockingStub(channel).withCompression("gzip").withMaxInboundMessageSize(Constants.MAX_MESSAGE_SIZE).withMaxOutboundMessageSize(Constants.MAX_MESSAGE_SIZE).withWaitForReady(); + this.dastEntitlementServiceBlockingStub = DastEntitlementServiceGrpc.newBlockingStub(channel).withCompression("gzip").withMaxInboundMessageSize(Constants.MAX_MESSAGE_SIZE).withMaxOutboundMessageSize(Constants.MAX_MESSAGE_SIZE).withWaitForReady(); this.defaultTimeoutSeconds = defaultTimeoutSeconds; this.processingExecutor = Executors.newFixedThreadPool(4, r -> { Thread t = new Thread(r, "aviator-client-processing-" + r.hashCode()); @@ -231,4 +237,10 @@ public List listEntitlements(String tenantName, String signature, S ListEntitlementsByTenantResponse response = GrpcUtil.executeGrpcCall(entitlementServiceBlockingStub, EntitlementServiceGrpc.EntitlementServiceBlockingStub::listEntitlementsByTenant, request, Constants.OP_LIST_ENTITLEMENTS); return response.getEntitlementsList(); } + + public List listDastEntitlements(String tenantName, String signature, String message) { + ListDastEntitlementsByTenantRequest request = ListDastEntitlementsByTenantRequest.newBuilder().setTenantName(tenantName).setSignature(signature).setMessage(message).build(); + ListDastEntitlementsByTenantResponse response = GrpcUtil.executeGrpcCall(dastEntitlementServiceBlockingStub, DastEntitlementServiceGrpc.DastEntitlementServiceBlockingStub::listDastEntitlementsByTenant, request, Constants.OP_LIST_DAST_ENTITLEMENTS); + return response.getEntitlementsList(); + } } \ No newline at end of file diff --git a/fcli-core/fcli-aviator-common/src/main/java/com/fortify/cli/aviator/util/Constants.java b/fcli-core/fcli-aviator-common/src/main/java/com/fortify/cli/aviator/util/Constants.java index 98810954215..ffcc2bd2635 100644 --- a/fcli-core/fcli-aviator-common/src/main/java/com/fortify/cli/aviator/util/Constants.java +++ b/fcli-core/fcli-aviator-common/src/main/java/com/fortify/cli/aviator/util/Constants.java @@ -72,6 +72,7 @@ public class Constants { public static final String OP_VALIDATE_TOKEN = "validating token"; public static final String OP_VALIDATE_USER_TOKEN = "validating user token"; public static final String OP_LIST_ENTITLEMENTS = "listing entitlements"; + public static final String OP_LIST_DAST_ENTITLEMENTS = "listing DAST entitlements"; public static final long DEFAULT_PING_INTERVAL_SECONDS = 30; public static final int DEFAULT_TIMEOUT_SECONDS = 30; diff --git a/fcli-core/fcli-aviator-common/src/main/proto/dast_entitlement.proto b/fcli-core/fcli-aviator-common/src/main/proto/dast_entitlement.proto new file mode 100644 index 00000000000..190f4839518 --- /dev/null +++ b/fcli-core/fcli-aviator-common/src/main/proto/dast_entitlement.proto @@ -0,0 +1,116 @@ +syntax = "proto3"; + +package com.fortify.aviator; + +option java_multiple_files = true; +option java_package = "com.fortify.aviator.dastentitlement"; +option java_outer_classname = "DastEntitlementProto"; + +import "google/protobuf/timestamp.proto"; + +// Service definition for DAST entitlement operations. +// Used by fcli to check DAST credit availability before initiating recordings. +service DastEntitlementService { + // Lists DAST entitlements for a given tenant with credit balance information. + rpc ListDastEntitlementsByTenant (ListDastEntitlementsByTenantRequest) returns (ListDastEntitlementsByTenantResponse) {} +} + +// Request message for listing DAST entitlements by tenant. +message ListDastEntitlementsByTenantRequest { + // The tenant name to look up entitlements for. + string tenant_name = 1; + + // Message to be signed: "tenantName;timestamp" + string message = 2; + + // HMAC-SHA256 signature of the message, signed with tenant's secret key. + string signature = 3; +} + +// Response message containing DAST entitlements and credit information. +message ListDastEntitlementsByTenantResponse { + // List of active DAST entitlements with credit details. + repeated DastEntitlement entitlements = 1; + + // Summary of credit availability across all entitlements. + DastCreditSummary credit_summary = 2; +} + +// DAST entitlement message containing credit balance details. +message DastEntitlement { + // Unique entitlement ID. + int64 id = 1; + + // Start date of the entitlement validity period (ISO-8601 format: YYYY-MM-DD). + string start_date = 2; + + // End date of the entitlement validity period (ISO-8601 format: YYYY-MM-DD). + string end_date = 3; + + // When the entitlement was created. + google.protobuf.Timestamp created_at = 4; + + // When the entitlement was last updated. + google.protobuf.Timestamp updated_at = 5; + + // Number of recording credits purchased with this entitlement. + int32 number_of_units = 6; + + // Number of credits consumed (successful recordings). + int32 credits_consumed = 7; + + // Number of credits currently reserved (recordings in progress). + int32 credits_reserved = 8; + + // Total credit adjustments (positive for top-ups, can be negative). + int32 credit_adjustments = 9; + + // Remaining credits available for new recordings. + // Formula: number_of_units - (credits_consumed + credits_reserved) + credit_adjustments + int32 credits_remaining = 10; + + // Contract ID associated with this entitlement (optional). + string contract_id = 11; + + // Tenant information. + DastTenantInfo tenant = 12; + + // Whether this entitlement is currently valid (within date range and not deleted). + bool is_valid = 13; +} + +// Tenant information for DAST entitlements. +message DastTenantInfo { + // Unique tenant ID. + int64 id = 1; + + // Tenant name. + string name = 2; +} + +// Summary of credit availability across all entitlements. +message DastCreditSummary { + // Total credits purchased across all active entitlements. + int32 total_credits = 1; + + // Total credits consumed (successful recordings) across all entitlements. + int32 total_consumed = 2; + + // Total credits currently reserved (in-progress recordings). + int32 total_reserved = 3; + + // Total credit adjustments across all entitlements. + int32 total_adjustments = 4; + + // Total remaining credits available for new recordings. + int32 total_remaining = 5; + + // Whether the tenant has any active entitlement. + bool has_active_entitlement = 6; + + // Whether the tenant can start a new recording (has remaining credits). + bool can_record = 7; + + // Earliest expiration date among active entitlements (ISO-8601 format). + string earliest_expiration = 8; +} diff --git a/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/_common/output/cli/mixin/AviatorOutputHelperMixins.java b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/_common/output/cli/mixin/AviatorOutputHelperMixins.java new file mode 100644 index 00000000000..82a7abfc2d5 --- /dev/null +++ b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/_common/output/cli/mixin/AviatorOutputHelperMixins.java @@ -0,0 +1,34 @@ +/* + * Copyright 2021-2026 Open Text. + * + * The only warranties for products and services of Open Text + * and its affiliates and licensors ("Open Text") are as may + * be set forth in the express warranty statements accompanying + * such products and services. Nothing herein should be construed + * as constituting an additional warranty. Open Text shall not be + * liable for technical or editorial errors or omissions contained + * herein. The information contained herein is subject to change + * without notice. + */ +package com.fortify.cli.aviator._common.output.cli.mixin; + +import com.fortify.cli.common.output.cli.mixin.IOutputHelper; +import com.fortify.cli.common.output.cli.mixin.OutputHelperMixins; + +import picocli.CommandLine.Command; + +/** + * Provides Aviator-specific {@link IOutputHelper} implementations for + * command names not covered by the standard {@link OutputHelperMixins}. + */ +public class AviatorOutputHelperMixins { + @Command(aliases = {"lss"}) + public static class ListSast extends OutputHelperMixins.TableWithQuery { + public static final String CMD_NAME = "list-sast"; + } + + @Command(aliases = {"lsd"}) + public static class ListDast extends OutputHelperMixins.TableWithQuery { + public static final String CMD_NAME = "list-dast"; + } +} diff --git a/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AbstractAviatorSastEntitlementListCommand.java b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AbstractAviatorSastEntitlementListCommand.java new file mode 100644 index 00000000000..5f9bfa4ea51 --- /dev/null +++ b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AbstractAviatorSastEntitlementListCommand.java @@ -0,0 +1,83 @@ +/* + * Copyright 2021-2026 Open Text. + * + * The only warranties for products and services of Open Text + * and its affiliates and licensors ("Open Text") are as may + * be set forth in the express warranty statements accompanying + * such products and services. Nothing herein should be construed + * as constituting an additional warranty. Open Text shall not be + * liable for technical or editorial errors or omissions contained + * herein. The information contained herein is subject to change + * without notice. + */ +package com.fortify.cli.aviator.entitlement.cli.cmd; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fortify.aviator.entitlement.Entitlement; +import com.fortify.cli.aviator._common.config.admin.helper.AviatorAdminConfigDescriptor; +import com.fortify.cli.aviator._common.output.cli.cmd.AbstractAviatorAdminSessionOutputCommand; +import com.fortify.cli.aviator._common.util.AviatorGrpcUtils; +import com.fortify.cli.aviator._common.util.AviatorSignatureUtils; +import com.fortify.cli.aviator.grpc.AviatorGrpcClient; +import com.fortify.cli.aviator.grpc.AviatorGrpcClientHelper; + +/** + * Abstract base class for SAST entitlement list commands. Provides the shared + * logic for fetching and formatting SAST entitlements via gRPC, allowing + * concrete subclasses to differ only in command name and output helper mixin. + */ +public abstract class AbstractAviatorSastEntitlementListCommand extends AbstractAviatorAdminSessionOutputCommand { + private static final Logger LOG = LoggerFactory.getLogger(AbstractAviatorSastEntitlementListCommand.class); + + @Override + protected JsonNode getJsonNode(AviatorAdminConfigDescriptor configDescriptor) { + try (AviatorGrpcClient client = AviatorGrpcClientHelper.createClient(configDescriptor.getAviatorUrl())) { + var messageAndSignature = AviatorSignatureUtils.createMessageAndSignature(configDescriptor, configDescriptor.getTenant()); + String message = messageAndSignature[0]; + String signature = messageAndSignature[1]; + List entitlements = client.listEntitlements(configDescriptor.getTenant(), signature, message); + ArrayNode result = formatEntitlements(entitlements); + logEntitlementCount(entitlements.size(), configDescriptor.getTenant()); + return result; + } + } + + private ArrayNode formatEntitlements(List entitlements) { + ArrayNode array = AviatorGrpcUtils.createArrayNode(); + for (Entitlement entitlement : entitlements) { + JsonNode node = AviatorGrpcUtils.grpcToJsonNode(entitlement); + ObjectNode formatted = node.deepCopy(); + formatTenantNode(formatted, node); + array.add(formatted); + } + return array; + } + + private void formatTenantNode(ObjectNode formattedNode, JsonNode node) { + JsonNode tenantNode = node.get("tenant"); + if (tenantNode != null && tenantNode.has("name")) { + formattedNode.put("tenant_name", tenantNode.get("name").asText()); + formattedNode.remove("tenant"); + } + } + + private void logEntitlementCount(int count, String tenant) { + if (count == 0) { + LOG.info("No SAST entitlements found for tenant: {}", tenant); + } else { + LOG.info("Successfully listed {} SAST entitlements for tenant: {}", count, tenant); + } + } + + @Override + public boolean isSingular() { + return false; + } +} diff --git a/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementCommands.java b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementCommands.java index 4dcabc5b291..9214b912bd8 100644 --- a/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementCommands.java +++ b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementCommands.java @@ -19,7 +19,9 @@ @Command(name = "entitlement", subcommands = { - AviatorEntitlementListCommand.class + AviatorEntitlementListCommand.class, + AviatorEntitlementListSastCommand.class, + AviatorEntitlementListDastCommand.class } ) public class AviatorEntitlementCommands extends AbstractContainerCommand { diff --git a/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListCommand.java b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListCommand.java index 65953d1f455..85901e3af1a 100644 --- a/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListCommand.java +++ b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListCommand.java @@ -12,84 +12,17 @@ */ package com.fortify.cli.aviator.entitlement.cli.cmd; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fortify.aviator.entitlement.Entitlement; -import com.fortify.cli.aviator._common.config.admin.helper.AviatorAdminConfigDescriptor; -import com.fortify.cli.aviator._common.exception.AviatorSimpleException; -import com.fortify.cli.aviator._common.exception.AviatorTechnicalException; -import com.fortify.cli.aviator._common.output.cli.cmd.AbstractAviatorAdminSessionOutputCommand; -import com.fortify.cli.aviator._common.util.AviatorGrpcUtils; -import com.fortify.cli.aviator._common.util.AviatorSignatureUtils; -import com.fortify.cli.aviator.grpc.AviatorGrpcClient; -import com.fortify.cli.aviator.grpc.AviatorGrpcClientHelper; import com.fortify.cli.common.output.cli.mixin.OutputHelperMixins; import lombok.Getter; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; +/** + * Deprecated entitlement list command retained for backward compatibility. + * Use {@code fcli aviator entitlement list-sast} instead. + */ @Command(name = OutputHelperMixins.List.CMD_NAME) -public class AviatorEntitlementListCommand extends AbstractAviatorAdminSessionOutputCommand { +public class AviatorEntitlementListCommand extends AbstractAviatorSastEntitlementListCommand { @Getter @Mixin private OutputHelperMixins.List outputHelper; - private static final Logger LOG = LoggerFactory.getLogger(AviatorEntitlementListCommand.class); - - @Override - protected JsonNode getJsonNode(AviatorAdminConfigDescriptor configDescriptor) throws AviatorSimpleException, AviatorTechnicalException { - try (AviatorGrpcClient client = AviatorGrpcClientHelper.createClient(configDescriptor.getAviatorUrl())) { - String[] messageAndSignature = createMessageAndSignature(configDescriptor); - List entitlements = fetchEntitlements(client, configDescriptor, messageAndSignature); - ArrayNode entitlementsArray = formatEntitlements(entitlements); - logEntitlementCount(entitlements.size(), configDescriptor.getTenant()); - return entitlementsArray; - } - } - - private String[] createMessageAndSignature(AviatorAdminConfigDescriptor configDescriptor) { - return AviatorSignatureUtils.createMessageAndSignature(configDescriptor, configDescriptor.getTenant()); - } - - private List fetchEntitlements(AviatorGrpcClient client, AviatorAdminConfigDescriptor configDescriptor, String[] messageAndSignature) { - String message = messageAndSignature[0]; - String signature = messageAndSignature[1]; - return client.listEntitlements(configDescriptor.getTenant(), signature, message); - } - - private ArrayNode formatEntitlements(List entitlements) { - ArrayNode entitlementsArray = AviatorGrpcUtils.createArrayNode(); - for (Entitlement entitlement : entitlements) { - JsonNode node = AviatorGrpcUtils.grpcToJsonNode(entitlement); - ObjectNode formattedNode = node.deepCopy(); - formatTenantNode(formattedNode, node); - entitlementsArray.add(formattedNode); - } - return entitlementsArray; - } - - private void formatTenantNode(ObjectNode formattedNode, JsonNode node) { - JsonNode tenantNode = node.get("tenant"); - if (tenantNode != null && tenantNode.has("name")) { - formattedNode.put("tenant_name", tenantNode.get("name").asText()); - formattedNode.remove("tenant"); - } - } - - private void logEntitlementCount(int count, String tenant) { - if (count == 0) { - LOG.info("No entitlements found for tenant: {}", tenant); - } else { - LOG.info("Successfully listed {} entitlements for tenant: {}", count, tenant); - } - } - - @Override - public boolean isSingular() { - return false; - } } \ No newline at end of file diff --git a/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListDastCommand.java b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListDastCommand.java new file mode 100644 index 00000000000..9e619dc1075 --- /dev/null +++ b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListDastCommand.java @@ -0,0 +1,85 @@ +/* + * Copyright 2021-2026 Open Text. + * + * The only warranties for products and services of Open Text + * and its affiliates and licensors ("Open Text") are as may + * be set forth in the express warranty statements accompanying + * such products and services. Nothing herein should be construed + * as constituting an additional warranty. Open Text shall not be + * liable for technical or editorial errors or omissions contained + * herein. The information contained herein is subject to change + * without notice. + */ +package com.fortify.cli.aviator.entitlement.cli.cmd; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fortify.aviator.dastentitlement.DastEntitlement; +import com.fortify.cli.aviator._common.config.admin.helper.AviatorAdminConfigDescriptor; +import com.fortify.cli.aviator._common.output.cli.cmd.AbstractAviatorAdminSessionOutputCommand; +import com.fortify.cli.aviator._common.output.cli.mixin.AviatorOutputHelperMixins; +import com.fortify.cli.aviator._common.util.AviatorGrpcUtils; +import com.fortify.cli.aviator._common.util.AviatorSignatureUtils; +import com.fortify.cli.aviator.grpc.AviatorGrpcClient; +import com.fortify.cli.aviator.grpc.AviatorGrpcClientHelper; + +import lombok.Getter; +import picocli.CommandLine.Command; +import picocli.CommandLine.Mixin; + +@Command(name = AviatorOutputHelperMixins.ListDast.CMD_NAME) +public class AviatorEntitlementListDastCommand extends AbstractAviatorAdminSessionOutputCommand { + @Getter @Mixin private AviatorOutputHelperMixins.ListDast outputHelper; + private static final Logger LOG = LoggerFactory.getLogger(AviatorEntitlementListDastCommand.class); + + @Override + protected JsonNode getJsonNode(AviatorAdminConfigDescriptor configDescriptor) { + try (AviatorGrpcClient client = AviatorGrpcClientHelper.createClient(configDescriptor.getAviatorUrl())) { + var messageAndSignature = AviatorSignatureUtils.createMessageAndSignature(configDescriptor, configDescriptor.getTenant()); + String message = messageAndSignature[0]; + String signature = messageAndSignature[1]; + List entitlements = client.listDastEntitlements(configDescriptor.getTenant(), signature, message); + ArrayNode result = formatEntitlements(entitlements); + logEntitlementCount(entitlements.size(), configDescriptor.getTenant()); + return result; + } + } + + private ArrayNode formatEntitlements(List entitlements) { + ArrayNode array = AviatorGrpcUtils.createArrayNode(); + for (DastEntitlement entitlement : entitlements) { + JsonNode node = AviatorGrpcUtils.grpcToJsonNode(entitlement); + ObjectNode formatted = node.deepCopy(); + formatTenantNode(formatted, node); + array.add(formatted); + } + return array; + } + + private void formatTenantNode(ObjectNode formattedNode, JsonNode node) { + JsonNode tenantNode = node.get("tenant"); + if (tenantNode != null && tenantNode.has("name")) { + formattedNode.put("tenant_name", tenantNode.get("name").asText()); + formattedNode.remove("tenant"); + } + } + + private void logEntitlementCount(int count, String tenant) { + if (count == 0) { + LOG.info("No DAST entitlements found for tenant: {}", tenant); + } else { + LOG.info("Successfully listed {} DAST entitlements for tenant: {}", count, tenant); + } + } + + @Override + public boolean isSingular() { + return false; + } +} diff --git a/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListSastCommand.java b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListSastCommand.java new file mode 100644 index 00000000000..8dd0b223e92 --- /dev/null +++ b/fcli-core/fcli-aviator/src/main/java/com/fortify/cli/aviator/entitlement/cli/cmd/AviatorEntitlementListSastCommand.java @@ -0,0 +1,24 @@ +/* + * Copyright 2021-2026 Open Text. + * + * The only warranties for products and services of Open Text + * and its affiliates and licensors ("Open Text") are as may + * be set forth in the express warranty statements accompanying + * such products and services. Nothing herein should be construed + * as constituting an additional warranty. Open Text shall not be + * liable for technical or editorial errors or omissions contained + * herein. The information contained herein is subject to change + * without notice. + */ +package com.fortify.cli.aviator.entitlement.cli.cmd; + +import com.fortify.cli.aviator._common.output.cli.mixin.AviatorOutputHelperMixins; + +import lombok.Getter; +import picocli.CommandLine.Command; +import picocli.CommandLine.Mixin; + +@Command(name = AviatorOutputHelperMixins.ListSast.CMD_NAME) +public class AviatorEntitlementListSastCommand extends AbstractAviatorSastEntitlementListCommand { + @Getter @Mixin private AviatorOutputHelperMixins.ListSast outputHelper; +} diff --git a/fcli-core/fcli-aviator/src/main/resources/com/fortify/cli/aviator/i18n/AviatorMessages.properties b/fcli-core/fcli-aviator/src/main/resources/com/fortify/cli/aviator/i18n/AviatorMessages.properties index 876006b50d3..acf7e3173d6 100644 --- a/fcli-core/fcli-aviator/src/main/resources/com/fortify/cli/aviator/i18n/AviatorMessages.properties +++ b/fcli-core/fcli-aviator/src/main/resources/com/fortify/cli/aviator/i18n/AviatorMessages.properties @@ -96,11 +96,18 @@ fcli.aviator.token.validate.usage.header = Validate a user access token. fcli.aviator.token.validate.usage.description = Validates an access token, checking its authenticity and status against the SAST Aviator service. # fcli aviator entitlement -fcli.aviator.entitlement.usage.header = Manage SAST Aviator entitlements. -fcli.aviator.entitlement.usage.description = Commands for managing SAST Aviator entitlements. These commands require an administrator configuration. +fcli.aviator.entitlement.usage.header = Manage Aviator entitlements. +fcli.aviator.entitlement.usage.description = Commands for managing SAST and DAST Aviator entitlements. These commands require an administrator configuration. -fcli.aviator.entitlement.list.usage.header = List entitlements for a tenant. -fcli.aviator.entitlement.list.usage.description = Retrieves a list of entitlements for the tenant specified in the admin configuration. +fcli.aviator.entitlement.list.usage.header = (DEPRECATED) List SAST entitlements for a tenant. +fcli.aviator.entitlement.list.usage.description = This command is deprecated, please use 'fcli aviator entitlement list-sast' instead. \ + Retrieves a list of SAST entitlements for the tenant specified in the admin configuration. + +fcli.aviator.entitlement.list-sast.usage.header = List SAST entitlements for a tenant. +fcli.aviator.entitlement.list-sast.usage.description = Retrieves a list of SAST entitlements for the tenant specified in the admin configuration. + +fcli.aviator.entitlement.list-dast.usage.header = List DAST entitlements for a tenant. +fcli.aviator.entitlement.list-dast.usage.description = Retrieves a list of DAST entitlements with credit balance details for the tenant specified in the admin configuration. # fcli aviator ssc fcli.aviator.ssc.usage.header = Use SAST Aviator with SSC. @@ -169,6 +176,8 @@ fcli.aviator.token.list.output.table.args = token_name,start_date,expiryDate,rev fcli.aviator.token.revoke.output.table.args = message fcli.aviator.token.validate.output.table.args = message fcli.aviator.entitlement.list.output.table.args = id,tenant_name,start_date,end_date,number_of_applications,number_of_developers,contract_id,currently_linked_applications,is_valid +fcli.aviator.entitlement.list-sast.output.table.args = id,tenant_name,start_date,end_date,number_of_applications,number_of_developers,contract_id,currently_linked_applications,is_valid +fcli.aviator.entitlement.list-dast.output.table.args = id,tenant_name,start_date,end_date,number_of_units,credits_consumed,credits_reserved,credit_adjustments,credits_remaining,contract_id,is_valid fcli.aviator.ssc.apply-remediations.output.table.args = artifactId,totalRemediation,appliedRemediation,skippedRemediation fcli.aviator.ssc.prepare.output.table.args = status,entity,details fcli.aviator.fod.apply-remediations.output.table.args = releaseId,totalRemediation,appliedRemediation,skippedRemediation