From c404c7af5a62b1be3842ae698d906c96dd06bafd Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 25 Jan 2021 10:56:40 +0100 Subject: [PATCH] make JWT secret and expiry time configurable --- configuration/config.go | 124 ++++++++++++++------------- routes/user/authenticate_endpoint.go | 28 +++++- routes/user/user_endpoints.go | 8 +- routes/user/user_middleware.go | 6 +- 4 files changed, 95 insertions(+), 71 deletions(-) diff --git a/configuration/config.go b/configuration/config.go index 54ef502..9222c84 100644 --- a/configuration/config.go +++ b/configuration/config.go @@ -38,49 +38,53 @@ func InitConfig() error { } var ( - dbHost = flag.String("db-host", "/var/run/postgresql", "Host of the PostgreSQL database (default is /var/run/postgresql for localhost DB on Ubuntu systems)") - dbName = flag.String("db-name", "villasdb", "Name of the database to use (default is villasdb)") - dbUser = flag.String("db-user", "", "Username of database connection (default is )") - dbPass = flag.String("db-pass", "", "Password of database connection (default is )") - dbSSLMode = flag.String("db-ssl-mode", "disable", "SSL mode of DB (default is disable)") // TODO: change default for production - amqpHost = flag.String("amqp-host", "", "If set, use this as host for AMQP broker (default is disabled)") - amqpUser = flag.String("amqp-user", "", "Username for AMQP broker") - amqpPass = flag.String("amqp-pass", "", "Password for AMQP broker") - configFile = flag.String("config", "", "Path to YAML configuration file") - mode = flag.String("mode", "release", "Select debug/release/test mode (default is release)") - port = flag.String("port", "4000", "Port of the backend (default is 4000)") - baseHost = flag.String("base-host", "localhost:4000", "The host at which the backend is hosted (default: localhost)") - basePath = flag.String("base-path", "/api/v2", "The path at which the API routes are located (default /api/v2)") - adminUser = flag.String("admin-user", "", "Initial admin username") - adminPass = flag.String("admin-pass", "", "Initial admin password") - adminMail = flag.String("admin-mail", "", "Initial admin mail address") - s3Bucket = flag.String("s3-bucket", "", "S3 Bucket for uploading files") - s3Endpoint = flag.String("s3-endpoint", "", "Endpoint of S3 API for file uploads") - s3Region = flag.String("s3-region", "default", "S3 Region for file uploads") - s3NoSSL = flag.Bool("s3-nossl", false, "Use encrypted connections to the S3 API") - s3PathStyle = flag.Bool("s3-pathstyle", false, "Use path-style S3 API") + dbHost = flag.String("db-host", "/var/run/postgresql", "Host of the PostgreSQL database (default is /var/run/postgresql for localhost DB on Ubuntu systems)") + dbName = flag.String("db-name", "villasdb", "Name of the database to use (default is villasdb)") + dbUser = flag.String("db-user", "", "Username of database connection (default is )") + dbPass = flag.String("db-pass", "", "Password of database connection (default is )") + dbSSLMode = flag.String("db-ssl-mode", "disable", "SSL mode of DB (default is disable)") // TODO: change default for production + amqpHost = flag.String("amqp-host", "", "If set, use this as host for AMQP broker (default is disabled)") + amqpUser = flag.String("amqp-user", "", "Username for AMQP broker") + amqpPass = flag.String("amqp-pass", "", "Password for AMQP broker") + configFile = flag.String("config", "", "Path to YAML configuration file") + mode = flag.String("mode", "release", "Select debug/release/test mode (default is release)") + port = flag.String("port", "4000", "Port of the backend (default is 4000)") + baseHost = flag.String("base-host", "localhost:4000", "The host at which the backend is hosted (default: localhost)") + basePath = flag.String("base-path", "/api/v2", "The path at which the API routes are located (default /api/v2)") + adminUser = flag.String("admin-user", "", "Initial admin username") + adminPass = flag.String("admin-pass", "", "Initial admin password") + adminMail = flag.String("admin-mail", "", "Initial admin mail address") + s3Bucket = flag.String("s3-bucket", "", "S3 Bucket for uploading files") + s3Endpoint = flag.String("s3-endpoint", "", "Endpoint of S3 API for file uploads") + s3Region = flag.String("s3-region", "default", "S3 Region for file uploads") + s3NoSSL = flag.Bool("s3-nossl", false, "Use encrypted connections to the S3 API") + s3PathStyle = flag.Bool("s3-pathstyle", false, "Use path-style S3 API") + jwtSecret = flag.String("jwt-secret", "This should NOT be here!!@33$8&", "The JSON Web Token secret") + jwtExpiresAfter = flag.String("jwt-expires-after", "1w", "The time after which the JSON Web Token expires") ) flag.Parse() static := map[string]string{ - "db.host": *dbHost, - "db.name": *dbName, - "db.user": *dbUser, - "db.pass": *dbPass, - "db.ssl": *dbSSLMode, - "amqp.host": *amqpHost, - "amqp.user": *amqpUser, - "amqp.pass": *amqpPass, - "mode": *mode, - "port": *port, - "base.host": *baseHost, - "base.path": *basePath, - "admin.user": *adminUser, - "admin.pass": *adminPass, - "admin.mail": *adminMail, - "s3.bucket": *s3Bucket, - "s3.endpoint": *s3Endpoint, - "s3.region": *s3Region, + "db.host": *dbHost, + "db.name": *dbName, + "db.user": *dbUser, + "db.pass": *dbPass, + "db.ssl": *dbSSLMode, + "amqp.host": *amqpHost, + "amqp.user": *amqpUser, + "amqp.pass": *amqpPass, + "mode": *mode, + "port": *port, + "base.host": *baseHost, + "base.path": *basePath, + "admin.user": *adminUser, + "admin.pass": *adminPass, + "admin.mail": *adminMail, + "s3.bucket": *s3Bucket, + "s3.endpoint": *s3Endpoint, + "s3.region": *s3Region, + "jwt.secret": *jwtSecret, + "jwt.expires-after": *jwtExpiresAfter, } if *s3NoSSL == true { @@ -96,26 +100,28 @@ func InitConfig() error { } mappings := map[string]string{ - "DB_HOST": "db.host", - "DB_NAME": "db.name", - "DB_USER": "db.user", - "DB_PASS": "db.pass", - "DB_SSLMODE": "db.ssl", - "AMQP_HOST": "amqp.host", - "AMQP_USER": "amqp.user", - "AMQP_PASS": "amqp.pass", - "BASE_HOST": "base.host", - "BASE_PATH": "base.path", - "MODE": "mode", - "PORT": "port", - "ADMIN_USER": "admin.user", - "ADMIN_PASS": "admin.pass", - "ADMIN_MAIL": "admin.mail", - "S3_BUCKET": "s3.bucket", - "S3_ENDPOINT": "s3.endpoint", - "S3_REGION": "s3.region", - "S3_NOSSL": "s3.nossl", - "S3_PATHSTYLE": "s3.pathstyle", + "DB_HOST": "db.host", + "DB_NAME": "db.name", + "DB_USER": "db.user", + "DB_PASS": "db.pass", + "DB_SSLMODE": "db.ssl", + "AMQP_HOST": "amqp.host", + "AMQP_USER": "amqp.user", + "AMQP_PASS": "amqp.pass", + "BASE_HOST": "base.host", + "BASE_PATH": "base.path", + "MODE": "mode", + "PORT": "port", + "ADMIN_USER": "admin.user", + "ADMIN_PASS": "admin.pass", + "ADMIN_MAIL": "admin.mail", + "S3_BUCKET": "s3.bucket", + "S3_ENDPOINT": "s3.endpoint", + "S3_REGION": "s3.region", + "S3_NOSSL": "s3.nossl", + "S3_PATHSTYLE": "s3.pathstyle", + "JWT_SECRET": "jwt.secret", + "JWT_EXPIRES_AFTER": "jwt.expires-after", } defaults := config.NewStatic(static) diff --git a/routes/user/authenticate_endpoint.go b/routes/user/authenticate_endpoint.go index 40cb490..36dcfc2 100644 --- a/routes/user/authenticate_endpoint.go +++ b/routes/user/authenticate_endpoint.go @@ -22,11 +22,13 @@ package user import ( + "net/http" + "time" + + "git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration" "git.rwth-aachen.de/acs/public/villas/web-backend-go/helper" "github.com/dgrijalva/jwt-go" "github.com/gin-gonic/gin" - "net/http" - "time" ) type tokenClaims struct { @@ -86,12 +88,30 @@ func authenticate(c *gin.Context) { return } + expiresStr, err := configuration.GolbalConfig.String("jwt.expires-after") + if err != nil { + helper.UnauthorizedError(c, "Backend configuration error") + return + } + + expiresDuration, err := time.ParseDuration(expiresStr) + if err != nil { + helper.UnauthorizedError(c, "Backend configuration error") + return + } + + secret, err := configuration.GolbalConfig.String("jwt.secret") + if err != nil { + helper.UnauthorizedError(c, "Backend configuration error") + return + } + // create authentication token claims := tokenClaims{ user.ID, user.Role, jwt.StandardClaims{ - ExpiresAt: time.Now().Add(weekHours).Unix(), + ExpiresAt: time.Now().Add(expiresDuration).Unix(), IssuedAt: time.Now().Unix(), Issuer: "http://web.villas.fein-aachen.org/", }, @@ -99,7 +119,7 @@ func authenticate(c *gin.Context) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - tokenString, err := token.SignedString([]byte(jwtSigningSecret)) + tokenString, err := token.SignedString([]byte(secret)) if err != nil { helper.InternalServerError(c, err.Error()) return diff --git a/routes/user/user_endpoints.go b/routes/user/user_endpoints.go index ba133fa..7799ce8 100644 --- a/routes/user/user_endpoints.go +++ b/routes/user/user_endpoints.go @@ -23,20 +23,16 @@ package user import ( "fmt" - "git.rwth-aachen.de/acs/public/villas/web-backend-go/helper" "net/http" "strings" - "time" + + "git.rwth-aachen.de/acs/public/villas/web-backend-go/helper" "github.com/gin-gonic/gin" "git.rwth-aachen.de/acs/public/villas/web-backend-go/database" ) -// TODO: the signing secret must be environmental variable -const jwtSigningSecret = "This should NOT be here!!@33$8&" -const weekHours = time.Hour * 24 * 7 - func RegisterUserEndpoints(r *gin.RouterGroup) { r.POST("", addUser) r.PUT("/:userID", updateUser) diff --git a/routes/user/user_middleware.go b/routes/user/user_middleware.go index 85cf76a..78a7992 100644 --- a/routes/user/user_middleware.go +++ b/routes/user/user_middleware.go @@ -24,8 +24,10 @@ package user import ( "fmt" + "git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration" "git.rwth-aachen.de/acs/public/villas/web-backend-go/database" "git.rwth-aachen.de/acs/public/villas/web-backend-go/helper" + "github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go/request" "github.com/gin-gonic/gin" @@ -66,8 +68,8 @@ func Authentication(unauthorized bool) gin.HandlerFunc { } // return secret in byte format - secret := ([]byte(jwtSigningSecret)) - return secret, nil + secret, _ := configuration.GolbalConfig.String("jwt.secret") + return []byte(secret), nil }) // If the authentication extraction fails return HTTP CODE 401