diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 8f78a24a9412b..57a62c393cdca 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -929,6 +929,8 @@ func (h *Handler) bindDefaultEndpoints() { // Channel can contain "/", hence the use of a catch-all parameter h.GET("/webapi/automaticupgrades/channel/*request", h.WithUnauthenticatedHighLimiter(h.automaticUpgrades)) + // GET Machine ID bot by name + h.GET("/webapi/sites/:site/machine-id/bot/:name", h.WithClusterAuth(h.getBot)) // Create Machine ID bots h.POST("/webapi/sites/:site/machine-id/bot", h.WithClusterAuth(h.createBot)) } diff --git a/lib/web/machineid.go b/lib/web/machineid.go index 3bc972831fd9e..7cdd7d8c4a591 100644 --- a/lib/web/machineid.go +++ b/lib/web/machineid.go @@ -68,3 +68,24 @@ func (h *Handler) createBot(w http.ResponseWriter, r *http.Request, p httprouter return OK(), nil } + +// getBot retrieves a bot by name +func (h *Handler) getBot(w http.ResponseWriter, r *http.Request, p httprouter.Params, sctx *SessionContext, site reversetunnelclient.RemoteSite) (interface{}, error) { + botName := p.ByName("name") + if botName == "" { + return nil, trace.BadParameter("empty name") + } + + clt, err := sctx.GetUserClient(r.Context(), site) + if err != nil { + return nil, trace.Wrap(err) + } + bot, err := clt.BotServiceClient().GetBot(r.Context(), &machineidv1.GetBotRequest{ + BotName: botName, + }) + if err != nil { + return nil, trace.Wrap(err, "error querying bot") + } + + return bot, nil +} diff --git a/lib/web/machineid_test.go b/lib/web/machineid_test.go index 70066ec63924c..7d80f170e6033 100644 --- a/lib/web/machineid_test.go +++ b/lib/web/machineid_test.go @@ -19,12 +19,16 @@ package web import ( "context" "encoding/json" + "fmt" + "net/http" "slices" "testing" "github.com/gravitational/trace" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + machineidv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/web/ui" @@ -82,3 +86,38 @@ func TestCreateBot(t *testing.T) { require.Error(t, err) require.True(t, trace.IsAccessDenied(err)) } + +func TestGetBotByName(t *testing.T) { + ctx := context.Background() + env := newWebPack(t, 1) + proxy := env.proxies[0] + pack := proxy.authPack(t, "admin", []types.Role{services.NewPresetEditorRole()}) + clusterName := env.server.ClusterName() + endpoint := pack.clt.Endpoint( + "webapi", + "sites", + clusterName, + "machine-id", + "bot", + ) + + // create a bot nammed `test-bot-1` + botName := "test-bot-1" + _, err := pack.clt.PostJSON(ctx, endpoint, CreateBotRequest{ + BotName: botName, + Roles: []string{""}, + }) + require.NoError(t, err) + + response, err := pack.clt.Get(ctx, fmt.Sprintf("%s/%s", endpoint, botName), nil) + require.NoError(t, err) + + var bot machineidv1.Bot + require.NoError(t, json.Unmarshal(response.Bytes(), &bot), "invalid response received") + assert.Equal(t, http.StatusOK, response.Code(), "unexpected status code getting connectors") + assert.Equal(t, botName, bot.Metadata.Name) + + // query an unexisting bot + _, err = pack.clt.Get(ctx, fmt.Sprintf("%s/%s", endpoint, "invalid-bot"), nil) + require.Error(t, err) +}