From d194789e11524b18f8fbd91e1afa1360e1115c1c Mon Sep 17 00:00:00 2001 From: Michael Myers Date: Mon, 17 Oct 2022 13:50:41 -0500 Subject: [PATCH 1/7] Conditionally render Access Request navigation item. The Access Requests navigation menu should now only show when a root cluster's auth server has Advanced Workflows feature enabled. --- .../teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx b/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx index fb243d2b0..8a67ce18c 100644 --- a/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx +++ b/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx @@ -59,7 +59,10 @@ export function NavigationMenu() { const [isPopoverOpened, setIsPopoverOpened] = useState(false); const selectorRef = useRef(); - if (!activeRootCluster) { + if ( + !activeRootCluster || + !activeRootCluster.features.advancedAccessWorkflows + ) { return <>; } From 043d4e6e9633fef4bbea110b855f10f9aedb8dbd Mon Sep 17 00:00:00 2001 From: Michael Myers Date: Mon, 17 Oct 2022 13:51:56 -0500 Subject: [PATCH 2/7] Add protogen files --- .../src/services/tshd/v1/cluster_pb.d.ts | 28 +++ .../src/services/tshd/v1/cluster_pb.js | 205 +++++++++++++++++- 2 files changed, 232 insertions(+), 1 deletion(-) diff --git a/packages/teleterm/src/services/tshd/v1/cluster_pb.d.ts b/packages/teleterm/src/services/tshd/v1/cluster_pb.d.ts index df5006cfb..43674de59 100644 --- a/packages/teleterm/src/services/tshd/v1/cluster_pb.d.ts +++ b/packages/teleterm/src/services/tshd/v1/cluster_pb.d.ts @@ -29,6 +29,12 @@ export class Cluster extends jspb.Message { setLoggedInUser(value?: LoggedInUser): Cluster; + hasFeatures(): boolean; + clearFeatures(): void; + getFeatures(): Features | undefined; + setFeatures(value?: Features): Cluster; + + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): Cluster.AsObject; static toObject(includeInstance: boolean, msg: Cluster): Cluster.AsObject; @@ -47,6 +53,7 @@ export namespace Cluster { connected: boolean, leaf: boolean, loggedInUser?: LoggedInUser.AsObject, + features?: Features.AsObject, } } @@ -233,3 +240,24 @@ export namespace ResourceAccess { pb_delete: boolean, } } + +export class Features extends jspb.Message { + getAdvancedAccessWorkflows(): boolean; + setAdvancedAccessWorkflows(value: boolean): Features; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): Features.AsObject; + static toObject(includeInstance: boolean, msg: Features): Features.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: Features, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): Features; + static deserializeBinaryFromReader(message: Features, reader: jspb.BinaryReader): Features; +} + +export namespace Features { + export type AsObject = { + advancedAccessWorkflows: boolean, + } +} diff --git a/packages/teleterm/src/services/tshd/v1/cluster_pb.js b/packages/teleterm/src/services/tshd/v1/cluster_pb.js index 4e88512ed..c8f481c31 100644 --- a/packages/teleterm/src/services/tshd/v1/cluster_pb.js +++ b/packages/teleterm/src/services/tshd/v1/cluster_pb.js @@ -17,6 +17,7 @@ var global = (function() { return this || window || global || self || Function(' goog.exportSymbol('proto.teleport.terminal.v1.ACL', null, global); goog.exportSymbol('proto.teleport.terminal.v1.Cluster', null, global); +goog.exportSymbol('proto.teleport.terminal.v1.Features', null, global); goog.exportSymbol('proto.teleport.terminal.v1.LoggedInUser', null, global); goog.exportSymbol('proto.teleport.terminal.v1.ResourceAccess', null, global); /** @@ -103,6 +104,27 @@ if (goog.DEBUG && !COMPILED) { */ proto.teleport.terminal.v1.ResourceAccess.displayName = 'proto.teleport.terminal.v1.ResourceAccess'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.teleport.terminal.v1.Features = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.teleport.terminal.v1.Features, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.teleport.terminal.v1.Features.displayName = 'proto.teleport.terminal.v1.Features'; +} @@ -140,7 +162,8 @@ proto.teleport.terminal.v1.Cluster.toObject = function(includeInstance, msg) { proxyHost: jspb.Message.getFieldWithDefault(msg, 3, ""), connected: jspb.Message.getBooleanFieldWithDefault(msg, 4, false), leaf: jspb.Message.getBooleanFieldWithDefault(msg, 5, false), - loggedInUser: (f = msg.getLoggedInUser()) && proto.teleport.terminal.v1.LoggedInUser.toObject(includeInstance, f) + loggedInUser: (f = msg.getLoggedInUser()) && proto.teleport.terminal.v1.LoggedInUser.toObject(includeInstance, f), + features: (f = msg.getFeatures()) && proto.teleport.terminal.v1.Features.toObject(includeInstance, f) }; if (includeInstance) { @@ -202,6 +225,11 @@ proto.teleport.terminal.v1.Cluster.deserializeBinaryFromReader = function(msg, r reader.readMessage(value,proto.teleport.terminal.v1.LoggedInUser.deserializeBinaryFromReader); msg.setLoggedInUser(value); break; + case 8: + var value = new proto.teleport.terminal.v1.Features; + reader.readMessage(value,proto.teleport.terminal.v1.Features.deserializeBinaryFromReader); + msg.setFeatures(value); + break; default: reader.skipField(); break; @@ -274,6 +302,14 @@ proto.teleport.terminal.v1.Cluster.serializeBinaryToWriter = function(message, w proto.teleport.terminal.v1.LoggedInUser.serializeBinaryToWriter ); } + f = message.getFeatures(); + if (f != null) { + writer.writeMessage( + 8, + f, + proto.teleport.terminal.v1.Features.serializeBinaryToWriter + ); + } }; @@ -404,6 +440,43 @@ proto.teleport.terminal.v1.Cluster.prototype.hasLoggedInUser = function() { }; +/** + * optional Features features = 8; + * @return {?proto.teleport.terminal.v1.Features} + */ +proto.teleport.terminal.v1.Cluster.prototype.getFeatures = function() { + return /** @type{?proto.teleport.terminal.v1.Features} */ ( + jspb.Message.getWrapperField(this, proto.teleport.terminal.v1.Features, 8)); +}; + + +/** + * @param {?proto.teleport.terminal.v1.Features|undefined} value + * @return {!proto.teleport.terminal.v1.Cluster} returns this +*/ +proto.teleport.terminal.v1.Cluster.prototype.setFeatures = function(value) { + return jspb.Message.setWrapperField(this, 8, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.teleport.terminal.v1.Cluster} returns this + */ +proto.teleport.terminal.v1.Cluster.prototype.clearFeatures = function() { + return this.setFeatures(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.teleport.terminal.v1.Cluster.prototype.hasFeatures = function() { + return jspb.Message.getField(this, 8) != null; +}; + + /** * List of repeated fields within this message type. @@ -1701,4 +1774,134 @@ proto.teleport.terminal.v1.ResourceAccess.prototype.setDelete = function(value) }; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.teleport.terminal.v1.Features.prototype.toObject = function(opt_includeInstance) { + return proto.teleport.terminal.v1.Features.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.teleport.terminal.v1.Features} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.teleport.terminal.v1.Features.toObject = function(includeInstance, msg) { + var f, obj = { + advancedAccessWorkflows: jspb.Message.getBooleanFieldWithDefault(msg, 1, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.teleport.terminal.v1.Features} + */ +proto.teleport.terminal.v1.Features.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.teleport.terminal.v1.Features; + return proto.teleport.terminal.v1.Features.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.teleport.terminal.v1.Features} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.teleport.terminal.v1.Features} + */ +proto.teleport.terminal.v1.Features.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setAdvancedAccessWorkflows(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.teleport.terminal.v1.Features.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.teleport.terminal.v1.Features.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.teleport.terminal.v1.Features} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.teleport.terminal.v1.Features.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getAdvancedAccessWorkflows(); + if (f) { + writer.writeBool( + 1, + f + ); + } +}; + + +/** + * optional bool advanced_access_workflows = 1; + * @return {boolean} + */ +proto.teleport.terminal.v1.Features.prototype.getAdvancedAccessWorkflows = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.teleport.terminal.v1.Features} returns this + */ +proto.teleport.terminal.v1.Features.prototype.setAdvancedAccessWorkflows = function(value) { + return jspb.Message.setProto3BooleanField(this, 1, value); +}; + + goog.object.extend(exports, proto.teleport.terminal.v1); From f61054a996407f3be5df3167528b7bad1063c57b Mon Sep 17 00:00:00 2001 From: Michael Myers Date: Mon, 17 Oct 2022 17:53:10 -0500 Subject: [PATCH 3/7] Remove syncClusterInfo from logout --- packages/teleterm/src/ui/services/clusters/clustersService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/teleterm/src/ui/services/clusters/clustersService.ts b/packages/teleterm/src/ui/services/clusters/clustersService.ts index 96500a47f..742be533a 100644 --- a/packages/teleterm/src/ui/services/clusters/clustersService.ts +++ b/packages/teleterm/src/ui/services/clusters/clustersService.ts @@ -72,7 +72,6 @@ export class ClustersService extends ImmutableStore { async logout(clusterUri: string) { // TODO(gzdunek): logout and removeCluster should be combined into a single acton in tshd await this.client.logout(clusterUri); - await this.syncClusterInfo(clusterUri); this.removeResources(clusterUri); await this.removeCluster(clusterUri); await this.removeClusterKubeConfigs(clusterUri); From 5bc9fc03a3cb25bcc3770cea470d87740a035454 Mon Sep 17 00:00:00 2001 From: Michael Myers Date: Mon, 17 Oct 2022 19:38:32 -0500 Subject: [PATCH 4/7] Remove outdated comment --- packages/teleterm/src/ui/services/clusters/clustersService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/teleterm/src/ui/services/clusters/clustersService.ts b/packages/teleterm/src/ui/services/clusters/clustersService.ts index 742be533a..2f78c111d 100644 --- a/packages/teleterm/src/ui/services/clusters/clustersService.ts +++ b/packages/teleterm/src/ui/services/clusters/clustersService.ts @@ -704,8 +704,6 @@ export class ClustersService extends ImmutableStore { return useStore(this).state; } - // Note: client.getCluster ultimately reads data from the disk, so syncClusterInfo will not fail - // with a retryable error in case the certs have expired. private async syncClusterInfo(clusterUri: string) { const cluster = await this.client.getCluster(clusterUri); const assumedRequests = cluster.loggedInUser From 3a1d27a7080e777a6968cb15444c65d895882c27 Mon Sep 17 00:00:00 2001 From: Michael Myers Date: Wed, 19 Oct 2022 17:51:29 -0500 Subject: [PATCH 5/7] Swap Identity and NavigationMenu positions in TopBar --- packages/teleterm/src/ui/TopBar/TopBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/teleterm/src/ui/TopBar/TopBar.tsx b/packages/teleterm/src/ui/TopBar/TopBar.tsx index f4d53b634..fada9a694 100644 --- a/packages/teleterm/src/ui/TopBar/TopBar.tsx +++ b/packages/teleterm/src/ui/TopBar/TopBar.tsx @@ -20,8 +20,8 @@ export function TopBar() { - + ); From d73d80d4341f7d9c63d94b4ba8e751619528ed10 Mon Sep 17 00:00:00 2001 From: Michael Myers Date: Wed, 19 Oct 2022 17:53:00 -0500 Subject: [PATCH 6/7] Update access request navigation menu conditional statement --- .../src/ui/TopBar/NavigationMenu/NavigationMenu.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx b/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx index 8a67ce18c..65f444c97 100644 --- a/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx +++ b/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx @@ -59,11 +59,9 @@ export function NavigationMenu() { const [isPopoverOpened, setIsPopoverOpened] = useState(false); const selectorRef = useRef(); - if ( - !activeRootCluster || - !activeRootCluster.features.advancedAccessWorkflows - ) { - return <>; + const shouldShowMenu = !!activeRootCluster?.features?.advancedAccessWorkflows; + if (!shouldShowMenu) { + return null; } return ( From 3a878c8c20a1e699c6483f319a565606ac7c8a85 Mon Sep 17 00:00:00 2001 From: Michael Myers Date: Wed, 19 Oct 2022 17:56:13 -0500 Subject: [PATCH 7/7] protofiles --- packages/teleterm/src/services/tshd/v1/service_grpc_pb.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/teleterm/src/services/tshd/v1/service_grpc_pb.js b/packages/teleterm/src/services/tshd/v1/service_grpc_pb.js index 16721498c..f2734c4bf 100644 --- a/packages/teleterm/src/services/tshd/v1/service_grpc_pb.js +++ b/packages/teleterm/src/services/tshd/v1/service_grpc_pb.js @@ -603,6 +603,7 @@ function deserialize_teleport_terminal_v1_SetGatewayTargetSubresourceNameRequest // TerminalService describes Teleterm service var TerminalServiceService = exports.TerminalServiceService = { // ListRootClusters lists root clusters +// Does not include detailed cluster information that would require a network request. listRootClusters: { path: '/teleport.terminal.v1.TerminalService/ListRootClusters', requestStream: false, @@ -615,6 +616,7 @@ listRootClusters: { responseDeserialize: deserialize_teleport_terminal_v1_ListClustersResponse, }, // ListLeafClusters lists leaf clusters +// Does not include detailed cluster information that would require a network request. listLeafClusters: { path: '/teleport.terminal.v1.TerminalService/ListLeafClusters', requestStream: false,