Explorar o código

add authorization headers to provisioner service requests

Alexander Belanger %!s(int64=4) %!d(string=hai) anos
pai
achega
420a0e43d3

+ 2 - 8
api/server/handlers/infra/create.go

@@ -16,8 +16,6 @@ import (
 	"github.com/porter-dev/porter/internal/models"
 	"gorm.io/gorm"
 
-	"github.com/porter-dev/porter/provisioner/client"
-
 	ptypes "github.com/porter-dev/porter/provisioner/types"
 )
 
@@ -103,8 +101,6 @@ func (c *InfraCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	}
 
 	// call apply on the provisioner service
-	pClient := client.NewClient("http://localhost:8082/api/v1")
-
 	vals := req.Values
 
 	// if this is cluster-scoped and the kind is RDS, run the postrenderer
@@ -123,7 +119,7 @@ func (c *InfraCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	resp, err := pClient.Apply(context.Background(), proj.ID, infra.ID, &ptypes.ApplyBaseRequest{
+	resp, err := c.Config().ProvisionerClient.Apply(context.Background(), proj.ID, infra.ID, &ptypes.ApplyBaseRequest{
 		Kind:          req.Kind,
 		Values:        vals,
 		OperationKind: "create",
@@ -228,10 +224,8 @@ func (i *InfraRDSPostrenderer) Run(w http.ResponseWriter, r *http.Request, opts
 
 		clusterInfraOperation, err := i.config.Repo.Infra().GetLatestOperation(clusterInfra)
 
-		pClient := client.NewClient("http://localhost:8082/api/v1")
-
 		// get the raw state for the cluster
-		rawState, err := pClient.GetRawState(context.Background(), models.GetWorkspaceID(clusterInfra, clusterInfraOperation))
+		rawState, err := i.config.ProvisionerClient.GetRawState(context.Background(), models.GetWorkspaceID(clusterInfra, clusterInfraOperation))
 
 		if err != nil {
 			apierrors.HandleAPIError(i.config.Logger, i.config.Alerter, w, r, apierrors.NewErrInternal(err), true)

+ 1 - 5
api/server/handlers/infra/delete.go

@@ -11,8 +11,6 @@ import (
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
 
-	"github.com/porter-dev/porter/provisioner/client"
-
 	ptypes "github.com/porter-dev/porter/provisioner/types"
 )
 
@@ -59,9 +57,7 @@ func (c *InfraDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	}
 
 	// call apply on the provisioner service
-	pClient := client.NewClient("http://localhost:8082/api/v1")
-
-	resp, err := pClient.Delete(context.Background(), proj.ID, infra.ID, &ptypes.DeleteBaseRequest{
+	resp, err := c.Config().ProvisionerClient.Delete(context.Background(), proj.ID, infra.ID, &ptypes.DeleteBaseRequest{
 		OperationKind: "delete",
 	})
 

+ 0 - 51
api/server/handlers/infra/get_current.go

@@ -1,51 +0,0 @@
-package infra
-
-import (
-	"errors"
-	"net/http"
-
-	"github.com/porter-dev/porter/api/server/handlers"
-	"github.com/porter-dev/porter/api/server/shared"
-	"github.com/porter-dev/porter/api/server/shared/apierrors"
-	"github.com/porter-dev/porter/api/server/shared/config"
-	"github.com/porter-dev/porter/api/types"
-	"github.com/porter-dev/porter/ee/integrations/httpbackend"
-	"github.com/porter-dev/porter/internal/models"
-)
-
-type InfraGetCurrentHandler struct {
-	handlers.PorterHandlerWriter
-}
-
-func NewInfraGetCurrentHandler(
-	config *config.Config,
-	writer shared.ResultWriter,
-) *InfraGetCurrentHandler {
-	return &InfraGetCurrentHandler{
-		PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
-	}
-}
-
-func (c *InfraGetCurrentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	infra, _ := r.Context().Value(types.InfraScope).(*models.Infra)
-
-	// TODO: move client out of this call
-	client := httpbackend.NewClient(c.Config().ServerConf.ProvisionerBackendURL)
-
-	// get the unique infra name and query from the TF HTTP backend
-	current, err := client.GetCurrentState(infra.GetUniqueName())
-
-	if err != nil && errors.Is(err, httpbackend.ErrNotFound) {
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
-			err,
-			http.StatusNotFound,
-		))
-
-		return
-	} else if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
-
-	c.WriteResult(w, r, current)
-}

+ 0 - 51
api/server/handlers/infra/get_desired.go

@@ -1,51 +0,0 @@
-package infra
-
-import (
-	"errors"
-	"net/http"
-
-	"github.com/porter-dev/porter/api/server/handlers"
-	"github.com/porter-dev/porter/api/server/shared"
-	"github.com/porter-dev/porter/api/server/shared/apierrors"
-	"github.com/porter-dev/porter/api/server/shared/config"
-	"github.com/porter-dev/porter/api/types"
-	"github.com/porter-dev/porter/ee/integrations/httpbackend"
-	"github.com/porter-dev/porter/internal/models"
-)
-
-type InfraGetDesiredHandler struct {
-	handlers.PorterHandlerWriter
-}
-
-func NewInfraGetDesiredHandler(
-	config *config.Config,
-	writer shared.ResultWriter,
-) *InfraGetDesiredHandler {
-	return &InfraGetDesiredHandler{
-		PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
-	}
-}
-
-func (c *InfraGetDesiredHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	infra, _ := r.Context().Value(types.InfraScope).(*models.Infra)
-
-	// TODO: move client out of this call
-	client := httpbackend.NewClient(c.Config().ServerConf.ProvisionerBackendURL)
-
-	// get the unique infra name and query from the TF HTTP backend
-	desired, err := client.GetDesiredState(infra.GetUniqueName())
-
-	if err != nil && errors.Is(err, httpbackend.ErrNotFound) {
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
-			err,
-			http.StatusNotFound,
-		))
-
-		return
-	} else if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
-
-	c.WriteResult(w, r, desired)
-}

+ 1 - 4
api/server/handlers/infra/get_operation_logs.go

@@ -10,7 +10,6 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
-	"github.com/porter-dev/porter/provisioner/client"
 )
 
 type InfraGetOperationLogsHandler struct {
@@ -33,9 +32,7 @@ func (c *InfraGetOperationLogsHandler) ServeHTTP(w http.ResponseWriter, r *http.
 	workspaceID := models.GetWorkspaceID(infra, operation)
 
 	// call apply on the provisioner service
-	pClient := client.NewClient("http://localhost:8082/api/v1")
-
-	resp, err := pClient.GetLogs(context.Background(), workspaceID)
+	resp, err := c.Config().ProvisionerClient.GetLogs(context.Background(), workspaceID)
 
 	if err != nil {
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))

+ 1 - 5
api/server/handlers/infra/get_state.go

@@ -10,8 +10,6 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
-
-	"github.com/porter-dev/porter/provisioner/client"
 )
 
 type InfraGetStateHandler struct {
@@ -32,9 +30,7 @@ func (c *InfraGetStateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	infra, _ := r.Context().Value(types.InfraScope).(*models.Infra)
 
 	// call apply on the provisioner service
-	pClient := client.NewClient("http://localhost:8082/api/v1")
-
-	resp, err := pClient.GetState(context.Background(), proj.ID, infra.ID)
+	resp, err := c.Config().ProvisionerClient.GetState(context.Background(), proj.ID, infra.ID)
 
 	if err != nil {
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))

+ 1 - 4
api/server/handlers/infra/retry_create.go

@@ -12,7 +12,6 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
-	"github.com/porter-dev/porter/provisioner/client"
 	ptypes "github.com/porter-dev/porter/provisioner/types"
 	"gorm.io/gorm"
 )
@@ -102,9 +101,7 @@ func (c *InfraRetryCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 	}
 
 	// call apply on the provisioner service
-	pClient := client.NewClient("http://localhost:8082/api/v1")
-
-	resp, err := pClient.Apply(context.Background(), proj.ID, infra.ID, &ptypes.ApplyBaseRequest{
+	resp, err := c.Config().ProvisionerClient.Apply(context.Background(), proj.ID, infra.ID, &ptypes.ApplyBaseRequest{
 		Kind:          string(infra.Kind),
 		Values:        vals,
 		OperationKind: "retry_create",

+ 1 - 4
api/server/handlers/infra/retry_delete.go

@@ -10,7 +10,6 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
-	"github.com/porter-dev/porter/provisioner/client"
 	ptypes "github.com/porter-dev/porter/provisioner/types"
 )
 
@@ -43,9 +42,7 @@ func (c *InfraRetryDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 	}
 
 	// call apply on the provisioner service
-	pClient := client.NewClient("http://localhost:8082/api/v1")
-
-	resp, err := pClient.Delete(context.Background(), proj.ID, infra.ID, &ptypes.DeleteBaseRequest{
+	resp, err := c.Config().ProvisionerClient.Delete(context.Background(), proj.ID, infra.ID, &ptypes.DeleteBaseRequest{
 		OperationKind: "retry_delete",
 	})
 

+ 2 - 19
api/server/handlers/infra/stream_logs.go

@@ -16,8 +16,6 @@ import (
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/provisioner/pb"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/metadata"
 )
 
 type InfraStreamLogHandler struct {
@@ -39,26 +37,11 @@ func (c *InfraStreamLogHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
 	operation, _ := r.Context().Value(types.OperationScope).(*models.Operation)
 	workspaceID := models.GetWorkspaceID(infra, operation)
 
-	conn, err := grpc.Dial("localhost:8082", grpc.WithInsecure())
-
-	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
-
-	client := pb.NewProvisionerClient(conn)
-
-	header := metadata.New(map[string]string{
-		"workspace_id": workspaceID,
-	})
-
-	ctx := metadata.NewOutgoingContext(context.Background(), header)
-
-	ctx, cancel := context.WithCancel(ctx)
+	ctx, cancel := c.Config().ProvisionerClient.NewGRPCContext(workspaceID)
 
 	defer cancel()
 
-	stream, err := client.GetLog(ctx, &pb.Infra{
+	stream, err := c.Config().ProvisionerClient.GRPCClient.GetLog(ctx, &pb.Infra{
 		ProjectId: int64(infra.ProjectID),
 		Id:        int64(infra.ID),
 		Suffix:    infra.Suffix,

+ 2 - 19
api/server/handlers/infra/stream_state.go

@@ -15,8 +15,6 @@ import (
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/provisioner/pb"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/metadata"
 )
 
 type InfraStreamStateHandler struct {
@@ -38,26 +36,11 @@ func (c *InfraStreamStateHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 	operation, _ := r.Context().Value(types.OperationScope).(*models.Operation)
 	workspaceID := models.GetWorkspaceID(infra, operation)
 
-	conn, err := grpc.Dial("localhost:8082", grpc.WithInsecure())
-
-	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
-
-	client := pb.NewProvisionerClient(conn)
-
-	header := metadata.New(map[string]string{
-		"workspace_id": workspaceID,
-	})
-
-	ctx := metadata.NewOutgoingContext(context.Background(), header)
-
-	ctx, cancel := context.WithCancel(ctx)
+	ctx, cancel := c.Config().ProvisionerClient.NewGRPCContext(workspaceID)
 
 	defer cancel()
 
-	stream, err := client.GetStateUpdate(ctx, &pb.Infra{
+	stream, err := c.Config().ProvisionerClient.GRPCClient.GetStateUpdate(ctx, &pb.Infra{
 		ProjectId: int64(infra.ProjectID),
 		Id:        int64(infra.ID),
 		Suffix:    infra.Suffix,

+ 1 - 4
api/server/handlers/infra/update.go

@@ -12,7 +12,6 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
-	"github.com/porter-dev/porter/provisioner/client"
 	ptypes "github.com/porter-dev/porter/provisioner/types"
 	"gorm.io/gorm"
 )
@@ -102,9 +101,7 @@ func (c *InfraUpdateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	}
 
 	// call apply on the provisioner service
-	pClient := client.NewClient("http://localhost:8082/api/v1")
-
-	resp, err := pClient.Apply(context.Background(), proj.ID, infra.ID, &ptypes.ApplyBaseRequest{
+	resp, err := c.Config().ProvisionerClient.Apply(context.Background(), proj.ID, infra.ID, &ptypes.ApplyBaseRequest{
 		Kind:          string(infra.Kind),
 		Values:        vals,
 		OperationKind: "update",

+ 0 - 85
api/server/router/infra.go

@@ -344,91 +344,6 @@ func getInfraRoutes(
 		Router:   r,
 	})
 
-	// GET /api/projects/{project_id}/infras/{infra_id}/logs -> infra.NewInfraStreamLogsHandler
-	// streamLogsEndpoint := factory.NewAPIEndpoint(
-	// 	&types.APIRequestMetadata{
-	// 		Verb:   types.APIVerbGet,
-	// 		Method: types.HTTPVerbGet,
-	// 		Path: &types.Path{
-	// 			Parent:       basePath,
-	// 			RelativePath: relPath + "/logs",
-	// 		},
-	// 		Scopes: []types.PermissionScope{
-	// 			types.UserScope,
-	// 			types.ProjectScope,
-	// 			types.InfraScope,
-	// 		},
-	// 		IsWebsocket: true,
-	// 	},
-	// )
-
-	// streamLogsHandler := infra.NewInfraStreamLogsHandler(
-	// 	config,
-	// 	factory.GetResultWriter(),
-	// )
-
-	// routes = append(routes, &Route{
-	// 	Endpoint: streamLogsEndpoint,
-	// 	Handler:  streamLogsHandler,
-	// 	Router:   r,
-	// })
-
-	// GET /api/projects/{project_id}/infras/{infra_id}/current -> infra.NewInfraGetHandler
-	getCurrentEndpoint := factory.NewAPIEndpoint(
-		&types.APIRequestMetadata{
-			Verb:   types.APIVerbGet,
-			Method: types.HTTPVerbGet,
-			Path: &types.Path{
-				Parent:       basePath,
-				RelativePath: relPath + "/current",
-			},
-			Scopes: []types.PermissionScope{
-				types.UserScope,
-				types.ProjectScope,
-				types.InfraScope,
-			},
-		},
-	)
-
-	getCurrentHandler := infra.NewInfraGetCurrentHandler(
-		config,
-		factory.GetResultWriter(),
-	)
-
-	routes = append(routes, &Route{
-		Endpoint: getCurrentEndpoint,
-		Handler:  getCurrentHandler,
-		Router:   r,
-	})
-
-	// GET /api/projects/{project_id}/infras/{infra_id}/desired -> infra.NewInfraGetHandler
-	getDesiredEndpoint := factory.NewAPIEndpoint(
-		&types.APIRequestMetadata{
-			Verb:   types.APIVerbGet,
-			Method: types.HTTPVerbGet,
-			Path: &types.Path{
-				Parent:       basePath,
-				RelativePath: relPath + "/desired",
-			},
-			Scopes: []types.PermissionScope{
-				types.UserScope,
-				types.ProjectScope,
-				types.InfraScope,
-			},
-		},
-	)
-
-	getDesiredHandler := infra.NewInfraGetDesiredHandler(
-		config,
-		factory.GetResultWriter(),
-	)
-
-	routes = append(routes, &Route{
-		Endpoint: getDesiredEndpoint,
-		Handler:  getDesiredHandler,
-		Router:   r,
-	})
-
 	// GET /api/projects/{project_id}/infras/{infra_id}/state -> infra.NewInfraGetStateHandler
 	getStateEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 3 - 4
api/server/shared/config/config.go

@@ -10,12 +10,12 @@ import (
 	"github.com/porter-dev/porter/internal/billing"
 	"github.com/porter-dev/porter/internal/helm/urlcache"
 	"github.com/porter-dev/porter/internal/integrations/powerdns"
-	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/logger"
 	"github.com/porter-dev/porter/internal/notifier"
 	"github.com/porter-dev/porter/internal/oauth"
 	"github.com/porter-dev/porter/internal/repository"
 	"github.com/porter-dev/porter/internal/repository/credentials"
+	"github.com/porter-dev/porter/provisioner/client"
 	"golang.org/x/oauth2"
 	"gorm.io/gorm"
 )
@@ -75,9 +75,8 @@ type Config struct {
 	// URLCache contains a cache of chart names to chart repos
 	URLCache *urlcache.ChartURLCache
 
-	// ProvisionerAgent is the kubernetes client responsible for creating new provisioner
-	// jobs
-	ProvisionerAgent *kubernetes.Agent
+	// ProvisionerClient is an authenticated client for the provisioner service
+	ProvisionerClient *client.Client
 
 	// DB is the gorm DB instance
 	DB *gorm.DB

+ 3 - 10
api/server/shared/config/env/envconfs.go

@@ -65,12 +65,9 @@ type ServerConf struct {
 	DOClientID     string `env:"DO_CLIENT_ID"`
 	DOClientSecret string `env:"DO_CLIENT_SECRET"`
 
-	// Options for the provisioner jobs
-	ProvisionerImageTag        string `env:"PROV_IMAGE_TAG,default=latest"`
-	ProvisionerImagePullSecret string `env:"PROV_IMAGE_PULL_SECRET"`
-	ProvisionerJobNamespace    string `env:"PROV_JOB_NAMESPACE,default=default"`
-	ProvisionerBackendURL      string `env:"PROV_BACKEND_URL"`
-	ProvisionerCredExchangeURL string `env:"PROV_CRED_EXCHANGE_URL,default=http://porter:8080"`
+	// Options for the provisioner service
+	ProvisionerServerURL string `env:"PROVISIONER_SERVER_URL"`
+	ProvisionerToken     string `env:"PROVISIONER_TOKEN"`
 
 	SegmentClientKey string `env:"SEGMENT_CLIENT_KEY"`
 
@@ -86,10 +83,6 @@ type ServerConf struct {
 	SentryDSN string `env:"SENTRY_DSN"`
 	SentryEnv string `env:"SENTRY_ENV,default=dev"`
 
-	ProvisionerCluster string `env:"PROVISIONER_CLUSTER"`
-	IngressCluster     string `env:"INGRESS_CLUSTER"`
-	SelfKubeconfig     string `env:"SELF_KUBECONFIG"`
-
 	WelcomeFormWebhook string `env:"WELCOME_FORM_WEBHOOK"`
 
 	// Token for internal retool to authenticate to internal API endpoints

+ 11 - 18
api/server/shared/config/loader/loader.go

@@ -18,13 +18,12 @@ import (
 	"github.com/porter-dev/porter/internal/billing"
 	"github.com/porter-dev/porter/internal/helm/urlcache"
 	"github.com/porter-dev/porter/internal/integrations/powerdns"
-	"github.com/porter-dev/porter/internal/kubernetes"
-	"github.com/porter-dev/porter/internal/kubernetes/local"
 	"github.com/porter-dev/porter/internal/notifier"
 	"github.com/porter-dev/porter/internal/notifier/sendgrid"
 	"github.com/porter-dev/porter/internal/oauth"
 	"github.com/porter-dev/porter/internal/repository/credentials"
 	"github.com/porter-dev/porter/internal/repository/gorm"
+	"github.com/porter-dev/porter/provisioner/client"
 
 	lr "github.com/porter-dev/porter/internal/logger"
 
@@ -203,17 +202,13 @@ func (e *EnvConfigLoader) LoadConfig() (res *config.Config, err error) {
 
 	res.URLCache = urlcache.Init(sc.DefaultApplicationHelmRepoURL, sc.DefaultAddonHelmRepoURL)
 
-	provAgent, err := getProvisionerAgent(sc)
+	provClient, err := getProvisionerServiceClient(sc)
 
 	if err != nil {
 		return nil, err
 	}
 
-	res.ProvisionerAgent = provAgent
-
-	if res.ProvisionerAgent != nil && res.RedisConf.Enabled {
-		res.Metadata.Provisioning = true
-	}
+	res.ProvisionerClient = provClient
 
 	res.AnalyticsClient = analytics.InitializeAnalyticsSegmentClient(sc.SegmentClientKey, res.Logger)
 
@@ -224,20 +219,18 @@ func (e *EnvConfigLoader) LoadConfig() (res *config.Config, err error) {
 	return res, nil
 }
 
-func getProvisionerAgent(sc *env.ServerConf) (*kubernetes.Agent, error) {
-	if sc.ProvisionerCluster == "kubeconfig" && sc.SelfKubeconfig != "" {
-		agent, err := local.GetSelfAgentFromFileConfig(sc.SelfKubeconfig)
+func getProvisionerServiceClient(sc *env.ServerConf) (*client.Client, error) {
+	if sc.ProvisionerServerURL != "" && sc.ProvisionerToken != "" {
+		baseURL := fmt.Sprintf("%s/api/v1", sc.ProvisionerServerURL)
+
+		pClient, err := client.NewClient(baseURL, sc.ProvisionerToken, 0)
 
 		if err != nil {
-			return nil, fmt.Errorf("could not get in-cluster agent: %v", err)
+			return nil, err
 		}
 
-		return agent, nil
-	} else if sc.ProvisionerCluster == "kubeconfig" {
-		return nil, fmt.Errorf(`"kubeconfig" cluster option requires path to kubeconfig`)
+		return pClient, nil
 	}
 
-	agent, _ := kubernetes.GetAgentInClusterConfig()
-
-	return agent, nil
+	return nil, fmt.Errorf("required env vars not set for provisioner")
 }

+ 7 - 2
dashboard/src/main/home/infrastructure/InfrastructureList.tsx

@@ -81,8 +81,13 @@ const InfrastructureList = () => {
 
           return (
             <KindContainer>
-              <Icon src={integrationList[original.kind].icon} />
-              <Kind>{integrationList[original.kind].label}</Kind>
+              <Icon
+                src={
+                  integrationList[original.kind]?.icon ||
+                  integrationList["dockerhub"].icon
+                }
+              />
+              <Kind>{integrationList[original.kind]?.label}</Kind>
             </KindContainer>
           );
         },

+ 47 - 6
provisioner/client/client.go

@@ -1,6 +1,7 @@
 package client
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 	"net/http"
@@ -10,23 +11,63 @@ import (
 
 	"github.com/gorilla/schema"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/provisioner/pb"
+
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/metadata"
 )
 
 type Client struct {
 	BaseURL    string
+	Token      string
+	TokenID    uint
 	HTTPClient *http.Client
+	GRPCClient pb.ProvisionerClient
 }
 
-// TODO: add token-based mechanism to client and server
-func NewClient(baseURL string) *Client {
+func NewClient(baseURL, token string, tokenID uint) (*Client, error) {
+	parsedURL, err := url.Parse(baseURL)
+
+	if err != nil {
+		return nil, err
+	}
+
+	conn, err := grpc.Dial(fmt.Sprintf("%s:%s", parsedURL.Host, parsedURL.Port()), grpc.WithInsecure())
+
+	if err != nil {
+		return nil, err
+	}
+
+	gClient := pb.NewProvisionerClient(conn)
+
 	client := &Client{
 		BaseURL: baseURL,
+		Token:   token,
+		TokenID: tokenID,
 		HTTPClient: &http.Client{
 			Timeout: time.Minute,
 		},
+		GRPCClient: gClient,
+	}
+
+	return client, nil
+}
+
+func (c *Client) NewGRPCContext(workspaceID string) (context.Context, context.CancelFunc) {
+	headers := map[string]string{
+		"workspace_id": workspaceID,
+		"token":        c.Token,
 	}
 
-	return client
+	if c.TokenID != 0 {
+		headers["token_id"] = fmt.Sprintf("%d", c.TokenID)
+	}
+
+	header := metadata.New(headers)
+
+	ctx := metadata.NewOutgoingContext(context.Background(), header)
+
+	return context.WithCancel(ctx)
 }
 
 func (c *Client) getRequest(relPath string, data interface{}, response interface{}) error {
@@ -153,9 +194,9 @@ func (c *Client) sendRequest(req *http.Request, v interface{}) (*types.ExternalE
 	req.Header.Set("Content-Type", "application/json; charset=utf-8")
 	req.Header.Set("Accept", "application/json; charset=utf-8")
 
-	// if c.Token != "" {
-	// 	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.Token))
-	// }
+	if c.Token != "" {
+		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.Token))
+	}
 
 	res, err := c.HTTPClient.Do(req)
 

+ 186 - 0
provisioner/server/authn/authn.go

@@ -0,0 +1,186 @@
+package authn
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"strconv"
+	"strings"
+
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/provisioner/server/config"
+	"golang.org/x/crypto/bcrypt"
+)
+
+// AuthNFactory generates a middleware handler `AuthN`
+type AuthNStaticFactory struct {
+	config *config.Config
+}
+
+// NewAuthNStaticFactory returns an `AuthNStaticFactory` that uses the passed-in server
+// config
+func NewAuthNStaticFactory(
+	config *config.Config,
+) *AuthNStaticFactory {
+	return &AuthNStaticFactory{config}
+}
+
+// NewAuthenticated creates a new instance of `AuthN` that implements the http.Handler
+// interface.
+func (f *AuthNStaticFactory) NewAuthenticated(next http.Handler) http.Handler {
+	return &AuthNStatic{next, f.config}
+}
+
+// AuthNStatic implements the authentication middleware
+type AuthNStatic struct {
+	next   http.Handler
+	config *config.Config
+}
+
+// ServeHTTP calls next if the authentication token is valid,
+// or serves a forbidden error.
+func (authn *AuthNStatic) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	// first check for a static bearer token
+	err := authn.validateStaticTokenFromRequest(r)
+
+	// if the error is not an invalid auth error, the token was invalid, and we throw error
+	// forbidden. If the error was an invalid auth error, we look for a cookie.
+	if err == nil {
+		authn.next.ServeHTTP(w, r)
+		return
+	}
+
+	authn.sendForbiddenError(err, w, r)
+	return
+}
+
+// sendForbiddenError sends a 403 Forbidden error to the end user while logging a
+// specific error
+func (authn *AuthNStatic) sendForbiddenError(err error, w http.ResponseWriter, r *http.Request) {
+	reqErr := apierrors.NewErrForbidden(err)
+
+	apierrors.HandleAPIError(authn.config.Logger, authn.config.Alerter, w, r, reqErr, true)
+}
+
+var errInvalidToken = fmt.Errorf("authorization header exists, but token is not valid")
+var errInvalidAuthHeader = fmt.Errorf("invalid authorization header in request")
+
+// getTokenFromRequest finds an `Authorization` header of the form `Bearer <token>`,
+// and returns a valid token if it exists.
+func (authn *AuthNStatic) validateStaticTokenFromRequest(r *http.Request) error {
+	reqToken := r.Header.Get("Authorization")
+	splitToken := strings.Split(reqToken, "Bearer")
+
+	if len(splitToken) != 2 {
+		return errInvalidAuthHeader
+	}
+
+	reqToken = strings.TrimSpace(splitToken[1])
+
+	// check that request token matches static config token
+	if err := ValidateStaticToken(authn.config, reqToken); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func ValidateStaticToken(config *config.Config, reqToken string) error {
+	if reqToken != config.ProvisionerConf.StaticAuthToken {
+		return errInvalidToken
+	}
+
+	return nil
+}
+
+// AuthNPorterTokenFactory generates a middleware handler `AuthN`
+type AuthNPorterTokenFactory struct {
+	config *config.Config
+}
+
+// NewAuthNPorterTokenFactory returns an `AuthNPorterTokenFactory` that uses the passed-in server
+// config
+func NewAuthNPorterTokenFactory(
+	config *config.Config,
+) *AuthNPorterTokenFactory {
+	return &AuthNPorterTokenFactory{config}
+}
+
+// NewAuthenticated creates a new instance of `AuthN` that implements the http.Handler
+// interface.
+func (f *AuthNPorterTokenFactory) NewAuthenticated(next http.Handler) http.Handler {
+	return &AuthNStatic{next, f.config}
+}
+
+// AuthNPorterToken implements the authentication middleware
+type AuthNPorterToken struct {
+	next   http.Handler
+	config *config.Config
+}
+
+// ServeHTTP calls next if the authentication token is valid,
+// or serves a forbidden error.
+func (authn *AuthNPorterToken) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	// next we check for an issued token
+	ceToken, err := authn.getPorterTokenFromRequest(r)
+
+	if err == nil {
+		// attach ce token to context
+		// add the user to the context
+		ctx := r.Context()
+		ctx = context.WithValue(ctx, "ce_token", ceToken)
+		r = r.Clone(ctx)
+
+		authn.next.ServeHTTP(w, r)
+		return
+	}
+
+	authn.sendForbiddenError(err, w, r)
+	return
+}
+
+// sendForbiddenError sends a 403 Forbidden error to the end user while logging a
+// specific error
+func (authn *AuthNPorterToken) sendForbiddenError(err error, w http.ResponseWriter, r *http.Request) {
+	reqErr := apierrors.NewErrForbidden(err)
+
+	apierrors.HandleAPIError(authn.config.Logger, authn.config.Alerter, w, r, reqErr, true)
+}
+
+func (authn *AuthNPorterToken) getPorterTokenFromRequest(r *http.Request) (*models.CredentialsExchangeToken, error) {
+	porterToken := r.Header.Get("X-Porter-Token")
+
+	if porterToken == "" {
+		return nil, fmt.Errorf("X-Porter-Token header does not exist")
+	}
+
+	porterTokenID, err := strconv.ParseUint(r.Header.Get("X-Porter-Token-ID"), 10, 64)
+
+	if err != nil {
+		return nil, errInvalidToken
+	}
+
+	return ValidatePorterToken(authn.config, uint(porterTokenID), porterToken)
+}
+
+func ValidatePorterToken(config *config.Config, tokenID uint, token string) (*models.CredentialsExchangeToken, error) {
+	// read the access token in the header, check against DB
+	ceToken, err := config.Repo.CredentialsExchangeToken().ReadCredentialsExchangeToken(tokenID)
+
+	if err != nil {
+		return nil, err
+	}
+
+	// make sure the token is still valid and has not expired
+	if ceToken.IsExpired() {
+		return nil, fmt.Errorf("token is expired")
+	}
+
+	// make sure the token is correct
+	if err := bcrypt.CompareHashAndPassword([]byte(ceToken.Token), []byte(token)); err != nil {
+		return nil, fmt.Errorf("verify token failed: %s", err)
+	}
+
+	return ceToken, nil
+}

+ 2 - 0
provisioner/server/config/config.go

@@ -78,6 +78,8 @@ type ProvisionerConf struct {
 	TimeoutWrite time.Duration `env:"SERVER_TIMEOUT_WRITE,default=10s"`
 	TimeoutIdle  time.Duration `env:"SERVER_TIMEOUT_IDLE,default=15s"`
 
+	StaticAuthToken string `env:"STATIC_AUTH_TOKEN"`
+
 	SentryDSN string `env:"SENTRY_DSN"`
 	SentryEnv string `env:"SENTRY_ENV,default=dev"`
 

+ 1 - 17
provisioner/server/grpc/get_log.go

@@ -3,33 +3,17 @@ package grpc
 import (
 	"fmt"
 
-	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/provisioner/integrations/redis_stream"
 	"github.com/porter-dev/porter/provisioner/pb"
-	"google.golang.org/grpc/metadata"
 )
 
 func (s *ProvisionerServer) GetLog(infra *pb.Infra, server pb.Provisioner_GetLogServer) error {
-	// read metadata to get infra object
-	streamContext, ok := metadata.FromIncomingContext(server.Context())
+	name, ok := verifyStaticTokenContext(s.config, server.Context())
 
 	if !ok {
 		return fmt.Errorf("unauthorized")
 	}
 
-	workspaceID, exists := streamContext["workspace_id"]
-
-	if !exists || len(workspaceID) != 1 {
-		return fmt.Errorf("unauthorized")
-	}
-
-	// parse workspace id
-	name, err := models.ParseWorkspaceID(workspaceID[0])
-
-	if err != nil {
-		return err
-	}
-
 	modelInfra, err := s.config.Repo.Infra().ReadInfra(name.ProjectID, name.InfraID)
 
 	if err != nil {

+ 1 - 17
provisioner/server/grpc/get_state.go

@@ -3,35 +3,19 @@ package grpc
 import (
 	"fmt"
 
-	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/provisioner/integrations/redis_stream"
 	"github.com/porter-dev/porter/provisioner/pb"
-	"google.golang.org/grpc/metadata"
 
 	ptypes "github.com/porter-dev/porter/provisioner/types"
 )
 
 func (s *ProvisionerServer) GetStateUpdate(infra *pb.Infra, server pb.Provisioner_GetStateUpdateServer) error {
-	// read metadata to get infra object
-	streamContext, ok := metadata.FromIncomingContext(server.Context())
+	name, ok := verifyStaticTokenContext(s.config, server.Context())
 
 	if !ok {
 		return fmt.Errorf("unauthorized")
 	}
 
-	workspaceID, exists := streamContext["workspace_id"]
-
-	if !exists || len(workspaceID) != 1 {
-		return fmt.Errorf("unauthorized")
-	}
-
-	// parse workspace id
-	name, err := models.ParseWorkspaceID(workspaceID[0])
-
-	if err != nil {
-		return err
-	}
-
 	modelInfra, err := s.config.Repo.Infra().ReadInfra(name.ProjectID, name.InfraID)
 
 	if err != nil {

+ 89 - 0
provisioner/server/grpc/grpc.go

@@ -1,8 +1,14 @@
 package grpc
 
 import (
+	"context"
+	"strconv"
+
+	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/provisioner/pb"
+	"github.com/porter-dev/porter/provisioner/server/authn"
 	"github.com/porter-dev/porter/provisioner/server/config"
+	"google.golang.org/grpc/metadata"
 )
 
 type ProvisionerServer struct {
@@ -16,3 +22,86 @@ func NewProvisionerServer(config *config.Config) *ProvisionerServer {
 		config: config,
 	}
 }
+
+func verifyStaticTokenContext(config *config.Config, ctx context.Context) (*models.UniqueNameWithOperation, bool) {
+	streamContext, ok := metadata.FromIncomingContext(ctx)
+
+	if !ok {
+		return nil, false
+	}
+
+	tokenArr, exists := streamContext["token"]
+
+	if !exists || len(tokenArr) != 1 {
+		return nil, false
+	}
+
+	err := authn.ValidateStaticToken(config, tokenArr[0])
+
+	if err != nil {
+		return nil, false
+	}
+
+	workspaceID, exists := streamContext["workspace_id"]
+
+	if !exists || len(workspaceID) != 1 {
+		return nil, false
+	}
+
+	// parse workspace id
+	name, err := models.ParseWorkspaceID(workspaceID[0])
+
+	if err != nil {
+		return nil, false
+	}
+
+	return name, true
+}
+
+func verifyPorterTokenContext(config *config.Config, ctx context.Context) (*models.UniqueNameWithOperation, bool) {
+	streamContext, ok := metadata.FromIncomingContext(ctx)
+
+	if !ok {
+		return nil, false
+	}
+
+	// check token and token id
+	tokenIDArr, exists := streamContext["token_id"]
+
+	if !exists || len(tokenIDArr) != 1 {
+		return nil, false
+	}
+
+	tokenID, err := strconv.ParseUint(tokenIDArr[0], 10, 64)
+
+	if err != nil {
+		return nil, false
+	}
+
+	tokenArr, exists := streamContext["token"]
+
+	if !exists || len(tokenArr) != 1 {
+		return nil, false
+	}
+
+	_, err = authn.ValidatePorterToken(config, uint(tokenID), tokenArr[0])
+
+	if err != nil {
+		return nil, false
+	}
+
+	workspaceID, exists := streamContext["workspace_id"]
+
+	if !exists || len(workspaceID) != 1 {
+		return nil, false
+	}
+
+	// parse workspace id
+	name, err := models.ParseWorkspaceID(workspaceID[0])
+
+	if err != nil {
+		return nil, false
+	}
+
+	return name, true
+}

+ 1 - 18
provisioner/server/grpc/store_log.go

@@ -5,35 +5,18 @@ import (
 	"io"
 	"strings"
 
-	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/provisioner/integrations/redis_stream"
 	"github.com/porter-dev/porter/provisioner/pb"
 	"github.com/porter-dev/porter/provisioner/types"
-
-	"google.golang.org/grpc/metadata"
 )
 
 func (s *ProvisionerServer) StoreLog(stream pb.Provisioner_StoreLogServer) error {
-	// read metadata to get infra object
-	streamContext, ok := metadata.FromIncomingContext(stream.Context())
+	name, ok := verifyPorterTokenContext(s.config, stream.Context())
 
 	if !ok {
 		return fmt.Errorf("unauthorized")
 	}
 
-	workspaceID, exists := streamContext["workspace_id"]
-
-	if !exists || len(workspaceID) != 1 {
-		return fmt.Errorf("unauthorized")
-	}
-
-	// parse workspace id
-	name, err := models.ParseWorkspaceID(workspaceID[0])
-
-	if err != nil {
-		return err
-	}
-
 	infra, err := s.config.Repo.Infra().ReadInfra(name.ProjectID, name.InfraID)
 
 	if err != nil {

+ 2 - 25
provisioner/server/handlers/credentials/get_credentials_ee.go

@@ -5,7 +5,6 @@ package credentials
 import (
 	"fmt"
 	"net/http"
-	"strconv"
 
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
@@ -34,35 +33,13 @@ func NewCredentialsGetHandler(
 }
 
 func (c *CredentialsGetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	ceToken, _ := r.Context().Value("ce_token").(*models.CredentialsExchangeToken)
+
 	// read the request to get the token id and hashed token
 	req := &types.CredentialsExchangeRequest{}
 
-	// populate the request from the headers
-	req.CredExchangeToken = r.Header.Get("X-Porter-Token")
-	tokID, err := strconv.ParseUint(r.Header.Get("X-Porter-Token-ID"), 10, 64)
-
-	if err != nil {
-		apierrors.HandleAPIError(c.config.Logger, c.config.Alerter, w, r, apierrors.NewErrForbidden(err), true)
-		return
-	}
-
-	req.CredExchangeID = uint(tokID)
 	req.VaultToken = r.Header.Get("X-Vault-Token")
 
-	// read the access token in the header, check against DB
-	ceToken, err := c.config.Repo.CredentialsExchangeToken().ReadCredentialsExchangeToken(req.CredExchangeID)
-
-	if err != nil {
-		apierrors.HandleAPIError(c.config.Logger, c.config.Alerter, w, r, apierrors.NewErrForbidden(err), true)
-		return
-	}
-
-	// TODO: verify hashed token!!
-	if valid, err := verifyToken(req.CredExchangeToken, ceToken); !valid {
-		apierrors.HandleAPIError(c.config.Logger, c.config.Alerter, w, r, apierrors.NewErrForbidden(err), true)
-		return
-	}
-
 	resp := &types.CredentialsExchangeResponse{}
 	repo := c.config.Repo
 

+ 0 - 118
provisioner/server/handlers/desired.go

@@ -1,118 +0,0 @@
-package handlers
-
-// import (
-// 	"bytes"
-// 	"encoding/json"
-// 	"log"
-// 	"net/http"
-
-// 	"github.com/aws/aws-sdk-go/aws/awserr"
-// 	"github.com/aws/aws-sdk-go/service/s3"
-// 	"github.com/gin-gonic/gin"
-// 	"github.com/porter-dev/tf-http-backend/models"
-// )
-
-// // SetDesiredState is the POST handler that creates or
-// // updates the desired state for a particular provisioning job
-// func SetDesiredState(c *gin.Context) {
-// 	var desiredState models.DesiredTFState
-
-// 	err := c.BindJSON(&desiredState)
-// 	if err != nil {
-// 		log.Println("cannot read request body. error:", err.Error())
-
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-// 		return
-// 	}
-
-// 	orgID := c.Param("org")
-
-// 	data, err := json.Marshal(desiredState)
-// 	if err != nil {
-// 		log.Println("cannot marshal json. error:", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	err = s3Client.PutObject(orgID, "desired.json", data)
-// 	if err != nil {
-// 		log.Printf("cannot create desired state file. error: %s\n", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": "cannot create state file",
-// 		})
-
-// 		return
-// 	}
-
-// 	c.JSON(http.StatusCreated, gin.H{})
-// 	return
-// }
-
-// // GetDesiredState is the GET handler for returning
-// // the desired state. To be used mostly by the API server
-// func GetDesiredState(c *gin.Context) {
-// 	var desiredState models.DesiredTFState
-
-// 	orgID := c.Param("org")
-
-// 	reader, err := s3Client.GetObject(orgID, "desired.json")
-// 	if err != nil {
-// 		if aerr, ok := err.(awserr.Error); ok {
-// 			switch aerr.Code() {
-// 			case s3.ErrCodeNoSuchKey:
-// 				log.Println(aerr.Error())
-// 				c.JSON(http.StatusNotFound, gin.H{
-// 					"error": aerr.Error(),
-// 				})
-
-// 				return
-// 			default:
-// 				log.Println(aerr.Error())
-// 				c.JSON(http.StatusInternalServerError, gin.H{
-// 					"error": aerr.Error(),
-// 				})
-
-// 				return
-// 			}
-// 		}
-
-// 		log.Println("cannot cast to awserr. error:", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	var data bytes.Buffer
-// 	_, err = data.ReadFrom(reader)
-// 	if err != nil {
-// 		log.Println("cannot read from reader. error:", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	err = json.Unmarshal(data.Bytes(), &desiredState)
-// 	if err != nil {
-// 		log.Println("cannot unmarshal desired state. error:", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	c.JSON(http.StatusOK, gin.H{
-// 		"data": desiredState,
-// 	})
-
-// 	return
-// }

+ 0 - 28
provisioner/server/handlers/init.go

@@ -1,28 +0,0 @@
-package handlers
-
-// var s3Client *s3.Client
-// var redisClient *redis.Client
-// var eventProcessor *processor.EventProcessor
-
-// func init() {
-// 	BUCKET := os.Getenv("BUCKET")
-
-// 	// construct redis client, fallback to localhost
-// 	host := os.Getenv("REDIS_HOST")
-
-// 	if host == "" {
-// 		host = "localhost"
-// 	}
-
-// 	redisClient = redis.NewClient(
-// 		host,
-// 		"6379",
-// 		os.Getenv("REDIS_USER"),
-// 		os.Getenv("REDIS_PASS"),
-// 		0,
-// 	)
-
-// 	s3Client = s3.NewS3Client(BUCKET)
-
-// 	eventProcessor = processor.NewEventProcessor()
-// }

+ 0 - 144
provisioner/server/handlers/log.go

@@ -1,144 +0,0 @@
-package handlers
-
-// import (
-// 	"encoding/json"
-// 	"io/ioutil"
-// 	"log"
-// 	"net/http"
-// 	"strings"
-
-// 	"github.com/gin-gonic/gin"
-// 	"github.com/porter-dev/tf-http-backend/models"
-// 	"github.com/porter-dev/tf-http-backend/pkg/processor"
-// )
-
-// // StreamLogMsg is responsible for handling the POST of the
-// // log message from provisioner cli and pushing the content
-// // to a redis stream for showing on the UI. It is also
-// // responsible for testing and performing some additional
-// // tasks like specially handling error messages and resource
-// // provisioning completion events
-// func StreamLogMsg(c *gin.Context) {
-// 	var logMsg models.TFLogLine
-// 	orgID := c.Param("org")
-
-// 	err := c.BindJSON(&logMsg)
-// 	if err != nil {
-// 		log.Println("cannot get json from log msg body. error:", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	filterAndProcessError(orgID, &logMsg)
-
-// 	data, err := json.Marshal(logMsg)
-// 	if err != nil {
-// 		log.Println("cannot marshal to json for pushing to redis stream, error:", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	// check if type is error/created/destroyed,
-// 	// also push to global stream in that case
-// 	if logMsg.Type == models.ApplyErrored {
-// 		_, err = redisClient.AddToStream("global", map[string]interface{}{
-// 			"id":     orgID,
-// 			"status": "error",
-// 		})
-
-// 		if err != nil {
-// 			log.Println("cannot push to global stream, error:", err.Error())
-// 		}
-// 	} else if logMsg.Type == models.ChangeSummary {
-// 		if strings.Contains(logMsg.Message, "Destroy complete") {
-// 			_, err = redisClient.AddToStream("global", map[string]interface{}{
-// 				"id":     orgID,
-// 				"status": "destroyed",
-// 			})
-// 		}
-
-// 		if err != nil {
-// 			log.Println("cannot push to global stream, error:", err.Error())
-// 		}
-// 	}
-
-// 	// push to redis
-// 	id, err := redisClient.AddToStream(orgID, map[string]interface{}{
-// 		"data": data,
-// 	})
-
-// 	if err != nil {
-// 		log.Println("cannot add to redis stream. error:", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	c.JSON(http.StatusCreated, gin.H{
-// 		"id": id,
-// 	})
-
-// 	return
-// }
-
-// // StreamOutput is the handler responsible for
-// // posting the terraform output to the global stream
-// func StreamOutput(c *gin.Context) {
-// 	orgID := c.Param("org")
-
-// 	body, err := ioutil.ReadAll(c.Request.Body)
-// 	if err != nil {
-// 		log.Println("cannot read body. error:", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	c.Request.Body.Close()
-
-// 	id, err := redisClient.AddToStream("global", map[string]interface{}{
-// 		"id":     orgID,
-// 		"status": "created",
-// 		"data":   body,
-// 	})
-
-// 	if err != nil {
-// 		log.Println("cannot push output to global stream. error:", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	log.Println("successfully added output to global stream. id:", id)
-// 	c.JSON(http.StatusOK, gin.H{
-// 		"id": id,
-// 	})
-
-// 	return
-// }
-
-// func filterAndProcessError(orgID string, logMsg *models.TFLogLine) {
-// 	// process log message to filter out error
-// 	err := eventProcessor.Filter(&processor.Event{
-// 		OrgID:     orgID,
-// 		TFLogLine: logMsg,
-// 	})
-
-// 	if err != nil {
-// 		log.Printf("cannot mark errored resource %s in desired state\nerror: %s\n",
-// 			logMsg.Hook.Resource.Resource,
-// 			err.Error())
-// 	}
-// }

+ 0 - 100
provisioner/server/handlers/tfstate.go

@@ -1,100 +0,0 @@
-package handlers
-
-// import (
-// 	"bytes"
-// 	"encoding/json"
-// 	"log"
-// 	"net/http"
-
-// 	"github.com/gin-gonic/gin"
-// 	"github.com/porter-dev/tf-http-backend/models"
-// )
-
-// func GetState(c *gin.Context) {
-// 	orgID := c.Param("org")
-
-// 	stateFile, err := s3Client.GetObject(orgID, "default.tfstate")
-// 	if err != nil {
-// 		log.Println("cannot find state file. error: ", err.Error())
-// 		log.Println("must be an init operation")
-
-// 		c.JSON(http.StatusNotFound, gin.H{
-// 			"error": err.Error(),
-// 		})
-// 		return
-// 	}
-
-// 	defer stateFile.Close()
-
-// 	var data bytes.Buffer
-
-// 	readCount, err := data.ReadFrom(stateFile)
-// 	if err != nil {
-// 		log.Println("cannot read state file. error: ", err.Error())
-
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	log.Println("read", readCount, "bytes")
-// 	if readCount == 0 {
-// 		// empty state file, delete it
-// 		log.Println("detected empty state file, deleting for sanity")
-// 		s3Client.DeleteObject(orgID, "default.tfstate")
-
-// 		c.JSON(http.StatusNotFound, gin.H{})
-// 		return
-// 	}
-
-// 	body := make(gin.H)
-
-// 	err = json.Unmarshal(data.Bytes(), &body)
-// 	if err != nil {
-// 		log.Println("error unmarshaling the state. error: ", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": err.Error(),
-// 		})
-
-// 		return
-// 	}
-
-// 	c.JSON(http.StatusOK, body)
-// }
-
-// func UpdateState(c *gin.Context) {
-// 	log.Println("updating/creating state")
-
-// 	var state models.TFState
-// 	err := c.BindJSON(&state)
-// 	if err != nil {
-// 		log.Fatalln("cannot read request body", err)
-// 	}
-
-// 	orgID := c.Param("org")
-
-// 	data, err := json.Marshal(state)
-// 	if err != nil {
-// 		log.Printf("cannot marshal json. error: %s\n", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": "cannot marshal JSON",
-// 		})
-
-// 		return
-// 	}
-
-// 	err = s3Client.PutObject(orgID, "default.tfstate", data)
-// 	if err != nil {
-// 		log.Printf("cannot create state file. error: %s\n", err.Error())
-// 		c.JSON(http.StatusInternalServerError, gin.H{
-// 			"error": "cannot create state file",
-// 		})
-
-// 		return
-// 	}
-
-// 	c.JSON(http.StatusCreated, gin.H{})
-// 	return
-// }

+ 23 - 8
provisioner/server/router/router.go

@@ -3,6 +3,7 @@ package router
 import (
 	"github.com/go-chi/chi"
 	"github.com/porter-dev/porter/api/server/router/middleware"
+	"github.com/porter-dev/porter/provisioner/server/authn"
 	"github.com/porter-dev/porter/provisioner/server/authz"
 	"github.com/porter-dev/porter/provisioner/server/config"
 	"github.com/porter-dev/porter/provisioner/server/handlers/credentials"
@@ -18,26 +19,40 @@ func NewAPIRouter(config *config.Config) *chi.Mux {
 		r.Use(middleware.ContentTypeJSON)
 
 		// create new group for raw state endpoints which use workspace authz middleware
+		staticTokenAuth := authn.NewAuthNStaticFactory(config)
+		porterTokenAuth := authn.NewAuthNPorterTokenFactory(config)
 		workspaceAuth := authz.NewWorkspaceScopedFactory(config)
 
 		r.Group(func(r chi.Router) {
-			r.Use(workspaceAuth.Middleware)
+			// This group is meant to be called via a provisioner pod
+			r.Group(func(r chi.Router) {
+				r.Use(porterTokenAuth.NewAuthenticated)
+				r.Use(workspaceAuth.Middleware)
 
-			r.Method("GET", "/{workspace_id}/tfstate", state.NewRawStateGetHandler(config))
-			r.Method("POST", "/{workspace_id}/tfstate", state.NewRawStateUpdateHandler(config))
+				r.Method("GET", "/{workspace_id}/tfstate", state.NewRawStateGetHandler(config))
+				r.Method("POST", "/{workspace_id}/tfstate", state.NewRawStateUpdateHandler(config))
+				r.Method("POST", "/{workspace_id}/resource", state.NewCreateResourceHandler(config))
+				r.Method("DELETE", "/{workspace_id}/resource", state.NewDeleteResourceHandler(config))
+				r.Method("POST", "/{workspace_id}/error", state.NewReportErrorHandler(config))
+				r.Method("GET", "/{workspace_id}/credentials", credentials.NewCredentialsGetHandler(config))
+			})
 
-			r.Method("POST", "/{workspace_id}/resource", state.NewCreateResourceHandler(config))
-			r.Method("DELETE", "/{workspace_id}/resource", state.NewDeleteResourceHandler(config))
-			r.Method("POST", "/{workspace_id}/error", state.NewReportErrorHandler(config))
-			r.Method("GET", "/{workspace_id}/logs", state.NewLogsGetHandler(config))
-			r.Method("GET", "/{workspace_id}/credentials", credentials.NewCredentialsGetHandler(config))
+			// This group is meant to be called via the API server
+			r.Group(func(r chi.Router) {
+				r.Use(staticTokenAuth.NewAuthenticated)
+				r.Use(workspaceAuth.Middleware)
+
+				r.Method("GET", "/{workspace_id}/logs", state.NewLogsGetHandler(config))
+			})
 		})
 
 		// use project and infra-scoped middleware
 		projectAuth := authz.NewProjectScopedFactory(config)
 		infraAuth := authz.NewInfraScopedFactory(config)
 
+		// This group is meant to be called via the API server
 		r.Group(func(r chi.Router) {
+			r.Use(staticTokenAuth.NewAuthenticated)
 			r.Use(projectAuth.Middleware)
 			r.Use(infraAuth.Middleware)