From 3aab10d5c2e225a71841844198a8ff7ac5f357c4 Mon Sep 17 00:00:00 2001 From: Callum Powell Date: Mon, 26 Jan 2026 14:18:35 +0100 Subject: [PATCH 1/2] chore(auth): update OAuth client configuration for local development --- internal/build/build.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/build/build.go b/internal/build/build.go index d63a838..fba67d8 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -34,8 +34,8 @@ var ( OTELCollectorToken = "" AuthPlatformAudience = "api://cd5aff56-575c-4e7e-b68e-3f67fa42eb31/user_impersonation" - AuthAuthority = "https://login.microsoftonline.com/intility.no" - AuthClientID = "b65cf9b0-290c-4b44-a4b1-0b02b7752b3c" + AuthAuthority = "https://login.microsoftonline.com/organizations" + AuthClientID = "27f5ab79-28cb-4824-b603-4b0795b8985e" AuthRedirect = "http://localhost:42069" ) @@ -121,7 +121,7 @@ func PlatformAPIHost() string { func ClientID() string { if IsDev { - return "b65cf9b0-290c-4b44-a4b1-0b02b7752b3c" + return "27f5ab79-28cb-4824-b603-4b0795b8985e" } return AuthClientID @@ -129,7 +129,7 @@ func ClientID() string { func Authority() string { if IsDev { - return "https://login.microsoftonline.com/intility.no" + return "https://login.microsoftonline.com/organizations" } return AuthAuthority From 6148d5c049376eee8cbfac2cd90a31f2dc992576 Mon Sep 17 00:00:00 2001 From: Callum Powell Date: Mon, 26 Jan 2026 14:22:00 +0100 Subject: [PATCH 2/2] refactor: improve data fetching by using new backend endpoints --- pkg/client/client.go | 8 ++++- pkg/client/teams.go | 22 +++++++++++++- pkg/client/user.go | 22 +++++++++++++- pkg/commands/cluster/access/helpers.go | 41 +++++++------------------- pkg/commands/cluster/create_test.go | 8 ++++- pkg/commands/cluster/delete.go | 18 ++--------- pkg/commands/cluster/get.go | 15 ++-------- pkg/commands/cluster/login.go | 19 +++--------- pkg/commands/cluster/open.go | 26 +++++----------- pkg/commands/cluster/status.go | 16 ++-------- pkg/commands/teams/delete.go | 14 ++------- pkg/commands/teams/get.go | 13 +------- pkg/commands/teams/member/helpers.go | 26 +++------------- 13 files changed, 93 insertions(+), 155 deletions(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index 0bfa415..67d15b4 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -41,6 +41,7 @@ type MeClient interface { type TeamsClient interface { ListTeams(ctx context.Context) ([]Team, error) + GetTeam(ctx context.Context, name string) (*Team, error) GetTeamMembers(ctx context.Context, teamId string) ([]TeamMember, error) CreateTeam(ctx context.Context, request NewTeamRequest) (*Team, error) DeleteTeam(ctx context.Context, request DeleteTeamRequest) error @@ -53,6 +54,7 @@ type MemberClient interface { type UserClient interface { ListUsers(ctx context.Context) ([]User, error) + GetUser(ctx context.Context, upn string) (*User, error) } type Client interface { @@ -165,7 +167,7 @@ func (c *RestClient) CreateCluster(ctx context.Context, request NewClusterReques } func (c *RestClient) GetCluster(ctx context.Context, name string) (*Cluster, error) { - req, err := c.createAuthenticatedRequest(ctx, "GET", c.baseURI+"/api/v1/clusters/"+name, nil) + req, err := c.createAuthenticatedRequest(ctx, "GET", c.baseURI+"/api/v1/clusters/by-name/"+name, nil) if err != nil { return nil, err } @@ -175,6 +177,10 @@ func (c *RestClient) GetCluster(ctx context.Context, name string) (*Cluster, err return nil, fmt.Errorf("request failed: %w", err) } + if cluster.Name != name { + return nil, fmt.Errorf("cluster not found: %s", name) + } + return &cluster, nil } diff --git a/pkg/client/teams.go b/pkg/client/teams.go index 198fc01..1bac687 100644 --- a/pkg/client/teams.go +++ b/pkg/client/teams.go @@ -16,6 +16,8 @@ type Team struct { Role []string `json:"roles"` } +type TeamList []Team + type Subject struct { Type string `json:"type"` Name string `json:"name"` @@ -78,7 +80,7 @@ type AddTeamMemberRequest struct { } func (c *RestClient) ListTeams(ctx context.Context) ([]Team, error) { - var teams []Team + var teams TeamList req, err := c.createAuthenticatedRequest(ctx, "GET", c.baseURI+"/api/v1/teams", nil) if err != nil { @@ -92,6 +94,24 @@ func (c *RestClient) ListTeams(ctx context.Context) ([]Team, error) { return teams, nil } +func (c *RestClient) GetTeam(ctx context.Context, name string) (*Team, error) { + req, err := c.createAuthenticatedRequest(ctx, "GET", c.baseURI+"/api/v1/teams/by-name/"+name, nil) + if err != nil { + return nil, err + } + + var team Team + if err = doRequest(c.httpClient, req, &team); err != nil { + return nil, fmt.Errorf("request failed: %w", err) + } + + if team.Name != name { + return nil, fmt.Errorf("team not found: %s", name) + } + + return &team, nil +} + func (c *RestClient) GetTeamMembers(ctx context.Context, teamId string) ([]TeamMember, error) { var members []TeamMember diff --git a/pkg/client/user.go b/pkg/client/user.go index 5a9709d..452c97d 100644 --- a/pkg/client/user.go +++ b/pkg/client/user.go @@ -12,8 +12,10 @@ type User struct { Roles []string `json:"roles"` } +type UserList []User + func (c *RestClient) ListUsers(ctx context.Context) ([]User, error) { - var users []User + var users UserList req, err := c.createAuthenticatedRequest(ctx, "GET", c.baseURI+"/api/v1/users", nil) if err != nil { @@ -26,3 +28,21 @@ func (c *RestClient) ListUsers(ctx context.Context) ([]User, error) { return users, nil } + +func (c *RestClient) GetUser(ctx context.Context, upn string) (*User, error) { + req, err := c.createAuthenticatedRequest(ctx, "GET", c.baseURI+"/api/v1/users/by-upn/"+upn, nil) + if err != nil { + return nil, err + } + + var user User + if err = doRequest(c.httpClient, req, &user); err != nil { + return nil, fmt.Errorf("request failed: %w", err) + } + + if user.UPN != upn { + return nil, fmt.Errorf("user not found: %s", upn) + } + + return &user, nil +} diff --git a/pkg/commands/cluster/access/helpers.go b/pkg/commands/cluster/access/helpers.go index 3e137e0..4259354 100644 --- a/pkg/commands/cluster/access/helpers.go +++ b/pkg/commands/cluster/access/helpers.go @@ -2,10 +2,8 @@ package access import ( "context" - "strings" "github.com/intility/indev/internal/redact" - "github.com/intility/indev/pkg/client" "github.com/intility/indev/pkg/clientset" ) @@ -20,34 +18,23 @@ func resolveClusterID(ctx context.Context, set clientset.ClientSet, clusterName, return "", redact.Errorf("cluster name or ID is required") } - // List clusters to find the one by name - clusters, err := set.PlatformClient.ListClusters(ctx) + // Get cluster by name + cluster, err := set.PlatformClient.GetCluster(ctx, clusterName) if err != nil { - return "", redact.Errorf("could not list clusters: %w", redact.Safe(err)) + return "", redact.Errorf("could not get cluster: %w", redact.Safe(err)) } - // Find the cluster with the matching name - for _, c := range clusters { - if strings.EqualFold(c.Name, clusterName) { - return c.ID, nil - } + if cluster == nil { + return "", redact.Errorf("cluster not found: %s", clusterName) } - return "", redact.Errorf("cluster not found: %s", clusterName) + return cluster.ID, nil } func getUserIDByUPN(ctx context.Context, set clientset.ClientSet, upn string) (string, error) { - users, err := set.PlatformClient.ListUsers(ctx) + user, err := set.PlatformClient.GetUser(ctx, upn) if err != nil { - return "", redact.Errorf("could not list users: %w", redact.Safe(err)) - } - - var user *client.User - for _, u := range users { - if strings.EqualFold(u.UPN, upn) { - user = &u - break - } + return "", redact.Errorf("could not get user: %w", redact.Safe(err)) } if user == nil { @@ -58,17 +45,9 @@ func getUserIDByUPN(ctx context.Context, set clientset.ClientSet, upn string) (s } func getTeamIDByName(ctx context.Context, set clientset.ClientSet, teamName string) (string, error) { - teams, err := set.PlatformClient.ListTeams(ctx) + team, err := set.PlatformClient.GetTeam(ctx, teamName) if err != nil { - return "", redact.Errorf("could not list teams: %w", redact.Safe(err)) - } - - var team *client.Team - for _, t := range teams { - if strings.EqualFold(t.Name, teamName) { - team = &t - break - } + return "", redact.Errorf("could not get team: %w", redact.Safe(err)) } if team == nil { diff --git a/pkg/commands/cluster/create_test.go b/pkg/commands/cluster/create_test.go index 522662e..fedcd80 100644 --- a/pkg/commands/cluster/create_test.go +++ b/pkg/commands/cluster/create_test.go @@ -299,10 +299,13 @@ func (m *mockClient) AddClusterMember(_ context.Context, _ string, _ []client.Ad return nil } func (m *mockClient) RemoveClusterMember(_ context.Context, _, _ string) error { return nil } -func (m *mockClient) GetMe(_ context.Context) (client.Me, error) { return client.Me{}, nil } +func (m *mockClient) GetMe(_ context.Context) (client.Me, error) { return client.Me{}, nil } func (m *mockClient) ListTeams(_ context.Context) ([]client.Team, error) { return nil, nil } +func (m *mockClient) GetTeam(_ context.Context, _ string) (*client.Team, error) { + return nil, nil +} func (m *mockClient) GetTeamMembers(_ context.Context, _ string) ([]client.TeamMember, error) { return nil, nil } @@ -315,6 +318,9 @@ func (m *mockClient) AddTeamMember(_ context.Context, _ string, _ []client.AddTe } func (m *mockClient) RemoveTeamMember(_ context.Context, _, _ string) error { return nil } func (m *mockClient) ListUsers(_ context.Context) ([]client.User, error) { return nil, nil } +func (m *mockClient) GetUser(_ context.Context, _ string) (*client.User, error) { + return nil, nil +} func TestSelectSSOProvisioner(t *testing.T) { tests := []struct { diff --git a/pkg/commands/cluster/delete.go b/pkg/commands/cluster/delete.go index 5f1b634..52f8f3f 100644 --- a/pkg/commands/cluster/delete.go +++ b/pkg/commands/cluster/delete.go @@ -1,14 +1,11 @@ package cluster import ( - "strings" - "github.com/spf13/cobra" "github.com/intility/indev/internal/redact" "github.com/intility/indev/internal/telemetry" "github.com/intility/indev/internal/ux" - "github.com/intility/indev/pkg/client" "github.com/intility/indev/pkg/clientset" ) @@ -37,19 +34,10 @@ func NewDeleteCommand(set clientset.ClientSet) *cobra.Command { return errEmptyName } - // List clusters to find the one by name - clusters, err := set.PlatformClient.ListClusters(ctx) + // Get cluster by name + cluster, err := set.PlatformClient.GetCluster(ctx, clusterName) if err != nil { - return redact.Errorf("could not list clusters: %w", redact.Safe(err)) - } - - // Find the cluster with the matching name - var cluster *client.Cluster - for _, c := range clusters { - if strings.EqualFold(c.Name, clusterName) { - cluster = &c - break - } + return redact.Errorf("could not get cluster: %w", redact.Safe(err)) } if cluster == nil { diff --git a/pkg/commands/cluster/get.go b/pkg/commands/cluster/get.go index 2370d20..2c8bd36 100644 --- a/pkg/commands/cluster/get.go +++ b/pkg/commands/cluster/get.go @@ -40,19 +40,10 @@ func NewGetCommand(set clientset.ClientSet) *cobra.Command { return errEmptyName } - // List clusters to find the one by name - clusters, err := set.PlatformClient.ListClusters(ctx) + // Get cluster by name + cluster, err := set.PlatformClient.GetCluster(ctx, clusterName) if err != nil { - return redact.Errorf("could not list clusters: %w", redact.Safe(err)) - } - - // Find the cluster with the matching name - var cluster *client.Cluster - for _, c := range clusters { - if strings.EqualFold(c.Name, clusterName) { - cluster = &c - break - } + return redact.Errorf("could not get cluster: %w", redact.Safe(err)) } if cluster == nil { diff --git a/pkg/commands/cluster/login.go b/pkg/commands/cluster/login.go index dd90489..81774ad 100644 --- a/pkg/commands/cluster/login.go +++ b/pkg/commands/cluster/login.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "os/exec" - "strings" "github.com/spf13/cobra" @@ -43,23 +42,13 @@ func NewLoginCommand(set clientset.ClientSet) *cobra.Command { return errEmptyName } - // List clusters to verify the cluster exists - clusters, err := set.PlatformClient.ListClusters(ctx) + // Get cluster by name + cluster, err := set.PlatformClient.GetCluster(ctx, clusterName) if err != nil { - return redact.Errorf("could not list clusters: %w", redact.Safe(err)) + return redact.Errorf("could not get cluster: %w", redact.Safe(err)) } - // Find the cluster with the matching name (case-insensitive) - var found bool - for _, c := range clusters { - if strings.EqualFold(c.Name, clusterName) { - clusterName = c.Name // Use the exact name from the API - found = true - break - } - } - - if !found { + if cluster == nil { return redact.Errorf("cluster not found: %s", clusterName) } diff --git a/pkg/commands/cluster/open.go b/pkg/commands/cluster/open.go index bd678ef..82951b1 100644 --- a/pkg/commands/cluster/open.go +++ b/pkg/commands/cluster/open.go @@ -1,15 +1,12 @@ package cluster import ( - "strings" - "github.com/pkg/browser" "github.com/spf13/cobra" "github.com/intility/indev/internal/redact" "github.com/intility/indev/internal/telemetry" "github.com/intility/indev/internal/ux" - "github.com/intility/indev/pkg/client" "github.com/intility/indev/pkg/clientset" ) @@ -20,10 +17,10 @@ func NewOpenCommand(set clientset.ClientSet) *cobra.Command { ) cmd := &cobra.Command{ - Use: "open [name]", - Short: "Open the cluster console in a browser", - Long: `Open the OpenShift web console for the specified cluster in your default browser.`, - Args: cobra.MaximumNArgs(1), + Use: "open [name]", + Short: "Open the cluster console in a browser", + Long: `Open the OpenShift web console for the specified cluster in your default browser.`, + Args: cobra.MaximumNArgs(1), PreRunE: set.EnsureSignedInPreHook, RunE: func(cmd *cobra.Command, args []string) error { ctx, span := telemetry.StartSpan(cmd.Context(), "cluster.open") @@ -40,19 +37,10 @@ func NewOpenCommand(set clientset.ClientSet) *cobra.Command { return errEmptyName } - // List clusters to find the one by name - clusters, err := set.PlatformClient.ListClusters(ctx) + // Get cluster by name + cluster, err := set.PlatformClient.GetCluster(ctx, clusterName) if err != nil { - return redact.Errorf("could not list clusters: %w", redact.Safe(err)) - } - - // Find the cluster with the matching name (case-insensitive) - var cluster *client.Cluster - for _, c := range clusters { - if strings.EqualFold(c.Name, clusterName) { - cluster = &c - break - } + return redact.Errorf("could not get cluster: %w", redact.Safe(err)) } if cluster == nil { diff --git a/pkg/commands/cluster/status.go b/pkg/commands/cluster/status.go index f949886..ce492f1 100644 --- a/pkg/commands/cluster/status.go +++ b/pkg/commands/cluster/status.go @@ -2,7 +2,6 @@ package cluster import ( "io" - "strings" "github.com/spf13/cobra" @@ -40,19 +39,10 @@ func NewStatusCommand(set clientset.ClientSet) *cobra.Command { return errEmptyName } - // List clusters to find the one by name - clusters, err := set.PlatformClient.ListClusters(ctx) + // Get cluster by name + cluster, err := set.PlatformClient.GetCluster(ctx, clusterName) if err != nil { - return redact.Errorf("could not list clusters: %w", redact.Safe(err)) - } - - // Find the cluster with the matching name - var cluster *client.Cluster - for _, c := range clusters { - if strings.EqualFold(c.Name, clusterName) { - cluster = &c - break - } + return redact.Errorf("could not get cluster: %w", redact.Safe(err)) } if cluster == nil { diff --git a/pkg/commands/teams/delete.go b/pkg/commands/teams/delete.go index 998bf22..d2ec09a 100644 --- a/pkg/commands/teams/delete.go +++ b/pkg/commands/teams/delete.go @@ -1,8 +1,6 @@ package teams import ( - "strings" - "github.com/spf13/cobra" "github.com/intility/indev/internal/redact" @@ -33,17 +31,9 @@ func NewDeleteCommand(set clientset.ClientSet) *cobra.Command { return errEmptyName } - teams, err := set.PlatformClient.ListTeams(ctx) + team, err := set.PlatformClient.GetTeam(ctx, teamName) if err != nil { - return redact.Errorf("could not list teams: %w", redact.Safe(err)) - } - - var team *client.Team - for _, t := range teams { - if strings.EqualFold(t.Name, teamName) { - team = &t - break - } + return redact.Errorf("could not get team: %w", redact.Safe(err)) } if team == nil { diff --git a/pkg/commands/teams/get.go b/pkg/commands/teams/get.go index 508d955..62abed3 100644 --- a/pkg/commands/teams/get.go +++ b/pkg/commands/teams/get.go @@ -3,7 +3,6 @@ package teams import ( "io" "slices" - "strings" "github.com/spf13/cobra" @@ -41,21 +40,11 @@ func NewGetCommand(set clientset.ClientSet) *cobra.Command { return errEmptyName } - // List teams to find the one by name - teams, err := set.PlatformClient.ListTeams(ctx) + team, err := set.PlatformClient.GetTeam(ctx, teamName) if err != nil { return redact.Errorf("could not get team: %w", redact.Safe(err)) } - // Find the team with the matching name - var team *client.Team - for _, c := range teams { - if strings.EqualFold(c.Name, teamName) { - team = &c - break - } - } - if team == nil { return redact.Errorf("team not found: %s", teamName) } diff --git a/pkg/commands/teams/member/helpers.go b/pkg/commands/teams/member/helpers.go index 8fecc21..0978e0a 100644 --- a/pkg/commands/teams/member/helpers.go +++ b/pkg/commands/teams/member/helpers.go @@ -2,25 +2,15 @@ package member import ( "context" - "strings" "github.com/intility/indev/internal/redact" - "github.com/intility/indev/pkg/client" "github.com/intility/indev/pkg/clientset" ) func getTeamIdByName(ctx context.Context, set clientset.ClientSet, teamName string) (string, error) { - teams, err := set.PlatformClient.ListTeams(ctx) + team, err := set.PlatformClient.GetTeam(ctx, teamName) if err != nil { - return "", redact.Errorf("could not list teams: %w", redact.Safe(err)) - } - - var team *client.Team - for _, t := range teams { - if strings.EqualFold(t.Name, teamName) { - team = &t - break - } + return "", redact.Errorf("could not get team: %w", redact.Safe(err)) } if team == nil { @@ -31,17 +21,9 @@ func getTeamIdByName(ctx context.Context, set clientset.ClientSet, teamName stri } func getUserIdByUpn(ctx context.Context, set clientset.ClientSet, upn string) (string, error) { - users, err := set.PlatformClient.ListUsers(ctx) + user, err := set.PlatformClient.GetUser(ctx, upn) if err != nil { - return "", redact.Errorf("could not list users: %w", redact.Safe(err)) - } - - var user *client.User - for _, u := range users { - if strings.EqualFold(u.UPN, upn) { - user = &u - break - } + return "", redact.Errorf("could not get user: %w", redact.Safe(err)) } if user == nil {