1 package server
2
3 import (
4 "context"
5 "flag"
6 "fmt"
7 "net/http"
8 "os"
9
10 "github.com/gin-contrib/requestid"
11 "github.com/gin-gonic/gin"
12 "github.com/go-logr/logr"
13 "github.com/peterbourgon/ff/v3"
14
15 "edge-infra.dev/pkg/edge/api/middleware"
16 eamiddleware "edge-infra.dev/pkg/sds/emergencyaccess/middleware"
17 rulesengine "edge-infra.dev/pkg/sds/emergencyaccess/rules"
18 "edge-infra.dev/pkg/sds/emergencyaccess/rules/setup"
19 )
20
21 type RulesEngine interface {
22 GetEARolesForCommand(ctx context.Context, command rulesengine.Command, bannerID string) ([]string, error)
23 UserHasRoles(_ string, eaRoles []string, userEARoles []string) bool
24
25 AddCommands(ctx context.Context, names []rulesengine.PostCommandPayload) (rulesengine.AddNameResult, error)
26 AddPrivileges(ctx context.Context, privs []rulesengine.PostPrivilegePayload) (rulesengine.AddNameResult, error)
27 AddBannerRules(ctx context.Context, bannerName string, rules rulesengine.WriteRules) (rulesengine.AddRuleResult, error)
28 AddDefaultRules(ctx context.Context, rules rulesengine.WriteRules) (rulesengine.AddRuleResult, error)
29
30 DeleteCommand(ctx context.Context, name string) (rulesengine.DeleteResult, error)
31 DeletePrivilege(ctx context.Context, name string) (rulesengine.DeleteResult, error)
32 DeleteDefaultRule(ctx context.Context, commandName, privilegeName string) (rulesengine.DeleteResult, error)
33 DeletePrivilegeFromBannerRule(ctx context.Context, bannerName, commandName, privilegeName string) (rulesengine.DeleteResult, error)
34
35 ReadCommands(ctx context.Context) ([]rulesengine.Command, error)
36 ReadCommand(ctx context.Context, name string) (rulesengine.Command, error)
37 ReadPrivileges(ctx context.Context) ([]rulesengine.Privilege, error)
38 ReadPrivilege(ctx context.Context, name string) (rulesengine.Privilege, error)
39 ReadAllDefaultRules(ctx context.Context) ([]rulesengine.Rule, error)
40 ReadDefaultRulesForCommand(ctx context.Context, commandName string) ([]rulesengine.Rule, error)
41 ReadRulesForBanner(ctx context.Context, bannerName string) ([]rulesengine.Rule, error)
42 ReadRulesForAllBanners(ctx context.Context) ([]rulesengine.ReadBannerRule, error)
43 ReadBannerRulesForCommand(ctx context.Context, commandName string) (rulesengine.ReadBannerRule, error)
44 ReadBannerRulesForCommandAndBanner(ctx context.Context, bannerName string, commandName string) (rulesengine.Rule, error)
45 ReadAllRulesForCommand(ctx context.Context, commandName string) (rulesengine.RuleWithOverrides, error)
46 }
47
48 type RulesEngineService struct {
49 GinEngine *gin.Engine
50 RulesEngine RulesEngine
51 logger logr.Logger
52 }
53
54 func (res RulesEngineService) newGinServer(checks ...func() error) {
55 router := res.GinEngine
56
57 router.ContextWithFallback = true
58
59
60 router.Use(middleware.SetRequestContext())
61 router.Use(gin.Recovery())
62
63 router.Any("/ready", func(c *gin.Context) {
64 c.String(http.StatusOK, "ok")
65 })
66 router.Any("/health", eamiddleware.HealthCheck(checks...))
67
68 public := router.Group("/")
69 public.Use(requestid.New(requestid.WithCustomHeaderStrKey(eamiddleware.CorrelationIDKey)))
70 public.Use(eamiddleware.SetLoggerInContext(res.logger))
71 public.Use(eamiddleware.RequestBookendLogs())
72 public.POST("/validatecommand", res.validateCommand)
73
74 admin := public.Group("/admin")
75
76 admin.POST("/commands", res.postCommands)
77 admin.DELETE("/commands/:name", res.deleteCommand)
78 admin.GET("/commands", res.readCommands)
79 admin.GET("commands/:name", res.readCommand)
80
81 admin.POST("/privileges", res.postPrivileges)
82 admin.DELETE("/privileges/:name", res.deletePrivilege)
83 admin.GET("/privileges", res.readPrivileges)
84 admin.GET("/privileges/:name", res.readPrivilege)
85
86 admin.POST("/rules/default/commands", res.postDefaultRules)
87 admin.DELETE("/rules/default/commands/:commandName/privileges/:privilegeName", res.deleteDefaultRule)
88 admin.GET("/rules/default/commands", res.readAllDefaultRules)
89 admin.GET("/rules/default/commands/:commandName", res.readDefaultRule)
90
91
92 admin.POST("/rules/banner/commands", res.postRulesInBanner)
93 admin.GET("/rules/banner/commands", res.readAllRulesForBanners)
94 admin.GET("/rules/banner/commands/:commandName", res.readBannerRulesForCommand)
95 admin.DELETE("/rules/banner/commands/:commandName/privileges/:privilegeName", res.deletePrivilegeFromBannerRule)
96
97
98 admin.GET("/rules/commands/:commandName", res.readAllRulesForCommand)
99 }
100
101 func New(router *gin.Engine, rulesengine RulesEngine, logger logr.Logger, checks ...func() error) (RulesEngineService, error) {
102 res := RulesEngineService{
103 RulesEngine: rulesengine,
104 GinEngine: router,
105 logger: logger,
106 }
107 res.newGinServer(checks...)
108 return res, nil
109 }
110
111
112 func Run() error {
113 log := newLogger()
114
115 flags := flag.NewFlagSet("ea-rules", flag.ExitOnError)
116 conf := setup.RulesConfig{}
117 conf.BindFlags(flags)
118
119 if err := ff.Parse(flags, os.Args[1:], ff.WithEnvVarNoPrefix(), ff.WithIgnoreUndefined(true)); err != nil {
120 return fmt.Errorf("failed to parse arguments: %w", err)
121 }
122
123 router := gin.New()
124
125 ds, checks, err := setup.CreateDataset(log, conf)
126 if err != nil {
127 return err
128 }
129
130 rulesengine := rulesengine.New(ds)
131 res, err := New(router, rulesengine, log, checks...)
132 if err != nil {
133 return err
134 }
135
136 return res.GinEngine.Run()
137 }
138
View as plain text