mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00
add support for external authentication via OAuth2 proxy
This commit is contained in:
parent
ffa532bddc
commit
fe2d291bb3
4 changed files with 103 additions and 31 deletions
|
@ -62,6 +62,7 @@ func InitConfig() error {
|
||||||
jwtSecret = flag.String("jwt-secret", "This should NOT be here!!@33$8&", "The JSON Web Token secret")
|
jwtSecret = flag.String("jwt-secret", "This should NOT be here!!@33$8&", "The JSON Web Token secret")
|
||||||
jwtExpiresAfter = flag.String("jwt-expires-after", "168h" /* 1 week */, "The time after which the JSON Web Token expires")
|
jwtExpiresAfter = flag.String("jwt-expires-after", "168h" /* 1 week */, "The time after which the JSON Web Token expires")
|
||||||
testDataPath = flag.String("test-data-path", "database/testdata.json", "The path to the test data json file (used in test mode)")
|
testDataPath = flag.String("test-data-path", "database/testdata.json", "The path to the test data json file (used in test mode)")
|
||||||
|
authExternal = flag.Bool("auth.external", false, "Use external authentication via X-Forwarded-User header (e.g. OAuth2 Proxy)")
|
||||||
)
|
)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -101,6 +102,12 @@ func InitConfig() error {
|
||||||
static["s3.pathstyle"] = "false"
|
static["s3.pathstyle"] = "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *authExternal == true {
|
||||||
|
static["auth.external"] = "true"
|
||||||
|
} else {
|
||||||
|
static["auth.external"] = "false"
|
||||||
|
}
|
||||||
|
|
||||||
mappings := map[string]string{
|
mappings := map[string]string{
|
||||||
"DB_HOST": "db.host",
|
"DB_HOST": "db.host",
|
||||||
"DB_NAME": "db.name",
|
"DB_NAME": "db.name",
|
||||||
|
@ -125,6 +132,7 @@ func InitConfig() error {
|
||||||
"JWT_SECRET": "jwt.secret",
|
"JWT_SECRET": "jwt.secret",
|
||||||
"JWT_EXPIRES_AFTER": "jwt.expires-after",
|
"JWT_EXPIRES_AFTER": "jwt.expires-after",
|
||||||
"TEST_DATA_PATH": "test.datapath",
|
"TEST_DATA_PATH": "test.datapath",
|
||||||
|
"AUTH_EXTERNAL": "auth.external",
|
||||||
}
|
}
|
||||||
|
|
||||||
defaults := config.NewStatic(static)
|
defaults := config.NewStatic(static)
|
||||||
|
|
|
@ -23,8 +23,9 @@ package helper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetIDOfElement(c *gin.Context, elementName string, source string, providedID int) (int, error) {
|
func GetIDOfElement(c *gin.Context, elementName string, source string, providedID int) (int, error) {
|
||||||
|
@ -50,3 +51,14 @@ func GetIDOfElement(c *gin.Context, elementName string, source string, providedI
|
||||||
return -1, fmt.Errorf("invalid source of element ID")
|
return -1, fmt.Errorf("invalid source of element ID")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find takes a slice and looks for an element in it. If found it will
|
||||||
|
// return it's key, otherwise it will return -1 and a bool of false.
|
||||||
|
func Find(slice []string, val string) (int, bool) {
|
||||||
|
for i, item := range slice {
|
||||||
|
if item == val {
|
||||||
|
return i, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, false
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
|
||||||
|
@ -53,39 +54,18 @@ func RegisterAuthenticate(r *gin.RouterGroup) {
|
||||||
// @Failure 500 {object} api.ResponseError "Internal server error."
|
// @Failure 500 {object} api.ResponseError "Internal server error."
|
||||||
// @Router /authenticate [post]
|
// @Router /authenticate [post]
|
||||||
func authenticate(c *gin.Context) {
|
func authenticate(c *gin.Context) {
|
||||||
|
var user *User
|
||||||
|
|
||||||
// Bind the response (context) with the loginRequest struct
|
externalAuth, err := configuration.GlobalConfig.Bool("external-auth")
|
||||||
var credentials loginRequest
|
|
||||||
if err := c.ShouldBindJSON(&credentials); err != nil {
|
|
||||||
helper.UnauthorizedError(c, "Wrong username or password")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the login request
|
|
||||||
if errs := credentials.validate(); errs != nil {
|
|
||||||
helper.UnauthorizedError(c, "Wrong username or password")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the username in the database
|
|
||||||
var user User
|
|
||||||
err := user.ByUsername(credentials.Username)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.UnauthorizedError(c, "Wrong username or password")
|
helper.UnauthorizedError(c, "Backend configuration error")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is an active user
|
if err != nil || !externalAuth {
|
||||||
if !user.Active {
|
user = authenticateStandard(c)
|
||||||
helper.UnauthorizedError(c, "Wrong username or password")
|
} else {
|
||||||
return
|
user = authenticateExternal(c)
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the password
|
|
||||||
err = user.validatePassword(credentials.Password)
|
|
||||||
if err != nil {
|
|
||||||
helper.UnauthorizedError(c, "Wrong username or password")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expiresStr, err := configuration.GlobalConfig.String("jwt.expires-after")
|
expiresStr, err := configuration.GlobalConfig.String("jwt.expires-after")
|
||||||
|
@ -132,3 +112,77 @@ func authenticate(c *gin.Context) {
|
||||||
"user": user.User,
|
"user": user.User,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func authenticateStandard(c *gin.Context) *User {
|
||||||
|
// Bind the response (context) with the loginRequest struct
|
||||||
|
var credentials loginRequest
|
||||||
|
if err := c.ShouldBindJSON(&credentials); err != nil {
|
||||||
|
helper.UnauthorizedError(c, "Wrong username or password")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the login request
|
||||||
|
if errs := credentials.validate(); errs != nil {
|
||||||
|
helper.UnauthorizedError(c, "Failed to validate request")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the username in the database
|
||||||
|
var user User
|
||||||
|
err := user.ByUsername(credentials.Username)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnauthorizedError(c, "Unknown username")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is an active user
|
||||||
|
if !user.Active {
|
||||||
|
helper.UnauthorizedError(c, "User is not active")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the password
|
||||||
|
err = user.validatePassword(credentials.Password)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnauthorizedError(c, "Invalid password")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user
|
||||||
|
}
|
||||||
|
|
||||||
|
func authenticateExternal(c *gin.Context) *User {
|
||||||
|
username := c.Request.Header.Get("X-Forwarded-User")
|
||||||
|
if username == "" {
|
||||||
|
helper.UnauthorizedAbort(c, "Authentication failed (X-Forwarded-User headers)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
email := c.Request.Header.Get("X-Forwarded-Email")
|
||||||
|
if email == "" {
|
||||||
|
helper.UnauthorizedAbort(c, "Authentication failed (X-Forwarded-Email headers)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
groups := strings.Split(c.Request.Header.Get("X-Forwarded-Groups"), ",")
|
||||||
|
// preferred_username := c.Request.Header.Get("X-Forwarded-Preferred-Username")
|
||||||
|
|
||||||
|
var user User
|
||||||
|
if err := user.ByUsername(username); err == nil {
|
||||||
|
// There is already a user by this name
|
||||||
|
return &user
|
||||||
|
} else {
|
||||||
|
role := "User"
|
||||||
|
if _, found := helper.Find(groups, "admin"); found {
|
||||||
|
role = "Admin"
|
||||||
|
}
|
||||||
|
|
||||||
|
newUser, err := NewUser(username, "", email, role, true)
|
||||||
|
if err != nil {
|
||||||
|
helper.UnauthorizedAbort(c, "Authentication failed (failed to create new user: "+err.Error()+")")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return newUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -28,8 +28,6 @@ import (
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||||
|
|
Loading…
Add table
Reference in a new issue