From 37eb89b1730e3e5d370e53d8791a44289eb0fa2f Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Tue, 10 Mar 2026 00:55:09 +0000 Subject: [PATCH] Add predicates for `Auth` types --- src/start-proxy.test.ts | 7 ++++- src/start-proxy/types.ts | 62 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/start-proxy.test.ts b/src/start-proxy.test.ts index a4dd8d589..01a078c9e 100644 --- a/src/start-proxy.test.ts +++ b/src/start-proxy.test.ts @@ -350,7 +350,12 @@ test.serial( t.is(results.length, 1); t.is(results[0].type, "git_server"); t.is(results[0].host, "https://github.com/"); - t.assert(results[0].password?.startsWith("ghp_")); + + if (startProxyExports.isUsernamePassword(results[0])) { + t.assert(results[0].password?.startsWith("ghp_")); + } else { + t.fail("Expected a `UsernamePassword`-based credential."); + } // A warning should have been logged. checkExpectedLogMessages(t, loggedMessages, [ diff --git a/src/start-proxy/types.ts b/src/start-proxy/types.ts index 489459591..235fc7615 100644 --- a/src/start-proxy/types.ts +++ b/src/start-proxy/types.ts @@ -1,3 +1,5 @@ +import { isDefined } from "../util"; + /** * After parsing configurations from JSON, we don't know whether all the keys we expect are * present or not. This type is used to represent such values, which we expect to be @@ -11,6 +13,11 @@ export type Username = { username?: string; }; +/** Decides whether `config` has a username. */ +export function hasUsername(config: Partial): config is Username { + return "username" in config; +} + /** * Fields expected for authentication based on a username and password. * Both username and password are optional. @@ -20,6 +27,13 @@ export type UsernamePassword = { password?: string; } & Username; +/** Decides whether `config` is based on a username and password. */ +export function isUsernamePassword( + config: AuthConfig, +): config is UsernamePassword { + return hasUsername(config) && "password" in config; +} + /** * Fields expected for token-based authentication. * Both username and token are optional. @@ -29,9 +43,26 @@ export type Token = { token?: string; } & Username; +/** Decides whether `config` is token-based. */ +export function isToken(config: Partial): config is Token { + return "token" in config; +} + /** Configuration for Azure OIDC. */ export type AzureConfig = { tenant_id: string; client_id: string }; +/** Decides whether `config` is an Azure OIDC configuration. */ +export function isAzureConfig( + config: Partial, +): config is AzureConfig { + return ( + "tenant_id" in config && + "client_id" in config && + isDefined(config.tenant_id) && + isDefined(config.client_id) + ); +} + /** Configuration for AWS OIDC. */ export type AWSConfig = { aws_region: string; @@ -42,6 +73,25 @@ export type AWSConfig = { audience?: string; }; +/** Decides whether `config` is an AWS OIDC configuration. */ +export function isAWSConfig(config: Partial): config is AWSConfig { + // All of these properties are required. + const requiredProperties = [ + "aws_region", + "account_id", + "role_name", + "domain", + "domain_owner", + ]; + + for (const property of requiredProperties) { + if (!(property in config) || !isDefined(config[property])) { + return false; + } + } + return true; +} + /** Configuration for JFrog OIDC. */ export type JFrogConfig = { jfrog_oidc_provider_name: string; @@ -49,11 +99,21 @@ export type JFrogConfig = { identity_mapping_name?: string; }; +/** Decides whether `config` is a JFrog OIDC configuration. */ +export function isJFrogConfig( + config: Partial, +): config is JFrogConfig { + return ( + "jfrog_oidc_provider_name" in config && + isDefined(config.jfrog_oidc_provider_name) + ); +} + /** Represents all supported OIDC configurations. */ export type OIDC = AzureConfig | AWSConfig | JFrogConfig; /** All authentication-related fields. */ -export type AuthConfig = UsernamePassword & Token & OIDC; +export type AuthConfig = UsernamePassword | Token | OIDC; /** * A package registry configuration includes identifying information as well as