From ff2bbb543e55e8d36768ba53b1bb1f93a46283d4 Mon Sep 17 00:00:00 2001 From: Joel Schmid Date: Sun, 22 Aug 2021 19:52:34 +0200 Subject: [PATCH] decode userId from jwt token --- api/rest-api.go | 71 ++++++++++++++++++++++++++++++++++++++++-------- config/config.go | 2 ++ go.mod | 1 + go.sum | 2 ++ 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/api/rest-api.go b/api/rest-api.go index 194e1b6..e02f83c 100644 --- a/api/rest-api.go +++ b/api/rest-api.go @@ -3,17 +3,33 @@ package api import ( "bytes" "encoding/json" + "errors" "fmt" "net/http" + "regexp" "weather-data/config" "weather-data/storage" "weather-data/weathersource" + "github.com/golang-jwt/jwt" "github.com/google/uuid" "github.com/gorilla/handlers" "github.com/gorilla/mux" ) +var userIdHeader = "userid" + +var bearerTokenRegexPattern = "^(?i:Bearer\\s+)([A-Za-z0-9-_=]+\\.[A-Za-z0-9-_=]+\\.?[A-Za-z0-9-_.+\\/=]*)$" + +var bearerTokenRegex *regexp.Regexp = regexp.MustCompile(bearerTokenRegexPattern) + +type UserClaims struct { + Uid string `json:"uid"` + Username string `json:"username"` + Roles []string `json:"role"` + jwt.StandardClaims +} + type weatherRestApi struct { connection string config config.RestConfig @@ -66,9 +82,7 @@ func (api *weatherRestApi) handleRequests() *mux.Router { sensorRouter.HandleFunc("/{id}", api.getWeatherSensorHandler).Methods("GET") sensorRouter.HandleFunc("/{id}", api.updateWeatherSensorHandler).Methods("PUT") sensorRouter.HandleFunc("/{id}", api.deleteWeatherSensorHandler).Methods("DELETE") - - //registration - router.HandleFunc("/{_dummy:(?i)register/sensor}/{name}", api.registerWeatherSensorHandler).Methods("POST") + sensorRouter.HandleFunc("/{name}", api.registerWeatherSensorHandler).Methods("POST") return router } @@ -161,22 +175,22 @@ func (api *weatherRestApi) registerWeatherSensorHandler(w http.ResponseWriter, r return } + sensor.UserId = r.Header.Get(userIdHeader) + err = api.sensorRegistry.UpdateSensor(sensor) + if err != nil { + http.Error(w, "", http.StatusBadRequest) + return + } + w.Header().Add("content-type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(sensor) } func (api *weatherRestApi) getAllWeatherSensorHandler(w http.ResponseWriter, r *http.Request) { - var weatherSensors []*storage.WeatherSensor - var err error + userId := r.Header.Get(userIdHeader) - userId := r.URL.Query().Get("userId") - - if len(userId) == 0 { - weatherSensors, err = api.sensorRegistry.GetSensors() - } else { - weatherSensors, err = api.sensorRegistry.GetSensorsOfUser(userId) - } + weatherSensors, err := api.sensorRegistry.GetSensorsOfUser(userId) if err != nil { http.Error(w, "", http.StatusNotFound) @@ -225,6 +239,8 @@ func (api *weatherRestApi) updateWeatherSensorHandler(w http.ResponseWriter, r * http.Error(w, "", http.StatusBadRequest) return } + + sensor.UserId = r.Header.Get(userIdHeader) sensor.Id = sensorId exist, err := api.sensorRegistry.ExistSensor(sensorId) @@ -288,6 +304,12 @@ func (api *weatherRestApi) IsAuthorized(next http.Handler) http.Handler { return } + _, err = api.parseToken(r.Header) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if resp.StatusCode == http.StatusOK { next.ServeHTTP(w, r) return @@ -297,6 +319,31 @@ func (api *weatherRestApi) IsAuthorized(next http.Handler) http.Handler { }) } +func (api *weatherRestApi) parseToken(header http.Header) (*UserClaims, error) { + authorizationHeader, exists := header["Authorization"] + if !exists { + return nil, errors.New("missing authorization token") + } + + jwtFromHeader := bearerTokenRegex.FindStringSubmatch(authorizationHeader[0])[1] + + claims := new(UserClaims) + + _, err := jwt.ParseWithClaims( + jwtFromHeader, + claims, + func(token *jwt.Token) (interface{}, error) { + return []byte(api.config.JwtTokenSecret), nil + }, + ) + + if err == nil { + header.Add(userIdHeader, claims.Uid) + } + + return claims, err +} + //AddNewWeatherDataCallback adds a new callbackMethod for incoming weather data func (api *weatherRestApi) AddNewWeatherDataCallback(callback weathersource.NewWeatherDataCallbackFunc) { api.weatherSource.AddNewWeatherDataCallback(callback) diff --git a/config/config.go b/config/config.go index 06f7dd5..a7744e6 100644 --- a/config/config.go +++ b/config/config.go @@ -34,6 +34,7 @@ type RestConfig struct { AccessControlAllowOriginHeader string UseTokenAuthorization bool ValidateTokenUrl string + JwtTokenSecret string } var MongoConfiguration = MongoConfig{ @@ -64,6 +65,7 @@ var RestConfiguration = RestConfig{ AccessControlAllowOriginHeader: getEnv("ACCESS_CONTROL_ALLOW_ORIGIN_HEADER", "*"), UseTokenAuthorization: getEnvBool("USE_TOKEN_AUTHORIZATION", false), ValidateTokenUrl: getEnv("JWT_TOKEN_VALIDATION_URL", "https://api.swablab.de/ldap/validateToken"), + JwtTokenSecret: getEnv("JWT_TOKEN_SECRET", "my_token_string"), } var AllowUnregisteredSensors = getEnvBool("ALLOW_UNREGISTERED_SENSORS", false) diff --git a/go.mod b/go.mod index 6d2da68..21094fd 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.16 require ( github.com/eclipse/paho.mqtt.golang v1.3.5 + github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.3.0 github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 diff --git a/go.sum b/go.sum index d585432..21124a8 100644 --- a/go.sum +++ b/go.sum @@ -41,6 +41,8 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=