bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/pkg/apiserver/controllers/v1/decisions.go (about) 1 package v1 2 3 import ( 4 "crypto/sha512" 5 "fmt" 6 "net/http" 7 "strconv" 8 "time" 9 10 "bitbucket.org/Aishee/synsec/pkg/database/ent" 11 "bitbucket.org/Aishee/synsec/pkg/models" 12 "bitbucket.org/Aishee/synsec/pkg/types" 13 "github.com/gin-gonic/gin" 14 log "github.com/sirupsen/logrus" 15 ) 16 17 func FormatDecisions(decisions []*ent.Decision) ([]*models.Decision, error) { 18 var results []*models.Decision 19 for _, dbDecision := range decisions { 20 duration := dbDecision.Until.Sub(time.Now()).String() 21 decision := models.Decision{ 22 ID: int64(dbDecision.ID), 23 Duration: &duration, 24 Scenario: &dbDecision.Scenario, 25 Scope: &dbDecision.Scope, 26 Value: &dbDecision.Value, 27 Type: &dbDecision.Type, 28 Origin: &dbDecision.Origin, 29 } 30 results = append(results, &decision) 31 } 32 return results, nil 33 } 34 35 func (c *Controller) GetDecision(gctx *gin.Context) { 36 defer types.CatchPanic("synsec/controllersV1/GetDecision") 37 var err error 38 var results []*models.Decision 39 var data []*ent.Decision 40 41 data, err = c.DBClient.QueryDecisionWithFilter(gctx.Request.URL.Query()) 42 if err != nil { 43 c.HandleDBErrors(gctx, err) 44 return 45 } 46 47 results, err = FormatDecisions(data) 48 if err != nil { 49 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 50 return 51 } 52 /*let's follow a naive logic : when a bouncer queries /decisions, if the answer is empty, we assume there is no decision for this ip/user/..., 53 but if it's non-empty, it means that there is one or more decisions for this target*/ 54 if len(results) > 0 { 55 PrometheusBouncersHasNonEmptyDecision(gctx) 56 } else { 57 PrometheusBouncersHasEmptyDecision(gctx) 58 } 59 60 if gctx.Request.Method == "HEAD" { 61 gctx.String(http.StatusOK, "") 62 return 63 } 64 gctx.JSON(http.StatusOK, results) 65 return 66 } 67 68 func (c *Controller) DeleteDecisionById(gctx *gin.Context) { 69 defer types.CatchPanic("synsec/controllersV1/DeleteDecisionById") 70 var err error 71 72 decisionIDStr := gctx.Param("decision_id") 73 decisionID, err := strconv.Atoi(decisionIDStr) 74 if err != nil { 75 gctx.JSON(http.StatusBadRequest, gin.H{"message": "decision_id must be valid integer"}) 76 return 77 } 78 err = c.DBClient.SoftDeleteDecisionByID(decisionID) 79 if err != nil { 80 c.HandleDBErrors(gctx, err) 81 return 82 } 83 84 deleteDecisionResp := models.DeleteDecisionResponse{ 85 NbDeleted: "1", 86 } 87 88 gctx.JSON(http.StatusOK, deleteDecisionResp) 89 return 90 } 91 92 func (c *Controller) DeleteDecisions(gctx *gin.Context) { 93 defer types.CatchPanic("synsec/controllersV1/DeleteDecisions") 94 var err error 95 96 nbDeleted, err := c.DBClient.SoftDeleteDecisionsWithFilter(gctx.Request.URL.Query()) 97 if err != nil { 98 c.HandleDBErrors(gctx, err) 99 return 100 } 101 deleteDecisionResp := models.DeleteDecisionResponse{ 102 NbDeleted: nbDeleted, 103 } 104 105 gctx.JSON(http.StatusOK, deleteDecisionResp) 106 return 107 } 108 109 func (c *Controller) StreamDecision(gctx *gin.Context) { 110 defer types.CatchPanic("synsec/controllersV1/StreamDecision") 111 var data []*ent.Decision 112 ret := make(map[string][]*models.Decision, 0) 113 ret["new"] = []*models.Decision{} 114 ret["deleted"] = []*models.Decision{} 115 116 val := gctx.Request.Header.Get(c.APIKeyHeader) 117 hashedKey := sha512.New() 118 hashedKey.Write([]byte(val)) 119 hashStr := fmt.Sprintf("%x", hashedKey.Sum(nil)) 120 bouncerInfo, err := c.DBClient.SelectBouncer(hashStr) 121 if err != nil { 122 if _, ok := err.(*ent.NotFoundError); ok { 123 gctx.JSON(http.StatusForbidden, gin.H{"message": err.Error()}) 124 } else { 125 gctx.JSON(http.StatusUnauthorized, gin.H{"message": "not allowed"}) 126 } 127 return 128 } 129 130 if bouncerInfo == nil { 131 gctx.JSON(http.StatusUnauthorized, gin.H{"message": "not allowed"}) 132 return 133 } 134 135 // if the blocker just start, return all decisions 136 if val, ok := gctx.Request.URL.Query()["startup"]; ok { 137 if val[0] == "true" { 138 data, err := c.DBClient.QueryAllDecisions() 139 if err != nil { 140 log.Errorf("failed querying decisions: %v", err) 141 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 142 return 143 } 144 ret["new"], err = FormatDecisions(data) 145 if err != nil { 146 log.Errorf("unable to format expired decision for '%s' : %v", bouncerInfo.Name, err) 147 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 148 return 149 } 150 151 // getting expired decisions 152 data, err = c.DBClient.QueryExpiredDecisions() 153 if err != nil { 154 log.Errorf("unable to query expired decision for '%s' : %v", bouncerInfo.Name, err) 155 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 156 return 157 } 158 ret["deleted"], err = FormatDecisions(data) 159 if err != nil { 160 log.Errorf("unable to format expired decision for '%s' : %v", bouncerInfo.Name, err) 161 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 162 return 163 } 164 165 if err := c.DBClient.UpdateBouncerLastPull(time.Now(), bouncerInfo.ID); err != nil { 166 log.Errorf("unable to update bouncer '%s' pull: %v", bouncerInfo.Name, err) 167 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 168 return 169 } 170 if gctx.Request.Method == "HEAD" { 171 gctx.String(http.StatusOK, "") 172 return 173 } 174 gctx.JSON(http.StatusOK, ret) 175 return 176 } 177 } 178 179 // getting new decisions 180 data, err = c.DBClient.QueryNewDecisionsSince(bouncerInfo.LastPull) 181 if err != nil { 182 log.Errorf("unable to query new decision for '%s' : %v", bouncerInfo.Name, err) 183 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 184 return 185 } 186 ret["new"], err = FormatDecisions(data) 187 if err != nil { 188 log.Errorf("unable to format new decision for '%s' : %v", bouncerInfo.Name, err) 189 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 190 return 191 } 192 193 // getting expired decisions 194 data, err = c.DBClient.QueryExpiredDecisionsSince(bouncerInfo.LastPull.Add((-2 * time.Second))) // do we want to give exactly lastPull time ? 195 if err != nil { 196 log.Errorf("unable to query expired decision for '%s' : %v", bouncerInfo.Name, err) 197 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 198 return 199 } 200 ret["deleted"], err = FormatDecisions(data) 201 if err != nil { 202 log.Errorf("unable to format expired decision for '%s' : %v", bouncerInfo.Name, err) 203 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 204 return 205 } 206 207 if err := c.DBClient.UpdateBouncerLastPull(time.Now(), bouncerInfo.ID); err != nil { 208 log.Errorf("unable to update bouncer '%s' pull: %v", bouncerInfo.Name, err) 209 gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) 210 return 211 } 212 213 gctx.JSON(http.StatusOK, ret) 214 return 215 }