diff --git a/Makefile b/Makefile index 5bf74802..d1f3f9c1 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,6 @@ commit := $(shell git rev-list -1 HEAD) version := $(shell git describe --tags --dirty=-dirty) pre := github.com/valocode/bubbly - - all: build .PHONY: build @@ -40,7 +38,7 @@ display-coverage: test-coverage test-report: go test -coverprofile=coverage.txt -covermode=atomic -json ./... > test_report.json -# The integration tests depend on Bubbly Server and its Store (currently Postgres) being accessible. +# The integration tests depend on Bubbly Server and its Store (currently Postgres) being accessible. # This is what the env variables in the beginning of this Makefile are for. # The count flag prevents Go from caching test results as they are dependent on the DB content. test-integration: @@ -59,8 +57,7 @@ cleanup: docker-compose down # Project is CI-enabled with Github Actions. You can run CI locally -# using act (https://github.com/nektos/act). +# using act (https://github.com/nektos/act). # There are some caveats, but the following target should work: -act: +act: act -P ubuntu-latest=golang:latest --env-file act.env -j simple - diff --git a/README.md b/README.md index cfdddd75..b60aba8d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - ![bubbly-logo](/docs/static/img/bubbly-blue-wide.png) > Release Readiness in a Bubble. diff --git a/adapter/adapter.go b/adapter/adapter.go index be4cf74a..9611f139 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -14,8 +14,8 @@ import ( "github.com/open-policy-agent/conftest/parser" "github.com/open-policy-agent/opa/rego" "github.com/valocode/bubbly/client" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/store/api" ) @@ -50,7 +50,7 @@ type ( // RunFromID runs an adapter by the given id. // This will get the relevant adapter (from filesystem or remotely) and perform // the necessary rego queries and publish that data to the release. -func RunFromID(bCtx *env.BubblyContext, id string, opts ...func(r *runOptions)) (*AdapterResult, error) { +func RunFromID(bCtx *config.BubblyConfig, id string, opts ...func(r *runOptions)) (*AdapterResult, error) { // // Get the adapter module to run // @@ -254,7 +254,7 @@ func ParseAdpaterID(id string) (string, string, error) { // a path to a local file, the name of an adapter (which could lead to a local // an adapter file in the bubbly directory), or a name:tag for which the adapter // is fetched remotely. -func adapterFromID(bCtx *env.BubblyContext, id string) (string, error) { +func adapterFromID(bCtx *config.BubblyConfig, id string) (string, error) { switch strings.Count(id, ":") { case 0: // It could be a local adapter, or remote with a default tag. diff --git a/auth/auth.go b/auth/auth.go new file mode 100644 index 00000000..3f45936a --- /dev/null +++ b/auth/auth.go @@ -0,0 +1,172 @@ +package auth + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/coreos/go-oidc/v3/oidc" + "github.com/labstack/echo/v4" + "golang.org/x/oauth2" +) + +// ContextKey is used to store the auth context value in the request context +type ContextKey string + +var contextKey ContextKey = "AUTH_CONTEXT" + +// Session is used to store authentication data as a context value +type Session struct { + UserID string `json:"sub"` + Email string `json:"email"` +} + +// Config defines the configuration used for the authentication middleware and +// the token verifier +type Config struct { + ProviderURL string + ClientID string + ClientSecret string + RedirectURL string + + Scopes []string + Enabled bool +} + +// Provider stores the runtime configuration and verifier for the authorizer +// and the middleware +type Provider struct { + oidc *oidc.Provider + oidcConfig *oauth2.Config + Verifier *oidc.IDTokenVerifier + Disabled bool +} + +// NewProvider creates a new OIDC provider instance +func NewProvider(ctx context.Context, conf *Config) (*Provider, error) { + // TODO: figure out better way to handle auth disabled + if !conf.Enabled { + return &Provider{Disabled: !conf.Enabled}, nil + } + provider, err := oidc.NewProvider(ctx, conf.ProviderURL) + if err != nil { + return nil, fmt.Errorf("creating OIDC provider: %w", err) + } + + cf := &oauth2.Config{ + ClientID: conf.ClientID, + ClientSecret: conf.ClientSecret, + RedirectURL: conf.RedirectURL, + Scopes: conf.Scopes, + Endpoint: provider.Endpoint(), + } + + return &Provider{ + oidc: provider, + oidcConfig: cf, + Verifier: provider.Verifier(&oidc.Config{ClientID: cf.ClientID}), + Disabled: !conf.Enabled, + }, nil +} + +// AuthorizeHandler validates the authentication code from the client and +// returns a access token as a response +func (p *Provider) AuthorizeHandler(w http.ResponseWriter, r *http.Request) { + if p.Disabled { + http.Error(w, "server authentication disabled", http.StatusMethodNotAllowed) + return + } + ctx := r.Context() + oauth2Token, err := p.oidcConfig.Exchange(ctx, r.URL.Query().Get("code")) + if err != nil { + http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusUnauthorized) + return + } + + rawIDToken, ok := oauth2Token.Extra("access_token").(string) + if !ok { + http.Error(w, "No id_token field in oauth2 token.", http.StatusUnauthorized) + return + } + idToken, err := p.Verifier.Verify(ctx, rawIDToken) + if err != nil { + http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusUnauthorized) + return + } + + resp := struct { + OAuth2Token *oauth2.Token + IDTokenClaims *json.RawMessage + }{oauth2Token, new(json.RawMessage)} + + if err := idToken.Claims(&resp.IDTokenClaims); err != nil { + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } + data, err := json.Marshal(resp) + if err != nil { + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } + w.Write(data) +} + +// EchoAuthorizeHandler returns a wrapped http handler for the echo router +func (p *Provider) EchoAuthorizeHandler() echo.HandlerFunc { + return echo.WrapHandler(http.HandlerFunc(p.AuthorizeHandler)) +} + +// Middleware reads and verifies the bearer tokens and injects the extracted +// data to the request context +func (p *Provider) Middleware(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + if p.Disabled { + session := &Session{ + UserID: "ANONYMOUS", + Email: "noemail", + } + ctx = context.WithValue(ctx, contextKey, session) + r = r.WithContext(ctx) + + h.ServeHTTP(w, r) + return + } + + rawIDToken := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ") + idToken, err := p.Verifier.Verify(ctx, rawIDToken) + if err != nil { + http.Error(w, "Failed to verify ID Token", http.StatusUnauthorized) + return + } + + session := &Session{} + + if err := idToken.Claims(&session); err != nil { + http.Error(w, "Invalid token claims", http.StatusUnauthorized) + return + } + + ctx = context.WithValue(ctx, contextKey, session) + r = r.WithContext(ctx) + + h.ServeHTTP(w, r) + }) +} + +// EchoMiddleware returns an instance of the Middleware wrapped for Echo +func (p *Provider) EchoMiddleware() echo.MiddlewareFunc { + return echo.WrapMiddleware(p.Middleware) +} + +// GetSession is a helper function to return the session struct stored in +// the request context +func GetSession(ctx context.Context) *Session { + session, ok := ctx.Value(contextKey).(*Session) + if !ok { + return nil + } + return session +} diff --git a/client/client.go b/client/client.go index 72acdb22..5fb2f70f 100644 --- a/client/client.go +++ b/client/client.go @@ -4,20 +4,20 @@ import ( "fmt" "net/http" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/store/api" ) -func CreateRelease(bCtx *env.BubblyContext, req *api.ReleaseCreateRequest) error { +func CreateRelease(bCtx *config.BubblyConfig, req *api.ReleaseCreateRequest) error { return handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), WithPayload(req), WithRequestURL("releases"), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), WithPayload(req), WithRequestURL("releases"), ) } -func GetReleases(bCtx *env.BubblyContext, req *api.ReleaseGetRequest) (*api.ReleaseGetResponse, error) { +func GetReleases(bCtx *config.BubblyConfig, req *api.ReleaseGetRequest) (*api.ReleaseGetResponse, error) { var r api.ReleaseGetResponse if err := handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodGet), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodGet), WithResponse(&r), WithRequestURL("releases"), WithQueryParamsStruct(req), ); err != nil { return nil, err @@ -25,24 +25,24 @@ func GetReleases(bCtx *env.BubblyContext, req *api.ReleaseGetRequest) (*api.Rele return &r, nil } -func SaveCodeScan(bCtx *env.BubblyContext, req *api.CodeScanRequest) error { +func SaveCodeScan(bCtx *config.BubblyConfig, req *api.CodeScanRequest) error { return handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), WithPayload(req), WithRequestURL("codescans"), ) } -func SaveTestRun(bCtx *env.BubblyContext, req *api.TestRunRequest) error { +func SaveTestRun(bCtx *config.BubblyConfig, req *api.TestRunRequest) error { return handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), WithPayload(req), WithRequestURL("testruns"), ) } -func GetAdapters(bCtx *env.BubblyContext, req *api.AdapterGetRequest) (*api.AdapterGetResponse, error) { +func GetAdapters(bCtx *config.BubblyConfig, req *api.AdapterGetRequest) (*api.AdapterGetResponse, error) { var a api.AdapterGetResponse if err := handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodGet), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodGet), WithRequestURL("adapters"), WithQueryParamsStruct(req), WithResponse(&a), ); err != nil { return nil, err @@ -50,17 +50,17 @@ func GetAdapters(bCtx *env.BubblyContext, req *api.AdapterGetRequest) (*api.Adap return &a, nil } -func SaveAdapter(bCtx *env.BubblyContext, req *api.AdapterSaveRequest) error { +func SaveAdapter(bCtx *config.BubblyConfig, req *api.AdapterSaveRequest) error { return handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), WithPayload(req), WithRequestURL("adapters"), ) } -func GetPolicies(bCtx *env.BubblyContext, req *api.ReleasePolicyGetRequest) (*api.ReleasePolicyGetResponse, error) { +func GetPolicies(bCtx *config.BubblyConfig, req *api.ReleasePolicyGetRequest) (*api.ReleasePolicyGetResponse, error) { var r api.ReleasePolicyGetResponse if err := handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodGet), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodGet), WithRequestURL("policies"), WithQueryParamsStruct(req), WithResponse(&r), ); err != nil { return nil, err @@ -68,24 +68,24 @@ func GetPolicies(bCtx *env.BubblyContext, req *api.ReleasePolicyGetRequest) (*ap return &r, nil } -func SavePolicy(bCtx *env.BubblyContext, req *api.ReleasePolicySaveRequest) error { +func SavePolicy(bCtx *config.BubblyConfig, req *api.ReleasePolicySaveRequest) error { return handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), WithPayload(req), WithRequestURL("policies"), ) } -func SetPolicy(bCtx *env.BubblyContext, req *api.ReleasePolicyUpdateRequest) error { +func SetPolicy(bCtx *config.BubblyConfig, req *api.ReleasePolicyUpdateRequest) error { return handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodPut), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodPut), WithPayload(req), WithRequestURL(fmt.Sprintf("policies/%d", *req.ID)), ) } -func GetVulnerabilityReviews(bCtx *env.BubblyContext, req *api.VulnerabilityReviewGetRequest) (*api.VulnerabilityReviewGetResponse, error) { +func GetVulnerabilityReviews(bCtx *config.BubblyConfig, req *api.VulnerabilityReviewGetRequest) (*api.VulnerabilityReviewGetResponse, error) { var r api.VulnerabilityReviewGetResponse if err := handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodGet), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodGet), WithRequestURL("vulnerabilityreviews"), WithQueryParamsStruct(req), WithResponse(&r), ); err != nil { return nil, err @@ -93,16 +93,16 @@ func GetVulnerabilityReviews(bCtx *env.BubblyContext, req *api.VulnerabilityRevi return &r, nil } -func SaveVulnerabilityReview(bCtx *env.BubblyContext, req *api.VulnerabilityReviewSaveRequest) error { +func SaveVulnerabilityReview(bCtx *config.BubblyConfig, req *api.VulnerabilityReviewSaveRequest) error { return handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodPost), WithPayload(req), WithRequestURL("vulnerabilityreviews"), ) } -func UpdateVulnerabilityReview(bCtx *env.BubblyContext, req *api.VulnerabilityReviewUpdateRequest) error { +func UpdateVulnerabilityReview(bCtx *config.BubblyConfig, req *api.VulnerabilityReviewUpdateRequest) error { return handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodPut), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodPut), WithPayload(req), WithRequestURL(fmt.Sprintf("vulnerabilityreviews/%d", *req.ID)), ) } diff --git a/client/events.go b/client/events.go index 3b83dfe6..652e4b63 100644 --- a/client/events.go +++ b/client/events.go @@ -3,14 +3,14 @@ package client import ( "net/http" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/store/api" ) -func GetEvents(bCtx *env.BubblyContext, req *api.EventGetRequest) (*api.EventGetResponse, error) { +func GetEvents(bCtx *config.BubblyConfig, req *api.EventGetRequest) (*api.EventGetResponse, error) { var r api.EventGetResponse if err := handleRequest( - WithBubblyContext(bCtx), WithAPIV1(true), WithMethod(http.MethodGet), + WithBubblyConfig(bCtx), WithAPIV1(true), WithMethod(http.MethodGet), WithResponse(&r), WithRequestURL("events"), WithQueryParamsStruct(req), ); err != nil { return nil, err diff --git a/client/query.go b/client/query.go index d4a168ab..4c2573f3 100644 --- a/client/query.go +++ b/client/query.go @@ -3,10 +3,10 @@ package client import ( "net/http" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" ) -func RunQuery(bCtx *env.BubblyContext, query string) (map[string]interface{}, error) { +func RunQuery(bCtx *config.BubblyConfig, query string) (map[string]interface{}, error) { var resp map[string]interface{} req := struct { @@ -15,7 +15,7 @@ func RunQuery(bCtx *env.BubblyContext, query string) (map[string]interface{}, er Query: &query, } if err := handleRequest( - WithBubblyContext(bCtx), WithGraphQL(true), WithMethod(http.MethodPost), + WithBubblyConfig(bCtx), WithGraphQL(true), WithMethod(http.MethodPost), WithPayload(req), WithRequestURL("graphql"), WithResponse(&resp), ); err != nil { return nil, err diff --git a/client/request.go b/client/request.go index f89d0218..a24d5346 100644 --- a/client/request.go +++ b/client/request.go @@ -9,11 +9,11 @@ import ( "net/http" "github.com/labstack/echo/v4" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" ) type request struct { - bCtx *env.BubblyContext + bCtx *config.BubblyConfig apiV1 bool graphql bool method string @@ -24,7 +24,7 @@ type request struct { payload interface{} } -func WithBubblyContext(bCtx *env.BubblyContext) func(r *request) { +func WithBubblyConfig(bCtx *config.BubblyConfig) func(r *request) { return func(r *request) { r.bCtx = bCtx } diff --git a/cmd/adapter/adapter.go b/cmd/adapter/adapter.go index cd59e238..d43201cf 100644 --- a/cmd/adapter/adapter.go +++ b/cmd/adapter/adapter.go @@ -5,13 +5,12 @@ import ( "github.com/valocode/bubbly/cmd/adapter/save" "github.com/valocode/bubbly/cmd/adapter/test" "github.com/valocode/bubbly/cmd/adapter/view" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/spf13/cobra" ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { cmd := &cobra.Command{ Use: "adapter ", Short: "Manage bubbly adapters", diff --git a/cmd/adapter/run/run.go b/cmd/adapter/run/run.go index a1a0fc47..c1a4d080 100644 --- a/cmd/adapter/run/run.go +++ b/cmd/adapter/run/run.go @@ -8,7 +8,7 @@ import ( "github.com/labstack/gommon/color" "github.com/valocode/bubbly/adapter" "github.com/valocode/bubbly/client" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/release" "github.com/valocode/bubbly/store/api" @@ -33,8 +33,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { var ( // runner runner.AdapterRun trace bool @@ -93,7 +92,6 @@ func New(bCtx *env.BubblyContext) *cobra.Command { return err } } - switch output { case "json": printJSON(result) diff --git a/cmd/adapter/save/save.go b/cmd/adapter/save/save.go index 06954c46..b3259138 100644 --- a/cmd/adapter/save/save.go +++ b/cmd/adapter/save/save.go @@ -8,8 +8,8 @@ import ( "github.com/valocode/bubbly/adapter" "github.com/valocode/bubbly/client" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/store/api" "github.com/spf13/cobra" @@ -33,8 +33,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { var ( name string tag string diff --git a/cmd/adapter/test/test.go b/cmd/adapter/test/test.go index c0fa87cb..91ddf94e 100644 --- a/cmd/adapter/test/test.go +++ b/cmd/adapter/test/test.go @@ -10,7 +10,7 @@ import ( "github.com/labstack/gommon/color" "github.com/open-policy-agent/opa/tester" "github.com/valocode/bubbly/adapter" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/spf13/cobra" @@ -33,8 +33,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { var ( trace bool output string diff --git a/cmd/adapter/view/view.go b/cmd/adapter/view/view.go index 2d5c9f4a..4cc4bca0 100644 --- a/cmd/adapter/view/view.go +++ b/cmd/adapter/view/view.go @@ -5,7 +5,7 @@ import ( "github.com/valocode/bubbly/adapter" "github.com/valocode/bubbly/client" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/store/api" "github.com/spf13/cobra" @@ -29,8 +29,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { cmd := &cobra.Command{ Use: "view name[:tag] [flags]", Short: "View a Bubbly adapter", diff --git a/cmd/demo/demo.go b/cmd/demo/demo.go index 9c36bec2..28b40ec8 100644 --- a/cmd/demo/demo.go +++ b/cmd/demo/demo.go @@ -3,7 +3,7 @@ package demo import ( "fmt" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/server" "github.com/valocode/bubbly/store" @@ -34,15 +34,13 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { cmd := &cobra.Command{ Use: "demo [flags]", Short: "Start the bubbly server in demo mode", Long: cmdLong + "\n\n", Example: cmdExamples, RunE: func(cmd *cobra.Command, args []string) error { - fmt.Println("Initializing store...") s, err := store.New(bCtx) if err != nil { diff --git a/cmd/events/events.go b/cmd/events/events.go index afa7561a..31b97db6 100644 --- a/cmd/events/events.go +++ b/cmd/events/events.go @@ -6,8 +6,8 @@ import ( "github.com/ryanuber/columnize" "github.com/valocode/bubbly/client" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/store/api" "github.com/spf13/cobra" @@ -33,8 +33,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { var req api.EventGetRequest cmd := &cobra.Command{ @@ -80,7 +79,6 @@ func printEvents(dbEvents []*ent.Event) { eventLines = append(eventLines, fmt.Sprintf( "%s | %s | %s | %s", msg, "", "", "", )) - } } } diff --git a/cmd/policy/list/list.go b/cmd/policy/list/list.go index 98e28a70..c61c0609 100644 --- a/cmd/policy/list/list.go +++ b/cmd/policy/list/list.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/valocode/bubbly/client" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/store/api" "github.com/spf13/cobra" @@ -28,11 +28,8 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - - var ( - name string - ) +func New(bCtx *config.BubblyConfig) *cobra.Command { + var name string cmd := &cobra.Command{ Use: "list [flags]", Short: "List Bubbly policies", diff --git a/cmd/policy/policy.go b/cmd/policy/policy.go index fea0719a..8e8569fe 100644 --- a/cmd/policy/policy.go +++ b/cmd/policy/policy.go @@ -5,13 +5,12 @@ import ( "github.com/valocode/bubbly/cmd/policy/save" "github.com/valocode/bubbly/cmd/policy/set" "github.com/valocode/bubbly/cmd/policy/view" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/spf13/cobra" ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { cmd := &cobra.Command{ Use: "policy ", Short: "Manage bubbly policies", diff --git a/cmd/policy/save/save.go b/cmd/policy/save/save.go index f7b00296..3bb9f447 100644 --- a/cmd/policy/save/save.go +++ b/cmd/policy/save/save.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/valocode/bubbly/client" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/policy" "github.com/valocode/bubbly/store/api" @@ -33,8 +33,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { var ( name string setProjects []string diff --git a/cmd/policy/set/set.go b/cmd/policy/set/set.go index 9c3fa161..d5efc4fa 100644 --- a/cmd/policy/set/set.go +++ b/cmd/policy/set/set.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/valocode/bubbly/client" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/store/api" "github.com/spf13/cobra" @@ -28,8 +28,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { var ( setProjects []string notSetProjects []string diff --git a/cmd/policy/view/view.go b/cmd/policy/view/view.go index 55f7f79e..8efb84fb 100644 --- a/cmd/policy/view/view.go +++ b/cmd/policy/view/view.go @@ -5,7 +5,7 @@ import ( "strings" "github.com/valocode/bubbly/client" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/store/api" "github.com/spf13/cobra" @@ -29,11 +29,8 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - - var ( - withAffects bool - ) +func New(bCtx *config.BubblyConfig) *cobra.Command { + var withAffects bool cmd := &cobra.Command{ Use: "view name [flags]", diff --git a/cmd/query/query.go b/cmd/query/query.go index 3f20c11d..5c3719cc 100644 --- a/cmd/query/query.go +++ b/cmd/query/query.go @@ -7,7 +7,7 @@ import ( "os" "github.com/valocode/bubbly/client" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/spf13/cobra" @@ -32,8 +32,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { cmd := &cobra.Command{ Use: "query [flags]", Short: "Run a GraphQL query against the Bubbly server", diff --git a/cmd/release/create/create.go b/cmd/release/create/create.go index 3121ecdd..9aecfcd2 100644 --- a/cmd/release/create/create.go +++ b/cmd/release/create/create.go @@ -6,7 +6,7 @@ import ( "github.com/fatih/color" "github.com/ryanuber/columnize" "github.com/valocode/bubbly/client" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/release" "github.com/spf13/cobra" @@ -30,8 +30,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { cmd := &cobra.Command{ Use: "create [flags]", Short: "Create a bubbly release", diff --git a/cmd/release/release.go b/cmd/release/release.go index a3141641..b114a3f9 100644 --- a/cmd/release/release.go +++ b/cmd/release/release.go @@ -3,13 +3,12 @@ package release import ( "github.com/valocode/bubbly/cmd/release/create" "github.com/valocode/bubbly/cmd/release/view" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/spf13/cobra" ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { cmd := &cobra.Command{ Use: "release ", Short: "Manage bubbly releases", diff --git a/cmd/release/view/view.go b/cmd/release/view/view.go index ddbd8ebc..49e4e070 100644 --- a/cmd/release/view/view.go +++ b/cmd/release/view/view.go @@ -6,7 +6,7 @@ import ( "github.com/ryanuber/columnize" "github.com/valocode/bubbly/client" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/release" "github.com/valocode/bubbly/store/api" @@ -31,8 +31,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { var ( commit string repo string diff --git a/cmd/root.go b/cmd/root.go index a83e9cd4..f4ccfd28 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -15,11 +15,10 @@ import ( "github.com/valocode/bubbly/cmd/version" "github.com/valocode/bubbly/cmd/vulnerabilityreview" "github.com/valocode/bubbly/config" - "github.com/valocode/bubbly/env" ) // NewCmdRoot creates a new cobra.Command representing "bubbly" -func NewCmdRoot(bCtx *env.BubblyContext) *cobra.Command { +func NewCmdRoot(bCtx *config.BubblyConfig) *cobra.Command { // cmd represents the apply command cmd := &cobra.Command{ Use: "bubbly", diff --git a/cmd/server/server.go b/cmd/server/server.go index ad112317..9c282245 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/valocode/bubbly/config" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/server" "github.com/spf13/cobra" @@ -31,8 +30,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { var dbProvider string cmd := &cobra.Command{ @@ -47,7 +45,7 @@ func New(bCtx *env.BubblyContext) *cobra.Command { s, err := server.New(bCtx) if err != nil { - return fmt.Errorf("initializing store: %w", err) + return fmt.Errorf("initializing server: %w", err) } if err := s.Start(); err != nil { return err diff --git a/cmd/version/version.go b/cmd/version/version.go index 9a3e9e35..2c9e4f3a 100644 --- a/cmd/version/version.go +++ b/cmd/version/version.go @@ -4,11 +4,11 @@ import ( "fmt" "github.com/spf13/cobra" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" ) // New creates a new Cobra command -func New(bCtx *env.BubblyContext) *cobra.Command { +func New(bCtx *config.BubblyConfig) *cobra.Command { return &cobra.Command{ Use: "version", Short: "Show the Bubbly version", diff --git a/cmd/vulnerabilityreview/list/list.go b/cmd/vulnerabilityreview/list/list.go index ac0a9aa8..e38450ac 100644 --- a/cmd/vulnerabilityreview/list/list.go +++ b/cmd/vulnerabilityreview/list/list.go @@ -5,7 +5,7 @@ import ( "github.com/ryanuber/columnize" "github.com/valocode/bubbly/client" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/store/api" "github.com/spf13/cobra" @@ -30,8 +30,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { cmd := &cobra.Command{ Use: "list [flags]", Short: "List vulnerability reviews", diff --git a/cmd/vulnerabilityreview/save/save.go b/cmd/vulnerabilityreview/save/save.go index 44aa2d78..fa5ca8ae 100644 --- a/cmd/vulnerabilityreview/save/save.go +++ b/cmd/vulnerabilityreview/save/save.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/valocode/bubbly/client" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent" "github.com/valocode/bubbly/ent/vulnerabilityreview" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/store/api" "github.com/spf13/cobra" @@ -33,8 +33,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { var ( note string decision string diff --git a/cmd/vulnerabilityreview/set/set.go b/cmd/vulnerabilityreview/set/set.go index ae5b17ef..0d270c23 100644 --- a/cmd/vulnerabilityreview/set/set.go +++ b/cmd/vulnerabilityreview/set/set.go @@ -5,9 +5,9 @@ import ( "strconv" "github.com/valocode/bubbly/client" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent" "github.com/valocode/bubbly/ent/vulnerabilityreview" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/store/api" "github.com/spf13/cobra" @@ -33,8 +33,7 @@ var ( ) ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { var ( note string decision string diff --git a/cmd/vulnerabilityreview/vulnerabilityreviews.go b/cmd/vulnerabilityreview/vulnerabilityreviews.go index 803a9379..ab3ed03e 100644 --- a/cmd/vulnerabilityreview/vulnerabilityreviews.go +++ b/cmd/vulnerabilityreview/vulnerabilityreviews.go @@ -4,13 +4,12 @@ import ( "github.com/valocode/bubbly/cmd/vulnerabilityreview/list" "github.com/valocode/bubbly/cmd/vulnerabilityreview/save" "github.com/valocode/bubbly/cmd/vulnerabilityreview/set" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/spf13/cobra" ) -func New(bCtx *env.BubblyContext) *cobra.Command { - +func New(bCtx *config.BubblyConfig) *cobra.Command { cmd := &cobra.Command{ Aliases: []string{"vr"}, Use: "vulnerabilityreview ", diff --git a/env/context.go b/config/bubbly.go similarity index 55% rename from env/context.go rename to config/bubbly.go index 614df10b..1173a1b1 100644 --- a/env/context.go +++ b/config/bubbly.go @@ -1,55 +1,57 @@ -package env +package config import ( "embed" "os" "github.com/rs/zerolog" - "github.com/valocode/bubbly/config" + "github.com/valocode/bubbly/auth" ) -// BubblyContext holds global bubbly state that is required to be injected into +// BubblyConfig holds global bubbly state that is required to be injected into // functions throughout the codebase. -type BubblyContext struct { +type BubblyConfig struct { // UI stores the embedded bubbly frontend UI *embed.FS // Logger stores the global bubbly logger Logger zerolog.Logger - ReleaseConfig *config.ReleaseConfig - ServerConfig *config.ServerConfig + ReleaseConfig *ReleaseConfig + ServerConfig *ServerConfig // Store provider configuration - StoreConfig *config.StoreConfig - ClientConfig *config.ClientConfig - CLIConfig *config.CLIConfig + StoreConfig *StoreConfig + ClientConfig *ClientConfig + CLIConfig *CLIConfig + AuthConfig *auth.Config Version *Version } -// NewBubblyContext sets up a default Bubbly Context -func NewBubblyContext(opts ...func(*BubblyContext)) *BubblyContext { - bCtx := BubblyContext{ +// NewBubblyConfig sets up a default Bubbly Config +func NewBubblyConfig(opts ...func(*BubblyConfig)) *BubblyConfig { + b := &BubblyConfig{ Logger: NewDefaultLogger(), - ReleaseConfig: config.DefaultReleaseConfig(), - ServerConfig: config.DefaultServerConfig(), - StoreConfig: config.DefaultStoreConfig(), - ClientConfig: config.DefaultClientConfig(), - CLIConfig: config.DefaultCLIConfig(), + ReleaseConfig: DefaultReleaseConfig(), + ServerConfig: DefaultServerConfig(), + StoreConfig: DefaultStoreConfig(), + ClientConfig: DefaultClientConfig(), + CLIConfig: DefaultCLIConfig(), + AuthConfig: DefaultAuthConfig(), Version: NewVersionInfo(), } + for _, opt := range opts { - opt(&bCtx) + opt(b) } - - return &bCtx + return b } -func WithBubblyUI(fs *embed.FS) func(*BubblyContext) { - return func(bCtx *BubblyContext) { +func WithBubblyUI(fs *embed.FS) func(*BubblyConfig) { + return func(bCtx *BubblyConfig) { bCtx.UI = fs } } -func WithVersion(version *Version) func(*BubblyContext) { - return func(bCtx *BubblyContext) { +func WithVersion(version *Version) func(*BubblyConfig) { + return func(bCtx *BubblyConfig) { bCtx.Version = version } } @@ -87,7 +89,7 @@ func NewDefaultLogger() zerolog.Logger { } // UpdateLogLevel is a convenience method for updating the log level of -// the zerolog.Logger managed by a BubblyContext instance -func (bCtx *BubblyContext) UpdateLogLevel(level zerolog.Level) { +// the zerolog.Logger managed by a BubblyConfig instance +func (bCtx *BubblyConfig) UpdateLogLevel(level zerolog.Level) { bCtx.Logger = bCtx.Logger.Level(level) } diff --git a/config/defaults.go b/config/defaults.go index 0728fc1d..8aefcdfc 100644 --- a/config/defaults.go +++ b/config/defaults.go @@ -3,6 +3,8 @@ package config import ( "os" "strconv" + + "github.com/valocode/bubbly/auth" ) // Default CLI configuration @@ -161,3 +163,18 @@ func DefaultCLIConfig() *CLIConfig { NoColor: DefaultEnvBool("NO_COLOR", DefaultCLINoColorToggle), } } + +// ########################################### +// Auth +// ########################################### + +func DefaultAuthConfig() *auth.Config { + return &auth.Config{ + ProviderURL: DefaultEnvStr("AUTH_PROVIDER_URL", ""), + ClientID: DefaultEnvStr("AUTH_CLIENT_ID", ""), + ClientSecret: DefaultEnvStr("AUTH_CLIENT_SECRET", ""), + RedirectURL: DefaultEnvStr("AUTH_REDIRECT_URL", ""), + Scopes: []string{"email"}, + Enabled: DefaultEnvBool("BUBBLY_AUTH_ENABLED", false), + } +} diff --git a/docker-compose.yaml b/docker-compose.yaml index 3bf6a7c0..224b0c62 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -48,6 +48,19 @@ services: - type: volume source: postgres-data target: /var/lib/postgresql/data + grafana: + image: grafana/grafana:8.0.6 + container_name: grafana + networks: + - bubbly-net + ports: + # 3000 clashes with local svelte development... + - "3001:3000" + volumes: + - type: bind + read_only: true + source: "./grafana/provisioning/datasources" + target: "/etc/grafana/provisioning/datasources" # User-defined bridge network is different from the default bridge networks: diff --git a/docs/gen.go b/docs/gen.go index 46b4b680..424281ac 100644 --- a/docs/gen.go +++ b/docs/gen.go @@ -13,7 +13,7 @@ import ( "entgo.io/ent/entc/gen" "github.com/spf13/cobra/doc" "github.com/valocode/bubbly/cmd" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" ) func main() { @@ -22,7 +22,7 @@ func main() { } func genCLIDocs() { - cmd := cmd.NewCmdRoot(env.NewBubblyContext()) + cmd := cmd.NewCmdRoot(config.NewBubblyConfig()) err := doc.GenMarkdownTreeCustom(cmd, "./docs/docs/cli", func(s string) string { filename := filepath.Base(s) diff --git a/ent/releasepolicyviolation/releasepolicyviolation.go b/ent/releasepolicyviolation/releasepolicyviolation.go index edd645ae..3f38569b 100644 --- a/ent/releasepolicyviolation/releasepolicyviolation.go +++ b/ent/releasepolicyviolation/releasepolicyviolation.go @@ -71,10 +71,8 @@ func ValidColumn(column string) bool { return false } -var ( - // MessageValidator is a validator for the "message" field. It is called by the builders before save. - MessageValidator func(string) error -) +// MessageValidator is a validator for the "message" field. It is called by the builders before save. +var MessageValidator func(string) error // Type defines the type for the "type" enum field. type Type string diff --git a/ent/vulnerability/vulnerability.go b/ent/vulnerability/vulnerability.go index a2bff513..44137bfb 100644 --- a/ent/vulnerability/vulnerability.go +++ b/ent/vulnerability/vulnerability.go @@ -88,11 +88,9 @@ var ForeignKeys = []string{ "vulnerability_owner", } -var ( - // ComponentsPrimaryKey and ComponentsColumn2 are the table columns denoting the - // primary key for the components relation (M2M). - ComponentsPrimaryKey = []string{"component_id", "vulnerability_id"} -) +// ComponentsPrimaryKey and ComponentsColumn2 are the table columns denoting the +// primary key for the components relation (M2M). +var ComponentsPrimaryKey = []string{"component_id", "vulnerability_id"} // ValidColumn reports if the column name is valid (part of the table columns). func ValidColumn(column string) bool { diff --git a/go.mod b/go.mod index be64f22e..2a319e81 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/agnivade/levenshtein v1.1.1 // indirect github.com/bytecodealliance/wasmtime-go v0.29.0 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/coreos/go-oidc/v3 v3.0.0 github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/docker/docker v20.10.8+incompatible // indirect github.com/fatih/color v1.12.0 @@ -48,6 +49,7 @@ require ( golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect golang.org/x/mod v0.5.0 // indirect golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect + golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 // indirect golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect diff --git a/go.sum b/go.sum index 84585a4b..869f176c 100644 --- a/go.sum +++ b/go.sum @@ -407,7 +407,10 @@ github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-oidc/v3 v3.0.0 h1:/mAA0XMgYJw2Uqm7WKGCsKnjitE/+A0FFbOmiRJm7LQ= +github.com/coreos/go-oidc/v3 v3.0.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -1703,6 +1706,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -1740,6 +1744,7 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2033,6 +2038,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -2157,6 +2163,7 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= diff --git a/integrations/spdx_test.go b/integrations/spdx_test.go index f91a17f5..cdb8b374 100644 --- a/integrations/spdx_test.go +++ b/integrations/spdx_test.go @@ -4,12 +4,12 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/store" ) func TestSpdx(t *testing.T) { - s, err := store.New(env.NewBubblyContext()) + s, err := store.New(config.NewBubblyConfig()) require.NoError(t, err) m, err := NewSPDXMonitor(WithStore(s)) require.NoError(t, err) diff --git a/main.go b/main.go index 60251621..310aecc9 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,7 @@ import ( "os" "github.com/valocode/bubbly/cmd" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ui" ) @@ -17,10 +17,10 @@ var ( //go:generate go run docs/gen.go func main() { - // Set up the initial BubblyContext with config.Config defaults - bCtx := env.NewBubblyContext( - env.WithBubblyUI(&ui.Build), - env.WithVersion(&env.Version{ + // Set up the initial BubblyConfig with config.Config defaults + bCtx := config.NewBubblyConfig( + config.WithBubblyUI(&ui.Build), + config.WithVersion(&config.Version{ Version: version, Commit: commit, Date: date, diff --git a/policy/policy.go b/policy/policy.go index a1ce40af..ee06bfc7 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -107,7 +107,7 @@ func Validate(module string, opts ...func(r *runOptions)) error { ctx := context.Background() regoInstance, err := newRego(module, opts...) if err != nil { - return fmt.Errorf("creating rego instance: %w", err) + return err } query, pErr := regoInstance.PrepareForEval(ctx) if pErr != nil { diff --git a/policy/policy_test.go b/policy/policy_test.go index fb1019c7..11e1772a 100644 --- a/policy/policy_test.go +++ b/policy/policy_test.go @@ -19,7 +19,7 @@ type fakeResolver struct { } func (r *fakeResolver) Functions() []func(*rego.Rego) { - var funcs = make([]func(*rego.Rego), 0, len(r.data)) + funcs := make([]func(*rego.Rego), 0, len(r.data)) for name, value := range r.data { funcs = append(funcs, rego.Function1(®o.Function{ diff --git a/release/release.go b/release/release.go index e879ce0e..e4c23e22 100644 --- a/release/release.go +++ b/release/release.go @@ -10,8 +10,8 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/store/api" ) @@ -32,13 +32,13 @@ type ReleaseSpecWrap struct { Release ReleaseSpec `json:"release"` } -func DefaultReleaseSpec(bCtx *env.BubblyContext) *ReleaseSpec { +func DefaultReleaseSpec(bCtx *config.BubblyConfig) *ReleaseSpec { return &ReleaseSpec{ Project: bCtx.ReleaseConfig.Project, } } -func Commit(bCtx *env.BubblyContext) (string, error) { +func Commit(bCtx *config.BubblyConfig) (string, error) { spec, err := ParseReleaseSpec(bCtx) if err != nil { return "", err @@ -51,7 +51,7 @@ func Commit(bCtx *env.BubblyContext) (string, error) { return commit, nil } -func CreateRelease(bCtx *env.BubblyContext) (*api.ReleaseCreateRequest, error) { +func CreateRelease(bCtx *config.BubblyConfig) (*api.ReleaseCreateRequest, error) { spec, err := ParseReleaseSpec(bCtx) if err != nil { return nil, err @@ -70,7 +70,7 @@ func CreateRelease(bCtx *env.BubblyContext) (*api.ReleaseCreateRequest, error) { }, nil } -func ParseReleaseSpec(bCtx *env.BubblyContext) (*ReleaseSpec, error) { +func ParseReleaseSpec(bCtx *config.BubblyConfig) (*ReleaseSpec, error) { release, err := decodeReleaseSpec(bCtx) if err != nil { return nil, fmt.Errorf("decoding release spec: %w", err) @@ -91,9 +91,9 @@ func ParseReleaseSpec(bCtx *env.BubblyContext) (*ReleaseSpec, error) { return release, nil } -func decodeReleaseSpec(bCtx *env.BubblyContext) (*ReleaseSpec, error) { +func decodeReleaseSpec(bCtx *config.BubblyConfig) (*ReleaseSpec, error) { spec := DefaultReleaseSpec(bCtx) - var possiblePaths = []string{ + possiblePaths := []string{ ".bubbly.json", filepath.Join(bCtx.ReleaseConfig.BubblyDir, ".bubbly.json"), } diff --git a/server/errors.go b/server/errors.go index d5c4e857..1524d9a1 100644 --- a/server/errors.go +++ b/server/errors.go @@ -4,11 +4,11 @@ import ( "net/http" "github.com/labstack/echo/v4" - "github.com/valocode/bubbly/env" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/store" ) -func httpErrorHandler(bCtx *env.BubblyContext, err error, c echo.Context) error { +func httpErrorHandler(bCtx *config.BubblyConfig, err error, c echo.Context) error { bCtx.Logger.Error(). Str("Path", c.Path()). Strs("QueryParams", c.ParamValues()). diff --git a/server/server.go b/server/server.go index 993bd7b6..fed76f37 100644 --- a/server/server.go +++ b/server/server.go @@ -1,6 +1,7 @@ package server import ( + "context" "fmt" "io/fs" "net/http" @@ -12,15 +13,16 @@ import ( "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "github.com/labstack/gommon/log" + "github.com/valocode/bubbly/auth" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/gql" "github.com/valocode/bubbly/store" "github.com/valocode/bubbly/store/api" "github.com/ziflex/lecho/v2" ) -func New(bCtx *env.BubblyContext) (*Server, error) { +func New(bCtx *config.BubblyConfig) (*Server, error) { store, err := store.New(bCtx) if err != nil { return nil, fmt.Errorf("error initializing store: %w", err) @@ -28,7 +30,7 @@ func New(bCtx *env.BubblyContext) (*Server, error) { return NewWithStore(bCtx, store) } -func NewWithStore(bCtx *env.BubblyContext, store *store.Store) (*Server, error) { +func NewWithStore(bCtx *config.BubblyConfig, store *store.Store) (*Server, error) { var ( e = echo.New() s = Server{ @@ -60,11 +62,15 @@ func NewWithStore(bCtx *env.BubblyContext, store *store.Store) (*Server, error) e.DefaultHTTPErrorHandler(httpError, c) } - // TODO: use only for dev/debugging, but disable by default - if true { - e.Use(middleware.CORS()) + authProvider, err := auth.NewProvider(context.TODO(), bCtx.AuthConfig) + if err != nil { + return &s, err } + e.Use(authProvider.EchoMiddleware()) + + e.GET("/auth/token", authProvider.EchoAuthorizeHandler()) + // // Setup the Bubbly UI // @@ -120,37 +126,21 @@ func NewWithStore(bCtx *env.BubblyContext, store *store.Store) (*Server, error) v1.POST("/vulnerabilityreviews", s.postVulnerabilityReview) v1.PUT("/vulnerabilityreviews/:id", s.putVulnerabilityReview) - // TODO: Miika - integrate Casbin as middleware or similar - // https://echo.labstack.com/middleware/casbin-auth/ - // Authentication // /login endpoint that redirects to OIDC provider, which provides access token - - // Send X-Bubbly-Flavour=oss then auto redirect to default org - - // If SaaS, create a group "/o/:organization" and mount these under there - v1.POST("/users", nil) - v1.PUT("/users/me", nil) - v1.PUT("/users/:id", nil) - v1.GET("/users/me", nil) - v1.GET("/users/:id", nil) - v1.DELETE("/users/:id", nil) - - v1.POST("/groups", nil) - v1.POST("/groups/:id/users", nil) // Add users to a group - v1.POST("/groups/:id/roles", nil) // Add roles to a group - v1.PUT("/groups/:id", nil) - v1.GET("/groups/:id", nil) - v1.DELETE("/groups/:id", nil) - - // What are the predifined roles? - // admins: can add/remove users/groups from their organization - // users: ?? - // Nice to have: - // - access control by project (that they are assigned to, either by user or by group) - - // SaaS specific things: - // - organizations/ PUT,GET, etc + // v1.POST("/users", nil) + // v1.PUT("/users/me", nil) + // v1.PUT("/users/:id", nil) + // v1.GET("/users/me", nil) + // v1.GET("/users/:id", nil) + // v1.DELETE("/users/:id", nil) + + // v1.POST("/groups", nil) + // v1.POST("/groups/:id/users", nil) // Add users to a group + // v1.POST("/groups/:id/roles", nil) // Add roles to a group + // v1.PUT("/groups/:id", nil) + // v1.GET("/groups/:id", nil) + // v1.DELETE("/groups/:id", nil) // Initialise monitoring services if err := s.initMonitoring(); err != nil { @@ -162,13 +152,12 @@ func NewWithStore(bCtx *env.BubblyContext, store *store.Store) (*Server, error) type Server struct { store *store.Store - bCtx *env.BubblyContext + bCtx *config.BubblyConfig e *echo.Echo validator *validator.Validate } func (s *Server) Start() error { - addr := fmt.Sprintf("%s:%s", s.bCtx.ServerConfig.Host, s.bCtx.ServerConfig.Port) if err := s.e.Start(addr); err != http.ErrServerClosed { return err diff --git a/server/server_test.go b/server/server_test.go index c3301bf7..909ae7a2 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -12,8 +12,8 @@ import ( "github.com/labstack/echo/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/store/api" "github.com/valocode/bubbly/test" ) @@ -64,7 +64,7 @@ func testPutRequest(t *testing.T, e *echo.Echo, h echo.HandlerFunc, path string, } func TestRelease(t *testing.T) { - s, err := New(env.NewBubblyContext()) + s, err := New(config.NewBubblyConfig()) require.NoError(t, err) { @@ -102,7 +102,7 @@ func TestRelease(t *testing.T) { } func TestAdapter(t *testing.T) { - s, err := New(env.NewBubblyContext()) + s, err := New(config.NewBubblyConfig()) require.NoError(t, err) { @@ -141,7 +141,7 @@ func TestAdapter(t *testing.T) { } func TestPolicy(t *testing.T) { - s, err := New(env.NewBubblyContext()) + s, err := New(config.NewBubblyConfig()) require.NoError(t, err) { @@ -193,7 +193,7 @@ func TestPolicy(t *testing.T) { } func TestServer(t *testing.T) { - s, err := New(env.NewBubblyContext()) + s, err := New(config.NewBubblyConfig()) require.NoError(t, err) data := test.CreateDummyData() diff --git a/store/adapter.go b/store/adapter.go index 7ba792fa..6355555a 100644 --- a/store/adapter.go +++ b/store/adapter.go @@ -46,7 +46,7 @@ func (h *Handler) GetAdapters(query *AdapterQuery) ([]*api.Adapter, error) { if err != nil { return nil, HandleEntError(err, "get adapters") } - var adapters = make([]*api.Adapter, 0, len(dbAdapters)) + adapters := make([]*api.Adapter, 0, len(dbAdapters)) for _, a := range dbAdapters { adapters = append(adapters, &api.Adapter{ AdapterModelRead: *ent.NewAdapterModelRead().FromEnt(a), diff --git a/store/codescan.go b/store/codescan.go index 5218efb1..844bf152 100644 --- a/store/codescan.go +++ b/store/codescan.go @@ -38,7 +38,6 @@ func (h *Handler) SaveCodeScan(req *api.CodeScanRequest) (*ent.CodeScan, error) func (h *Handler) saveCodeScan(dbRelease *ent.Release, scan *api.CodeScan) (*ent.CodeScan, error) { var codeScan *ent.CodeScan txErr := WithTx(h.ctx, h.client, func(tx *ent.Tx) error { - var err error codeScan, err = tx.CodeScan.Create(). SetModelCreate(&scan.CodeScanModelCreate). diff --git a/store/components.go b/store/components.go index c5b93ede..4ff8bf7d 100644 --- a/store/components.go +++ b/store/components.go @@ -16,7 +16,7 @@ func (h *Handler) SaveComponentVulnerabilities(req *api.ComponentVulnerabilityRe if err != nil { return err } - var dbVulns = make([]*ent.Vulnerability, 0, len(comp.Vulnerabilities)) + dbVulns := make([]*ent.Vulnerability, 0, len(comp.Vulnerabilities)) for _, vuln := range comp.Vulnerabilities { dbVuln, err := h.GetVulnerabilityOrCreate(tx.Client(), vuln) if err != nil { diff --git a/store/policy_test.go b/store/policy_test.go index 89723c2e..7f0e2515 100644 --- a/store/policy_test.go +++ b/store/policy_test.go @@ -6,14 +6,14 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent" - "github.com/valocode/bubbly/env" "github.com/valocode/bubbly/store/api" "github.com/valocode/bubbly/test" ) func TestPolicyAffects(t *testing.T) { - s, err := New(env.NewBubblyContext()) + s, err := New(config.NewBubblyConfig()) require.NoError(t, err) h, err := NewHandler(WithStore(s)) diff --git a/store/store.go b/store/store.go index 9410d269..a77173e1 100644 --- a/store/store.go +++ b/store/store.go @@ -13,7 +13,6 @@ import ( "github.com/valocode/bubbly/ent" "github.com/valocode/bubbly/ent/migrate" "github.com/valocode/bubbly/ent/organization" - "github.com/valocode/bubbly/env" _ "github.com/jackc/pgx/v4/stdlib" _ "github.com/xiaoqidun/entps" @@ -22,8 +21,7 @@ import ( _ "github.com/valocode/bubbly/ent/runtime" ) -func New(bCtx *env.BubblyContext) (*Store, error) { - +func New(bCtx *config.BubblyConfig) (*Store, error) { var ( client *ent.Client err error diff --git a/store/store_test.go b/store/store_test.go index cc2fe678..87c459f9 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -5,14 +5,14 @@ import ( "testing" "github.com/stretchr/testify/require" + "github.com/valocode/bubbly/config" "github.com/valocode/bubbly/ent/release" "github.com/valocode/bubbly/ent/releasepolicyviolation" "github.com/valocode/bubbly/ent/repo" - "github.com/valocode/bubbly/env" ) func TestStore(t *testing.T) { - bCtx := env.NewBubblyContext() + bCtx := config.NewBubblyConfig() s, err := New(bCtx) require.NoError(t, err) h, err := NewHandler(WithStore(s)) @@ -47,5 +47,4 @@ func TestStore(t *testing.T) { for _, v := range vs { t.Log(v.String()) } - } diff --git a/test/demo.go b/test/demo.go index 52721870..17fed344 100644 --- a/test/demo.go +++ b/test/demo.go @@ -155,7 +155,7 @@ func createRepoData(opt DemoRepoOptions) []ReleaseData { SetTime(cTime), } - var passAll = false + passAll := false if rand.Intn(10) > 3 { passAll = true }