Skip to content

Commit

Permalink
Add APIs for SPIRE Federation (#483)
Browse files Browse the repository at this point in the history
* linted

Signed-off-by: Maia Iyer <[email protected]>

* added list function

Signed-off-by: Maia Iyer <[email protected]>

* added other trustdomain apis

Signed-off-by: Maia Iyer <[email protected]>

* Added apis

Signed-off-by: Maia Iyer <[email protected]>

* fixing spacing

Signed-off-by: Maia Iyer <[email protected]>

* Adding workaround for json parsing

Signed-off-by: Maia Iyer <[email protected]>

* add workaround for update federation

Signed-off-by: Maia Iyer <[email protected]>

* updated OpenAPI doc

Signed-off-by: Maia Iyer <[email protected]>

---------

Signed-off-by: Maia Iyer <[email protected]>
  • Loading branch information
maia-iyer authored Sep 9, 2024
1 parent 0482028 commit 7a393f6
Show file tree
Hide file tree
Showing 14 changed files with 964 additions and 374 deletions.
87 changes: 87 additions & 0 deletions api/agent/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
bundle "github.com/spiffe/spire-api-sdk/proto/spire/api/server/bundle/v1"
debugServer "github.com/spiffe/spire-api-sdk/proto/spire/api/server/debug/v1"
entry "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1"
trustdomain "github.com/spiffe/spire-api-sdk/proto/spire/api/server/trustdomain/v1"
types "github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"google.golang.org/grpc/health/grpc_health_v1"

Expand Down Expand Up @@ -322,6 +323,92 @@ func (s *Server) DeleteFederatedBundle(inp DeleteFederatedBundleRequest) (*Delet
return (*DeleteFederatedBundleResponse)(bundle), nil
}

// Federation APIs
type ListFederationRelationshipsRequest trustdomain.ListFederationRelationshipsRequest
type ListFederationRelationshipsResponse trustdomain.ListFederationRelationshipsResponse

func (s *Server) ListFederationRelationships(inp ListFederationRelationshipsRequest) (*ListFederationRelationshipsResponse, error) { //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
inpReq := trustdomain.ListFederationRelationshipsRequest(inp) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
var conn *grpc.ClientConn
conn, err := grpc.Dial(s.SpireServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, err
}
defer conn.Close()
client := trustdomain.NewTrustDomainClient(conn)

bundle, err := client.ListFederationRelationships(context.Background(), &inpReq)
if err != nil {
return nil, err
}

return (*ListFederationRelationshipsResponse)(bundle), nil
}

type CreateFederationRelationshipRequest trustdomain.BatchCreateFederationRelationshipRequest
type CreateFederationRelationshipResponse trustdomain.BatchCreateFederationRelationshipResponse

func (s *Server) CreateFederationRelationship(inp CreateFederationRelationshipRequest) (*CreateFederationRelationshipResponse, error) { //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
inpReq := trustdomain.BatchCreateFederationRelationshipRequest(inp) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
var conn *grpc.ClientConn
conn, err := grpc.Dial(s.SpireServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, err
}
defer conn.Close()
client := trustdomain.NewTrustDomainClient(conn)

bundle, err := client.BatchCreateFederationRelationship(context.Background(), &inpReq)
if err != nil {
return nil, err
}

return (*CreateFederationRelationshipResponse)(bundle), nil
}

type UpdateFederationRelationshipRequest trustdomain.BatchUpdateFederationRelationshipRequest
type UpdateFederationRelationshipResponse trustdomain.BatchUpdateFederationRelationshipResponse

func (s *Server) UpdateFederationRelationship(inp UpdateFederationRelationshipRequest) (*UpdateFederationRelationshipResponse, error) { //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
inpReq := trustdomain.BatchUpdateFederationRelationshipRequest(inp) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
var conn *grpc.ClientConn
conn, err := grpc.Dial(s.SpireServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, err
}
defer conn.Close()
client := trustdomain.NewTrustDomainClient(conn)

bundle, err := client.BatchUpdateFederationRelationship(context.Background(), &inpReq)
if err != nil {
return nil, err
}

return (*UpdateFederationRelationshipResponse)(bundle), nil
}

type DeleteFederationRelationshipRequest trustdomain.BatchDeleteFederationRelationshipRequest
type DeleteFederationRelationshipResponse trustdomain.BatchDeleteFederationRelationshipResponse

func (s *Server) DeleteFederationRelationship(inp DeleteFederationRelationshipRequest) (*DeleteFederationRelationshipResponse, error) { //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
inpReq := trustdomain.BatchDeleteFederationRelationshipRequest(inp) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
var conn *grpc.ClientConn
conn, err := grpc.Dial(s.SpireServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, err
}
defer conn.Close()
client := trustdomain.NewTrustDomainClient(conn)

bundle, err := client.BatchDeleteFederationRelationship(context.Background(), &inpReq)
if err != nil {
return nil, err
}

return (*DeleteFederationRelationshipResponse)(bundle), nil
}


/*
Agent
Expand Down
175 changes: 175 additions & 0 deletions api/agent/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
"github.com/hashicorp/hcl/hcl/token"
"github.com/pkg/errors"

"google.golang.org/protobuf/encoding/protojson"
trustdomain "github.com/spiffe/spire-api-sdk/proto/spire/api/server/trustdomain/v1"

"github.com/spiffe/tornjak/pkg/agent/authentication/authenticator"
"github.com/spiffe/tornjak/pkg/agent/authorization"
agentdb "github.com/spiffe/tornjak/pkg/agent/db"
Expand Down Expand Up @@ -607,6 +610,174 @@ func (s *Server) federatedBundleDelete(w http.ResponseWriter, r *http.Request) {
}
}

// Federation APIs
func (s *Server) federationList(w http.ResponseWriter, r *http.Request) {
var input ListFederationRelationshipsRequest
buf := new(strings.Builder)

n, err := io.Copy(buf, r.Body)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
data := buf.String()

if n == 0 {
input = ListFederationRelationshipsRequest{}
} else {
err := json.Unmarshal([]byte(data), &input)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
}

ret, err := s.ListFederationRelationships(input) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusInternalServerError)
return
}

cors(w, r)
je := json.NewEncoder(w)
err = je.Encode(ret)
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
}

func (s *Server) federationCreate(w http.ResponseWriter, r *http.Request) {
var input CreateFederationRelationshipRequest
var rawInput trustdomain.BatchCreateFederationRelationshipRequest
buf := new(strings.Builder)

n, err := io.Copy(buf, r.Body)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
data := buf.String()

if n == 0 {
input = CreateFederationRelationshipRequest{}
} else {
// required to use protojson because of oneof field
err := protojson.Unmarshal([]byte(data), &rawInput)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
input = CreateFederationRelationshipRequest(rawInput) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
}

ret, err := s.CreateFederationRelationship(input) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusInternalServerError)
return
}

cors(w, r)
je := json.NewEncoder(w)
err = je.Encode(ret)
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
}

func (s *Server) federationUpdate(w http.ResponseWriter, r *http.Request) {
var input UpdateFederationRelationshipRequest
var rawInput trustdomain.BatchUpdateFederationRelationshipRequest
buf := new(strings.Builder)

n, err := io.Copy(buf, r.Body)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
data := buf.String()

if n == 0 {
input = UpdateFederationRelationshipRequest{}
} else {
err := protojson.Unmarshal([]byte(data), &rawInput)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
input = UpdateFederationRelationshipRequest(rawInput) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet

}

ret, err := s.UpdateFederationRelationship(input) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusInternalServerError)
return
}

cors(w, r)
je := json.NewEncoder(w)
err = je.Encode(ret)
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
}

func (s *Server) federationDelete(w http.ResponseWriter, r *http.Request) {
var input DeleteFederationRelationshipRequest
buf := new(strings.Builder)

n, err := io.Copy(buf, r.Body)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
data := buf.String()

if n == 0 {
input = DeleteFederationRelationshipRequest{}
} else {
err := json.Unmarshal([]byte(data), &input)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
}

ret, err := s.DeleteFederationRelationship(input) //nolint:govet //Ignoring mutex (not being used) - sync.Mutex by value is unused for linter govet
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusInternalServerError)
return
}

cors(w, r)
je := json.NewEncoder(w)
err = je.Encode(ret)
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
}


func cors(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
Expand Down Expand Up @@ -813,6 +984,10 @@ func (s *Server) GetRouter() http.Handler {
apiRtr.HandleFunc("/api/v1/spire/federations/bundles", s.federatedBundleCreate).Methods("POST")
apiRtr.HandleFunc("/api/v1/spire/federations/bundles", s.federatedBundleUpdate).Methods("PATCH")
apiRtr.HandleFunc("/api/v1/spire/federations/bundles", s.federatedBundleDelete).Methods("DELETE")
apiRtr.HandleFunc("/api/v1/spire/federations", s.federationList).Methods("GET")
apiRtr.HandleFunc("/api/v1/spire/federations", s.federationCreate).Methods("POST")
apiRtr.HandleFunc("/api/v1/spire/federations", s.federationUpdate).Methods("PATCH")
apiRtr.HandleFunc("/api/v1/spire/federations", s.federationDelete).Methods("DELETE")

// Tornjak specific
apiRtr.HandleFunc("/api/v1/tornjak/serverinfo", s.tornjakGetServerInfo).Methods("GET")
Expand Down
3 changes: 1 addition & 2 deletions cmd/agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

type cliOptions struct {
genericOptions struct {
spireFile string
spireFile string
tornjakFile string
expandEnv bool
}
Expand Down Expand Up @@ -124,7 +124,6 @@ func GetServerInfo(configData string) (agentapi.TornjakSpireServerInfo, error) {
return agentapi.TornjakSpireServerInfo{}, errors.Errorf("Could not parse SPIRE Config: %v", err)
}


if config.Plugins == nil {
return agentapi.TornjakSpireServerInfo{}, errors.New("config plugins map should not be nil")
}
Expand Down
Loading

0 comments on commit 7a393f6

Please sign in to comment.