Skip to content

Commit

Permalink
feat: crl cache (#462)
Browse files Browse the repository at this point in the history
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
  • Loading branch information
Two-Hearts authored Sep 26, 2024
1 parent 9faa6e2 commit 84c2ec0
Show file tree
Hide file tree
Showing 11 changed files with 736 additions and 28 deletions.
7 changes: 7 additions & 0 deletions dir/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,10 @@ func ConfigFS() SysFS {
func PluginFS() SysFS {
return NewSysFS(filepath.Join(userLibexecDirPath(), PathPlugins))
}

// CacheFS is the cache SysFS.
//
// To get the root of crl file cache, use `CacheFS().SysFS(PathCRLCache)`.
func CacheFS() SysFS {
return NewSysFS(userCacheDirPath())
}
11 changes: 11 additions & 0 deletions dir/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,14 @@ func TestPluginFS(t *testing.T) {
t.Fatalf(`SysPath() failed. got: %q, want: %q`, path, filepath.Join(userLibexecDirPath(), PathPlugins, "plugin"))
}
}

func TestCRLFileCacheFS(t *testing.T) {
cacheFS := CacheFS()
path, err := cacheFS.SysPath(PathCRLCache)
if err != nil {
t.Fatalf("SysPath() failed. err = %v", err)
}
if path != filepath.Join(UserCacheDir, PathCRLCache) {
t.Fatalf(`SysPath() failed. got: %q, want: %q`, path, UserConfigDir)
}
}
41 changes: 36 additions & 5 deletions dir/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// limitations under the License.

// Package dir implements Notation directory structure.
// [directory spec]: https://github.com/notaryproject/notation/blob/main/specs/directory.md
// [directory spec]: https://notaryproject.dev/docs/user-guides/how-to/directory-structure/
//
// Example:
//
Expand All @@ -31,7 +31,7 @@
// - Set custom configurations directory:
// dir.UserConfigDir = '/path/to/configurations/'
//
// Only user level directory is supported for RC.1, and system level directory
// Only user level directory is supported, and system level directory
// may be added later.
package dir

Expand All @@ -44,6 +44,7 @@ import (
var (
UserConfigDir string // Absolute path of user level {NOTATION_CONFIG}
UserLibexecDir string // Absolute path of user level {NOTATION_LIBEXEC}
UserCacheDir string // Absolute path of user level {NOTATION_CACHE}
)

const (
Expand All @@ -65,8 +66,6 @@ const (
PathOCITrustPolicy = "trustpolicy.oci.json"
// PathBlobTrustPolicy is the Blob trust policy file relative path.
PathBlobTrustPolicy = "trustpolicy.blob.json"
// PathPlugins is the plugins directory relative path.
PathPlugins = "plugins"
// LocalKeysDir is the directory name for local key relative path.
LocalKeysDir = "localkeys"
// LocalCertificateExtension defines the extension of the certificate files.
Expand All @@ -77,7 +76,24 @@ const (
TrustStoreDir = "truststore"
)

var userConfigDir = os.UserConfigDir // for unit test
// The relative path to {NOTATION_LIBEXEC}
const (
// PathPlugins is the plugins directory relative path.
PathPlugins = "plugins"
)

// The relative path to {NOTATION_CACHE}
const (
// PathCRLCache is the crl file cache directory relative path.
PathCRLCache = "crl"
)

// for unit tests
var (
userConfigDir = os.UserConfigDir

userCacheDir = os.UserCacheDir
)

// userConfigDirPath returns the user level {NOTATION_CONFIG} path.
func userConfigDirPath() string {
Expand All @@ -103,6 +119,21 @@ func userLibexecDirPath() string {
return UserLibexecDir
}

// userCacheDirPath returns the user level {NOTATION_CACHE} path.
func userCacheDirPath() string {
if UserCacheDir == "" {
userDir, err := userCacheDir()
if err != nil {
// fallback to current directory
UserCacheDir = filepath.Join("."+notation, "cache")
return UserCacheDir
}
// set user cache
UserCacheDir = filepath.Join(userDir, notation)
}
return UserCacheDir
}

// LocalKeyPath returns the local key and local cert relative paths.
func LocalKeyPath(name string) (keyPath, certPath string) {
basePath := path.Join(LocalKeysDir, name)
Expand Down
29 changes: 23 additions & 6 deletions dir/path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,22 @@ package dir

import (
"os"
"path/filepath"
"testing"
)

func mockGetUserConfig() (string, error) {
func mockUserPath() (string, error) {
return "/path/", nil
}

func setup() {
UserConfigDir = ""
UserLibexecDir = ""
UserCacheDir = ""
}

func Test_UserConfigDirPath(t *testing.T) {
userConfigDir = mockGetUserConfig
userConfigDir = mockUserPath
setup()
got := userConfigDirPath()
if got != "/path/notation" {
Expand All @@ -39,25 +41,40 @@ func Test_UserConfigDirPath(t *testing.T) {
func Test_NoHomeVariable(t *testing.T) {
t.Setenv("HOME", "")
t.Setenv("XDG_CONFIG_HOME", "")
t.Setenv("XDG_CACHE_HOME", "")
setup()
userConfigDir = os.UserConfigDir
got := userConfigDirPath()
if got != ".notation" {
t.Fatalf(`UserConfigDirPath() = %q, want ".notation"`, UserConfigDir)
t.Fatalf(`userConfigDirPath() = %q, want ".notation"`, got)
}
got = userCacheDirPath()
want := filepath.Join("."+notation, "cache")
if got != want {
t.Fatalf(`userCacheDirPath() = %q, want %q`, got, want)
}
}

func Test_UserLibexecDirPath(t *testing.T) {
userConfigDir = mockGetUserConfig
userConfigDir = mockUserPath
setup()
got := userLibexecDirPath()
if got != "/path/notation" {
t.Fatalf(`UserConfigDirPath() = %q, want "/path/notation"`, got)
}
}

func Test_UserCacheDirPath(t *testing.T) {
userCacheDir = mockUserPath
setup()
got := userCacheDirPath()
if got != "/path/notation" {
t.Fatalf(`UserCacheDirPath() = %q, want "/path/notation"`, got)
}
}

func TestLocalKeyPath(t *testing.T) {
userConfigDir = mockGetUserConfig
userConfigDir = mockUserPath
setup()
_ = userConfigDirPath()
_ = userLibexecDirPath()
Expand All @@ -71,7 +88,7 @@ func TestLocalKeyPath(t *testing.T) {
}

func TestX509TrustStoreDir(t *testing.T) {
userConfigDir = mockGetUserConfig
userConfigDir = mockUserPath
setup()
_ = userConfigDirPath()
_ = userLibexecDirPath()
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.22.0

require (
github.com/go-ldap/ldap/v3 v3.4.8
github.com/notaryproject/notation-core-go v1.1.1-0.20240918011623-695ea0c1ad1f
github.com/notaryproject/notation-core-go v1.1.1-0.20240920045731-0786f51de737
github.com/notaryproject/notation-plugin-framework-go v1.0.0
github.com/notaryproject/tspclient-go v0.2.0
github.com/opencontainers/go-digest v1.0.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/notaryproject/notation-core-go v1.1.1-0.20240918011623-695ea0c1ad1f h1:TmwJtM3AZ7iQ1LJEbHRPAMRw4hA52/AbVrllSVjCNP0=
github.com/notaryproject/notation-core-go v1.1.1-0.20240918011623-695ea0c1ad1f/go.mod h1:+6AOh41JPrnVLbW/19SJqdhVHwKgIINBO/np0e7nXJA=
github.com/notaryproject/notation-core-go v1.1.1-0.20240920045731-0786f51de737 h1:Hp93KBCABE29+6zdS0GTg0T1SXj6qGatJyN1JMvTQqk=
github.com/notaryproject/notation-core-go v1.1.1-0.20240920045731-0786f51de737/go.mod h1:b/70rA4OgOHlg0A7pb8zTWKJadFO6781zS3a37KHEJQ=
github.com/notaryproject/notation-plugin-framework-go v1.0.0 h1:6Qzr7DGXoCgXEQN+1gTZWuJAZvxh3p8Lryjn5FaLzi4=
github.com/notaryproject/notation-plugin-framework-go v1.0.0/go.mod h1:RqWSrTOtEASCrGOEffq0n8pSg2KOgKYiWqFWczRSics=
github.com/notaryproject/tspclient-go v0.2.0 h1:g/KpQGmyk/h7j60irIRG1mfWnibNOzJ8WhLqAzuiQAQ=
Expand Down
34 changes: 34 additions & 0 deletions internal/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package file

import (
"errors"
"fmt"
"io"
"io/fs"
"os"
Expand All @@ -23,6 +24,11 @@ import (
"strings"
)

const (
// tempFileNamePrefix is the prefix of the temporary file
tempFileNamePrefix = "notation-*"
)

// ErrNotRegularFile is returned when the file is not an regular file.
var ErrNotRegularFile = errors.New("not regular file")

Expand Down Expand Up @@ -110,3 +116,31 @@ func CopyDirToDir(src, dst string) error {
func TrimFileExtension(fileName string) string {
return strings.TrimSuffix(fileName, filepath.Ext(fileName))
}

// WriteFile writes content to a temporary file and moves it to path.
// If path already exists and is a file, WriteFile overwrites it.
func WriteFile(path string, content []byte) (writeErr error) {
tempFile, err := os.CreateTemp("", tempFileNamePrefix)
if err != nil {
return fmt.Errorf("failed to create temp file: %w", err)
}
defer func() {
// remove the temp file in case of error
if writeErr != nil {
tempFile.Close()
os.Remove(tempFile.Name())
}
}()

if _, err := tempFile.Write(content); err != nil {
return fmt.Errorf("failed to write content to temp file: %w", err)
}

// close before moving
if err := tempFile.Close(); err != nil {
return fmt.Errorf("failed to close temp file: %w", err)
}

// rename is atomic on UNIX-like platforms
return os.Rename(tempFile.Name(), path)
}
61 changes: 48 additions & 13 deletions internal/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"testing"
)

Expand All @@ -26,7 +27,10 @@ func TestCopyToDir(t *testing.T) {
tempDir := t.TempDir()
data := []byte("data")
filename := filepath.Join(tempDir, "a", "file.txt")
if err := writeFile(filename, data); err != nil {
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(filename, data); err != nil {
t.Fatal(err)
}

Expand All @@ -45,7 +49,10 @@ func TestCopyToDir(t *testing.T) {
destDir := t.TempDir()
data := []byte("data")
filename := filepath.Join(tempDir, "a", "file.txt")
if err := writeFile(filename, data); err != nil {
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(filename, data); err != nil {
t.Fatal(err)
}

Expand Down Expand Up @@ -77,7 +84,10 @@ func TestCopyToDir(t *testing.T) {
data := []byte("data")
// prepare file
filename := filepath.Join(tempDir, "a", "file.txt")
if err := writeFile(filename, data); err != nil {
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(filename, data); err != nil {
t.Fatal(err)
}
// forbid reading
Expand All @@ -100,7 +110,10 @@ func TestCopyToDir(t *testing.T) {
data := []byte("data")
// prepare file
filename := filepath.Join(tempDir, "a", "file.txt")
if err := writeFile(filename, data); err != nil {
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(filename, data); err != nil {
t.Fatal(err)
}
// forbid dest directory operation
Expand All @@ -123,7 +136,10 @@ func TestCopyToDir(t *testing.T) {
data := []byte("data")
// prepare file
filename := filepath.Join(tempDir, "a", "file.txt")
if err := writeFile(filename, data); err != nil {
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(filename, data); err != nil {
t.Fatal(err)
}
// forbid writing to destTempDir
Expand All @@ -140,7 +156,10 @@ func TestCopyToDir(t *testing.T) {
tempDir := t.TempDir()
data := []byte("data")
filename := filepath.Join(tempDir, "a", "file.txt")
if err := writeFile(filename, data); err != nil {
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(filename, data); err != nil {
t.Fatal(err)
}

Expand All @@ -161,6 +180,29 @@ func TestFileNameWithoutExtension(t *testing.T) {
}
}

func TestWriteFile(t *testing.T) {
tempDir := t.TempDir()
content := []byte("test WriteFile")

t.Run("permission denied", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on Windows")
}
err := os.Chmod(tempDir, 0)
if err != nil {
t.Fatal(err)
}
err = WriteFile(filepath.Join(tempDir, "testFile"), content)
if err == nil || !strings.Contains(err.Error(), "permission denied") {
t.Fatalf("expected permission denied error, but got %s", err)
}
err = os.Chmod(tempDir, 0700)
if err != nil {
t.Fatal(err)
}
})
}

func validFileContent(t *testing.T, filename string, content []byte) {
b, err := os.ReadFile(filename)
if err != nil {
Expand All @@ -170,10 +212,3 @@ func validFileContent(t *testing.T, filename string, content []byte) {
t.Fatal("file content is not correct")
}
}

func writeFile(path string, data []byte) error {
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
return err
}
return os.WriteFile(path, data, 0600)
}
Loading

0 comments on commit 84c2ec0

Please sign in to comment.