package server import ( "context" "flag" "fmt" "net/http" "net/url" "os" "github.com/gin-contrib/requestid" "github.com/gin-gonic/gin" "github.com/go-logr/logr" "github.com/peterbourgon/ff/v3" "edge-infra.dev/pkg/edge/api/middleware" "edge-infra.dev/pkg/lib/fog" "edge-infra.dev/pkg/sds/emergencyaccess/client" "edge-infra.dev/pkg/sds/emergencyaccess/config" "edge-infra.dev/pkg/sds/emergencyaccess/eaconst" "edge-infra.dev/pkg/sds/emergencyaccess/eagateway" eamiddleware "edge-infra.dev/pkg/sds/emergencyaccess/middleware" "edge-infra.dev/pkg/sds/emergencyaccess/msgsvc" "edge-infra.dev/pkg/sds/emergencyaccess/remotecli" "edge-infra.dev/pkg/sds/emergencyaccess/requestservice" ) const ( defaultAuthorizeCommandPath = "authorizeCommand" defaultAuthorizeRequestPath = "authorizeRequest" defaultAuthorizeTargetPath = "authorizeTarget" defaultAuthorizeUserPath = "authorizeUser" defaultResolveTargetPath = "resolveTarget" ) var ( ErrUnauthorizedUser = fmt.Errorf("user is unauthorized") ) type GatewayServer struct { GinEngine *gin.Engine rcli eagateway.RemoteCLI log logr.Logger client *http.Client requestService *requestservice.RequestService authorizeCommandURL *url.URL authorizeRequestURL *url.URL resolveTargetURL *url.URL authorizeTargetURL *url.URL authorizeUserURL *url.URL } // New returns an EAGAteway service with our standard pattern. Function will // return an error if setAuthServiceURL fails. func New(config eagateway.Config, router *gin.Engine, log logr.Logger, rcli eagateway.RemoteCLI, requestService *requestservice.RequestService, checks ...func() error) (server *GatewayServer, err error) { cl := client.New() server = &GatewayServer{ GinEngine: router, rcli: rcli, requestService: requestService, log: log, client: cl, } err = server.setAuthServiceURLs(config) if err != nil { return nil, err } server.newGinServer(checks...) return server, nil } func (server *GatewayServer) setAuthServiceURLs(config eagateway.Config) error { host, err := url.Parse("http://" + config.AuthServiceHost) if err != nil { return err } server.authorizeCommandURL = host.JoinPath(defaultAuthorizeCommandPath) server.authorizeRequestURL = host.JoinPath(defaultAuthorizeRequestPath) server.resolveTargetURL = host.JoinPath(defaultResolveTargetPath) server.authorizeTargetURL = host.JoinPath(defaultAuthorizeTargetPath) server.authorizeUserURL = host.JoinPath(defaultAuthorizeUserPath) return nil } func (server *GatewayServer) newGinServer(checks ...func() error) { router := server.GinEngine router.ContextWithFallback = true router.Use(middleware.SetRequestContext()) router.Use(gin.Recovery()) // public endpoints router.Any("/health", eamiddleware.HealthCheck(checks...)) router.Any("/ready", func(c *gin.Context) { c.String(http.StatusOK, "ok") }) public := router.Group("/ea") public.Use(requestid.New(requestid.WithCustomHeaderStrKey(eamiddleware.CorrelationIDKey))) public.Use(eamiddleware.VerifyAPIVersion(eaconst.APIVersion)) public.Use(eamiddleware.SetLoggerInContext(server.log)) public.Use(eamiddleware.RequestBookendLogs()) public.Use(eamiddleware.SaveAuthToContext()) // authorized endpoints authorized := public.Group("/") authorized.POST("/startSession", server.StartSession) authorized.POST("/sendCommand", server.SendCommand) authorized.POST("/endSession", server.EndSession) } func Run() error { conf := Config{} flags := flag.NewFlagSet("eagateway", flag.ExitOnError) conf.BindFlags(flags) // flags passed as --auth-service-host from cli or AUTH_SERVICE_HOST in env will be parsed here. if err := ff.Parse(flags, os.Args[1:], ff.WithEnvVarNoPrefix(), ff.WithIgnoreUndefined(true)); err != nil { return err } router := gin.New() log := newLogger() // Remotecli uses the global logger from context for some log messages ctx := fog.IntoContext(context.Background(), log) ms, err := msgsvc.NewMessageService(ctx) if err != nil { return fmt.Errorf("failed to initialise message service: %w", err) } rcli := remotecli.New(ctx, ms) db, check, err := config.DB(conf.SQL) if err != nil { return fmt.Errorf("error connecting to database: %w", err) } requestService, err := requestservice.New(db) if err != nil { return fmt.Errorf("failed to initialise request service: %w", err) } server, err := New(conf.EAGAteway, router, log, rcli, requestService, check) if err != nil { return err } return server.GinEngine.Run() }