Files
access-manager-api/app/main.go
T
alu 4783d79523
tronnet/workflows: Static Analysis -- Golang / static-go (push) Successful in 30s
refactor routes,
use json encoding for route body parameters,
implement modify user/group/pool operations,
simplify schema by using comon lib struct tags and RequireAll and AtLeastOne helper functions,
properly implement locadb as Backend interface
2026-08-21 20:49:15 +00:00

180 lines
5.0 KiB
Go

package app
import (
"context"
"crypto/rand"
"crypto/tls"
"flag"
"fmt"
"log"
"net/http"
"strconv"
common "access-manager-api/app/common"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"github.com/luthermonson/go-proxmox"
)
type Realm struct {
Type string
Config any
}
type UserSession struct {
PVE common.Backend
Realm struct {
Name string
Handler common.Backend
}
DB common.Backend
}
var Version = "1.0.0"
var Config common.Config
var UserSessions map[string]*UserSession
var Realms map[string]Realm
func Run() {
configPath := flag.String("config", "config.json", "path to config.json file")
flag.Parse()
// load config values
Config = common.GetConfig(*configPath)
// already exits if failed
log.Printf("Read in config from %s\n", *configPath)
// setup gin
gin.SetMode(gin.ReleaseMode)
router := gin.Default()
// setup api auth cookies
SetupAPISessionStore(router, &Config)
// get realms from proxmox
Realms = make(map[string]Realm)
Realms = GetRealmsFromPVE(&Config)
// make global session map
UserSessions = make(map[string]*UserSession)
router.GET("/version", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"version": Version})
})
router.POST("/ticket", POST_Ticket)
router.DELETE("/ticket", DELETE_Ticket)
router.GET("/pools/:poolid", GET_Pool)
router.POST("/pools/:poolid", POST_Pool)
router.DELETE("/pools/:poolid", DELETE_Pool)
router.GET("/groups/:groupname", GET_Group)
router.POST("/groups/:groupname", POST_Group)
router.DELETE("/groups/:groupname", DELETE_Group)
router.POST("/pools/:poolid/groups/:groupname", POST_Pool_Group)
router.DELETE("/pools/:poolid/groups/:groupname")
router.GET("/users/:username", GET_User)
router.POST("/users/:username", POST_User)
router.DELETE("/users/:username", DELETE_User)
router.POST("/groups/:groupname/users/:username", POST_Group_User)
router.DELETE("/groups/:groupname/users/:username", DELETE_Group_User)
log.Printf("Starting Access Manager API on port %s\n", strconv.Itoa(Config.ListenPort))
err := router.Run("0.0.0.0:" + strconv.Itoa(Config.ListenPort))
if err != nil {
log.Fatalf("Error starting router: %s", err.Error())
}
}
func SetupAPISessionStore(router *gin.Engine, config *common.Config) {
authKey := make([]byte, 32)
encrKey := make([]byte, 32)
n, _ := rand.Read(authKey) // rand Read never returns an error, always crashes on error
log.Printf("Generated cookie session authentication key of length %d\n", n)
n, _ = rand.Read(encrKey) // rand Read never returns an error, always crashes on error
log.Printf("Generated cookie session encryption key of length %d\n", n)
store := cookie.NewStore(authKey, encrKey)
store.Options(sessions.Options{
Path: config.SessionCookie.Path,
HttpOnly: config.SessionCookie.HttpOnly,
Secure: config.SessionCookie.Secure,
MaxAge: config.SessionCookie.MaxAge,
})
router.Use(sessions.Sessions(config.SessionCookieName, store))
log.Printf("Started cookie store (Name: %s Params: %+v)\n", config.SessionCookieName, config.SessionCookie)
}
func GetUserSessionFromContext(c *gin.Context) (*UserSession, int, error) {
session := sessions.Default(c)
SessionUUID := session.Get("SessionUUID")
if SessionUUID == nil {
return nil, http.StatusUnauthorized, fmt.Errorf("no auth session found")
}
uuid := SessionUUID.(string)
usersession := UserSessions[uuid]
return usersession, http.StatusOK, nil
}
func GetRealmsFromPVE(config *common.Config) map[string]Realm {
realms := map[string]Realm{}
HTTPClient := http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{},
},
}
token := fmt.Sprintf(`%s@%s!%s`, config.PVE.Token.User, config.PVE.Token.Realm, config.PVE.Token.ID)
client := proxmox.NewClient(config.PVE.URL,
proxmox.WithHTTPClient(&HTTPClient),
proxmox.WithAPIToken(token, config.PVE.Token.UUID),
)
pverealms, err := client.Domains(context.Background())
if err != nil {
// failure to get realms is a fatal error
log.Fatalf("Error getting authentication realms: %s", err.Error())
}
// add required pve realm handler, removing the pve api token
pveconfig := common.PVEConfig{
URL: config.PVE.URL,
PAASClientRole: config.PVE.PAASClientRole,
}
realms["pve"] = Realm{
Type: "pve",
Config: pveconfig,
}
log.Printf("Configured default authentication realm pve")
// iterate through handlers and add to realms
for _, r := range pverealms {
realm, err := client.Domain(context.Background(), r.Realm)
if err != nil {
log.Printf("Error getting authentication realm %s: %s", r.Realm, err.Error())
}
if realm.Type == "ldap" {
ldapconfig := common.LDAPConfig{
BaseDN: realm.BaseDN,
Hostname: realm.Server1,
TLS: realm.Mode == "ldaps",
StartTLS: realm.Mode == "ldap+starttls",
Verify: bool(realm.Verify),
}
realms[realm.Realm] = Realm{
Type: realm.Type,
Config: ldapconfig,
}
log.Printf("Configured external authentication realm %s", realm.Realm)
} else {
continue
}
}
return realms
}