Provide secret manager for proxy cache project

This commit provides the secret manager for proxy cache.
The secret is used for pushing blobs to local when it's proxied from
remote registry.
Each secret can be used only once and has a relatively short expiration
time.

Signed-off-by: Daniel Jiang <jiangd@vmware.com>
This commit is contained in:
Daniel Jiang 2020-07-16 17:15:36 +08:00
parent 4d4a04fad4
commit 840aa86dfa
2 changed files with 103 additions and 0 deletions

View File

@ -0,0 +1,70 @@
package secret
import (
"sync"
"time"
"github.com/goharbor/harbor/src/common/utils"
)
const defaultExpiration = 15 * time.Second
type targetRepository struct {
name string
expiresAt time.Time
}
// Manager generates and verifies the secret for repositories under proxy project
// The secret normally is used for authorizing a request trying to push artifact to a project
// A secret can be used only once and expires in a short period of time.
// As the request will be sent to 127.0.0.1 so the secret will live in one process.
type Manager interface {
// Generate generates a secret for the given repository, sample value for repository: "library/ubuntu"
Generate(repository string) string
// Verify verifies the secret against repo name, after the verification the secret should be invalid
Verify(secret, repository string) bool
}
type mgr struct {
m *sync.Map
exp time.Duration
}
func (man *mgr) Generate(rn string) string {
s := utils.GenerateRandomStringWithLen(8)
man.m.Store(s, targetRepository{name: rn, expiresAt: time.Now().Add(man.exp)})
return s
}
func (man *mgr) Verify(sec, rn string) bool {
v, ok := man.m.Load(sec)
if !ok {
return false
}
p, ok := v.(targetRepository)
if ok && p.name == rn {
defer man.m.Delete(sec)
return p.expiresAt.After(time.Now())
}
return false
}
var (
defaultManager Manager
once sync.Once
)
// GetManager returns the default manager which is a singleton in the package
func GetManager() Manager {
once.Do(func() {
defaultManager = createManager(defaultExpiration)
})
return defaultManager
}
func createManager(d time.Duration) Manager {
return &mgr{
m: &sync.Map{},
exp: d,
}
}

View File

@ -0,0 +1,33 @@
package secret
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestManger(t *testing.T) {
manager := GetManager()
rn1 := "project1/golang"
assert.False(t, manager.Verify("whatever", rn1))
s1 := manager.Generate(rn1)
s2 := manager.Generate(rn1)
assert.False(t, s1 == s2)
assert.False(t, manager.Verify(s1, "project1/donotexist"))
assert.True(t, manager.Verify(s1, rn1))
// A secret can be used only once.
assert.False(t, manager.Verify(s1, rn1))
manager2 := GetManager()
assert.Equal(t, manager2, manager)
}
func TestExpiration(t *testing.T) {
manager := createManager(1 * time.Second)
rn1 := "project1/golang"
s := manager.Generate(rn1)
// Sleep till the secret expires
time.Sleep(2 * time.Second)
assert.False(t, manager.Verify(s, rn1))
}