bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/pkg/apiserver/middlewares/v1/jwt.go (about) 1 package v1 2 3 import ( 4 "crypto/rand" 5 "fmt" 6 "os" 7 "strings" 8 "time" 9 10 "errors" 11 12 jwt "github.com/appleboy/gin-jwt/v2" 13 "bitbucket.org/Aishee/synsec/pkg/database" 14 "bitbucket.org/Aishee/synsec/pkg/database/ent/machine" 15 "bitbucket.org/Aishee/synsec/pkg/models" 16 "bitbucket.org/Aishee/synsec/pkg/types" 17 "github.com/gin-gonic/gin" 18 "github.com/go-openapi/strfmt" 19 log "github.com/sirupsen/logrus" 20 "golang.org/x/crypto/bcrypt" 21 ) 22 23 var identityKey = "id" 24 25 type JWT struct { 26 Middleware *jwt.GinJWTMiddleware 27 DbClient *database.Client 28 } 29 30 func PayloadFunc(data interface{}) jwt.MapClaims { 31 if value, ok := data.(*models.WatcherAuthRequest); ok { 32 return jwt.MapClaims{ 33 identityKey: &value.MachineID, 34 } 35 } 36 return jwt.MapClaims{} 37 } 38 39 func IdentityHandler(c *gin.Context) interface{} { 40 claims := jwt.ExtractClaims(c) 41 machineId := claims[identityKey].(string) 42 return &models.WatcherAuthRequest{ 43 MachineID: &machineId, 44 } 45 } 46 47 func (j *JWT) Authenticator(c *gin.Context) (interface{}, error) { 48 defer types.CatchPanic("synsec/middlewaresV1/jwt/Authenticator") 49 var loginInput models.WatcherAuthRequest 50 var scenarios string 51 var err error 52 if err := c.ShouldBindJSON(&loginInput); err != nil { 53 return "", errors.New(fmt.Sprintf("missing : %v", err.Error())) 54 } 55 if err := loginInput.Validate(strfmt.Default); err != nil { 56 return "", errors.New("input format error") 57 } 58 machineID := *loginInput.MachineID 59 password := *loginInput.Password 60 scenariosInput := loginInput.Scenarios 61 62 machine, err := j.DbClient.Ent.Machine.Query(). 63 Where(machine.MachineId(machineID)). 64 First(j.DbClient.CTX) 65 if err != nil { 66 log.Printf("Error machine login for %s : %+v ", machineID, err) 67 return nil, err 68 } 69 70 if machine == nil { 71 log.Errorf("Nothing for '%s'", machineID) 72 return nil, jwt.ErrFailedAuthentication 73 } 74 75 if !machine.IsValidated { 76 return nil, fmt.Errorf("machine %s not validated", machineID) 77 } 78 79 if err = bcrypt.CompareHashAndPassword([]byte(machine.Password), []byte(password)); err != nil { 80 return nil, jwt.ErrFailedAuthentication 81 } 82 83 if len(scenariosInput) > 0 { 84 for _, scenario := range scenariosInput { 85 if scenarios == "" { 86 scenarios = scenario 87 } else { 88 scenarios += "," + scenario 89 } 90 } 91 err = j.DbClient.UpdateMachineScenarios(scenarios, machine.ID) 92 if err != nil { 93 log.Errorf("Failed to update scenarios list for '%s': %s\n", machineID, err) 94 return nil, jwt.ErrFailedAuthentication 95 } 96 } 97 98 if machine.IpAddress == "" { 99 err = j.DbClient.UpdateMachineIP(c.ClientIP(), machine.ID) 100 if err != nil { 101 log.Errorf("Failed to update ip address for '%s': %s\n", machineID, err) 102 return nil, jwt.ErrFailedAuthentication 103 } 104 } 105 106 if machine.IpAddress != c.ClientIP() && machine.IpAddress != "" { 107 log.Warningf("new IP address detected for machine '%s': %s (old: %s)", machine.MachineId, c.ClientIP(), machine.IpAddress) 108 err = j.DbClient.UpdateMachineIP(c.ClientIP(), machine.ID) 109 if err != nil { 110 log.Errorf("Failed to update ip address for '%s': %s\n", machine.MachineId, err) 111 return nil, jwt.ErrFailedAuthentication 112 } 113 } 114 115 useragent := strings.Split(c.Request.UserAgent(), "/") 116 if len(useragent) != 2 { 117 log.Warningf("bad user agent '%s' from '%s'", c.Request.UserAgent(), c.ClientIP()) 118 return nil, jwt.ErrFailedAuthentication 119 } 120 121 if err := j.DbClient.UpdateMachineVersion(useragent[1], machine.ID); err != nil { 122 log.Errorf("unable to update machine '%s' version '%s': %s", machine.MachineId, useragent[1], err) 123 log.Errorf("bad user agent from : %s", c.ClientIP()) 124 return nil, jwt.ErrFailedAuthentication 125 } 126 127 return &models.WatcherAuthRequest{ 128 MachineID: &machineID, 129 }, nil 130 131 } 132 133 func Authorizator(data interface{}, c *gin.Context) bool { 134 return true 135 } 136 137 func Unauthorized(c *gin.Context, code int, message string) { 138 c.JSON(code, gin.H{ 139 "code": code, 140 "message": message, 141 }) 142 } 143 144 func NewJWT(dbClient *database.Client) (*JWT, error) { 145 // Get secret from environment variable "SECRET" 146 var ( 147 secret []byte 148 ) 149 150 //Please be aware that brute force HS256 is possible. 151 //PLEASE choose a STRONG secret 152 secret_string := os.Getenv("CS_LAPI_SECRET") 153 if secret_string == "" { 154 secret = make([]byte, 64) 155 if n, err := rand.Read(secret); err != nil { 156 log.Fatalf("unable to generate a new random seed for JWT generation") 157 } else { 158 if n != 64 { 159 log.Fatalf("not enough entropy at random seed generation for JWT generation") 160 } 161 } 162 } else { 163 secret = []byte(secret_string) 164 if len(secret) < 64 { 165 log.Fatalf("secret not strong enough") 166 } 167 } 168 169 jwtMiddleware := &JWT{ 170 DbClient: dbClient, 171 } 172 173 ret, err := jwt.New(&jwt.GinJWTMiddleware{ 174 Realm: "Synsec API local", 175 Key: secret, 176 Timeout: time.Hour, 177 MaxRefresh: time.Hour, 178 IdentityKey: identityKey, 179 PayloadFunc: PayloadFunc, 180 IdentityHandler: IdentityHandler, 181 Authenticator: jwtMiddleware.Authenticator, 182 Authorizator: Authorizator, 183 Unauthorized: Unauthorized, 184 TokenLookup: "header: Authorization, query: token, cookie: jwt", 185 TokenHeadName: "Bearer", 186 TimeFunc: time.Now, 187 }) 188 189 errInit := ret.MiddlewareInit() 190 if errInit != nil { 191 return &JWT{}, fmt.Errorf("authMiddleware.MiddlewareInit() Error:" + errInit.Error()) 192 } 193 194 if err != nil { 195 return &JWT{}, err 196 } 197 198 return &JWT{Middleware: ret}, nil 199 }