Files
zeekay 285bb5a76b rename: ziti → zt across all paths
- Repo names: ziti-* → zt-*
- Go modules: github.com/hanzozt/ziti → github.com/hanzozt/zt
- Docker images: hanzozt/ziti-* → hanzozt/zt-*
- C/Java/JS symbols: ziti_* → zt_*
- File and directory names updated
2026-02-18 18:34:10 -08:00

167 lines
5.2 KiB
Go

package posture
import (
"fmt"
"github.com/hanzozt/edge-api/rest_model"
edge_apis "github.com/hanzozt/sdk-golang/edge-apis"
"github.com/hanzozt/sdk-golang/zt/edge"
)
// Submitter handles transmission of posture response data to authentication and policy
// enforcement endpoints.
type Submitter interface {
SendPostureResponse(response rest_model.PostureResponseCreate) error
SendPostureResponseBulk(responses []rest_model.PostureResponseCreate) error
}
// RouterConnectionProvider supplies active router connections for submitting posture data
// directly to edge routers in high-availability deployments.
type RouterConnectionProvider interface {
GetRouterConnections() []edge.RouterConn
}
// ApiSessionProvider supplies the current API session, enabling submitters to determine
// the appropriate destination for posture responses based on authentication type.
type ApiSessionProvider interface {
GetCurrentApiSession() edge_apis.ApiSession
}
var _ Submitter = (*MultiSubmitter)(nil)
// MultiSubmitter routes posture responses to appropriate destinations based on session type
// and router capabilities. Legacy sessions always submit to the controller, while OIDC sessions
// submit to routers that support posture checks and fall back to the controller for older routers.
type MultiSubmitter struct {
ApiSessionProvider ApiSessionProvider
LegacySubmitter Submitter
RouterConnectionProvider RouterConnectionProvider
}
// NewMultiSubmitter creates a submitter that intelligently routes posture responses based on
// session authentication method and router capabilities.
func NewMultiSubmitter(apiSessionProvider ApiSessionProvider, legacySubmitter Submitter, routerConnectionProvider RouterConnectionProvider) *MultiSubmitter {
return &MultiSubmitter{
ApiSessionProvider: apiSessionProvider,
LegacySubmitter: legacySubmitter,
RouterConnectionProvider: routerConnectionProvider,
}
}
func (m *MultiSubmitter) SendPostureResponse(response rest_model.PostureResponseCreate) error {
if response == nil {
return nil
}
return m.SendPostureResponseBulk([]rest_model.PostureResponseCreate{response})
}
func (m *MultiSubmitter) SendPostureResponseBulk(responses []rest_model.PostureResponseCreate) error {
if len(responses) == 0 {
return nil
}
apiSession := m.ApiSessionProvider.GetCurrentApiSession()
//legacy api sessions do not use router posture always goes to the controller
if apiSession.GetType() == edge_apis.ApiSessionTypeLegacy {
return m.LegacySubmitter.SendPostureResponseBulk(responses)
}
sendToController := false
routerConns := m.RouterConnectionProvider.GetRouterConnections()
errors := &MultiDestinationError{
routerErrors: map[edge.RouterConn]error{},
controllerError: nil,
}
for _, routerConn := range routerConns {
if routerConn.GetBoolHeader(edge.SupportsPostureChecksHeader) {
err := routerConn.SendPosture(responses)
if err != nil {
errors.routerErrors[routerConn] = err
}
} else {
sendToController = true
}
}
if sendToController {
legacyResponse := filterToLegacyPostureResponses(responses)
errors.controllerError = m.LegacySubmitter.SendPostureResponseBulk(legacyResponse)
}
if errors.HasErrors() {
return errors
}
return nil
}
// filterToLegacyPostureResponses removes responses from the provided list, returning
// only posture response types supported by older controller versions that do not handle
// newer types supported by routers.
func filterToLegacyPostureResponses(responses []rest_model.PostureResponseCreate) []rest_model.PostureResponseCreate {
legacyResponse := make([]rest_model.PostureResponseCreate, 0, len(responses))
for _, response := range responses {
if response.TypeID() != edge.PostureCheckTypeTOTP {
legacyResponse = append(legacyResponse, response)
}
}
return legacyResponse
}
// MultiDestinationError aggregates errors from posture response submission attempts
// to multiple destinations (controller and routers), providing detailed failure information.
type MultiDestinationError struct {
routerErrors map[edge.RouterConn]error
controllerError error
}
// Error formats all submission failures into a comprehensive error message identifying
// which destinations failed and why.
func (e *MultiDestinationError) Error() string {
result := ""
if !e.HasErrors() {
if e == nil {
return ""
}
panic("unexpected error state, there are no errors, but treated as an error")
}
if e.controllerError != nil {
result = "failed to send posture response to controller: " + e.controllerError.Error()
}
if len(e.routerErrors) > 0 {
if result != "" {
result += " and "
}
routerErrStr := ""
for routerConn, err := range e.routerErrors {
if routerErrStr != "" {
routerErrStr += ", "
}
routerErrStr = routerErrStr + fmt.Sprintf("router [%s]: %s", routerConn.GetRouterName(), err.Error())
}
result = result + fmt.Sprintf("failed to send posture response to %d routers: %s", len(e.routerErrors), routerErrStr)
}
return result
}
// HasErrors returns true if any submission attempts failed, either to the controller
// or to any routers.
func (e *MultiDestinationError) HasErrors() bool {
return len(e.routerErrors) > 0 || e.controllerError != nil
}