Some checks failed
Build and Push Docker Image / Build image (push) Has been cancelled
231 lines
5.6 KiB
Go
231 lines
5.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.acooldomain.co/server-manager/backend/dbhandler"
|
|
"git.acooldomain.co/server-manager/backend/factories"
|
|
"git.acooldomain.co/server-manager/backend/models"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/golang-jwt/jwt"
|
|
)
|
|
|
|
type AuthApi struct {
|
|
config models.GlobalConfig
|
|
|
|
tokenHandler dbhandler.InviteTokenDbHandler
|
|
userAuthDbHandler dbhandler.UserPassAuthanticationDbHandler
|
|
serverAuthDbHandler dbhandler.ServersAuthorizationDbHandler
|
|
OidcAuthDbHandler dbhandler.OidcAuthenticationDbHandler
|
|
}
|
|
|
|
type Claims struct {
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
Permissions models.Permission `json:"permissions"`
|
|
}
|
|
|
|
type AuthClaims struct {
|
|
*jwt.StandardClaims
|
|
Claims
|
|
}
|
|
|
|
func (con *AuthApi) signToken(token Claims) (string, error) {
|
|
t := jwt.New(jwt.GetSigningMethod(con.config.Signing.Algorithm))
|
|
|
|
t.Claims = &AuthClaims{
|
|
&jwt.StandardClaims{
|
|
ExpiresAt: time.Now().Add(time.Hour * 24 * 30).Unix(),
|
|
},
|
|
token,
|
|
}
|
|
|
|
return t.SignedString([]byte(con.config.Signing.Key))
|
|
}
|
|
|
|
func (con *AuthApi) LoggedIn(ctx *gin.Context) {
|
|
authCookie, err := ctx.Request.Cookie("auth")
|
|
if err != nil {
|
|
ctx.AbortWithError(403, err)
|
|
return
|
|
}
|
|
|
|
token, err := jwt.ParseWithClaims(authCookie.Value, &AuthClaims{}, func(token *jwt.Token) (any, error) {
|
|
// Don't forget to validate the alg is what you expect:
|
|
if token.Method.Alg() != con.config.Signing.Algorithm {
|
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
|
}
|
|
|
|
// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
|
|
return []byte(con.config.Signing.Key), nil
|
|
})
|
|
if err != nil {
|
|
ctx.AbortWithError(403, err)
|
|
return
|
|
}
|
|
|
|
if !token.Valid {
|
|
ctx.AbortWithStatus(403)
|
|
return
|
|
}
|
|
claims, ok := token.Claims.(*AuthClaims)
|
|
if !ok {
|
|
ctx.AbortWithStatus(500)
|
|
return
|
|
}
|
|
|
|
ctx.Set("claims", claims)
|
|
}
|
|
|
|
type SignUpRequest struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
func (con AuthApi) signUp(ctx *gin.Context) {
|
|
var request SignUpRequest
|
|
rawToken := ctx.Query("token")
|
|
|
|
err := json.NewDecoder(ctx.Request.Body).Decode(&request)
|
|
if err != nil {
|
|
ctx.AbortWithError(500, err)
|
|
return
|
|
}
|
|
|
|
token, err := con.tokenHandler.GetInviteToken(ctx, rawToken)
|
|
if err != nil {
|
|
ctx.AbortWithError(500, err)
|
|
return
|
|
}
|
|
|
|
if token.Token == "" {
|
|
ctx.AbortWithStatusJSON(403, "PermissionDenied")
|
|
return
|
|
}
|
|
|
|
err = con.userAuthDbHandler.CreateUser(ctx, request.Username, request.Password, token.Permissions, token.Email, con.config.Users.DefaultMaxOwnedServers)
|
|
if err != nil {
|
|
ctx.AbortWithError(500, err)
|
|
return
|
|
}
|
|
|
|
con.signIn(ctx)
|
|
}
|
|
|
|
type SignInRequest struct {
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
func (con AuthApi) signIn(ctx *gin.Context) {
|
|
var request SignInRequest
|
|
err := json.NewDecoder(ctx.Request.Body).Decode(&request)
|
|
if err != nil {
|
|
ctx.AbortWithError(500, err)
|
|
return
|
|
}
|
|
userItem, err := con.userAuthDbHandler.AuthenticateUser(ctx, request.Username, request.Password)
|
|
if err != nil {
|
|
println("handler")
|
|
ctx.AbortWithError(403, err)
|
|
return
|
|
}
|
|
|
|
token := Claims{
|
|
Username: userItem.Username,
|
|
Permissions: userItem.Permissions,
|
|
}
|
|
|
|
signedToken, err := con.signToken(token)
|
|
if err != nil {
|
|
ctx.AbortWithError(500, err)
|
|
return
|
|
}
|
|
|
|
ctx.SetCookie("auth", signedToken, int(time.Hour)*24*30, "", "."+con.config.Domain, false, false)
|
|
ctx.IndentedJSON(http.StatusOK, signedToken)
|
|
}
|
|
|
|
func (con AuthApi) Verify(ctx *gin.Context) {
|
|
claimsPointer, exists := ctx.Get("claims")
|
|
if !exists {
|
|
ctx.Status(403)
|
|
ctx.Error(errors.New("failed to get claims, not logged in"))
|
|
return
|
|
}
|
|
|
|
claims, ok := claimsPointer.(*AuthClaims)
|
|
if !ok {
|
|
ctx.Error(errors.New("failed to convert claims"))
|
|
ctx.Status(500)
|
|
return
|
|
}
|
|
|
|
forwardedUri := ctx.Request.Header.Get("x-forwarded-uri")
|
|
|
|
pathSegments := strings.Split(forwardedUri, "/")
|
|
|
|
serverId, service := pathSegments[2], pathSegments[1]
|
|
|
|
switch service {
|
|
case "browsers":
|
|
fmt.Printf("%#v %s", claims, serverId)
|
|
serverPermissions, err := con.serverAuthDbHandler.GetPermissions(ctx, claims.Username, serverId)
|
|
if err != nil {
|
|
ctx.AbortWithError(500, err)
|
|
return
|
|
}
|
|
if (claims.Permissions|serverPermissions)&models.Admin == models.Admin {
|
|
ctx.Header("X-Auth-Username", claims.Username)
|
|
log.Printf("Set header X-Username %s", claims.Username)
|
|
ctx.Status(200)
|
|
return
|
|
}
|
|
case "cloud":
|
|
if claims.Permissions&models.Cloud == models.Cloud || claims.Permissions&models.Admin == models.Admin {
|
|
log.Printf("Set header X-Username %s", claims.Username)
|
|
ctx.Header("X-Auth-Username", claims.Username)
|
|
ctx.Status(200)
|
|
return
|
|
}
|
|
}
|
|
|
|
ctx.Redirect(303, fmt.Sprintf("http://%s/login", con.config.Domain))
|
|
}
|
|
|
|
func LoadGroup(group *gin.RouterGroup, config models.GlobalConfig) gin.HandlerFunc {
|
|
userAuthHandler, err := factories.GetUserPassAuthDbHandler(config.Authentication.UserPass)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
serverAuthDbHandler, err := factories.GetServersAuthorizationDbHandler(config.ServersAuthorizationDatabase)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
inviteHandler, err := factories.GetInviteTokenDbHandler(config.Authentication.UserPass.InviteTokenDatabase)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
connection := AuthApi{
|
|
userAuthDbHandler: userAuthHandler,
|
|
serverAuthDbHandler: serverAuthDbHandler,
|
|
tokenHandler: inviteHandler,
|
|
config: config,
|
|
}
|
|
|
|
group.POST("/signin", connection.signIn)
|
|
group.POST("/signup", connection.signUp)
|
|
group.Any("/verify", connection.LoggedIn, connection.Verify)
|
|
|
|
return connection.LoggedIn
|
|
}
|