diff --git a/cmd/serve.go b/cmd/serve.go index a6a8140f6..b81a0729e 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "io" "net/http" "os" "os/signal" @@ -13,6 +14,7 @@ import ( _ "github.com/authzed/authzed-go/proto/authzed/api/v0" _ "github.com/jackc/pgx/v4/stdlib" newrelic "github.com/newrelic/go-agent" + "go.uber.org/zap" "github.com/goto/shield/config" "github.com/goto/shield/core/action" @@ -92,8 +94,19 @@ func StartServer(logger *log.Zap, cfg *config.Shield) error { schemaMigrationConfig := schema.NewSchemaMigrationConfig(cfg.App.DefaultSystemEmail) - // - activityRepository := postgres.NewActivityRepository(dbClient) + var activityRepository activity.Repository + switch cfg.Log.Activity.Sink { + case activity.SinkTypeDB: + activityRepository = postgres.NewActivityRepository(dbClient) + case activity.SinkTypeStdout: + stdoutLogger, err := zap.NewStdLogAt(logger.GetInternalZapLogger().Desugar(), logger.GetInternalZapLogger().Level()) + if err != nil { + return err + } + activityRepository = activity.NewStdoutRepository(stdoutLogger.Writer()) + default: + activityRepository = activity.NewStdoutRepository(io.Discard) + } activityService := activity.NewService(activityRepository) userRepository := postgres.NewUserRepository(dbClient) @@ -128,7 +141,7 @@ func StartServer(logger *log.Zap, cfg *config.Shield) error { return err } - deps, err := BuildAPIDependencies(ctx, logger, resourceBlobRepository, dbClient, spiceDBClient) + deps, err := BuildAPIDependencies(ctx, logger, activityRepository, resourceBlobRepository, dbClient, spiceDBClient) if err != nil { return err } @@ -166,11 +179,11 @@ func StartServer(logger *log.Zap, cfg *config.Shield) error { func BuildAPIDependencies( ctx context.Context, logger log.Logger, + activityRepository activity.Repository, resourceBlobRepository *blob.ResourcesRepository, dbc *db.Client, sdb *spicedb.SpiceDB, ) (api.Deps, error) { - activityRepository := postgres.NewActivityRepository(dbc) activityService := activity.NewService(activityRepository) userRepository := postgres.NewUserRepository(dbc) diff --git a/cmd/server.go b/cmd/server.go index 33920f9ad..edd62f8d2 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -107,7 +107,7 @@ func serverStartCommand() *cobra.Command { if err != nil { panic(err) } - logger := shieldlogger.InitLogger(appConfig.Log) + logger := shieldlogger.InitLogger(shieldlogger.Config{Level: appConfig.Log.Level}) return StartServer(logger, appConfig) }, diff --git a/config/config.go b/config/config.go index c235c794a..8b8e68c95 100644 --- a/config/config.go +++ b/config/config.go @@ -10,14 +10,28 @@ import ( "github.com/goto/shield/internal/server" "github.com/goto/shield/internal/store/spicedb" "github.com/goto/shield/pkg/db" - "github.com/goto/shield/pkg/logger" ) +type Log struct { + // log level - debug, info, warning, error, fatal + Level string `yaml:"level" mapstructure:"level" default:"info"` + + // format strategy - plain, json + Format string `yaml:"format" mapstructure:"format" default:"json"` + + Activity ActivityLogConfig `yaml:"activity" mapstructure:"activity"` +} + +type ActivityLogConfig struct { + // activity sink strategy - db, stdout, none + Sink string `yaml:"sink" mapstructure:"sink" default:"none"` +} + type Shield struct { // configuration version Version int `yaml:"version"` Proxy proxy.ServicesConfig `yaml:"proxy"` - Log logger.Config `yaml:"log"` + Log Log `yaml:"log"` NewRelic NewRelic `yaml:"new_relic"` App server.Config `yaml:"app"` DB db.Config `yaml:"db"` diff --git a/core/activity/activity.go b/core/activity/activity.go index 3b0d73f91..e8528c45f 100644 --- a/core/activity/activity.go +++ b/core/activity/activity.go @@ -6,6 +6,12 @@ import ( "github.com/goto/salt/audit" ) +const ( + SinkTypeDB = "db" + SinkTypeStdout = "stdout" + SinkTypeNone = "none" +) + type Repository interface { Insert(ctx context.Context, log *audit.Log) error } diff --git a/core/activity/stdout_repository.go b/core/activity/stdout_repository.go new file mode 100644 index 000000000..7a899b377 --- /dev/null +++ b/core/activity/stdout_repository.go @@ -0,0 +1,23 @@ +package activity + +import ( + "context" + "encoding/json" + "io" + + "github.com/goto/salt/audit" +) + +type StdoutRepository struct { + writer io.Writer +} + +func NewStdoutRepository(writer io.Writer) *StdoutRepository { + return &StdoutRepository{ + writer: writer, + } +} + +func (r StdoutRepository) Insert(ctx context.Context, log *audit.Log) error { + return json.NewEncoder(r.writer).Encode(log) +} diff --git a/docs/docs/reference/configurations.md b/docs/docs/reference/configurations.md index e175fe936..933bfa1b3 100644 --- a/docs/docs/reference/configurations.md +++ b/docs/docs/reference/configurations.md @@ -9,6 +9,10 @@ version: 1 log: # debug, info, warning, error, fatal - default 'info' level: debug + activity: + # none, stdout, db - default 'none' + sink: stdout + app: port: 8000 diff --git a/go.mod b/go.mod index 1fc979220..5e5966ca8 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 github.com/MakeNowJust/heredoc v1.0.0 github.com/abbot/go-http-auth v0.4.0 - github.com/antonmedv/expr v1.15.3 github.com/authzed/authzed-go v0.7.1-0.20221109204547-1aa903788b3b github.com/authzed/grpcutil v0.0.0-20230109193425-40ce0530e048 github.com/authzed/spicedb v1.15.0 diff --git a/go.sum b/go.sum index 4fa8fb7c7..bd304a1c8 100644 --- a/go.sum +++ b/go.sum @@ -526,8 +526,6 @@ github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= -github.com/antonmedv/expr v1.15.3 h1:q3hOJZNvLvhqE8OHBs1cFRdbXFNKuA+bHmRaI+AmRmI= -github.com/antonmedv/expr v1.15.3/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE= github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY= github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= diff --git a/internal/proxy/hook/authz/authz_test.go b/internal/proxy/hook/authz/authz_test.go index ae3ae1863..6a54997ea 100644 --- a/internal/proxy/hook/authz/authz_test.go +++ b/internal/proxy/hook/authz/authz_test.go @@ -108,10 +108,7 @@ func TestServeHook(t *testing.T) { mockRelationTransformer = new(mocks.RelationTransformer) ) - logger := shieldlogger.InitLogger(shieldlogger.Config{ - Level: "info", - Format: "json", - }) + logger := shieldlogger.InitLogger(shieldlogger.Config{Level: "debug"}) rootHook := hook.New() a := New(logger, rootHook, rootHook, mockResourceService, mockRelationService, mockRelationTransformer, "X-Shield-Email") diff --git a/pkg/body_extractor/json_payload_test.go b/pkg/body_extractor/json_payload_test.go new file mode 100644 index 000000000..f978eeb20 --- /dev/null +++ b/pkg/body_extractor/json_payload_test.go @@ -0,0 +1,63 @@ +package body_extractor + +import ( + "bytes" + "encoding/json" + "io" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" +) + +func TestJSONPayloadHandler_Extract(t *testing.T) { + defaultTestMessage := map[string]any{ + "k1": "v1", + "nested_k1": map[string]any{ + "k1_1": "v1", + "k2_2": 1, + }, + } + + tests := []struct { + name string + key string + testMessage any + want any + wantErrString string + }{ + { + name: "should return error if field not exist", + key: "x", + testMessage: defaultTestMessage, + wantErrString: "failed to find field: x", + }, + { + name: "should return value if field found", + key: "nested_k1.k2_2", + testMessage: defaultTestMessage, + want: float64(1), + }, + } + for _, tt := range tests { + h := JSONPayloadHandler{} + t.Run(tt.name, func(t *testing.T) { + tt := tt + t.Parallel() + + msg, err := json.Marshal(tt.testMessage) + assert.NoError(t, err) + + testReader := io.NopCloser(bytes.NewBuffer(msg)) + extractedData, err := h.Extract(&testReader, tt.key) + + if tt.wantErrString != "" { + assert.Equal(t, tt.wantErrString, err.Error()) + } else { + if diff := cmp.Diff(tt.want, extractedData); diff != "" { + t.Fatal(diff) + } + } + }) + } +} diff --git a/pkg/expression/expression.go b/pkg/expression/expression.go index f4ed5cba3..c84bb7bd8 100644 --- a/pkg/expression/expression.go +++ b/pkg/expression/expression.go @@ -1,7 +1,6 @@ package expression import ( - "errors" "fmt" "reflect" ) @@ -21,7 +20,7 @@ func (e Expression) Evaluate() (bool, error) { output = reflect.DeepEqual(e.Attribute, e.Value) err = nil default: - err = errors.New(fmt.Sprintf("unsupported operator %s", e.Operator)) + err = fmt.Errorf("unsupported operator %s", e.Operator) } return output, err diff --git a/pkg/expression/expression_test.go b/pkg/expression/expression_test.go index f309526b5..ee9297c54 100644 --- a/pkg/expression/expression_test.go +++ b/pkg/expression/expression_test.go @@ -50,6 +50,16 @@ func TestExpression_Evaluate(t *testing.T) { wantNilErr: true, wantOutput: false, }, + { + name: "unknown expression return error", + expression: Expression{ + Attribute: "A", + Operator: "';l", + Value: "B", + }, + wantNilErr: false, + wantOutput: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/test/e2e_test/testbench/testbench.go b/test/e2e_test/testbench/testbench.go index bd635908b..3bf641a00 100644 --- a/test/e2e_test/testbench/testbench.go +++ b/test/e2e_test/testbench/testbench.go @@ -19,7 +19,6 @@ import ( "github.com/goto/shield/internal/store/postgres/migrations" "github.com/goto/shield/internal/store/spicedb" "github.com/goto/shield/pkg/db" - "github.com/goto/shield/pkg/logger" shieldv1beta1 "github.com/goto/shield/proto/v1beta1" "github.com/ory/dockertest" "github.com/ory/dockertest/docker" @@ -191,7 +190,7 @@ func SetupTests(t *testing.T) (shieldv1beta1.ShieldServiceClient, *config.Shield } appConfig := &config.Shield{ - Log: logger.Config{ + Log: config.Log{ Level: "fatal", }, App: server.Config{