Skip to content

Commit 2ac9a84

Browse files
authored
TFE webhook auth (#2379)
* add tfe webhook auth * adjust uuid resolution * remove temporary logs
1 parent ac16028 commit 2ac9a84

File tree

5 files changed

+400
-121
lines changed

5 files changed

+400
-121
lines changed

go.work

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use (
1616

1717
./taco/cmd/statesman
1818
./taco/cmd/taco
19+
./taco/cmd/token_service
1920
./taco/internal
2021
./taco/pkg/sdk
2122
./taco/providers/terraform/opentaco

taco/internal/api/internal.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ import (
55
"net/http"
66
"os"
77

8+
"github.com/diggerhq/digger/opentaco/internal/auth"
89
"github.com/diggerhq/digger/opentaco/internal/domain"
910
"github.com/diggerhq/digger/opentaco/internal/middleware"
11+
"github.com/diggerhq/digger/opentaco/internal/oidc"
1012
"github.com/diggerhq/digger/opentaco/internal/rbac"
1113
"github.com/diggerhq/digger/opentaco/internal/repositories"
14+
"github.com/diggerhq/digger/opentaco/internal/sts"
15+
"github.com/diggerhq/digger/opentaco/internal/tfe"
1216
unithandlers "github.com/diggerhq/digger/opentaco/internal/unit"
1317
"github.com/labstack/echo/v4"
1418
)
@@ -123,6 +127,59 @@ func RegisterInternalRoutes(e *echo.Echo, deps Dependencies) {
123127
internal.GET("/units/:id/versions", unitHandler.ListVersions)
124128
internal.POST("/units/:id/restore", unitHandler.RestoreVersion)
125129

130+
// ====================================================================================
131+
// TFE API Routes with Webhook Auth (for UI forwarding)
132+
// ====================================================================================
133+
// These mirror the public TFE routes but use webhook auth instead of opaque tokens
134+
// This allows the UI to forward Terraform Cloud API requests on behalf of users
135+
136+
// Prepare auth deps for TFE handler
137+
stsi, _ := sts.NewStatelessIssuerFromEnv()
138+
ver, _ := oidc.NewFromEnv()
139+
authHandler := auth.NewHandler(deps.Signer, stsi, ver)
140+
apiTokenMgr := auth.NewAPITokenManagerFromStore(deps.BlobStore)
141+
authHandler.SetAPITokenManager(apiTokenMgr)
142+
143+
// Create identifier resolver for TFE org resolution
144+
var tfeIdentifierResolver domain.IdentifierResolver
145+
if deps.QueryStore != nil {
146+
if db := repositories.GetDBFromQueryStore(deps.QueryStore); db != nil {
147+
tfeIdentifierResolver = repositories.NewIdentifierResolver(db)
148+
}
149+
}
150+
151+
// Create TFE handler with webhook auth context
152+
tfeHandler := tfe.NewTFETokenHandler(authHandler, deps.Repository, deps.BlobStore, deps.RBACManager, tfeIdentifierResolver)
153+
154+
// TFE group with webhook auth (for UI pass-through)
155+
tfeInternal := e.Group("/internal/tfe/api/v2")
156+
tfeInternal.Use(middleware.WebhookAuth())
157+
158+
// Add org resolution middleware for TFE routes
159+
if tfeIdentifierResolver != nil {
160+
tfeInternal.Use(middleware.ResolveOrgContextMiddleware(tfeIdentifierResolver))
161+
log.Println("Org context resolution middleware enabled for internal TFE routes")
162+
}
163+
164+
// Register TFE endpoints (same handlers as public TFE routes)
165+
tfeInternal.GET("/ping", tfeHandler.Ping)
166+
tfeInternal.GET("/organizations/:org_name/entitlement-set", tfeHandler.GetOrganizationEntitlements)
167+
tfeInternal.GET("/account/details", tfeHandler.AccountDetails)
168+
tfeInternal.GET("/organizations/:org_name/workspaces/:workspace_name", tfeHandler.GetWorkspace)
169+
tfeInternal.POST("/workspaces/:workspace_id/actions/lock", tfeHandler.LockWorkspace)
170+
tfeInternal.POST("/workspaces/:workspace_id/actions/unlock", tfeHandler.UnlockWorkspace)
171+
tfeInternal.POST("/workspaces/:workspace_id/actions/force-unlock", tfeHandler.ForceUnlockWorkspace)
172+
tfeInternal.GET("/workspaces/:workspace_id/current-state-version", tfeHandler.GetCurrentStateVersion)
173+
tfeInternal.POST("/workspaces/:workspace_id/state-versions", tfeHandler.CreateStateVersion)
174+
tfeInternal.GET("/state-versions/:id/download", tfeHandler.DownloadStateVersion)
175+
tfeInternal.GET("/state-versions/:id", tfeHandler.ShowStateVersion)
176+
177+
log.Println("TFE API endpoints registered at /internal/tfe/api/v2 with webhook auth")
178+
179+
// ====================================================================================
180+
// Health and Info Endpoints
181+
// ====================================================================================
182+
126183
// Health check for internal routes
127184
internal.GET("/health", func(c echo.Context) error {
128185
return c.JSON(http.StatusOK, map[string]interface{}{

taco/internal/storage/s3store.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,14 @@ func (s *s3Store) Create(ctx context.Context, id string) (*UnitMetadata, error)
170170
}
171171

172172
func (s *s3Store) Get(ctx context.Context, id string) (*UnitMetadata, error) {
173+
174+
s3Key := s.objKey(id)
175+
173176
head, err := s.client.HeadObject(ctx, &s3.HeadObjectInput{
174177
Bucket: &s.bucket,
175-
Key: aws.String(s.objKey(id)),
178+
Key: aws.String(s3Key),
176179
})
180+
177181
if err != nil {
178182
if isNotFound(err) {
179183
return nil, ErrNotFound

0 commit comments

Comments
 (0)