modified config+authorization
This commit is contained in:
parent
261cfb96c4
commit
5c33c5ff14
6 changed files with 103 additions and 71 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -12,7 +12,4 @@
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
|
||||||
#run-script including environment-variable values for test instances
|
|
||||||
run.ps1
|
|
42
.vscode/launch.json
vendored
Normal file
42
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "weather-api",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${workspaceFolder}/main.go",
|
||||||
|
"env": {
|
||||||
|
"MONGO_HOST":"localhost:27017",
|
||||||
|
"MONGO_DB":"weathersensors",
|
||||||
|
"MONGO_COLLECTION":"data",
|
||||||
|
"MONGO_USER":"root",
|
||||||
|
"MONGO_PASSWORD":"rootPassXXX",
|
||||||
|
|
||||||
|
"INFLUX_HOST":"localhost",
|
||||||
|
"INFLUX_TOKEN":"token",
|
||||||
|
"INFLUX_ORG":"weather-org",
|
||||||
|
"INFLUX_BUCKET":"weatherdata",
|
||||||
|
|
||||||
|
"MQTT_HOST":"localhost:1883",
|
||||||
|
"MQTT_TOPIC":"sensor/#",
|
||||||
|
"MQTT_USER":"mqtt",
|
||||||
|
"MQTT_PASSWORD":"mqtt",
|
||||||
|
"MQTT_PUBLISH_DELAY":"1000",
|
||||||
|
"MQTT_ANONYMOUS":"false",
|
||||||
|
|
||||||
|
"ACCESS_CONTROL_ALLOW_ORIGIN_HEADER":"*",
|
||||||
|
"USE_JWT_TOKEN_VALIDATION_URL":"false",
|
||||||
|
"JWT_TOKEN_VALIDATION_URL":"localhost:5000",
|
||||||
|
"USE_JWT_TOKEN_VALIDATION_SECRET":"false",
|
||||||
|
"JWT_TOKEN_VALIDATION_SECRET":"token_Secret_value",
|
||||||
|
|
||||||
|
"ALLOW_UNREGISTERED_SENSORS":"true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
14
README.md
14
README.md
|
@ -24,7 +24,7 @@ Key | Default-Wert | Auswirkung
|
||||||
MONGO_HOST | localhost:27017 | Hostadresse mongodb
|
MONGO_HOST | localhost:27017 | Hostadresse mongodb
|
||||||
MONGO_DB | weathersensors | DB-Namen mongodb
|
MONGO_DB | weathersensors | DB-Namen mongodb
|
||||||
MONGO_USER | admin | Username mongodb
|
MONGO_USER | admin | Username mongodb
|
||||||
MONGO_PASS | admin | Passwort mongodb
|
MONGO_PASSWORD | admin | Passwort mongodb
|
||||||
MONGO_COLLECTION | sensors | mongodb-Collection, in der Wettersensoren gespeichert werden
|
MONGO_COLLECTION | sensors | mongodb-Collection, in der Wettersensoren gespeichert werden
|
||||||
INFLUX_HOST | localhost:8086 | Hostadresse influxdb
|
INFLUX_HOST | localhost:8086 | Hostadresse influxdb
|
||||||
INFLUX_TOKEN | token | Token für influxDB
|
INFLUX_TOKEN | token | Token für influxDB
|
||||||
|
@ -33,13 +33,13 @@ INFLUX_BUCKET | bucket_name | Bucket-Namen, in dem die Wetterdaten abgespeichert
|
||||||
MQTT_HOST | localhost:1883 | Hostadresse MQTT-Broker
|
MQTT_HOST | localhost:1883 | Hostadresse MQTT-Broker
|
||||||
MQTT_TOPIC | sensor/# | MQTT-Topic, in welchem nach Wetterdaten geschaut wird
|
MQTT_TOPIC | sensor/# | MQTT-Topic, in welchem nach Wetterdaten geschaut wird
|
||||||
MQTT_USER | mqtt | Username für MQTT
|
MQTT_USER | mqtt | Username für MQTT
|
||||||
MQTT_PASS | mqtt | Passwort für MQTT
|
MQTT_PASSWORD | mqtt | Passwort für MQTT
|
||||||
MQTT_PUBLISH_DELAY | 1000 | Innerhalb dieser Zeitspanne wird ein Wetterdatensatz noch durch weiter eintreffende Werte ergänzt. Danach wird der Datensatz veröffentlicht (in Millisekunden)
|
MQTT_PUBLISH_DELAY | 1000 | Innerhalb dieser Zeitspanne wird ein Wetterdatensatz noch durch weiter eintreffende Werte ergänzt. Danach wird der Datensatz veröffentlicht (in Millisekunden)
|
||||||
MQTT_ANONYMOUS | false | Anonyme Anmeldung am MQTT-Broker verwenden (ohne Username und Passwort)
|
MQTT_ANONYMOUS | false | Anonyme Anmeldung am MQTT-Broker verwenden (ohne Username und Passwort)
|
||||||
|
ACCESS_CONTROL_ALLOW_ORIGIN_HEADER | * | CORS-Header
|
||||||
|
USE_JWT_TOKEN_VALIDATION_URL | false | Tokenvalidierung an einer URL
|
||||||
|
JWT_TOKEN_VALIDATION_URL | localhost:5000 | URL für die JWT-Token Validierung
|
||||||
|
USE_JWT_TOKEN_VALIDATION_SECRET | true | Tokenvalidierung mit der Angabe eines Secrets
|
||||||
|
JWT_TOKEN_VALIDATION_SECRET | token_Secret_value | Secret um die Signatur des JWT-Tokens zu überprüfen
|
||||||
ALLOW_UNREGISTERED_SENSORS | false | Wetterdaten nicht registrierter Sensoren erlauben
|
ALLOW_UNREGISTERED_SENSORS | false | Wetterdaten nicht registrierter Sensoren erlauben
|
||||||
|
|
||||||
|
|
||||||
## Applikation lokal ausführen
|
|
||||||
|
|
||||||
Eine lokal ausgeführte Test-Instanz der Wetter-API muss mit URLs, Tokens und ähnlichem über Umgebungsvariablen konfiguriert werden.
|
|
||||||
Das PowerShell-Skript `run_default.ps1` ist eine Vorlage für den start einer eigenen Instanz, lediglich die Umgebungsvariablen müsssen hierzu angepasst werden. Am besten wird der Inhalt dieses Skriptes in ein weiteres Skript (z.B. `run.ps1`) kopiert. Dieses wird von Git ignoriert, geheime Zugangsdaten (z.B. zu MQTT Broker, InfluxDB) werden so nicht ins Git-Repository eingefügt.
|
|
||||||
|
|
|
@ -24,10 +24,19 @@ var bearerTokenRegexPattern = "^(?i:Bearer\\s+)([A-Za-z0-9-_=]+\\.[A-Za-z0-9-_=]
|
||||||
|
|
||||||
var bearerTokenRegex *regexp.Regexp = regexp.MustCompile(bearerTokenRegexPattern)
|
var bearerTokenRegex *regexp.Regexp = regexp.MustCompile(bearerTokenRegexPattern)
|
||||||
|
|
||||||
type UserClaims struct {
|
type User struct {
|
||||||
Uid string `json:"uid"`
|
Uid string `json:"uid"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Roles []string `json:"role"`
|
Roles []string `json:"role"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValidationResponse struct {
|
||||||
|
ValidationSuccessfull bool
|
||||||
|
Identity User
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserClaims struct {
|
||||||
|
User
|
||||||
jwt.StandardClaims
|
jwt.StandardClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +83,8 @@ func (api *weatherRestApi) handleRequests() *mux.Router {
|
||||||
|
|
||||||
//sensor specific stuff
|
//sensor specific stuff
|
||||||
sensorRouter := router.PathPrefix("/{_dummy:(?i)sensor}").Subrouter()
|
sensorRouter := router.PathPrefix("/{_dummy:(?i)sensor}").Subrouter()
|
||||||
sensorRouter.Use(api.IsAuthorized)
|
sensorRouter.Use(api.UseJwtTokenValidationSecret)
|
||||||
|
sensorRouter.Use(api.UseJwtTokenValidationUrl)
|
||||||
|
|
||||||
sensorRouter.HandleFunc("/{id}/{_dummy:(?i)weather-data}", api.getWeatherDataHandler).Methods("GET")
|
sensorRouter.HandleFunc("/{id}/{_dummy:(?i)weather-data}", api.getWeatherDataHandler).Methods("GET")
|
||||||
sensorRouter.HandleFunc("/{id}/{_dummy:(?i)weather-data}", api.addWeatherDataHandler).Methods("POST")
|
sensorRouter.HandleFunc("/{id}/{_dummy:(?i)weather-data}", api.addWeatherDataHandler).Methods("POST")
|
||||||
|
@ -281,14 +291,13 @@ func (api *weatherRestApi) homePageHandler(w http.ResponseWriter, r *http.Reques
|
||||||
fmt.Fprintf(w, "Welcome to the Weather API!")
|
fmt.Fprintf(w, "Welcome to the Weather API!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *weatherRestApi) IsAuthorized(next http.Handler) http.Handler {
|
func (api *weatherRestApi) UseJwtTokenValidationUrl(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if !api.config.UseTokenAuthorization {
|
if !api.config.UseJwtTokenValidationUrl {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
req, err := http.NewRequest(http.MethodGet, api.config.JwtTokenValidationUrl, &bytes.Buffer{})
|
||||||
req, err := http.NewRequest(http.MethodGet, api.config.ValidateTokenUrl, &bytes.Buffer{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
@ -302,18 +311,35 @@ func (api *weatherRestApi) IsAuthorized(next http.Handler) http.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
claims, err := api.parseToken(r.Header)
|
if resp.StatusCode != http.StatusOK {
|
||||||
if err != nil {
|
http.Error(w, resp.Status, resp.StatusCode)
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.Header.Set(userIdHeader, claims.Uid)
|
|
||||||
if resp.StatusCode == http.StatusOK {
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Error(w, "", http.StatusUnauthorized)
|
validation := new(ValidationResponse)
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(validation)
|
||||||
|
if err != nil || !validation.ValidationSuccessfull {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Header.Set(userIdHeader, validation.Identity.Uid)
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *weatherRestApi) UseJwtTokenValidationSecret(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !api.config.UseJwtTokenValidationSecret {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := api.parseToken(r.Header)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Header.Set(userIdHeader, claims.Uid)
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +357,7 @@ func (api *weatherRestApi) parseToken(header http.Header) (*UserClaims, error) {
|
||||||
jwtFromHeader,
|
jwtFromHeader,
|
||||||
claims,
|
claims,
|
||||||
func(token *jwt.Token) (interface{}, error) {
|
func(token *jwt.Token) (interface{}, error) {
|
||||||
return []byte(api.config.JwtTokenSecret), nil
|
return []byte(api.config.JwtTokenValidationSecret), nil
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return claims, err
|
return claims, err
|
||||||
|
|
|
@ -32,16 +32,18 @@ type MqttConfig struct {
|
||||||
|
|
||||||
type RestConfig struct {
|
type RestConfig struct {
|
||||||
AccessControlAllowOriginHeader string
|
AccessControlAllowOriginHeader string
|
||||||
UseTokenAuthorization bool
|
Insecure bool
|
||||||
ValidateTokenUrl string
|
UseJwtTokenValidationUrl bool
|
||||||
JwtTokenSecret string
|
JwtTokenValidationUrl string
|
||||||
|
UseJwtTokenValidationSecret bool
|
||||||
|
JwtTokenValidationSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
var MongoConfiguration = MongoConfig{
|
var MongoConfiguration = MongoConfig{
|
||||||
Host: getEnv("MONGO_HOST", "localhost:27017"),
|
Host: getEnv("MONGO_HOST", "localhost:27017"),
|
||||||
Database: getEnv("MONGO_DB", "weathersensors"),
|
Database: getEnv("MONGO_DB", "weathersensors"),
|
||||||
Username: getEnv("MONGO_USER", "admin"),
|
Username: getEnv("MONGO_USER", "admin"),
|
||||||
Password: getEnv("MONGO_PASS", "admin"),
|
Password: getEnv("MONGO_PASSWORD", "admin"),
|
||||||
Collection: getEnv("MONGO_COLLECTION", "sensors"),
|
Collection: getEnv("MONGO_COLLECTION", "sensors"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,16 +58,17 @@ var MqttConfiguration = MqttConfig{
|
||||||
Host: getEnv("MQTT_HOST", "localhost:1883"),
|
Host: getEnv("MQTT_HOST", "localhost:1883"),
|
||||||
Topic: getEnv("MQTT_TOPIC", "sensor/#"),
|
Topic: getEnv("MQTT_TOPIC", "sensor/#"),
|
||||||
Username: getEnv("MQTT_USER", "mqtt"),
|
Username: getEnv("MQTT_USER", "mqtt"),
|
||||||
Password: getEnv("MQTT_PASS", "mqtt"),
|
Password: getEnv("MQTT_PASSWORD", "mqtt"),
|
||||||
PublishDelay: getEnvDuration("MQTT_PUBLISH_DELAY", time.Second),
|
PublishDelay: getEnvDuration("MQTT_PUBLISH_DELAY", time.Second),
|
||||||
AllowAnonymousAuthentication: getEnvBool("MQTT_ANONYMOUS", false),
|
AllowAnonymousAuthentication: getEnvBool("MQTT_ANONYMOUS", false),
|
||||||
}
|
}
|
||||||
|
|
||||||
var RestConfiguration = RestConfig{
|
var RestConfiguration = RestConfig{
|
||||||
AccessControlAllowOriginHeader: getEnv("ACCESS_CONTROL_ALLOW_ORIGIN_HEADER", "*"),
|
AccessControlAllowOriginHeader: getEnv("ACCESS_CONTROL_ALLOW_ORIGIN_HEADER", "*"),
|
||||||
UseTokenAuthorization: getEnvBool("USE_TOKEN_AUTHORIZATION", false),
|
UseJwtTokenValidationUrl: getEnvBool("USE_JWT_TOKEN_VALIDATION_URL", false),
|
||||||
ValidateTokenUrl: getEnv("JWT_TOKEN_VALIDATION_URL", "https://api.swablab.de/ldap/validateToken"),
|
JwtTokenValidationUrl: getEnv("JWT_TOKEN_VALIDATION_URL", "localhost:5000"),
|
||||||
JwtTokenSecret: getEnv("JWT_TOKEN_SECRET", "my_token_string"),
|
UseJwtTokenValidationSecret: getEnvBool("USE_JWT_TOKEN_VALIDATION_SECRET", true),
|
||||||
|
JwtTokenValidationSecret: getEnv("JWT_TOKEN_VALIDATION_SECRET", "my_token_string"),
|
||||||
}
|
}
|
||||||
|
|
||||||
var AllowUnregisteredSensors = getEnvBool("ALLOW_UNREGISTERED_SENSORS", false)
|
var AllowUnregisteredSensors = getEnvBool("ALLOW_UNREGISTERED_SENSORS", false)
|
||||||
|
@ -90,16 +93,6 @@ func getEnvBool(key string, fallback bool) bool {
|
||||||
return fallback
|
return fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnvInt(key string, fallback int64) int64 {
|
|
||||||
if value, ok := os.LookupEnv(key); ok {
|
|
||||||
if iValue, err := strconv.ParseInt(value, 10, 64); err == nil {
|
|
||||||
return iValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEnvDuration(key string, fallback time.Duration) time.Duration {
|
func getEnvDuration(key string, fallback time.Duration) time.Duration {
|
||||||
if value, ok := os.LookupEnv(key); ok {
|
if value, ok := os.LookupEnv(key); ok {
|
||||||
if iValue, err := strconv.ParseInt(value, 10, 64); err == nil {
|
if iValue, err := strconv.ParseInt(value, 10, 64); err == nil {
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
#build the application
|
|
||||||
go build main.go
|
|
||||||
|
|
||||||
#set environment variables for weather-api configuration
|
|
||||||
Set-Item -Path "Env:INFLUX_HOST" -Value "localhost:8086"
|
|
||||||
Set-Item -Path "Env:INFLUX_TOKEN" -Value "token"
|
|
||||||
Set-Item -Path "Env:INFLUX_ORG" -Value "org-name"
|
|
||||||
Set-Item -Path "Env:INFLUX_BUCKET" -Value "bucket-name"
|
|
||||||
|
|
||||||
Set-Item -Path "Env:MQTT_HOST" -Value "localhost:1883"
|
|
||||||
Set-Item -Path "Env:MQTT_TOPIC" -Value "sensor/#"
|
|
||||||
Set-Item -Path "Env:MQTT_USER" -Value "mqtt"
|
|
||||||
Set-Item -Path "Env:MQTT_PASS" -Value "mqtt"
|
|
||||||
Set-Item -Path "Env:MQTT_PUBLISH_DELAY" -Value "1000"
|
|
||||||
Set-Item -Path "Env:MQTT_ANONYMOUS" -Value "false"
|
|
||||||
|
|
||||||
Set-Item -Path "Env:MONGO_HOST" -Value "localhost:27017"
|
|
||||||
Set-Item -Path "Env:MONGO_DB" -Value "weathersensors"
|
|
||||||
Set-Item -Path "Env:MONGO_COLLECTION" -Value "sensors"
|
|
||||||
Set-Item -Path "Env:MONGO_USER" -Value "admin"
|
|
||||||
Set-Item -Path "Env:MONGO_PASS" -Value "admin"
|
|
||||||
|
|
||||||
Set-Item -Path "Env:ALLOW_UNREGISTERED_SENSORS" -Value "false"
|
|
||||||
|
|
||||||
#start application
|
|
||||||
Start-Process "main.exe" -Wait -NoNewWindow
|
|
Loading…
Add table
Reference in a new issue