Skip to content

Commit 6103178

Browse files
authored
APP-7002: Implement “DeleteOAuthApplication” CLI command (viamrobotics#4648)
1 parent 7297934 commit 6103178

File tree

4 files changed

+129
-0
lines changed

4 files changed

+129
-0
lines changed

cli/app.go

+32
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ const (
135135
authApplicationFlagOriginURIs = "origin-uris"
136136
authApplicationFlagRedirectURIs = "redirect-uris"
137137
authApplicationFlagLogoutURI = "logout-uri"
138+
authApplicationFlagClientID = "client-id"
138139

139140
cpFlagRecursive = "recursive"
140141
cpFlagPreserve = "preserve"
@@ -454,6 +455,37 @@ var app = &cli.App{
454455
Usage: "work with organizations",
455456
HideHelpCommand: true,
456457
Subcommands: []*cli.Command{
458+
{
459+
Name: "auth-service",
460+
Usage: "manage auth-service",
461+
Subcommands: []*cli.Command{
462+
{
463+
Name: "oauth-app",
464+
Usage: "manage the OAuth applications for an organization",
465+
Subcommands: []*cli.Command{
466+
{
467+
Name: "delete",
468+
Usage: "delete an OAuth application",
469+
UsageText: createUsageText("delete", []string{generalFlagOrgID, authApplicationFlagClientID}, true),
470+
Flags: []cli.Flag{
471+
&cli.StringFlag{
472+
Name: generalFlagOrgID,
473+
Required: true,
474+
Usage: "organization ID tied to the OAuth application",
475+
},
476+
&cli.StringFlag{
477+
Name: authApplicationFlagClientID,
478+
Required: true,
479+
Usage: "client ID of the OAuth application to delete",
480+
},
481+
},
482+
Before: createCommandWithT[deleteOAuthAppArgs](DeleteOAuthAppConfirmation),
483+
Action: createCommandWithT[deleteOAuthAppArgs](DeleteOAuthAppAction),
484+
},
485+
},
486+
},
487+
},
488+
},
457489
{
458490
Name: "list",
459491
Usage: "list organizations for the current user",

cli/client.go

+67
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package cli
33

44
import (
5+
"bufio"
56
"context"
67
"encoding/json"
78
"fmt"
@@ -2109,3 +2110,69 @@ func logEntryFieldsToString(fields []*structpb.Struct) (string, error) {
21092110
}
21102111
return message + "}", nil
21112112
}
2113+
2114+
type deleteOAuthAppArgs struct {
2115+
OrgID string
2116+
ClientID string
2117+
}
2118+
2119+
// DeleteOAuthAppConfirmation is the Before action for 'organizations auth-service oauth-app delete'.
2120+
// It asks for the user to confirm that they want to delete the oauth app.
2121+
func DeleteOAuthAppConfirmation(c *cli.Context, args deleteOAuthAppArgs) error {
2122+
if args.OrgID == "" {
2123+
return errors.New("cannot delete oauth app without an organization ID")
2124+
}
2125+
2126+
if args.ClientID == "" {
2127+
return errors.New("cannot delete oauth app without a client ID")
2128+
}
2129+
2130+
yellow := "\033[1;33m%s\033[0m"
2131+
printf(c.App.Writer, yellow, "WARNING!!\n")
2132+
printf(c.App.Writer, yellow, fmt.Sprintf("You are trying to delete an OAuth application with client ID %s. "+
2133+
"Once deleted, any existing apps that rely on this OAuth application will no longer be able to authenticate users.\n", args.ClientID))
2134+
printf(c.App.Writer, yellow, "If you wish to continue, please type \"delete\":")
2135+
if err := c.Err(); err != nil {
2136+
return err
2137+
}
2138+
2139+
rawInput, err := bufio.NewReader(c.App.Reader).ReadString('\n')
2140+
if err != nil {
2141+
return err
2142+
}
2143+
2144+
input := strings.ToUpper(strings.TrimSpace(rawInput))
2145+
if input != "DELETE" {
2146+
return errors.New("aborted")
2147+
}
2148+
return nil
2149+
}
2150+
2151+
// DeleteOAuthAppAction is the corresponding action for 'oauth-app delete'.
2152+
func DeleteOAuthAppAction(c *cli.Context, args deleteOAuthAppArgs) error {
2153+
client, err := newViamClient(c)
2154+
if err != nil {
2155+
return err
2156+
}
2157+
2158+
return client.deleteOAuthAppAction(c, args.OrgID, args.ClientID)
2159+
}
2160+
2161+
func (c *viamClient) deleteOAuthAppAction(cCtx *cli.Context, orgID, clientID string) error {
2162+
if err := c.ensureLoggedIn(); err != nil {
2163+
return err
2164+
}
2165+
2166+
req := &apppb.DeleteOAuthAppRequest{
2167+
OrgId: orgID,
2168+
ClientId: clientID,
2169+
}
2170+
2171+
_, err := c.client.DeleteOAuthApp(c.c.Context, req)
2172+
if err != nil {
2173+
return err
2174+
}
2175+
2176+
printf(cCtx.App.Writer, "Successfully deleted OAuth application")
2177+
return nil
2178+
}

cli/client_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,24 @@ func TestGetLogoAction(t *testing.T) {
352352
test.That(t, out.messages[0], test.ShouldContainSubstring, "https://logo.com")
353353
}
354354

355+
func TestDeleteOAuthAppAction(t *testing.T) {
356+
deleteOAuthAppFunc := func(ctx context.Context, in *apppb.DeleteOAuthAppRequest, opts ...grpc.CallOption) (
357+
*apppb.DeleteOAuthAppResponse, error,
358+
) {
359+
return &apppb.DeleteOAuthAppResponse{}, nil
360+
}
361+
362+
asc := &inject.AppServiceClient{
363+
DeleteOAuthAppFunc: deleteOAuthAppFunc,
364+
}
365+
366+
cCtx, ac, out, errOut := setup(asc, nil, nil, nil, nil, "token")
367+
test.That(t, ac.deleteOAuthAppAction(cCtx, "test-org", "client-id"), test.ShouldBeNil)
368+
test.That(t, len(errOut.messages), test.ShouldEqual, 0)
369+
test.That(t, len(out.messages), test.ShouldEqual, 1)
370+
test.That(t, out.messages[0], test.ShouldContainSubstring, "Successfully deleted OAuth application")
371+
}
372+
355373
func TestUpdateBillingServiceAction(t *testing.T) {
356374
updateConfigFunc := func(ctx context.Context, in *apppb.UpdateBillingServiceRequest, opts ...grpc.CallOption) (
357375
*apppb.UpdateBillingServiceResponse, error,

testutils/inject/app_service_client.go

+12
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ type AppServiceClient struct {
5656
opts ...grpc.CallOption) (*apppb.OrganizationSetLogoResponse, error)
5757
OrganizationGetLogoFunc func(ctx context.Context, in *apppb.OrganizationGetLogoRequest,
5858
opts ...grpc.CallOption) (*apppb.OrganizationGetLogoResponse, error)
59+
DeleteOAuthAppFunc func(ctx context.Context, in *apppb.DeleteOAuthAppRequest,
60+
opts ...grpc.CallOption) (*apppb.DeleteOAuthAppResponse, error)
5961
CreateLocationFunc func(ctx context.Context, in *apppb.CreateLocationRequest,
6062
opts ...grpc.CallOption) (*apppb.CreateLocationResponse, error)
6163
GetLocationFunc func(ctx context.Context, in *apppb.GetLocationRequest,
@@ -403,6 +405,16 @@ func (asc *AppServiceClient) OrganizationGetLogo(
403405
return asc.OrganizationGetLogoFunc(ctx, in, opts...)
404406
}
405407

408+
// DeleteOAuthApp calls the injected DeleteOAuthAppFunc or the real version.
409+
func (asc *AppServiceClient) DeleteOAuthApp(
410+
ctx context.Context, in *apppb.DeleteOAuthAppRequest, opts ...grpc.CallOption,
411+
) (*apppb.DeleteOAuthAppResponse, error) {
412+
if asc.DeleteOAuthAppFunc == nil {
413+
return asc.AppServiceClient.DeleteOAuthApp(ctx, in, opts...)
414+
}
415+
return asc.DeleteOAuthAppFunc(ctx, in, opts...)
416+
}
417+
406418
// CreateLocation calls the injected CreateLocationFunc or the real version.
407419
func (asc *AppServiceClient) CreateLocation(
408420
ctx context.Context, in *apppb.CreateLocationRequest, opts ...grpc.CallOption,

0 commit comments

Comments
 (0)