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

registration entry federated trust domains support #563

Merged
merged 2 commits into from
Sep 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ required = ["github.com/hashicorp/go-plugin",
version = "1.0.3"

[[constraint]]
branch = "master"
name = "github.com/jinzhu/gorm"
version = "1.9.1"

[[constraint]]
name = "github.com/mitchellh/cli"
Expand Down
15 changes: 14 additions & 1 deletion pkg/server/plugin/datastore/sql/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

const (
// version of the database in the code
codeVersion = 1
codeVersion = 2
Copy link
Member

Choose a reason for hiding this comment

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

image

)

func migrateDB(db *gorm.DB) (err error) {
Expand Down Expand Up @@ -98,6 +98,8 @@ func migrateVersion(tx *gorm.DB, version int) (versionOut int, err error) {
switch version {
case 0:
err = migrateToV1(tx)
case 1:
err = migrateToV2(tx)
default:
err = sqlError.New("no migration support for version %d", version)
}
Expand Down Expand Up @@ -135,3 +137,14 @@ func migrateToV1(tx *gorm.DB) error {
}
return nil
}

func migrateToV2(tx *gorm.DB) error {
// creates the join table.... no changes to the tables backing these
// models is expected. It's too bad GORM doesn't expose a way to piecemeal
// migrate.
if err := tx.AutoMigrate(&RegisteredEntry{}, &Bundle{}).Error; err != nil {
return sqlError.Wrap(err)
}

return nil
}
14 changes: 8 additions & 6 deletions pkg/server/plugin/datastore/sql/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ type Bundle struct {

TrustDomain string `gorm:"not null;unique_index"`
CACerts []CACert

FederatedEntries []RegisteredEntry `gorm:"many2many:federated_registration_entries;"`
}

type AttestedNode struct {
Expand Down Expand Up @@ -55,12 +57,12 @@ func (NodeSelector) TableName() string {
type RegisteredEntry struct {
Model

EntryID string `gorm:"unique_index"`
SpiffeID string
ParentID string
TTL int32
Selectors []Selector
// TODO: Add support to Federated Bundles [https://github.com/spiffe/spire/issues/42]
EntryID string `gorm:"unique_index"`
SpiffeID string
ParentID string
TTL int32
Selectors []Selector
FederatesWith []Bundle `gorm:"many2many:federated_registration_entries;"`
}

// Keep time simple and easily comparable with UNIX time
Expand Down
161 changes: 130 additions & 31 deletions pkg/server/plugin/datastore/sql/sql.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package sql

import (
"bytes"
"context"
"crypto/x509"
"errors"
"fmt"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -531,6 +533,39 @@ func deleteBundle(tx *gorm.DB, req *datastore.DeleteBundleRequest) (*datastore.D
return nil, sqlError.Wrap(err)
}

// Get a count of associated registration entries
entriesAssociation := tx.Model(model).Association("FederatedEntries")
entriesCount := entriesAssociation.Count()
if err := entriesAssociation.Error; err != nil {
return nil, sqlError.Wrap(err)
}

if entriesCount > 0 {
switch req.Mode {
case datastore.DeleteBundleRequest_DELETE:
// TODO: figure out how to do this gracefully with GORM.
if err := tx.Exec(bindVars(tx, `DELETE FROM registered_entries WHERE id in (
SELECT
registered_entries.id
FROM
registered_entries
INNER JOIN
federated_registration_entries
ON
federated_registration_entries.registered_entry_id = registered_entries.id
WHERE
federated_registration_entries.bundle_id = ?)`), model.ID).Error; err != nil {
return nil, sqlError.Wrap(err)
}
case datastore.DeleteBundleRequest_DISSOCIATE:
if err := entriesAssociation.Clear().Error; err != nil {
return nil, sqlError.Wrap(err)
}
default:
return nil, sqlError.New("cannot delete bundle; federated with %d registration entries", entriesCount)
}
}

// Fetch related CA certs for response before we delete them
var caCerts []CACert
if err := tx.Model(model).Related(&caCerts).Error; err != nil {
Expand Down Expand Up @@ -774,13 +809,21 @@ func createRegistrationEntry(tx *gorm.DB,
SpiffeID: req.Entry.SpiffeId,
ParentID: req.Entry.ParentId,
TTL: req.Entry.Ttl,
// TODO: Add support to Federated Bundles [https://github.com/spiffe/spire/issues/42]
}

if err := tx.Create(&newRegisteredEntry).Error; err != nil {
return nil, sqlError.Wrap(err)
}

federatesWith, err := makeFederatesWith(tx, req.Entry.FederatesWith)
if err != nil {
return nil, err
}

if err := tx.Model(&newRegisteredEntry).Association("FederatesWith").Append(federatesWith).Error; err != nil {
return nil, err
}

for _, registeredSelector := range req.Entry.Selectors {
newSelector := Selector{
RegisteredEntryID: newRegisteredEntry.ID,
Expand Down Expand Up @@ -814,27 +857,13 @@ func fetchRegistrationEntry(tx *gorm.DB,
return nil, sqlError.Wrap(err)
}

var fetchedSelectors []*Selector
if err := tx.Model(&fetchedRegisteredEntry).Related(&fetchedSelectors).Error; err != nil {
return nil, sqlError.Wrap(err)
}

selectors := make([]*common.Selector, 0, len(fetchedSelectors))

for _, selector := range fetchedSelectors {
selectors = append(selectors, &common.Selector{
Type: selector.Type,
Value: selector.Value})
entry, err := modelToEntry(tx, fetchedRegisteredEntry)
if err != nil {
return nil, err
}

return &datastore.FetchRegistrationEntryResponse{
Entry: &common.RegistrationEntry{
EntryId: fetchedRegisteredEntry.EntryID,
Selectors: selectors,
SpiffeId: fetchedRegisteredEntry.SpiffeID,
ParentId: fetchedRegisteredEntry.ParentID,
Ttl: fetchedRegisteredEntry.TTL,
},
Entry: entry,
}, nil
}

Expand Down Expand Up @@ -953,7 +982,6 @@ func updateRegistrationEntry(tx *gorm.DB,
}

// Get the existing entry
// TODO: Refactor message type to take EntryID directly from the entry - see #449
entry := RegisteredEntry{}
if err := tx.Find(&entry, "entry_id = ?", req.Entry.EntryId).Error; err != nil {
return nil, sqlError.Wrap(err)
Expand Down Expand Up @@ -982,6 +1010,15 @@ func updateRegistrationEntry(tx *gorm.DB,
return nil, sqlError.Wrap(err)
}

federatesWith, err := makeFederatesWith(tx, req.Entry.FederatesWith)
if err != nil {
return nil, err
}

if err := tx.Model(&entry).Association("FederatesWith").Replace(federatesWith).Error; err != nil {
return nil, err
}

req.Entry.EntryId = entry.EntryID
return &datastore.UpdateRegistrationEntryResponse{
Entry: req.Entry,
Expand All @@ -996,15 +1033,19 @@ func deleteRegistrationEntry(tx *gorm.DB,
return nil, sqlError.Wrap(err)
}

if err := tx.Delete(&entry).Error; err != nil {
return nil, sqlError.Wrap(err)
}

respEntry, err := modelToEntry(tx, entry)
if err != nil {
return nil, err
}

if err := tx.Model(&entry).Association("FederatesWith").Clear().Error; err != nil {
return nil, err
}

if err := tx.Delete(&entry).Error; err != nil {
return nil, sqlError.Wrap(err)
}

return &datastore.DeleteRegistrationEntryResponse{
Entry: respEntry,
}, nil
Expand Down Expand Up @@ -1157,23 +1198,36 @@ func modelsToUnsortedEntries(tx *gorm.DB, fetchedRegisteredEntries []RegisteredE
}

func modelToEntry(tx *gorm.DB, model RegisteredEntry) (*common.RegistrationEntry, error) {
var selectors []*common.Selector
var fetchedSelectors []*Selector
if err := tx.Model(&model).Related(&fetchedSelectors).Error; err != nil {
return nil, sqlError.Wrap(err)
}

selectors := make([]*common.Selector, 0, len(fetchedSelectors))
for _, selector := range fetchedSelectors {
selectors = append(selectors, &common.Selector{
Type: selector.Type,
Value: selector.Value})
Value: selector.Value,
})
}

var fetchedBundles []*Bundle
if err := tx.Model(&model).Association("FederatesWith").Find(&fetchedBundles).Error; err != nil {
return nil, sqlError.Wrap(err)
}

var federatesWith []string
for _, bundle := range fetchedBundles {
federatesWith = append(federatesWith, bundle.TrustDomain)
}

return &common.RegistrationEntry{
EntryId: model.EntryID,
Selectors: selectors,
SpiffeId: model.SpiffeID,
ParentId: model.ParentID,
Ttl: model.TTL,
EntryId: model.EntryID,
Selectors: selectors,
SpiffeId: model.SpiffeID,
ParentId: model.ParentID,
Ttl: model.TTL,
FederatesWith: federatesWith,
}, nil
}

Expand All @@ -1196,3 +1250,48 @@ func modelToJoinToken(model JoinToken) *datastore.JoinToken {
Expiry: model.Expiry,
}
}

func makeFederatesWith(tx *gorm.DB, ids []string) ([]*Bundle, error) {
var bundles []*Bundle
if err := tx.Where("trust_domain in (?)", ids).Find(&bundles).Error; err != nil {
return nil, err
}

// make sure all of the ids were found
idset := make(map[string]bool)
for _, bundle := range bundles {
idset[bundle.TrustDomain] = true
}

for _, id := range ids {
if !idset[id] {
return nil, fmt.Errorf("unable to find federated bundle %q", id)
}
}

return bundles, nil
}

func bindVars(db *gorm.DB, query string) string {
dialect := db.Dialect()
if dialect.BindVar(1) == "?" {
return query
}

return bindVarsFn(func(n int) string {
return dialect.BindVar(n)
}, query)
}

func bindVarsFn(fn func(int) string, query string) string {
var buf bytes.Buffer
var n int
for i := strings.Index(query, "?"); i != -1; i = strings.Index(query, "?") {
n++
buf.WriteString(query[:i])
buf.WriteString(fn(n))
query = query[i+1:]
}
buf.WriteString(query)
return buf.String()
}
Loading