mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Update libtrust version
Updated to version of libtrust with the trust graph implementation Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
parent
61c6f206b0
commit
8a6c7100ea
6 changed files with 1002 additions and 1 deletions
|
@ -51,7 +51,7 @@ clone hg code.google.com/p/go.net 84a4013f96e0
|
|||
|
||||
clone hg code.google.com/p/gosqlite 74691fb6f837
|
||||
|
||||
clone git github.com/docker/libtrust 136d534cc940
|
||||
clone git github.com/docker/libtrust d273ef2565ca
|
||||
|
||||
# get Go tip's archive/tar, for xattr support and improved performance
|
||||
# TODO after Go 1.4 drops, bump our minimum supported version and drop this vendored dep
|
||||
|
|
50
vendor/src/github.com/docker/libtrust/trustgraph/graph.go
vendored
Normal file
50
vendor/src/github.com/docker/libtrust/trustgraph/graph.go
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
package trustgraph
|
||||
|
||||
import "github.com/docker/libtrust"
|
||||
|
||||
// TrustGraph represents a graph of authorization mapping
|
||||
// public keys to nodes and grants between nodes.
|
||||
type TrustGraph interface {
|
||||
// Verifies that the given public key is allowed to perform
|
||||
// the given action on the given node according to the trust
|
||||
// graph.
|
||||
Verify(libtrust.PublicKey, string, uint16) (bool, error)
|
||||
|
||||
// GetGrants returns an array of all grant chains which are used to
|
||||
// allow the requested permission.
|
||||
GetGrants(libtrust.PublicKey, string, uint16) ([][]*Grant, error)
|
||||
}
|
||||
|
||||
// Grant represents a transfer of permission from one part of the
|
||||
// trust graph to another. This is the only way to delegate
|
||||
// permission between two different sub trees in the graph.
|
||||
type Grant struct {
|
||||
// Subject is the namespace being granted
|
||||
Subject string
|
||||
|
||||
// Permissions is a bit map of permissions
|
||||
Permission uint16
|
||||
|
||||
// Grantee represents the node being granted
|
||||
// a permission scope. The grantee can be
|
||||
// either a namespace item or a key id where namespace
|
||||
// items will always start with a '/'.
|
||||
Grantee string
|
||||
|
||||
// statement represents the statement used to create
|
||||
// this object.
|
||||
statement *Statement
|
||||
}
|
||||
|
||||
// Permissions
|
||||
// Read node 0x01 (can read node, no sub nodes)
|
||||
// Write node 0x02 (can write to node object, cannot create subnodes)
|
||||
// Read subtree 0x04 (delegates read to each sub node)
|
||||
// Write subtree 0x08 (delegates write to each sub node, included create on the subject)
|
||||
//
|
||||
// Permission shortcuts
|
||||
// ReadItem = 0x01
|
||||
// WriteItem = 0x03
|
||||
// ReadAccess = 0x07
|
||||
// WriteAccess = 0x0F
|
||||
// Delegate = 0x0F
|
133
vendor/src/github.com/docker/libtrust/trustgraph/memory_graph.go
vendored
Normal file
133
vendor/src/github.com/docker/libtrust/trustgraph/memory_graph.go
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
package trustgraph
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
type grantNode struct {
|
||||
grants []*Grant
|
||||
children map[string]*grantNode
|
||||
}
|
||||
|
||||
type memoryGraph struct {
|
||||
roots map[string]*grantNode
|
||||
}
|
||||
|
||||
func newGrantNode() *grantNode {
|
||||
return &grantNode{
|
||||
grants: []*Grant{},
|
||||
children: map[string]*grantNode{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewMemoryGraph returns a new in memory trust graph created from
|
||||
// a static list of grants. This graph is immutable after creation
|
||||
// and any alterations should create a new instance.
|
||||
func NewMemoryGraph(grants []*Grant) TrustGraph {
|
||||
roots := map[string]*grantNode{}
|
||||
for _, grant := range grants {
|
||||
parts := strings.Split(grant.Grantee, "/")
|
||||
nodes := roots
|
||||
var node *grantNode
|
||||
var nodeOk bool
|
||||
for _, part := range parts {
|
||||
node, nodeOk = nodes[part]
|
||||
if !nodeOk {
|
||||
node = newGrantNode()
|
||||
nodes[part] = node
|
||||
}
|
||||
if part != "" {
|
||||
node.grants = append(node.grants, grant)
|
||||
}
|
||||
nodes = node.children
|
||||
}
|
||||
}
|
||||
return &memoryGraph{roots}
|
||||
}
|
||||
|
||||
func (g *memoryGraph) getGrants(name string) []*Grant {
|
||||
nameParts := strings.Split(name, "/")
|
||||
nodes := g.roots
|
||||
var node *grantNode
|
||||
var nodeOk bool
|
||||
for _, part := range nameParts {
|
||||
node, nodeOk = nodes[part]
|
||||
if !nodeOk {
|
||||
return nil
|
||||
}
|
||||
nodes = node.children
|
||||
}
|
||||
return node.grants
|
||||
}
|
||||
|
||||
func isSubName(name, sub string) bool {
|
||||
if strings.HasPrefix(name, sub) {
|
||||
if len(name) == len(sub) || name[len(sub)] == '/' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type walkFunc func(*Grant, []*Grant) bool
|
||||
|
||||
func foundWalkFunc(*Grant, []*Grant) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *memoryGraph) walkGrants(start, target string, permission uint16, f walkFunc, chain []*Grant, visited map[*Grant]bool, collect bool) bool {
|
||||
if visited == nil {
|
||||
visited = map[*Grant]bool{}
|
||||
}
|
||||
grants := g.getGrants(start)
|
||||
subGrants := make([]*Grant, 0, len(grants))
|
||||
for _, grant := range grants {
|
||||
if visited[grant] {
|
||||
continue
|
||||
}
|
||||
visited[grant] = true
|
||||
if grant.Permission&permission == permission {
|
||||
if isSubName(target, grant.Subject) {
|
||||
if f(grant, chain) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
subGrants = append(subGrants, grant)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, grant := range subGrants {
|
||||
var chainCopy []*Grant
|
||||
if collect {
|
||||
chainCopy = make([]*Grant, len(chain)+1)
|
||||
copy(chainCopy, chain)
|
||||
chainCopy[len(chainCopy)-1] = grant
|
||||
} else {
|
||||
chainCopy = nil
|
||||
}
|
||||
|
||||
if g.walkGrants(grant.Subject, target, permission, f, chainCopy, visited, collect) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *memoryGraph) Verify(key libtrust.PublicKey, node string, permission uint16) (bool, error) {
|
||||
return g.walkGrants(key.KeyID(), node, permission, foundWalkFunc, nil, nil, false), nil
|
||||
}
|
||||
|
||||
func (g *memoryGraph) GetGrants(key libtrust.PublicKey, node string, permission uint16) ([][]*Grant, error) {
|
||||
grants := [][]*Grant{}
|
||||
collect := func(grant *Grant, chain []*Grant) bool {
|
||||
grantChain := make([]*Grant, len(chain)+1)
|
||||
copy(grantChain, chain)
|
||||
grantChain[len(grantChain)-1] = grant
|
||||
grants = append(grants, grantChain)
|
||||
return false
|
||||
}
|
||||
g.walkGrants(key.KeyID(), node, permission, collect, nil, nil, true)
|
||||
return grants, nil
|
||||
}
|
174
vendor/src/github.com/docker/libtrust/trustgraph/memory_graph_test.go
vendored
Normal file
174
vendor/src/github.com/docker/libtrust/trustgraph/memory_graph_test.go
vendored
Normal file
|
@ -0,0 +1,174 @@
|
|||
package trustgraph
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
func createTestKeysAndGrants(count int) ([]*Grant, []libtrust.PrivateKey) {
|
||||
grants := make([]*Grant, count)
|
||||
keys := make([]libtrust.PrivateKey, count)
|
||||
for i := 0; i < count; i++ {
|
||||
pk, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
grant := &Grant{
|
||||
Subject: fmt.Sprintf("/user-%d", i+1),
|
||||
Permission: 0x0f,
|
||||
Grantee: pk.KeyID(),
|
||||
}
|
||||
keys[i] = pk
|
||||
grants[i] = grant
|
||||
}
|
||||
return grants, keys
|
||||
}
|
||||
|
||||
func testVerified(t *testing.T, g TrustGraph, k libtrust.PublicKey, keyName, target string, permission uint16) {
|
||||
if ok, err := g.Verify(k, target, permission); err != nil {
|
||||
t.Fatalf("Unexpected error during verification: %s", err)
|
||||
} else if !ok {
|
||||
t.Errorf("key failed verification\n\tKey: %s(%s)\n\tNamespace: %s", keyName, k.KeyID(), target)
|
||||
}
|
||||
}
|
||||
|
||||
func testNotVerified(t *testing.T, g TrustGraph, k libtrust.PublicKey, keyName, target string, permission uint16) {
|
||||
if ok, err := g.Verify(k, target, permission); err != nil {
|
||||
t.Fatalf("Unexpected error during verification: %s", err)
|
||||
} else if ok {
|
||||
t.Errorf("key should have failed verification\n\tKey: %s(%s)\n\tNamespace: %s", keyName, k.KeyID(), target)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerify(t *testing.T) {
|
||||
grants, keys := createTestKeysAndGrants(4)
|
||||
extraGrants := make([]*Grant, 3)
|
||||
extraGrants[0] = &Grant{
|
||||
Subject: "/user-3",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-2",
|
||||
}
|
||||
extraGrants[1] = &Grant{
|
||||
Subject: "/user-3/sub-project",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-4",
|
||||
}
|
||||
extraGrants[2] = &Grant{
|
||||
Subject: "/user-4",
|
||||
Permission: 0x07,
|
||||
Grantee: "/user-1",
|
||||
}
|
||||
grants = append(grants, extraGrants...)
|
||||
|
||||
g := NewMemoryGraph(grants)
|
||||
|
||||
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f)
|
||||
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1/some-project/sub-value", 0x0f)
|
||||
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-4", 0x07)
|
||||
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2/", 0x0f)
|
||||
testVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-3/sub-value", 0x0f)
|
||||
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3/sub-value", 0x0f)
|
||||
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3", 0x0f)
|
||||
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3/", 0x0f)
|
||||
testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3/sub-project", 0x0f)
|
||||
testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3/sub-project/app", 0x0f)
|
||||
testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-4", 0x0f)
|
||||
|
||||
testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-2", 0x0f)
|
||||
testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-3/sub-value", 0x0f)
|
||||
testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-4", 0x0f)
|
||||
testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-1/", 0x0f)
|
||||
testNotVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-2", 0x0f)
|
||||
testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-4", 0x0f)
|
||||
testNotVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3", 0x0f)
|
||||
}
|
||||
|
||||
func TestCircularWalk(t *testing.T) {
|
||||
grants, keys := createTestKeysAndGrants(3)
|
||||
user1Grant := &Grant{
|
||||
Subject: "/user-2",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-1",
|
||||
}
|
||||
user2Grant := &Grant{
|
||||
Subject: "/user-1",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-2",
|
||||
}
|
||||
grants = append(grants, user1Grant, user2Grant)
|
||||
|
||||
g := NewMemoryGraph(grants)
|
||||
|
||||
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f)
|
||||
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-2", 0x0f)
|
||||
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x0f)
|
||||
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-1", 0x0f)
|
||||
testVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-3", 0x0f)
|
||||
|
||||
testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-3", 0x0f)
|
||||
testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3", 0x0f)
|
||||
}
|
||||
|
||||
func assertGrantSame(t *testing.T, actual, expected *Grant) {
|
||||
if actual != expected {
|
||||
t.Fatalf("Unexpected grant retrieved\n\tExpected: %v\n\tActual: %v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGrants(t *testing.T) {
|
||||
grants, keys := createTestKeysAndGrants(5)
|
||||
extraGrants := make([]*Grant, 4)
|
||||
extraGrants[0] = &Grant{
|
||||
Subject: "/user-3/friend-project",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-2/friends",
|
||||
}
|
||||
extraGrants[1] = &Grant{
|
||||
Subject: "/user-3/sub-project",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-4",
|
||||
}
|
||||
extraGrants[2] = &Grant{
|
||||
Subject: "/user-2/friends",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-5/fun-project",
|
||||
}
|
||||
extraGrants[3] = &Grant{
|
||||
Subject: "/user-5/fun-project",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-1",
|
||||
}
|
||||
grants = append(grants, extraGrants...)
|
||||
|
||||
g := NewMemoryGraph(grants)
|
||||
|
||||
grantChains, err := g.GetGrants(keys[3], "/user-3/sub-project/specific-app", 0x0f)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting grants: %s", err)
|
||||
}
|
||||
if len(grantChains) != 1 {
|
||||
t.Fatalf("Expected number of grant chains returned, expected %d, received %d", 1, len(grantChains))
|
||||
}
|
||||
if len(grantChains[0]) != 2 {
|
||||
t.Fatalf("Unexpected number of grants retrieved\n\tExpected: %d\n\tActual: %d", 2, len(grantChains[0]))
|
||||
}
|
||||
assertGrantSame(t, grantChains[0][0], grants[3])
|
||||
assertGrantSame(t, grantChains[0][1], extraGrants[1])
|
||||
|
||||
grantChains, err = g.GetGrants(keys[0], "/user-3/friend-project/fun-app", 0x0f)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting grants: %s", err)
|
||||
}
|
||||
if len(grantChains) != 1 {
|
||||
t.Fatalf("Expected number of grant chains returned, expected %d, received %d", 1, len(grantChains))
|
||||
}
|
||||
if len(grantChains[0]) != 4 {
|
||||
t.Fatalf("Unexpected number of grants retrieved\n\tExpected: %d\n\tActual: %d", 2, len(grantChains[0]))
|
||||
}
|
||||
assertGrantSame(t, grantChains[0][0], grants[0])
|
||||
assertGrantSame(t, grantChains[0][1], extraGrants[3])
|
||||
assertGrantSame(t, grantChains[0][2], extraGrants[2])
|
||||
assertGrantSame(t, grantChains[0][3], extraGrants[0])
|
||||
}
|
227
vendor/src/github.com/docker/libtrust/trustgraph/statement.go
vendored
Normal file
227
vendor/src/github.com/docker/libtrust/trustgraph/statement.go
vendored
Normal file
|
@ -0,0 +1,227 @@
|
|||
package trustgraph
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
type jsonGrant struct {
|
||||
Subject string `json:"subject"`
|
||||
Permission uint16 `json:"permission"`
|
||||
Grantee string `json:"grantee"`
|
||||
}
|
||||
|
||||
type jsonRevocation struct {
|
||||
Subject string `json:"subject"`
|
||||
Revocation uint16 `json:"revocation"`
|
||||
Grantee string `json:"grantee"`
|
||||
}
|
||||
|
||||
type jsonStatement struct {
|
||||
Revocations []*jsonRevocation `json:"revocations"`
|
||||
Grants []*jsonGrant `json:"grants"`
|
||||
Expiration time.Time `json:"expiration"`
|
||||
IssuedAt time.Time `json:"issuedAt"`
|
||||
}
|
||||
|
||||
func (g *jsonGrant) Grant(statement *Statement) *Grant {
|
||||
return &Grant{
|
||||
Subject: g.Subject,
|
||||
Permission: g.Permission,
|
||||
Grantee: g.Grantee,
|
||||
statement: statement,
|
||||
}
|
||||
}
|
||||
|
||||
// Statement represents a set of grants made from a verifiable
|
||||
// authority. A statement has an expiration associated with it
|
||||
// set by the authority.
|
||||
type Statement struct {
|
||||
jsonStatement
|
||||
|
||||
signature *libtrust.JSONSignature
|
||||
}
|
||||
|
||||
// IsExpired returns whether the statement has expired
|
||||
func (s *Statement) IsExpired() bool {
|
||||
return s.Expiration.Before(time.Now().Add(-10 * time.Second))
|
||||
}
|
||||
|
||||
// Bytes returns an indented json representation of the statement
|
||||
// in a byte array. This value can be written to a file or stream
|
||||
// without alteration.
|
||||
func (s *Statement) Bytes() ([]byte, error) {
|
||||
return s.signature.PrettySignature("signatures")
|
||||
}
|
||||
|
||||
// LoadStatement loads and verifies a statement from an input stream.
|
||||
func LoadStatement(r io.Reader, authority *x509.CertPool) (*Statement, error) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
js, err := libtrust.ParsePrettySignature(b, "signatures")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload, err := js.Payload()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var statement Statement
|
||||
err = json.Unmarshal(payload, &statement.jsonStatement)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if authority == nil {
|
||||
_, err = js.Verify()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
_, err = js.VerifyChains(authority)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
statement.signature = js
|
||||
|
||||
return &statement, nil
|
||||
}
|
||||
|
||||
// CreateStatements creates and signs a statement from a stream of grants
|
||||
// and revocations in a JSON array.
|
||||
func CreateStatement(grants, revocations io.Reader, expiration time.Duration, key libtrust.PrivateKey, chain []*x509.Certificate) (*Statement, error) {
|
||||
var statement Statement
|
||||
err := json.NewDecoder(grants).Decode(&statement.jsonStatement.Grants)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.NewDecoder(revocations).Decode(&statement.jsonStatement.Revocations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statement.jsonStatement.Expiration = time.Now().UTC().Add(expiration)
|
||||
statement.jsonStatement.IssuedAt = time.Now().UTC()
|
||||
|
||||
b, err := json.MarshalIndent(&statement.jsonStatement, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statement.signature, err = libtrust.NewJSONSignature(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = statement.signature.SignWithChain(key, chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &statement, nil
|
||||
}
|
||||
|
||||
type statementList []*Statement
|
||||
|
||||
func (s statementList) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s statementList) Less(i, j int) bool {
|
||||
return s[i].IssuedAt.Before(s[j].IssuedAt)
|
||||
}
|
||||
|
||||
func (s statementList) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// CollapseStatements returns a single list of the valid statements as well as the
|
||||
// time when the next grant will expire.
|
||||
func CollapseStatements(statements []*Statement, useExpired bool) ([]*Grant, time.Time, error) {
|
||||
sorted := make(statementList, 0, len(statements))
|
||||
for _, statement := range statements {
|
||||
if useExpired || !statement.IsExpired() {
|
||||
sorted = append(sorted, statement)
|
||||
}
|
||||
}
|
||||
sort.Sort(sorted)
|
||||
|
||||
var minExpired time.Time
|
||||
var grantCount int
|
||||
roots := map[string]*grantNode{}
|
||||
for i, statement := range sorted {
|
||||
if statement.Expiration.Before(minExpired) || i == 0 {
|
||||
minExpired = statement.Expiration
|
||||
}
|
||||
for _, grant := range statement.Grants {
|
||||
parts := strings.Split(grant.Grantee, "/")
|
||||
nodes := roots
|
||||
g := grant.Grant(statement)
|
||||
grantCount = grantCount + 1
|
||||
|
||||
for _, part := range parts {
|
||||
node, nodeOk := nodes[part]
|
||||
if !nodeOk {
|
||||
node = newGrantNode()
|
||||
nodes[part] = node
|
||||
}
|
||||
node.grants = append(node.grants, g)
|
||||
nodes = node.children
|
||||
}
|
||||
}
|
||||
|
||||
for _, revocation := range statement.Revocations {
|
||||
parts := strings.Split(revocation.Grantee, "/")
|
||||
nodes := roots
|
||||
|
||||
var node *grantNode
|
||||
var nodeOk bool
|
||||
for _, part := range parts {
|
||||
node, nodeOk = nodes[part]
|
||||
if !nodeOk {
|
||||
break
|
||||
}
|
||||
nodes = node.children
|
||||
}
|
||||
if node != nil {
|
||||
for _, grant := range node.grants {
|
||||
if isSubName(grant.Subject, revocation.Subject) {
|
||||
grant.Permission = grant.Permission &^ revocation.Revocation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
retGrants := make([]*Grant, 0, grantCount)
|
||||
for _, rootNodes := range roots {
|
||||
retGrants = append(retGrants, rootNodes.grants...)
|
||||
}
|
||||
|
||||
return retGrants, minExpired, nil
|
||||
}
|
||||
|
||||
// FilterStatements filters the statements to statements including the given grants.
|
||||
func FilterStatements(grants []*Grant) ([]*Statement, error) {
|
||||
statements := map[*Statement]bool{}
|
||||
for _, grant := range grants {
|
||||
if grant.statement != nil {
|
||||
statements[grant.statement] = true
|
||||
}
|
||||
}
|
||||
retStatements := make([]*Statement, len(statements))
|
||||
var i int
|
||||
for statement := range statements {
|
||||
retStatements[i] = statement
|
||||
i++
|
||||
}
|
||||
return retStatements, nil
|
||||
}
|
417
vendor/src/github.com/docker/libtrust/trustgraph/statement_test.go
vendored
Normal file
417
vendor/src/github.com/docker/libtrust/trustgraph/statement_test.go
vendored
Normal file
|
@ -0,0 +1,417 @@
|
|||
package trustgraph
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/docker/libtrust/testutil"
|
||||
)
|
||||
|
||||
const testStatementExpiration = time.Hour * 5
|
||||
|
||||
func generateStatement(grants []*Grant, key libtrust.PrivateKey, chain []*x509.Certificate) (*Statement, error) {
|
||||
var statement Statement
|
||||
|
||||
statement.Grants = make([]*jsonGrant, len(grants))
|
||||
for i, grant := range grants {
|
||||
statement.Grants[i] = &jsonGrant{
|
||||
Subject: grant.Subject,
|
||||
Permission: grant.Permission,
|
||||
Grantee: grant.Grantee,
|
||||
}
|
||||
}
|
||||
statement.IssuedAt = time.Now()
|
||||
statement.Expiration = time.Now().Add(testStatementExpiration)
|
||||
statement.Revocations = make([]*jsonRevocation, 0)
|
||||
|
||||
marshalled, err := json.MarshalIndent(statement.jsonStatement, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig, err := libtrust.NewJSONSignature(marshalled)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = sig.SignWithChain(key, chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statement.signature = sig
|
||||
|
||||
return &statement, nil
|
||||
}
|
||||
|
||||
func generateTrustChain(t *testing.T, chainLen int) (libtrust.PrivateKey, *x509.CertPool, []*x509.Certificate) {
|
||||
caKey, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating key: %s", err)
|
||||
}
|
||||
ca, err := testutil.GenerateTrustCA(caKey.CryptoPublicKey(), caKey.CryptoPrivateKey())
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating ca: %s", err)
|
||||
}
|
||||
|
||||
parent := ca
|
||||
parentKey := caKey
|
||||
chain := make([]*x509.Certificate, chainLen)
|
||||
for i := chainLen - 1; i > 0; i-- {
|
||||
intermediatekey, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("Error generate key: %s", err)
|
||||
}
|
||||
chain[i], err = testutil.GenerateIntermediate(intermediatekey.CryptoPublicKey(), parentKey.CryptoPrivateKey(), parent)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating intermdiate certificate: %s", err)
|
||||
}
|
||||
parent = chain[i]
|
||||
parentKey = intermediatekey
|
||||
}
|
||||
trustKey, err := libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("Error generate key: %s", err)
|
||||
}
|
||||
chain[0], err = testutil.GenerateTrustCert(trustKey.CryptoPublicKey(), parentKey.CryptoPrivateKey(), parent)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generate trust cert: %s", err)
|
||||
}
|
||||
|
||||
caPool := x509.NewCertPool()
|
||||
caPool.AddCert(ca)
|
||||
|
||||
return trustKey, caPool, chain
|
||||
}
|
||||
|
||||
func TestLoadStatement(t *testing.T) {
|
||||
grantCount := 4
|
||||
grants, _ := createTestKeysAndGrants(grantCount)
|
||||
|
||||
trustKey, caPool, chain := generateTrustChain(t, 6)
|
||||
|
||||
statement, err := generateStatement(grants, trustKey, chain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating statement: %s", err)
|
||||
}
|
||||
|
||||
statementBytes, err := statement.Bytes()
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting statement bytes: %s", err)
|
||||
}
|
||||
|
||||
s2, err := LoadStatement(bytes.NewReader(statementBytes), caPool)
|
||||
if err != nil {
|
||||
t.Fatalf("Error loading statement: %s", err)
|
||||
}
|
||||
if len(s2.Grants) != grantCount {
|
||||
t.Fatalf("Unexpected grant length\n\tExpected: %d\n\tActual: %d", grantCount, len(s2.Grants))
|
||||
}
|
||||
|
||||
pool := x509.NewCertPool()
|
||||
_, err = LoadStatement(bytes.NewReader(statementBytes), pool)
|
||||
if err == nil {
|
||||
t.Fatalf("No error thrown verifying without an authority")
|
||||
} else if _, ok := err.(x509.UnknownAuthorityError); !ok {
|
||||
t.Fatalf("Unexpected error verifying without authority: %s", err)
|
||||
}
|
||||
|
||||
s2, err = LoadStatement(bytes.NewReader(statementBytes), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error loading statement: %s", err)
|
||||
}
|
||||
if len(s2.Grants) != grantCount {
|
||||
t.Fatalf("Unexpected grant length\n\tExpected: %d\n\tActual: %d", grantCount, len(s2.Grants))
|
||||
}
|
||||
|
||||
badData := make([]byte, len(statementBytes))
|
||||
copy(badData, statementBytes)
|
||||
badData[0] = '['
|
||||
_, err = LoadStatement(bytes.NewReader(badData), nil)
|
||||
if err == nil {
|
||||
t.Fatalf("No error thrown parsing bad json")
|
||||
}
|
||||
|
||||
alteredData := make([]byte, len(statementBytes))
|
||||
copy(alteredData, statementBytes)
|
||||
alteredData[30] = '0'
|
||||
_, err = LoadStatement(bytes.NewReader(alteredData), nil)
|
||||
if err == nil {
|
||||
t.Fatalf("No error thrown from bad data")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollapseGrants(t *testing.T) {
|
||||
grantCount := 8
|
||||
grants, keys := createTestKeysAndGrants(grantCount)
|
||||
linkGrants := make([]*Grant, 4)
|
||||
linkGrants[0] = &Grant{
|
||||
Subject: "/user-3",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-2",
|
||||
}
|
||||
linkGrants[1] = &Grant{
|
||||
Subject: "/user-3/sub-project",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-4",
|
||||
}
|
||||
linkGrants[2] = &Grant{
|
||||
Subject: "/user-6",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-7",
|
||||
}
|
||||
linkGrants[3] = &Grant{
|
||||
Subject: "/user-6/sub-project/specific-app",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-5",
|
||||
}
|
||||
trustKey, pool, chain := generateTrustChain(t, 3)
|
||||
|
||||
statements := make([]*Statement, 3)
|
||||
var err error
|
||||
statements[0], err = generateStatement(grants[0:4], trustKey, chain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating statement: %s", err)
|
||||
}
|
||||
statements[1], err = generateStatement(grants[4:], trustKey, chain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating statement: %s", err)
|
||||
}
|
||||
statements[2], err = generateStatement(linkGrants, trustKey, chain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating statement: %s", err)
|
||||
}
|
||||
|
||||
statementsCopy := make([]*Statement, len(statements))
|
||||
for i, statement := range statements {
|
||||
b, err := statement.Bytes()
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting statement bytes: %s", err)
|
||||
}
|
||||
verifiedStatement, err := LoadStatement(bytes.NewReader(b), pool)
|
||||
if err != nil {
|
||||
t.Fatalf("Error loading statement: %s", err)
|
||||
}
|
||||
// Force sort by reversing order
|
||||
statementsCopy[len(statementsCopy)-i-1] = verifiedStatement
|
||||
}
|
||||
statements = statementsCopy
|
||||
|
||||
collapsedGrants, expiration, err := CollapseStatements(statements, false)
|
||||
if len(collapsedGrants) != 12 {
|
||||
t.Fatalf("Unexpected number of grants\n\tExpected: %d\n\tActual: %s", 12, len(collapsedGrants))
|
||||
}
|
||||
if expiration.After(time.Now().Add(time.Hour*5)) || expiration.Before(time.Now()) {
|
||||
t.Fatalf("Unexpected expiration time: %s", expiration.String())
|
||||
}
|
||||
g := NewMemoryGraph(collapsedGrants)
|
||||
|
||||
testVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f)
|
||||
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x0f)
|
||||
testVerified(t, g, keys[2].PublicKey(), "user-key-3", "/user-3", 0x0f)
|
||||
testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-4", 0x0f)
|
||||
testVerified(t, g, keys[4].PublicKey(), "user-key-5", "/user-5", 0x0f)
|
||||
testVerified(t, g, keys[5].PublicKey(), "user-key-6", "/user-6", 0x0f)
|
||||
testVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-7", 0x0f)
|
||||
testVerified(t, g, keys[7].PublicKey(), "user-key-8", "/user-8", 0x0f)
|
||||
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3", 0x0f)
|
||||
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-3/sub-project/specific-app", 0x0f)
|
||||
testVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3/sub-project", 0x0f)
|
||||
testVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-6", 0x0f)
|
||||
testVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-6/sub-project/specific-app", 0x0f)
|
||||
testVerified(t, g, keys[4].PublicKey(), "user-key-5", "/user-6/sub-project/specific-app", 0x0f)
|
||||
|
||||
testNotVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-3", 0x0f)
|
||||
testNotVerified(t, g, keys[3].PublicKey(), "user-key-4", "/user-6/sub-project", 0x0f)
|
||||
testNotVerified(t, g, keys[4].PublicKey(), "user-key-5", "/user-6/sub-project", 0x0f)
|
||||
|
||||
// Add revocation grant
|
||||
statements = append(statements, &Statement{
|
||||
jsonStatement{
|
||||
IssuedAt: time.Now(),
|
||||
Expiration: time.Now().Add(testStatementExpiration),
|
||||
Grants: []*jsonGrant{},
|
||||
Revocations: []*jsonRevocation{
|
||||
&jsonRevocation{
|
||||
Subject: "/user-1",
|
||||
Revocation: 0x0f,
|
||||
Grantee: keys[0].KeyID(),
|
||||
},
|
||||
&jsonRevocation{
|
||||
Subject: "/user-2",
|
||||
Revocation: 0x08,
|
||||
Grantee: keys[1].KeyID(),
|
||||
},
|
||||
&jsonRevocation{
|
||||
Subject: "/user-6",
|
||||
Revocation: 0x0f,
|
||||
Grantee: "/user-7",
|
||||
},
|
||||
&jsonRevocation{
|
||||
Subject: "/user-9",
|
||||
Revocation: 0x0f,
|
||||
Grantee: "/user-10",
|
||||
},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
})
|
||||
|
||||
collapsedGrants, expiration, err = CollapseStatements(statements, false)
|
||||
if len(collapsedGrants) != 12 {
|
||||
t.Fatalf("Unexpected number of grants\n\tExpected: %d\n\tActual: %s", 12, len(collapsedGrants))
|
||||
}
|
||||
if expiration.After(time.Now().Add(time.Hour*5)) || expiration.Before(time.Now()) {
|
||||
t.Fatalf("Unexpected expiration time: %s", expiration.String())
|
||||
}
|
||||
g = NewMemoryGraph(collapsedGrants)
|
||||
|
||||
testNotVerified(t, g, keys[0].PublicKey(), "user-key-1", "/user-1", 0x0f)
|
||||
testNotVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x0f)
|
||||
testNotVerified(t, g, keys[6].PublicKey(), "user-key-7", "/user-6/sub-project/specific-app", 0x0f)
|
||||
|
||||
testVerified(t, g, keys[1].PublicKey(), "user-key-2", "/user-2", 0x07)
|
||||
}
|
||||
|
||||
func TestFilterStatements(t *testing.T) {
|
||||
grantCount := 8
|
||||
grants, keys := createTestKeysAndGrants(grantCount)
|
||||
linkGrants := make([]*Grant, 3)
|
||||
linkGrants[0] = &Grant{
|
||||
Subject: "/user-3",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-2",
|
||||
}
|
||||
linkGrants[1] = &Grant{
|
||||
Subject: "/user-5",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-4",
|
||||
}
|
||||
linkGrants[2] = &Grant{
|
||||
Subject: "/user-7",
|
||||
Permission: 0x0f,
|
||||
Grantee: "/user-6",
|
||||
}
|
||||
|
||||
trustKey, _, chain := generateTrustChain(t, 3)
|
||||
|
||||
statements := make([]*Statement, 5)
|
||||
var err error
|
||||
statements[0], err = generateStatement(grants[0:2], trustKey, chain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating statement: %s", err)
|
||||
}
|
||||
statements[1], err = generateStatement(grants[2:4], trustKey, chain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating statement: %s", err)
|
||||
}
|
||||
statements[2], err = generateStatement(grants[4:6], trustKey, chain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating statement: %s", err)
|
||||
}
|
||||
statements[3], err = generateStatement(grants[6:], trustKey, chain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating statement: %s", err)
|
||||
}
|
||||
statements[4], err = generateStatement(linkGrants, trustKey, chain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating statement: %s", err)
|
||||
}
|
||||
collapsed, _, err := CollapseStatements(statements, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Error collapsing grants: %s", err)
|
||||
}
|
||||
|
||||
// Filter 1, all 5 statements
|
||||
filter1, err := FilterStatements(collapsed)
|
||||
if err != nil {
|
||||
t.Fatalf("Error filtering statements: %s", err)
|
||||
}
|
||||
if len(filter1) != 5 {
|
||||
t.Fatalf("Wrong number of statements, expected %d, received %d", 5, len(filter1))
|
||||
}
|
||||
|
||||
// Filter 2, one statement
|
||||
filter2, err := FilterStatements([]*Grant{collapsed[0]})
|
||||
if err != nil {
|
||||
t.Fatalf("Error filtering statements: %s", err)
|
||||
}
|
||||
if len(filter2) != 1 {
|
||||
t.Fatalf("Wrong number of statements, expected %d, received %d", 1, len(filter2))
|
||||
}
|
||||
|
||||
// Filter 3, 2 statements, from graph lookup
|
||||
g := NewMemoryGraph(collapsed)
|
||||
lookupGrants, err := g.GetGrants(keys[1], "/user-3", 0x0f)
|
||||
if err != nil {
|
||||
t.Fatalf("Error looking up grants: %s", err)
|
||||
}
|
||||
if len(lookupGrants) != 1 {
|
||||
t.Fatalf("Wrong numberof grant chains returned from lookup, expected %d, received %d", 1, len(lookupGrants))
|
||||
}
|
||||
if len(lookupGrants[0]) != 2 {
|
||||
t.Fatalf("Wrong number of grants looked up, expected %d, received %d", 2, len(lookupGrants))
|
||||
}
|
||||
filter3, err := FilterStatements(lookupGrants[0])
|
||||
if err != nil {
|
||||
t.Fatalf("Error filtering statements: %s", err)
|
||||
}
|
||||
if len(filter3) != 2 {
|
||||
t.Fatalf("Wrong number of statements, expected %d, received %d", 2, len(filter3))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCreateStatement(t *testing.T) {
|
||||
grantJSON := bytes.NewReader([]byte(`[
|
||||
{
|
||||
"subject": "/user-2",
|
||||
"permission": 15,
|
||||
"grantee": "/user-1"
|
||||
},
|
||||
{
|
||||
"subject": "/user-7",
|
||||
"permission": 1,
|
||||
"grantee": "/user-9"
|
||||
},
|
||||
{
|
||||
"subject": "/user-3",
|
||||
"permission": 15,
|
||||
"grantee": "/user-2"
|
||||
}
|
||||
]`))
|
||||
revocationJSON := bytes.NewReader([]byte(`[
|
||||
{
|
||||
"subject": "user-8",
|
||||
"revocation": 12,
|
||||
"grantee": "user-9"
|
||||
}
|
||||
]`))
|
||||
|
||||
trustKey, pool, chain := generateTrustChain(t, 3)
|
||||
|
||||
statement, err := CreateStatement(grantJSON, revocationJSON, testStatementExpiration, trustKey, chain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating statement: %s", err)
|
||||
}
|
||||
|
||||
b, err := statement.Bytes()
|
||||
if err != nil {
|
||||
t.Fatalf("Error retrieving bytes: %s", err)
|
||||
}
|
||||
|
||||
verified, err := LoadStatement(bytes.NewReader(b), pool)
|
||||
if err != nil {
|
||||
t.Fatalf("Error loading statement: %s", err)
|
||||
}
|
||||
|
||||
if len(verified.Grants) != 3 {
|
||||
t.Errorf("Unexpected number of grants, expected %d, received %d", 3, len(verified.Grants))
|
||||
}
|
||||
|
||||
if len(verified.Revocations) != 1 {
|
||||
t.Errorf("Unexpected number of revocations, expected %d, received %d", 1, len(verified.Revocations))
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue