Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

authz: Stdout logger #6230

Merged
merged 30 commits into from May 17, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
065089d
Draft of StdoutLogger
erm-g Apr 25, 2023
e17b64e
Fitting StdoutLogger to lb patterns
erm-g Apr 25, 2023
92b53a7
Merge branch 'grpc:master' into AuditLoggerRegistry
erm-g Apr 27, 2023
5665daa
conversion from proto to json for laudit loggers
erm-g Apr 28, 2023
95ebc88
Tests for multiple loggers and empty Options
erm-g Apr 28, 2023
bcf4256
Added LoggerConfig impl
erm-g May 1, 2023
b918c0a
Switched to grpcLogger and added a unit test comparing log with os.St…
erm-g May 4, 2023
861d9f2
Minor fix in exception handling wording
erm-g May 4, 2023
270948b
Added timestamp for logging statement
erm-g May 4, 2023
8859707
Changed format to json and added custom marshalling
erm-g May 7, 2023
c073753
Migration to log.go and additional test for a full event
erm-g May 7, 2023
c6dd904
Migration of stdout logger to a separate package
erm-g May 7, 2023
e72bfa2
migration to grpcLogger, unit test fix
erm-g May 8, 2023
9866f61
Delete xds parsing functionality. Will be done in a separate PR
erm-g May 8, 2023
0d9a56e
Delete xds parsing functionality. Will be done in a separate PR
erm-g May 8, 2023
8c24380
Address PR comments (embedding interface, table test, pointer optimiz…
erm-g May 9, 2023
317f501
vet.sh fixes
erm-g May 9, 2023
e0800ba
Address PR comments
erm-g May 10, 2023
e0c53d8
Commit for go tidy changes
erm-g May 10, 2023
da716bb
vet.sh fix for buf usage
erm-g May 10, 2023
7c36609
Address PR comments
erm-g May 11, 2023
c903401
Address PR comments
erm-g May 11, 2023
9bcb689
Address PR comments (easwars)
erm-g May 12, 2023
a1e3e7a
Address PR comments (luwei)
erm-g May 14, 2023
c74d21b
Migrate printing to standard out from log package level func to a Log…
erm-g May 15, 2023
e6c450b
Changed event Timestamp format back to RFC3339
erm-g May 15, 2023
f60c376
Address PR comments
erm-g May 16, 2023
8f0ad3c
Address PR comments
erm-g May 16, 2023
6ef31dd
Address PR comments
erm-g May 16, 2023
d663a6d
Address PR comments
erm-g May 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 18 additions & 14 deletions authz/audit/stdout/stdout_logger.go
Expand Up @@ -22,6 +22,7 @@ package stdout
import (
"encoding/json"
"log"
"os"
"time"

"google.golang.org/grpc/authz/audit"
Expand All @@ -35,32 +36,32 @@ func init() {
}

type event struct {
FullMethodName string `json:"fullMethodName"`
FullMethodName string `json:"rpc_method"`
Principal string `json:"principal"`
PolicyName string `json:"policyName"`
MatchedRule string `json:"matchedRule"`
PolicyName string `json:"policy_name"`
MatchedRule string `json:"matched_rule"`
Authorized bool `json:"authorized"`
Timestamp string `json:"timestamp"` // Time when the audit event is logged via Log method
Timestamp int64 `json:"timestamp"` // Time when the audit event is logged via Log method
rockspore marked this conversation as resolved.
Show resolved Hide resolved
}

// logger implements the audit.Logger interface by logging to standard output.
type logger struct {
goLogger *log.Logger
}

// Log marshals the audit.Event to json and prints it using log.go
// Log marshals the audit.Event to json and prints it to standard output.
func (logger *logger) Log(event *audit.Event) {
rockspore marked this conversation as resolved.
Show resolved Hide resolved
jsonBytes, err := json.Marshal(convertEvent(event))
jsonContainer := map[string]interface{}{
"grpc_audit_log": convertEvent(event),
}
jsonBytes, err := json.Marshal(jsonContainer)
if err != nil {
grpcLogger.Errorf("failed to marshal AuditEvent data to JSON: %v", err)
easwars marked this conversation as resolved.
Show resolved Hide resolved
return
}
log.Println(string(jsonBytes))
logger.goLogger.Println(string(jsonBytes))
}

const (
stdName = "stdout"
)

// loggerConfig represents the configuration for the stdout logger.
// It is currently empty and implements the audit.Logger interface by embedding it.
type loggerConfig struct {
Expand All @@ -70,14 +71,17 @@ type loggerConfig struct {
type loggerBuilder struct{}

func (loggerBuilder) Name() string {
rockspore marked this conversation as resolved.
Show resolved Hide resolved
return stdName
return "stdout_logger"
}

// Build returns a new instance of the stdout logger.
// Passed in configuration is ignored as the stdout logger does not
// expect any configuration to be provided.
func (*loggerBuilder) Build(audit.LoggerConfig) audit.Logger {
return &logger{}
l := log.New(os.Stdout, "", log.LstdFlags)
rockspore marked this conversation as resolved.
Show resolved Hide resolved
return &logger{
goLogger: l,
}
}

// ParseLoggerConfig is a no-op since the stdout logger does not accept any configuration.
Expand All @@ -95,6 +99,6 @@ func convertEvent(auditEvent *audit.Event) *event {
PolicyName: auditEvent.PolicyName,
MatchedRule: auditEvent.MatchedRule,
Authorized: auditEvent.Authorized,
Timestamp: time.Now().Format(time.RFC3339),
Timestamp: time.Now().Unix(),
}
}
50 changes: 36 additions & 14 deletions authz/audit/stdout/stdout_logger_test.go
Expand Up @@ -22,8 +22,8 @@ import (
"bytes"
"encoding/json"
"log"
"strings"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"google.golang.org/grpc/authz/audit"
Expand Down Expand Up @@ -62,26 +62,29 @@ func (s) TestStdoutLogger_Log(t *testing.T) {
},
}

content := json.RawMessage(`{"name": "conf", "val": "to be ignored"}`)
builder := &loggerBuilder{}
config, _ := builder.ParseLoggerConfig(content)
auditLogger := builder.Build(config)
log.SetFlags(0)

for name, test := range tests {
t.Run(name, func(t *testing.T) {
before := time.Now().Unix()
var buf bytes.Buffer
log.SetOutput(&buf)
l := log.New(&buf, "", 0)
auditLogger := &logger{
goLogger: l,
}
auditLogger.Log(test.event)
var e event
if err := json.Unmarshal(buf.Bytes(), &e); err != nil {
var container map[string]interface{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Consider adding newlines to logically separate the steps of the test. At the very least, it would be good to separate out the following steps using newlines: setup, actual test logic, verification.

if err := json.Unmarshal(buf.Bytes(), &container); err != nil {
t.Fatalf("Failed to unmarshal audit log event: %v", err)
}
if strings.TrimSpace(e.Timestamp) == "" {
t.Fatalf("Resulted event has no timestamp: %v", e)
innerEvent := extractEvent(container["grpc_audit_log"].(map[string]interface{}))
if innerEvent.Timestamp == 0 {
t.Fatalf("Resulted event has no timestamp: %v", innerEvent)
}

if diff := cmp.Diff(trimEvent(e), test.event); diff != "" {
after := time.Now().Unix()
if before > innerEvent.Timestamp || after < innerEvent.Timestamp {
t.Fatalf("The audit event timestamp is outside of the test interval: test start %v, event timestamp %v, test end %v",
before, innerEvent.Timestamp, after)
}
if diff := cmp.Diff(trimEvent(innerEvent), test.event); diff != "" {
rockspore marked this conversation as resolved.
Show resolved Hide resolved
t.Fatalf("Unexpected message\ndiff (-got +want):\n%s", diff)
}
})
Expand All @@ -99,6 +102,25 @@ func (s) TestStdoutLoggerBuilder_NilConfig(t *testing.T) {
}
}

func (s) TestStdoutLoggerBuilder_Registration(t *testing.T) {
if audit.GetLoggerBuilder("stdout_logger") == nil {
t.Fatal("stdout logger is not registered")
}
}

// extractEvent extracts an stdout.event from a map
// unmarshalled from a logged json message.
func extractEvent(container map[string]interface{}) event {
return event{
FullMethodName: container["rpc_method"].(string),
Principal: container["principal"].(string),
PolicyName: container["policy_name"].(string),
MatchedRule: container["matched_rule"].(string),
Authorized: container["authorized"].(bool),
Timestamp: int64(container["timestamp"].(float64)),
}
}

// trimEvent converts a logged stdout.event into an audit.Event
// by removing Timestamp field. It is used for comparing events during testing.
func trimEvent(testEvent event) *audit.Event {
easwars marked this conversation as resolved.
Show resolved Hide resolved
Expand Down