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: create file watcher interceptor for gRPC SDK API #4760

Merged
merged 16 commits into from Oct 9, 2021
Merged
85 changes: 58 additions & 27 deletions authz/sdk_end2end_test.go
Expand Up @@ -21,7 +21,6 @@ package authz_test
import (
"context"
"io"
"io/ioutil"
"net"
"os"
"testing"
Expand Down Expand Up @@ -383,7 +382,7 @@ func (s) TestSDKFileWatcherEnd2End(t *testing.T) {
func (s) TestSDKFileWatcher_ValidPolicyRefresh(t *testing.T) {
valid1 := sdkTests["DeniesRpcMatchInDenyAndAllow"]
file := createTmpPolicyFile(t, "valid_policy_refresh", []byte(valid1.authzPolicy))
i, _ := authz.NewFileWatcher(file, 1*time.Second)
i, _ := authz.NewFileWatcher(file, 10*time.Millisecond)
ashithasantosh marked this conversation as resolved.
Show resolved Hide resolved
defer i.Close()

// Start a gRPC server with SDK unary server interceptor.
Expand Down Expand Up @@ -418,23 +417,34 @@ func (s) TestSDKFileWatcher_ValidPolicyRefresh(t *testing.T) {

// Rewrite the file with a different valid authorization policy.
valid2 := sdkTests["AllowsRpcEmptyDenyMatchInAllow"]
if err := ioutil.WriteFile(file, []byte(valid2.authzPolicy), os.ModePerm); err != nil {
t.Fatalf("ioutil.WriteFile(%q) failed: %v", file, err)
if err := os.WriteFile(file, []byte(valid2.authzPolicy), os.ModePerm); err != nil {
t.Fatalf("os.WriteFile(%q) failed: %v", file, err)
}
// Wait 2 seconds for background go routine to read the updated files.
time.Sleep(2 * time.Second)
// Wait 30 ms for background go routine to read the updated files.
time.Sleep(30 * time.Millisecond)
ashithasantosh marked this conversation as resolved.
Show resolved Hide resolved

// Verifying authorization decision.
_, err = client.UnaryCall(ctx, &pb.SimpleRequest{})
ashithasantosh marked this conversation as resolved.
Show resolved Hide resolved
if got := status.Convert(err); got.Code() != valid2.wantStatusCode || got.Message() != valid2.wantErr {
numRetries := 0
reloadSuccess := false
ashithasantosh marked this conversation as resolved.
Show resolved Hide resolved
got := status.Convert(err)
for numRetries <= 20 {
ashithasantosh marked this conversation as resolved.
Show resolved Hide resolved
if got.Code() == valid2.wantStatusCode && got.Message() == valid2.wantErr {
reloadSuccess = true
break
}
time.Sleep(10 * time.Millisecond)
numRetries++
}
if reloadSuccess == false {
t.Fatalf("error want:{%v %v} got:{%v %v}", valid2.wantStatusCode, valid2.wantErr, got.Code(), got.Message())
ashithasantosh marked this conversation as resolved.
Show resolved Hide resolved
}
}

func (s) TestSDKFileWatcher_InvalidPolicySkipReload(t *testing.T) {
valid := sdkTests["DeniesRpcMatchInDenyAndAllow"]
file := createTmpPolicyFile(t, "invalid_policy_skip_reload", []byte(valid.authzPolicy))
i, _ := authz.NewFileWatcher(file, 1*time.Second)
i, _ := authz.NewFileWatcher(file, 10*time.Millisecond)
defer i.Close()

// Start a gRPC server with SDK unary server interceptors.
Expand Down Expand Up @@ -468,23 +478,28 @@ func (s) TestSDKFileWatcher_InvalidPolicySkipReload(t *testing.T) {
}

// Skips the invalid policy update, and continues to use the valid policy.
if err := ioutil.WriteFile(file, []byte("{}"), os.ModePerm); err != nil {
t.Fatalf("ioutil.WriteFile(%q) failed: %v", file, err)
if err := os.WriteFile(file, []byte("{}"), os.ModePerm); err != nil {
t.Fatalf("os.WriteFile(%q) failed: %v", file, err)
}
// Wait 2 seconds for background go routine to read the updated files.
time.Sleep(2 * time.Second)
// Wait 30 ms for background go routine to read the updated files.
time.Sleep(30 * time.Millisecond)

// Verifying authorization decision.
_, err = client.UnaryCall(ctx, &pb.SimpleRequest{})
if got := status.Convert(err); got.Code() != valid.wantStatusCode || got.Message() != valid.wantErr {
t.Fatalf("[UnaryCall] error want:{%v %v} got:{%v %v}", valid.wantStatusCode, valid.wantErr, got.Code(), got.Message())
numRetries := 0
for numRetries <= 20 {
if got := status.Convert(err); got.Code() != valid.wantStatusCode || got.Message() != valid.wantErr {
t.Fatalf("error want:{%v %v} got:{%v %v}", valid.wantStatusCode, valid.wantErr, got.Code(), got.Message())
}
time.Sleep(10 * time.Millisecond)
numRetries++
}
}

func (s) TestSDKFileWatcher_RecoversFromReloadFailure(t *testing.T) {
valid1 := sdkTests["DeniesRpcMatchInDenyAndAllow"]
file := createTmpPolicyFile(t, "recovers_from_reload_failure", []byte(valid1.authzPolicy))
i, _ := authz.NewFileWatcher(file, 1*time.Second)
i, _ := authz.NewFileWatcher(file, 10*time.Millisecond)
defer i.Close()

// Start a gRPC server with SDK unary server interceptors.
Expand Down Expand Up @@ -518,29 +533,45 @@ func (s) TestSDKFileWatcher_RecoversFromReloadFailure(t *testing.T) {
}

// Skips the invalid policy update, and continues to use the valid policy.
if err := ioutil.WriteFile(file, []byte("{}"), os.ModePerm); err != nil {
t.Fatalf("ioutil.WriteFile(%q) failed: %v", file, err)
if err := os.WriteFile(file, []byte("{}"), os.ModePerm); err != nil {
t.Fatalf("os.WriteFile(%q) failed: %v", file, err)
}
// Wait 2 seconds for background go routine to read the updated files.
time.Sleep(2 * time.Second)
// Wait 30 ms for background go routine to read the updated files.
time.Sleep(30 * time.Millisecond)

// Verifying authorization decision.
_, err = client.UnaryCall(ctx, &pb.SimpleRequest{})
if got := status.Convert(err); got.Code() != valid1.wantStatusCode || got.Message() != valid1.wantErr {
t.Fatalf("[UnaryCall] error want:{%v %v} got:{%v %v}", valid1.wantStatusCode, valid1.wantErr, got.Code(), got.Message())
numRetries := 0
ashithasantosh marked this conversation as resolved.
Show resolved Hide resolved
for numRetries <= 20 {
if got := status.Convert(err); got.Code() != valid1.wantStatusCode || got.Message() != valid1.wantErr {
t.Fatalf("error want:{%v %v} got:{%v %v}", valid1.wantStatusCode, valid1.wantErr, got.Code(), got.Message())
}
time.Sleep(10 * time.Millisecond)
numRetries++
}

// Rewrite the file with a different valid authorization policy.
valid2 := sdkTests["AllowsRpcEmptyDenyMatchInAllow"]
if err := ioutil.WriteFile(file, []byte(valid2.authzPolicy), os.ModePerm); err != nil {
t.Fatalf("ioutil.WriteFile(%q) failed: %v", file, err)
if err := os.WriteFile(file, []byte(valid2.authzPolicy), os.ModePerm); err != nil {
t.Fatalf("os.WriteFile(%q) failed: %v", file, err)
}
// Wait 2 seconds for background go routine to read the updated files.
time.Sleep(2 * time.Second)
// Wait 30 ms for background go routine to read the updated files.
time.Sleep(30 * time.Millisecond)

// Verifying authorization decision.
_, err = client.UnaryCall(ctx, &pb.SimpleRequest{})
if got := status.Convert(err); got.Code() != valid2.wantStatusCode || got.Message() != valid2.wantErr {
t.Fatalf("[UnaryCall] error want:{%v %v} got:{%v %v}", valid2.wantStatusCode, valid2.wantErr, got.Code(), got.Message())
numRetries = 0
reloadSuccess := false
got := status.Convert(err)
for numRetries <= 20 {
if got.Code() == valid2.wantStatusCode && got.Message() == valid2.wantErr {
reloadSuccess = true
break
}
time.Sleep(10 * time.Millisecond)
numRetries++
}
if reloadSuccess == false {
t.Fatalf("error want:{%v %v} got:{%v %v}", valid2.wantStatusCode, valid2.wantErr, got.Code(), got.Message())
}
}
4 changes: 2 additions & 2 deletions authz/sdk_server_interceptors.go
Expand Up @@ -20,7 +20,7 @@ import (
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"sync/atomic"
"time"
"unsafe"
Expand Down Expand Up @@ -133,7 +133,7 @@ func (i *FileWatcherInterceptor) run(ctx context.Context) {
// constructor, if there is an error in reading the file or parsing the policy, the
// previous internalInterceptors will not be replaced.
func (i *FileWatcherInterceptor) updateInternalInterceptor() error {
policyContents, err := ioutil.ReadFile(i.policyFile)
policyContents, err := os.ReadFile(i.policyFile)
if err != nil {
return fmt.Errorf("policyFile(%s) read failed: %v", i.policyFile, err)
}
Expand Down
9 changes: 4 additions & 5 deletions authz/sdk_server_interceptors_test.go
Expand Up @@ -20,7 +20,6 @@ package authz_test

import (
"fmt"
"io/ioutil"
"os"
"path"
"testing"
Expand All @@ -34,15 +33,15 @@ func createTmpPolicyFile(t *testing.T, dirSuffix string, policy []byte) string {

// Create a temp directory. Passing an empty string for the first argument
// uses the system temp directory.
dir, err := ioutil.TempDir("", dirSuffix)
dir, err := os.MkdirTemp("", dirSuffix)
if err != nil {
t.Fatalf("ioutil.TempDir() failed: %v", err)
t.Fatalf("os.MkdirTemp() failed: %v", err)
}
t.Logf("Using tmpdir: %s", dir)
// Write policy into file.
filename := path.Join(dir, "policy.json")
if err := ioutil.WriteFile(filename, policy, os.ModePerm); err != nil {
t.Fatalf("ioutil.WriteFile(%q) failed: %v", filename, err)
if err := os.WriteFile(filename, policy, os.ModePerm); err != nil {
t.Fatalf("os.WriteFile(%q) failed: %v", filename, err)
}
t.Logf("Wrote policy %s to file at %s", string(policy), filename)
return filename
Expand Down