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
This commit is contained in:
@@ -25,6 +25,10 @@ type LDAPConfig struct {
|
|||||||
Verify bool
|
Verify bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LocalDBConfig struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ListenPort int `json:"listenPort"`
|
ListenPort int `json:"listenPort"`
|
||||||
SessionCookieName string `json:"sessionCookieName"`
|
SessionCookieName string `json:"sessionCookieName"`
|
||||||
@@ -34,7 +38,8 @@ type Config struct {
|
|||||||
Secure bool `json:"secure"`
|
Secure bool `json:"secure"`
|
||||||
MaxAge int `json:"maxAge"`
|
MaxAge int `json:"maxAge"`
|
||||||
}
|
}
|
||||||
PVE PVEConfig `json:"pve"`
|
PVE PVEConfig `json:"pve"`
|
||||||
|
LocalDB LocalDBConfig `json:"localdb"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfig(configPath string) Config {
|
func GetConfig(configPath string) Config {
|
||||||
|
|||||||
+2
-16
@@ -1,21 +1,7 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
type Login struct { // login body struct
|
type Login struct { // login body struct
|
||||||
UsernameRaw string `form:"username" binding:"required"`
|
UsernameRaw string `json:"username" binding:"required"`
|
||||||
Username Username
|
Username Username
|
||||||
Password string `form:"password" binding:"required"`
|
Password string `json:"password" binding:"required"`
|
||||||
}
|
|
||||||
|
|
||||||
type UserFormRequired struct { // add user body struct
|
|
||||||
CN string `form:"cn" binding:"required"`
|
|
||||||
SN string `form:"sn" binding:"required"`
|
|
||||||
Mail string `form:"mail" binding:"required"`
|
|
||||||
Password string `form:"password" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserFormOptional struct { // modify user body struct
|
|
||||||
CN string `form:"cn"`
|
|
||||||
SN string `form:"sn"`
|
|
||||||
Mail string `form:"mail"`
|
|
||||||
Password string `form:"password"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -3,15 +3,18 @@ package app
|
|||||||
import paas "proxmoxaas-common-lib"
|
import paas "proxmoxaas-common-lib"
|
||||||
|
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
NewPool(poolname string) (int, error)
|
NewPool(poolname string, pool Pool) (int, error)
|
||||||
|
ModPool(poolname string, pool Pool) (int, error)
|
||||||
GetPool(poolname string) (Pool, []string, int, error) // []string members
|
GetPool(poolname string) (Pool, []string, int, error) // []string members
|
||||||
DelPool(poolname string) (int, error)
|
DelPool(poolname string) (int, error)
|
||||||
NewGroup(groupname Groupname) (int, error)
|
NewGroup(groupname Groupname, group Group) (int, error)
|
||||||
|
ModGroup(groupname Groupname, group Group) (int, error)
|
||||||
GetGroup(groupname Groupname) (Group, []string, int, error) // []string members
|
GetGroup(groupname Groupname) (Group, []string, int, error) // []string members
|
||||||
DelGroup(groupname Groupname) (int, error)
|
DelGroup(groupname Groupname) (int, error)
|
||||||
AddGroupToPool(groupname Groupname, poolname string) (int, error)
|
AddGroupToPool(groupname Groupname, poolname string) (int, error)
|
||||||
DelGroupFromPool(groupname Groupname, poolname string) (int, error)
|
DelGroupFromPool(groupname Groupname, poolname string) (int, error)
|
||||||
NewUser(username Username, user User) (int, error)
|
NewUser(username Username, user User) (int, error)
|
||||||
|
ModUser(username Username, user User) (int, error)
|
||||||
GetUser(username Username) (User, int, error)
|
GetUser(username Username) (User, int, error)
|
||||||
DelUser(username Username) (int, error)
|
DelUser(username Username) (int, error)
|
||||||
AddUserToGroup(username Username, groupname Groupname) (int, error)
|
AddUserToGroup(username Username, groupname Groupname) (int, error)
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequireAll ensures that EVERY non-excluded exported field in the struct is non-zero.
|
||||||
|
func RequireAll(v any, excludes ...string) bool {
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
if val.Kind() == reflect.Pointer {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
typ := val.Type()
|
||||||
|
|
||||||
|
excludeMap := make(map[string]bool)
|
||||||
|
for _, ex := range excludes {
|
||||||
|
excludeMap[ex] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
fieldName := typ.Field(i).Name
|
||||||
|
if excludeMap[fieldName] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if val.Field(i).IsZero() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AtLeastOne ensures that AT LEAST ONE non-excluded exported field is non-zero.
|
||||||
|
func AtLeastOne(v any, excludes ...string) bool {
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
if val.Kind() == reflect.Pointer {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
typ := val.Type()
|
||||||
|
|
||||||
|
excludeMap := make(map[string]bool)
|
||||||
|
for _, ex := range excludes {
|
||||||
|
excludeMap[ex] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
fieldName := typ.Field(i).Name
|
||||||
|
if excludeMap[fieldName] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !val.Field(i).IsZero() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
+19
-23
@@ -78,8 +78,10 @@ func (l LDAPClient) GetUser(username common.Username) (common.User, int, error)
|
|||||||
)
|
)
|
||||||
|
|
||||||
searchResponse, err := l.client.Search(searchRequest) // perform search
|
searchResponse, err := l.client.Search(searchRequest) // perform search
|
||||||
if err != nil {
|
if ldap.IsErrorAnyOf(err, ldap.LDAPResultNoSuchObject) {
|
||||||
return user, http.StatusBadRequest, err
|
return user, http.StatusNotFound, err
|
||||||
|
} else if err != nil {
|
||||||
|
return user, http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := searchResponse.Entries[0]
|
entry := searchResponse.Entries[0]
|
||||||
@@ -91,10 +93,10 @@ func (l LDAPClient) GetUser(username common.Username) (common.User, int, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l LDAPClient) NewUser(username common.Username, user common.User) (int, error) {
|
func (l LDAPClient) NewUser(username common.Username, user common.User) (int, error) {
|
||||||
if user.CN == "" || user.SN == "" || user.Password == "" || user.Mail == "" {
|
if !common.RequireAll(user, "Username") {
|
||||||
return http.StatusBadRequest, ldap.NewError(
|
return http.StatusBadRequest, ldap.NewError(
|
||||||
ldap.LDAPResultUnwillingToPerform,
|
ldap.LDAPResultUnwillingToPerform,
|
||||||
errors.New("missing one of required fields: cn, sn, mail, userpassword"),
|
errors.New("requires all of fields: cn, sn, mail, password"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,10 +119,10 @@ func (l LDAPClient) NewUser(username common.Username, user common.User) (int, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l LDAPClient) ModUser(username common.Username, user common.User) (int, error) {
|
func (l LDAPClient) ModUser(username common.Username, user common.User) (int, error) {
|
||||||
if user.CN == "" && user.SN == "" && user.Password == "" && user.Mail == "" {
|
if !common.AtLeastOne(user, "Username") {
|
||||||
return http.StatusBadRequest, ldap.NewError(
|
return http.StatusBadRequest, ldap.NewError(
|
||||||
ldap.LDAPResultUnwillingToPerform,
|
ldap.LDAPResultUnwillingToPerform,
|
||||||
errors.New("requires one of fields: cn, sn, mail, userpassword"),
|
errors.New("requires one of fields: cn, sn, mail, password"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,8 +182,10 @@ func (l LDAPClient) GetGroup(groupname common.Groupname) (common.Group, []string
|
|||||||
)
|
)
|
||||||
|
|
||||||
searchResponse, err := l.client.Search(searchRequest) // perform search
|
searchResponse, err := l.client.Search(searchRequest) // perform search
|
||||||
if err != nil {
|
if ldap.IsErrorAnyOf(err, ldap.LDAPResultNoSuchObject) {
|
||||||
return group, members, http.StatusBadRequest, err
|
return group, members, http.StatusNotFound, err
|
||||||
|
} else if err != nil {
|
||||||
|
return group, members, http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := searchResponse.Entries[0]
|
entry := searchResponse.Entries[0]
|
||||||
@@ -195,7 +199,7 @@ func (l LDAPClient) GetGroup(groupname common.Groupname) (common.Group, []string
|
|||||||
return group, members, http.StatusOK, nil
|
return group, members, http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l LDAPClient) NewGroup(groupname common.Groupname) (int, error) {
|
func (l LDAPClient) NewGroup(groupname common.Groupname, group common.Group) (int, error) {
|
||||||
// add new group by ID only
|
// add new group by ID only
|
||||||
addRequest := ldap.NewAddRequest(
|
addRequest := ldap.NewAddRequest(
|
||||||
fmt.Sprintf("cn=%s,ou=groups,%s", groupname.GroupID, l.config.BaseDN), // DN
|
fmt.Sprintf("cn=%s,ou=groups,%s", groupname.GroupID, l.config.BaseDN), // DN
|
||||||
@@ -214,19 +218,7 @@ func (l LDAPClient) NewGroup(groupname common.Groupname) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l LDAPClient) ModGroup(groupname common.Groupname, group common.Group) (int, error) {
|
func (l LDAPClient) ModGroup(groupname common.Groupname, group common.Group) (int, error) {
|
||||||
modifyRequest := ldap.NewModifyRequest(
|
return http.StatusNotImplemented, fmt.Errorf("ldap does not implement modification of groups")
|
||||||
fmt.Sprintf("cn=%s,ou=groups,%s", groupname.GroupID, l.config.BaseDN),
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
|
|
||||||
modifyRequest.Replace("cn", []string{groupname.GroupID})
|
|
||||||
|
|
||||||
err := l.client.Modify(modifyRequest)
|
|
||||||
if err != nil {
|
|
||||||
return http.StatusBadRequest, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.StatusOK, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l LDAPClient) DelGroup(groupname common.Groupname) (int, error) {
|
func (l LDAPClient) DelGroup(groupname common.Groupname) (int, error) {
|
||||||
@@ -285,7 +277,11 @@ func (l LDAPClient) DelUserFromGroup(username common.Username, groupname common.
|
|||||||
return http.StatusOK, nil
|
return http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l LDAPClient) NewPool(poolname string) (int, error) {
|
func (l LDAPClient) NewPool(poolname string, pool common.Pool) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("ldap does not implement pools")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPClient) ModPool(poolname string, pool common.Pool) (int, error) {
|
||||||
return http.StatusNotImplemented, fmt.Errorf("ldap does not implement pools")
|
return http.StatusNotImplemented, fmt.Errorf("ldap does not implement pools")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+121
-9
@@ -9,32 +9,38 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DB struct {
|
type DB struct {
|
||||||
|
path string
|
||||||
data map[string]common.Pool
|
data map[string]common.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadDB(localDBPath string) (DB, error) {
|
var db *DB
|
||||||
db := DB{}
|
|
||||||
|
func (db *DB) load(localDBPath string) error {
|
||||||
|
db.data = make(map[string]common.Pool)
|
||||||
|
db.path = localDBPath
|
||||||
|
|
||||||
root, err := os.OpenRoot(".")
|
root, err := os.OpenRoot(".")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return db, err
|
return err
|
||||||
}
|
}
|
||||||
defer root.Close()
|
defer root.Close()
|
||||||
|
|
||||||
content, err := root.ReadFile(localDBPath)
|
content, err := root.ReadFile(localDBPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return db, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(content, &db.data)
|
err = json.Unmarshal(content, &db.data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return db, err
|
return err
|
||||||
}
|
}
|
||||||
return db, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveDB(localDBPath string, db DB) error {
|
func (db *DB) save() error {
|
||||||
json, err := json.Marshal(db.data)
|
localDBPath := db.path
|
||||||
|
// write to file with pretty print for readability reasons
|
||||||
|
json, err := json.MarshalIndent(db.data, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -42,10 +48,116 @@ func SaveDB(localDBPath string, db DB) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewClientFromCredentials(config common.LocalDBConfig, username common.Username, password string) (common.Backend, int, error) {
|
||||||
|
if db != nil {
|
||||||
|
return *db, http.StatusOK, nil
|
||||||
|
} else {
|
||||||
|
// load localdb if this is the first time
|
||||||
|
db = &DB{}
|
||||||
|
err := db.load(config.Path)
|
||||||
|
if err != nil {
|
||||||
|
return *db, http.StatusInternalServerError, err
|
||||||
|
} else {
|
||||||
|
return *db, http.StatusOK, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (localdb DB) GetPool(poolname string) (common.Pool, []string, int, error) {
|
func (localdb DB) GetPool(poolname string) (common.Pool, []string, int, error) {
|
||||||
pool, ok := localdb.data[poolname]
|
pool, ok := localdb.data[poolname]
|
||||||
if !ok {
|
if !ok {
|
||||||
return pool, []string{}, http.StatusNotFound, fmt.Errorf("pool %s not in localdb", poolname)
|
return pool, []string{}, http.StatusNotFound, fmt.Errorf("localdb pool %s does not exist", poolname)
|
||||||
}
|
}
|
||||||
return pool, []string{}, http.StatusOK, nil
|
return pool, []string{}, http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (localdb DB) NewPool(poolname string, pool common.Pool) (int, error) {
|
||||||
|
_, ok := localdb.data[poolname]
|
||||||
|
if ok {
|
||||||
|
return http.StatusBadRequest, fmt.Errorf("localdb pool %s already exists", poolname)
|
||||||
|
}
|
||||||
|
localdb.data[poolname] = pool
|
||||||
|
err := localdb.save()
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
} else {
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) ModPool(poolname string, pool common.Pool) (int, error) {
|
||||||
|
_, ok := localdb.data[poolname]
|
||||||
|
if !ok {
|
||||||
|
return http.StatusBadRequest, fmt.Errorf("localdb pool %s does not exist", poolname)
|
||||||
|
}
|
||||||
|
old_pool := localdb.data[poolname]
|
||||||
|
MergeNonZero(&old_pool, &pool)
|
||||||
|
err := localdb.save()
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
} else {
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) DelPool(poolname string) (int, error) {
|
||||||
|
_, ok := localdb.data[poolname]
|
||||||
|
if !ok {
|
||||||
|
return http.StatusBadRequest, fmt.Errorf("localdb pool %s does not exist", poolname)
|
||||||
|
}
|
||||||
|
delete(localdb.data, poolname)
|
||||||
|
err := localdb.save()
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
} else {
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) NewGroup(groupname common.Groupname, group common.Group) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("localdb does not implement groups")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) ModGroup(groupname common.Groupname, group common.Group) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("localdb does not implement groups")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) GetGroup(groupname common.Groupname) (common.Group, []string, int, error) {
|
||||||
|
return common.Group{}, []string{}, http.StatusNotImplemented, fmt.Errorf("localdb does not implement groups")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) DelGroup(groupname common.Groupname) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("localdb does not implement groups")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) AddGroupToPool(groupname common.Groupname, poolname string) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("localdb does not implement groups")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) DelGroupFromPool(groupname common.Groupname, poolname string) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("localdb does not implement groups")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) NewUser(username common.Username, user common.User) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("localdb does not implement users")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) ModUser(username common.Username, user common.User) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("localdb does not implement users")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) GetUser(username common.Username) (common.User, int, error) {
|
||||||
|
return common.User{}, http.StatusNotImplemented, fmt.Errorf("localdb does not implement users")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) DelUser(username common.Username) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("localdb does not implement users")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) AddUserToGroup(username common.Username, groupname common.Groupname) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("localdb does not implement users")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localdb DB) DelUserFromGroup(username common.Username, groupname common.Groupname) (int, error) {
|
||||||
|
return http.StatusNotImplemented, fmt.Errorf("localdb does not implement users")
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package localdb
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
// MergeNonZero overwrites fields in dst with fields in src iff src is not a zero value.
|
||||||
|
func MergeNonZero[T any](dst *T, src *T) {
|
||||||
|
vDst := reflect.ValueOf(dst).Elem()
|
||||||
|
vSrc := reflect.ValueOf(src).Elem()
|
||||||
|
|
||||||
|
for i := 0; i < vDst.NumField(); i++ {
|
||||||
|
dstField := vDst.Field(i)
|
||||||
|
srcField := vSrc.Field(i)
|
||||||
|
|
||||||
|
// Only set if the field is exported (CanSet) and the source is not a zero value
|
||||||
|
if dstField.CanSet() && !srcField.IsZero() {
|
||||||
|
dstField.Set(srcField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+53
-488
@@ -11,18 +11,27 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
common "access-manager-api/app/common"
|
common "access-manager-api/app/common"
|
||||||
ldap "access-manager-api/app/ldap"
|
|
||||||
localdb "access-manager-api/app/localdb"
|
|
||||||
pve "access-manager-api/app/pve"
|
|
||||||
paas "proxmoxaas-common-lib"
|
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-contrib/sessions/cookie"
|
"github.com/gin-contrib/sessions/cookie"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/luthermonson/go-proxmox"
|
"github.com/luthermonson/go-proxmox"
|
||||||
uuid "github.com/nu7hatch/gouuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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 Version = "1.0.0"
|
||||||
var Config common.Config
|
var Config common.Config
|
||||||
var UserSessions map[string]*UserSession
|
var UserSessions map[string]*UserSession
|
||||||
@@ -30,25 +39,19 @@ var Realms map[string]Realm
|
|||||||
|
|
||||||
func Run() {
|
func Run() {
|
||||||
configPath := flag.String("config", "config.json", "path to config.json file")
|
configPath := flag.String("config", "config.json", "path to config.json file")
|
||||||
localDBPath := flag.String("localdb", "localdb.json", "path to localdb.json file")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// load config values
|
// load config values
|
||||||
var err error
|
|
||||||
Config = common.GetConfig(*configPath)
|
Config = common.GetConfig(*configPath)
|
||||||
// already exits if failed
|
// already exits if failed
|
||||||
log.Printf("Read in config from %s\n", *configPath)
|
log.Printf("Read in config from %s\n", *configPath)
|
||||||
|
|
||||||
// load localdb
|
// setup gin
|
||||||
db, err := localdb.LoadDB(*localDBPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error when reading localdb file: %s\n", err)
|
|
||||||
}
|
|
||||||
log.Printf("Read in localdb from %s\n", *localDBPath)
|
|
||||||
|
|
||||||
// setup router
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
router := SetupAPISessionStore(&Config)
|
router := gin.Default()
|
||||||
|
|
||||||
|
// setup api auth cookies
|
||||||
|
SetupAPISessionStore(router, &Config)
|
||||||
|
|
||||||
// get realms from proxmox
|
// get realms from proxmox
|
||||||
Realms = make(map[string]Realm)
|
Realms = make(map[string]Realm)
|
||||||
@@ -61,486 +64,39 @@ func Run() {
|
|||||||
c.JSON(http.StatusOK, gin.H{"version": Version})
|
c.JSON(http.StatusOK, gin.H{"version": Version})
|
||||||
})
|
})
|
||||||
|
|
||||||
router.POST("/ticket", func(c *gin.Context) {
|
router.POST("/ticket", POST_Ticket)
|
||||||
body := common.Login{}
|
router.DELETE("/ticket", DELETE_Ticket)
|
||||||
err := c.ShouldBind(&body)
|
router.GET("/pools/:poolid", GET_Pool)
|
||||||
if err != nil { // bad request from binding
|
router.POST("/pools/:poolid", POST_Pool)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()})
|
router.DELETE("/pools/:poolid", DELETE_Pool)
|
||||||
return
|
router.GET("/groups/:groupname", GET_Group)
|
||||||
}
|
router.POST("/groups/:groupname", POST_Group)
|
||||||
|
router.DELETE("/groups/:groupname", DELETE_Group)
|
||||||
// attempt to parse username
|
router.POST("/pools/:poolid/groups/:groupname", POST_Pool_Group)
|
||||||
body.Username, err = paas.ParseUsername(body.UsernameRaw)
|
router.DELETE("/pools/:poolid/groups/:groupname")
|
||||||
if err != nil { // username format incorrect
|
router.GET("/users/:username", GET_User)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()})
|
router.POST("/users/:username", POST_User)
|
||||||
return
|
router.DELETE("/users/:username", DELETE_User)
|
||||||
}
|
router.POST("/groups/:groupname/users/:username", POST_Group_User)
|
||||||
handler := Realms[body.Username.Realm].Type
|
router.DELETE("/groups/:groupname/users/:username", DELETE_Group_User)
|
||||||
|
|
||||||
userbackends := UserSession{}
|
|
||||||
|
|
||||||
// always bind proxmox backend
|
|
||||||
PVEClient, code, err := pve.NewClientFromCredentials(Config.PVE, body.Username, body.Password)
|
|
||||||
if err != nil { // pve client failed to bind
|
|
||||||
c.JSON(code, gin.H{"auth": false, "error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userbackends.PVE = PVEClient
|
|
||||||
|
|
||||||
// bind backend by type
|
|
||||||
switch handler {
|
|
||||||
case "pve":
|
|
||||||
case "ldap":
|
|
||||||
config := Realms[body.Username.Realm].Config.(common.LDAPConfig)
|
|
||||||
LDAPClient, code, err := ldap.NewClientFromCredentials(config, body.Username, body.Password)
|
|
||||||
if err != nil { // ldap client failed to bind
|
|
||||||
c.JSON(code, gin.H{"auth": false, "error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userbackends.Realm.Name = body.Username.Realm
|
|
||||||
userbackends.Realm.Handler = LDAPClient
|
|
||||||
default:
|
|
||||||
c.JSON(code, gin.H{"auth": false, "error": fmt.Errorf("user realm %s is not supported", body.Username.Realm)})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userbackends.DB = &db
|
|
||||||
|
|
||||||
// successful binding at this point
|
|
||||||
// create new session
|
|
||||||
session := sessions.Default(c)
|
|
||||||
// create random uuid to map user to backends
|
|
||||||
uuid, _ := uuid.NewV4()
|
|
||||||
// set uuid mapping in session
|
|
||||||
session.Set("SessionUUID", uuid.String())
|
|
||||||
// set uuid mapping in LDAPSessions
|
|
||||||
UserSessions[uuid.String()] = &userbackends
|
|
||||||
// save the session
|
|
||||||
err = session.Save()
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"auth": false})
|
|
||||||
} else {
|
|
||||||
// return successful auth
|
|
||||||
c.JSON(http.StatusOK, gin.H{"auth": true})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.DELETE("/ticket", func(c *gin.Context) {
|
|
||||||
// get session uuid from session cookie
|
|
||||||
session := sessions.Default(c)
|
|
||||||
SessionUUID := session.Get("SessionUUID")
|
|
||||||
if SessionUUID == nil {
|
|
||||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uuid := SessionUUID.(string)
|
|
||||||
|
|
||||||
// delete uuid entry from user sessions
|
|
||||||
delete(UserSessions, uuid) // deletes uuid mapping
|
|
||||||
session.Options(sessions.Options{MaxAge: -1}) // set max age to -1 so session cookie is deleted
|
|
||||||
err := session.Save() // if save somehow fails, it should be ok since the uuid mapping is already deleted
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"auth": false})
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.GET("/pools/:poolid", func(c *gin.Context) {
|
|
||||||
poolid, ok := c.Params.Get("poolid")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pool, code, err := GetPool(backends, poolid)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusOK, gin.H{"pool": pool})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.POST("/pools/:poolid", func(c *gin.Context) {
|
|
||||||
poolid, ok := c.Params.Get("poolid")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, err = NewPool(backends, poolid)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.DELETE("/pools/:poolid", func(c *gin.Context) {
|
|
||||||
poolid, ok := c.Params.Get("poolid")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, err = DelPool(backends, poolid)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.GET("/groups/:groupname", func(c *gin.Context) {
|
|
||||||
groupname_str, ok := c.Params.Get("groupname")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groupname, err := common.ParseGroupname(groupname_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
group, code, err := GetGroup(backends, groupname)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusOK, gin.H{"group": group})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.POST("/groups/:groupname", func(c *gin.Context) {
|
|
||||||
groupname_str, ok := c.Params.Get("groupname")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groupname, err := paas.ParseGroupname(groupname_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, err = NewGroup(backends, groupname)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.DELETE("/groups/:groupname", func(c *gin.Context) {
|
|
||||||
groupname_str, ok := c.Params.Get("groupname")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groupname, err := paas.ParseGroupname(groupname_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, err = DelGroup(backends, groupname)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.POST("/pools/:poolid/groups/:groupname", func(c *gin.Context) {
|
|
||||||
poolid, ok := c.Params.Get("poolid")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groupname_str, ok := c.Params.Get("groupname")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groupname, err := paas.ParseGroupname(groupname_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, err = AddGroupToPool(backends, groupname, poolid)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.DELETE("/pools/:poolid/groups/:groupname", func(c *gin.Context) {
|
|
||||||
poolid, ok := c.Params.Get("poolid")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groupname_str, ok := c.Params.Get("groupname")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groupname, err := paas.ParseGroupname(groupname_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, err = DelGroupFromPool(backends, groupname, poolid)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.GET("/users/:username", func(c *gin.Context) {
|
|
||||||
username_str, ok := c.Params.Get("username")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
username, err := common.ParseUsername(username_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user, code, err := GetUser(backends, username)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusOK, gin.H{"user": user})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.POST("/users/:username", func(c *gin.Context) {
|
|
||||||
username_str, ok := c.Params.Get("username")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
username, err := paas.ParseUsername(username_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
form := common.UserFormRequired{}
|
|
||||||
err = c.ShouldBind(&form)
|
|
||||||
if err != nil { // failed binding, usually missing form field
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user := common.User{}
|
|
||||||
user.CN = form.CN
|
|
||||||
user.SN = form.SN
|
|
||||||
user.Mail = form.Mail
|
|
||||||
user.Password = form.Password
|
|
||||||
|
|
||||||
code, err = NewUser(backends, username, user)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.DELETE("/users/:username", func(c *gin.Context) {
|
|
||||||
username_str, ok := c.Params.Get("username")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
username, err := paas.ParseUsername(username_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, err = DelUser(backends, username)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.POST("/groups/:groupname/users/:username", func(c *gin.Context) {
|
|
||||||
groupname_str, ok := c.Params.Get("groupname")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
username_str, ok := c.Params.Get("username")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter username")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groupname, err := paas.ParseGroupname(groupname_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
username, err := paas.ParseUsername(username_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, err = AddUserToGroup(backends, username, groupname)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
router.DELETE("/groups/:groupname/users/:username", func(c *gin.Context) {
|
|
||||||
groupname_str, ok := c.Params.Get("groupname")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
username_str, ok := c.Params.Get("username")
|
|
||||||
if !ok {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter username")})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groupname, err := paas.ParseGroupname(groupname_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
username, err := paas.ParseUsername(username_str)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backends, code, err := GetUserSessionFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, err = DelUserFromGroup(backends, username, groupname)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(code, gin.H{"error": err.Error()})
|
|
||||||
} else {
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
log.Printf("Starting Access Manager API on port %s\n", strconv.Itoa(Config.ListenPort))
|
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))
|
err := router.Run("0.0.0.0:" + strconv.Itoa(Config.ListenPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error starting router: %s", err.Error())
|
log.Fatalf("Error starting router: %s", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupAPISessionStore(config *common.Config) *gin.Engine {
|
func SetupAPISessionStore(router *gin.Engine, config *common.Config) {
|
||||||
secretKey := make([]byte, 256)
|
authKey := make([]byte, 32)
|
||||||
n, _ := rand.Read(secretKey) // rand Read never returns an error, always crashes on error
|
encrKey := make([]byte, 32)
|
||||||
log.Printf("Generated session secret key of length %d\n", n)
|
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)
|
||||||
|
|
||||||
router := gin.Default()
|
store := cookie.NewStore(authKey, encrKey)
|
||||||
store := cookie.NewStore(secretKey)
|
|
||||||
store.Options(sessions.Options{
|
store.Options(sessions.Options{
|
||||||
Path: config.SessionCookie.Path,
|
Path: config.SessionCookie.Path,
|
||||||
HttpOnly: config.SessionCookie.HttpOnly,
|
HttpOnly: config.SessionCookie.HttpOnly,
|
||||||
@@ -550,8 +106,17 @@ func SetupAPISessionStore(config *common.Config) *gin.Engine {
|
|||||||
router.Use(sessions.Sessions(config.SessionCookieName, store))
|
router.Use(sessions.Sessions(config.SessionCookieName, store))
|
||||||
|
|
||||||
log.Printf("Started cookie store (Name: %s Params: %+v)\n", config.SessionCookieName, config.SessionCookie)
|
log.Printf("Started cookie store (Name: %s Params: %+v)\n", config.SessionCookieName, config.SessionCookie)
|
||||||
|
}
|
||||||
|
|
||||||
return router
|
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 {
|
func GetRealmsFromPVE(config *common.Config) map[string]Realm {
|
||||||
|
|||||||
+97
-23
@@ -3,13 +3,46 @@ package app
|
|||||||
import (
|
import (
|
||||||
common "access-manager-api/app/common"
|
common "access-manager-api/app/common"
|
||||||
"access-manager-api/app/ldap"
|
"access-manager-api/app/ldap"
|
||||||
|
proxmox "access-manager-api/app/pve"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPool(backends *UserSession, poolname string) (int, error) {
|
func NewPool(backends *UserSession, poolname string, pool common.Pool) (int, error) {
|
||||||
// only pve backend handles pools
|
code, err := backends.PVE.NewPool(poolname, pool)
|
||||||
return backends.PVE.NewPool(poolname)
|
if err != nil {
|
||||||
|
return code, err
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err = backends.DB.NewPool(poolname, pool)
|
||||||
|
if err != nil {
|
||||||
|
// try to undo pve add pool operation
|
||||||
|
backends.PVE.DelPool(poolname)
|
||||||
|
return code, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModPool(backends *UserSession, poolname string, pool common.Pool) (int, error) {
|
||||||
|
oldpool, code, err := GetPool(backends, poolname)
|
||||||
|
if err != nil {
|
||||||
|
return code, err
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err = backends.PVE.ModPool(poolname, pool)
|
||||||
|
if err != nil {
|
||||||
|
return code, err
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err = backends.DB.ModPool(poolname, pool)
|
||||||
|
if err != nil {
|
||||||
|
// try to undo pve mod pool operation
|
||||||
|
backends.PVE.ModPool(poolname, oldpool)
|
||||||
|
return code, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// get pool recursive resolving groups
|
// get pool recursive resolving groups
|
||||||
@@ -25,7 +58,8 @@ func GetPool(backends *UserSession, poolname string) (common.Pool, int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return pool, code, err
|
return pool, code, err
|
||||||
}
|
}
|
||||||
// assign pool id from PVE, assign everything else from DB
|
|
||||||
|
// assign pool id and pool members from PVE, assign everything else from DB
|
||||||
pool.PoolID = pvepool.PoolID
|
pool.PoolID = pvepool.PoolID
|
||||||
pool.Resources = dbpool.Resources
|
pool.Resources = dbpool.Resources
|
||||||
pool.AllowedNodes = dbpool.AllowedNodes
|
pool.AllowedNodes = dbpool.AllowedNodes
|
||||||
@@ -43,6 +77,7 @@ func GetPool(backends *UserSession, poolname string) (common.Pool, int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return pool, code, err
|
return pool, code, err
|
||||||
}
|
}
|
||||||
|
// members already are filtered by PAASClientRole
|
||||||
group.Role = Config.PVE.PAASClientRole
|
group.Role = Config.PVE.PAASClientRole
|
||||||
pool.Groups = append(pool.Groups, group)
|
pool.Groups = append(pool.Groups, group)
|
||||||
}
|
}
|
||||||
@@ -50,20 +85,42 @@ func GetPool(backends *UserSession, poolname string) (common.Pool, int, error) {
|
|||||||
return pool, http.StatusOK, nil
|
return pool, http.StatusOK, nil
|
||||||
}
|
}
|
||||||
func DelPool(backends *UserSession, poolname string) (int, error) {
|
func DelPool(backends *UserSession, poolname string) (int, error) {
|
||||||
// only pve backend handles pools
|
codepve, errpve := backends.PVE.DelPool(poolname)
|
||||||
return backends.PVE.DelPool(poolname)
|
|
||||||
|
codedb, errdb := backends.DB.DelPool(poolname)
|
||||||
|
|
||||||
|
if errpve != nil || errdb != nil {
|
||||||
|
return http.StatusInternalServerError, fmt.Errorf("error deleting pool: (pve:%d, %s) (db:%d, %s)", codepve, errpve, codedb, errdb)
|
||||||
|
} else {
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGroup(backends *UserSession, groupname common.Groupname) (int, error) {
|
func NewGroup(backends *UserSession, groupname common.Groupname, group common.Group) (int, error) {
|
||||||
if groupname.Realm == "pve" {
|
if groupname.Realm == "pve" {
|
||||||
return backends.PVE.NewGroup(groupname)
|
return backends.PVE.NewGroup(groupname, group)
|
||||||
} else if groupname.Realm == backends.Realm.Name {
|
} else if groupname.Realm == backends.Realm.Name {
|
||||||
realm_handler := backends.Realm.Handler.(common.Backend)
|
realm_handler := backends.Realm.Handler
|
||||||
code, err := realm_handler.NewGroup(groupname)
|
code, err := realm_handler.NewGroup(groupname, group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return code, err
|
return code, err
|
||||||
}
|
}
|
||||||
return backends.PVE.SyncRealms()
|
return backends.PVE.(proxmox.ProxmoxClient).SyncRealms()
|
||||||
|
} else {
|
||||||
|
return http.StatusUnauthorized, fmt.Errorf("user is not in the same realm as requested group")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModGroup(backends *UserSession, groupname common.Groupname, group common.Group) (int, error) {
|
||||||
|
if groupname.Realm == "pve" {
|
||||||
|
return backends.PVE.ModGroup(groupname, group)
|
||||||
|
} else if groupname.Realm == backends.Realm.Name {
|
||||||
|
realm_handler := backends.Realm.Handler
|
||||||
|
code, err := realm_handler.ModGroup(groupname, group)
|
||||||
|
if err != nil {
|
||||||
|
return code, err
|
||||||
|
}
|
||||||
|
return backends.PVE.(proxmox.ProxmoxClient).SyncRealms()
|
||||||
} else {
|
} else {
|
||||||
return http.StatusUnauthorized, fmt.Errorf("user is not in the same realm as requested group")
|
return http.StatusUnauthorized, fmt.Errorf("user is not in the same realm as requested group")
|
||||||
}
|
}
|
||||||
@@ -92,7 +149,7 @@ func GetGroup(backends *UserSession, groupname common.Groupname) (common.Group,
|
|||||||
|
|
||||||
return group, http.StatusOK, nil
|
return group, http.StatusOK, nil
|
||||||
} else if groupname.Realm == backends.Realm.Name {
|
} else if groupname.Realm == backends.Realm.Name {
|
||||||
group, members, code, err := backends.Realm.Handler.(common.Backend).GetGroup(groupname)
|
group, members, code, err := backends.Realm.Handler.GetGroup(groupname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Group{}, code, err
|
return common.Group{}, code, err
|
||||||
}
|
}
|
||||||
@@ -127,12 +184,12 @@ func DelGroup(backends *UserSession, groupname common.Groupname) (int, error) {
|
|||||||
if groupname.Realm == "pve" {
|
if groupname.Realm == "pve" {
|
||||||
return backends.PVE.DelGroup(groupname)
|
return backends.PVE.DelGroup(groupname)
|
||||||
} else if groupname.Realm == backends.Realm.Name {
|
} else if groupname.Realm == backends.Realm.Name {
|
||||||
realm_handler := backends.Realm.Handler.(common.Backend)
|
realm_handler := backends.Realm.Handler
|
||||||
code, err := realm_handler.DelGroup(groupname)
|
code, err := realm_handler.DelGroup(groupname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return code, err
|
return code, err
|
||||||
}
|
}
|
||||||
return backends.PVE.SyncRealms()
|
return backends.PVE.(proxmox.ProxmoxClient).SyncRealms()
|
||||||
} else {
|
} else {
|
||||||
return http.StatusUnauthorized, fmt.Errorf("user is not in the same realm as requested group")
|
return http.StatusUnauthorized, fmt.Errorf("user is not in the same realm as requested group")
|
||||||
}
|
}
|
||||||
@@ -152,12 +209,29 @@ func NewUser(backends *UserSession, username common.Username, user common.User)
|
|||||||
if username.Realm == "pve" {
|
if username.Realm == "pve" {
|
||||||
return backends.PVE.NewUser(username, user)
|
return backends.PVE.NewUser(username, user)
|
||||||
} else if username.Realm == backends.Realm.Name {
|
} else if username.Realm == backends.Realm.Name {
|
||||||
realm_handler := backends.Realm.Handler.(common.Backend)
|
realm_handler := backends.Realm.Handler
|
||||||
code, err := realm_handler.NewUser(username, user)
|
code, err := realm_handler.NewUser(username, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return code, err
|
return code, err
|
||||||
}
|
}
|
||||||
return backends.PVE.SyncRealms()
|
return backends.PVE.(proxmox.ProxmoxClient).SyncRealms()
|
||||||
|
} else {
|
||||||
|
return http.StatusUnauthorized, fmt.Errorf("user is not in the same realm as requested user")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModUser(backends *UserSession, username common.Username, user common.User) (int, error) {
|
||||||
|
if username.Realm == "pve" {
|
||||||
|
return backends.PVE.ModUser(username, user)
|
||||||
|
} else if username.Realm == backends.Realm.Name {
|
||||||
|
realm_handler := backends.Realm.Handler
|
||||||
|
code, err := realm_handler.ModUser(username, user)
|
||||||
|
if err != nil {
|
||||||
|
return code, err
|
||||||
|
}
|
||||||
|
// todo, most users will not have access to sync realms, but should be able to modify their own user
|
||||||
|
// will probably use priviledge escalation to give priviledge for modify user operations
|
||||||
|
return backends.PVE.(proxmox.ProxmoxClient).SyncRealms()
|
||||||
} else {
|
} else {
|
||||||
return http.StatusUnauthorized, fmt.Errorf("user is not in the same realm as requested user")
|
return http.StatusUnauthorized, fmt.Errorf("user is not in the same realm as requested user")
|
||||||
}
|
}
|
||||||
@@ -172,7 +246,7 @@ func GetUser(backends *UserSession, username common.Username) (common.User, int,
|
|||||||
}
|
}
|
||||||
return pveuser, http.StatusOK, nil
|
return pveuser, http.StatusOK, nil
|
||||||
} else if username.Realm == backends.Realm.Name {
|
} else if username.Realm == backends.Realm.Name {
|
||||||
realmuser, code, err := backends.Realm.Handler.(common.Backend).GetUser(username)
|
realmuser, code, err := backends.Realm.Handler.GetUser(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.User{}, code, err
|
return common.User{}, code, err
|
||||||
}
|
}
|
||||||
@@ -186,12 +260,12 @@ func DelUser(backends *UserSession, username common.Username) (int, error) {
|
|||||||
if username.Realm == "pve" {
|
if username.Realm == "pve" {
|
||||||
return backends.PVE.DelUser(username)
|
return backends.PVE.DelUser(username)
|
||||||
} else if username.Realm == backends.Realm.Name {
|
} else if username.Realm == backends.Realm.Name {
|
||||||
realm_handler := backends.Realm.Handler.(common.Backend)
|
realm_handler := backends.Realm.Handler
|
||||||
code, err := realm_handler.DelUser(username)
|
code, err := realm_handler.DelUser(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return code, err
|
return code, err
|
||||||
}
|
}
|
||||||
return backends.PVE.SyncRealms()
|
return backends.PVE.(proxmox.ProxmoxClient).SyncRealms()
|
||||||
} else {
|
} else {
|
||||||
return http.StatusUnauthorized, fmt.Errorf("user is not in the same realm as requested user")
|
return http.StatusUnauthorized, fmt.Errorf("user is not in the same realm as requested user")
|
||||||
}
|
}
|
||||||
@@ -207,12 +281,12 @@ func AddUserToGroup(backends *UserSession, username common.Username, groupname c
|
|||||||
// in the future support may be removed
|
// in the future support may be removed
|
||||||
return backends.PVE.AddUserToGroup(username, groupname)
|
return backends.PVE.AddUserToGroup(username, groupname)
|
||||||
} else if username.Realm == backends.Realm.Name && groupname.Realm == backends.Realm.Name { // both req user and req group are in realm
|
} else if username.Realm == backends.Realm.Name && groupname.Realm == backends.Realm.Name { // both req user and req group are in realm
|
||||||
realm_handler := backends.Realm.Handler.(common.Backend)
|
realm_handler := backends.Realm.Handler
|
||||||
code, err := realm_handler.AddUserToGroup(username, groupname)
|
code, err := realm_handler.AddUserToGroup(username, groupname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return code, err
|
return code, err
|
||||||
}
|
}
|
||||||
return backends.PVE.SyncRealms()
|
return backends.PVE.(proxmox.ProxmoxClient).SyncRealms()
|
||||||
} else { // req user in proxmox and req group in realm (not possible to do)
|
} else { // req user in proxmox and req group in realm (not possible to do)
|
||||||
return http.StatusUnauthorized, fmt.Errorf("cannot add %s to %s", username.ToString(), groupname.ToString())
|
return http.StatusUnauthorized, fmt.Errorf("cannot add %s to %s", username.ToString(), groupname.ToString())
|
||||||
}
|
}
|
||||||
@@ -228,12 +302,12 @@ func DelUserFromGroup(backends *UserSession, username common.Username, groupname
|
|||||||
// in the future support may be removed
|
// in the future support may be removed
|
||||||
return backends.PVE.DelUserFromGroup(username, groupname)
|
return backends.PVE.DelUserFromGroup(username, groupname)
|
||||||
} else if username.Realm == backends.Realm.Name && groupname.Realm == backends.Realm.Name { // both req user and req group are in realm
|
} else if username.Realm == backends.Realm.Name && groupname.Realm == backends.Realm.Name { // both req user and req group are in realm
|
||||||
realm_handler := backends.Realm.Handler.(common.Backend)
|
realm_handler := backends.Realm.Handler
|
||||||
code, err := realm_handler.DelUserFromGroup(username, groupname)
|
code, err := realm_handler.DelUserFromGroup(username, groupname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return code, err
|
return code, err
|
||||||
}
|
}
|
||||||
return backends.PVE.SyncRealms()
|
return backends.PVE.(proxmox.ProxmoxClient).SyncRealms()
|
||||||
} else { // req user in proxmox and req group in realm (not possible to do)
|
} else { // req user in proxmox and req group in realm (not possible to do)
|
||||||
return http.StatusUnauthorized, fmt.Errorf("cannot delete %s from %s", username.ToString(), groupname.ToString())
|
return http.StatusUnauthorized, fmt.Errorf("cannot delete %s from %s", username.ToString(), groupname.ToString())
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-7
@@ -21,13 +21,13 @@ type ProxmoxClient struct {
|
|||||||
func IsProxmoxNotFound(err error) bool {
|
func IsProxmoxNotFound(err error) bool {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// for whatever reason proxmox returns 500 for user/group/pool not found
|
// for whatever reason proxmox returns 500 for user/group/pool not found
|
||||||
return proxmox.IsNotFound(err) || strings.Contains(err.Error(), "no such user") || strings.Contains(err.Error(), "does not exist")
|
return proxmox.IsNotFound(err) || strings.Contains(err.Error(), "no such") || strings.Contains(err.Error(), "does not exist")
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a new client binding with associated permissions
|
// creates a new client binding with associated permissions
|
||||||
func NewClientFromCredentials(config common.PVEConfig, username common.Username, password string) (*ProxmoxClient, int, error) {
|
func NewClientFromCredentials(config common.PVEConfig, username common.Username, password string) (common.Backend, int, error) {
|
||||||
HTTPClient := http.Client{
|
HTTPClient := http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{},
|
TLSClientConfig: &tls.Config{},
|
||||||
@@ -47,7 +47,7 @@ func NewClientFromCredentials(config common.PVEConfig, username common.Username,
|
|||||||
return nil, http.StatusUnauthorized, err
|
return nil, http.StatusUnauthorized, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ProxmoxClient{config: &config, client: client}, http.StatusOK, nil
|
return ProxmoxClient{config: &config, client: client}, http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pve ProxmoxClient) SyncRealms() (int, error) {
|
func (pve ProxmoxClient) SyncRealms() (int, error) {
|
||||||
@@ -60,7 +60,7 @@ func (pve ProxmoxClient) SyncRealms() (int, error) {
|
|||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
if domain.Type != "pam" && domain.Type != "pve" { // pam and pve are not external realm types that require sync
|
if domain.Type != "pam" && domain.Type != "pve" { // pam and pve are not external realm types that require sync
|
||||||
e := proxmox.IntOrBool(true)
|
e := proxmox.IntOrBool(true)
|
||||||
r := string("acl;entry;properties")
|
r := "acl;entry;properties"
|
||||||
err := domain.Sync(context.Background(), proxmox.DomainSyncOptions{
|
err := domain.Sync(context.Background(), proxmox.DomainSyncOptions{
|
||||||
DryRun: false, // we want to make modifications
|
DryRun: false, // we want to make modifications
|
||||||
EnableNew: &e, // allow new users and groups
|
EnableNew: &e, // allow new users and groups
|
||||||
@@ -77,7 +77,7 @@ func (pve ProxmoxClient) SyncRealms() (int, error) {
|
|||||||
return http.StatusOK, nil
|
return http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pve ProxmoxClient) NewPool(poolname string) (int, error) {
|
func (pve ProxmoxClient) NewPool(poolname string, pool common.Pool) (int, error) {
|
||||||
err := pve.client.NewPool(context.Background(), poolname, "")
|
err := pve.client.NewPool(context.Background(), poolname, "")
|
||||||
if proxmox.IsNotAuthorized(err) {
|
if proxmox.IsNotAuthorized(err) {
|
||||||
return http.StatusUnauthorized, err
|
return http.StatusUnauthorized, err
|
||||||
@@ -88,6 +88,11 @@ func (pve ProxmoxClient) NewPool(poolname string) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pve ProxmoxClient) ModPool(poolname string, pool common.Pool) (int, error) {
|
||||||
|
// no-op
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (pve ProxmoxClient) GetPool(poolname string) (common.Pool, []string, int, error) {
|
func (pve ProxmoxClient) GetPool(poolname string) (common.Pool, []string, int, error) {
|
||||||
pool := common.Pool{}
|
pool := common.Pool{}
|
||||||
members := []string{}
|
members := []string{}
|
||||||
@@ -138,8 +143,8 @@ func (pve ProxmoxClient) DelPool(poolname string) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pve ProxmoxClient) NewGroup(groupname common.Groupname) (int, error) {
|
func (pve ProxmoxClient) NewGroup(groupname common.Groupname, group common.Group) (int, error) {
|
||||||
// add new group ny ID only
|
// add new group by ID only
|
||||||
err := pve.client.NewGroup(context.Background(), groupname.GroupID, "")
|
err := pve.client.NewGroup(context.Background(), groupname.GroupID, "")
|
||||||
if proxmox.IsNotAuthorized(err) {
|
if proxmox.IsNotAuthorized(err) {
|
||||||
return http.StatusUnauthorized, err
|
return http.StatusUnauthorized, err
|
||||||
@@ -150,6 +155,11 @@ func (pve ProxmoxClient) NewGroup(groupname common.Groupname) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pve ProxmoxClient) ModGroup(groupname common.Groupname, group common.Group) (int, error) {
|
||||||
|
// no-op
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (pve ProxmoxClient) GetGroup(groupname common.Groupname) (common.Group, []string, int, error) {
|
func (pve ProxmoxClient) GetGroup(groupname common.Groupname) (common.Group, []string, int, error) {
|
||||||
group := common.Group{}
|
group := common.Group{}
|
||||||
members := []string{}
|
members := []string{}
|
||||||
@@ -224,6 +234,10 @@ func (pve ProxmoxClient) DelGroupFromPool(groupname common.Groupname, poolname s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pve ProxmoxClient) NewUser(username common.Username, user common.User) (int, error) {
|
func (pve ProxmoxClient) NewUser(username common.Username, user common.User) (int, error) {
|
||||||
|
if !common.RequireAll(user, "Username") {
|
||||||
|
return http.StatusBadRequest, fmt.Errorf("missing one of required fields: cn, sn, mail, userpassword")
|
||||||
|
}
|
||||||
|
|
||||||
err := pve.client.NewUser(context.Background(), &proxmox.NewUser{
|
err := pve.client.NewUser(context.Background(), &proxmox.NewUser{
|
||||||
UserID: username.ToString(),
|
UserID: username.ToString(),
|
||||||
Firstname: user.CN,
|
Firstname: user.CN,
|
||||||
@@ -240,6 +254,35 @@ func (pve ProxmoxClient) NewUser(username common.Username, user common.User) (in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pve ProxmoxClient) ModUser(username common.Username, user common.User) (int, error) {
|
||||||
|
if !common.AtLeastOne(user, "Username") {
|
||||||
|
return http.StatusBadRequest, fmt.Errorf("requires one of fields: cn, sn, mail, userpassword")
|
||||||
|
}
|
||||||
|
|
||||||
|
pveuser, err := pve.client.User(context.Background(), username.ToString())
|
||||||
|
if proxmox.IsNotAuthorized(err) {
|
||||||
|
return http.StatusUnauthorized, err
|
||||||
|
} else if IsProxmoxNotFound(err) {
|
||||||
|
return http.StatusNotFound, err
|
||||||
|
} else if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pveuser.Update(context.Background(), proxmox.UserOptions{
|
||||||
|
Firstname: user.CN,
|
||||||
|
Lastname: user.SN,
|
||||||
|
Email: user.Mail,
|
||||||
|
// todo userpassword (its a separate pve endpoint)
|
||||||
|
})
|
||||||
|
if proxmox.IsNotAuthorized(err) {
|
||||||
|
return http.StatusUnauthorized, err
|
||||||
|
} else if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
} else {
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (pve ProxmoxClient) GetUser(username common.Username) (common.User, int, error) {
|
func (pve ProxmoxClient) GetUser(username common.Username) (common.User, int, error) {
|
||||||
user := common.User{}
|
user := common.User{}
|
||||||
pveuser, err := pve.client.User(context.Background(), username.ToString())
|
pveuser, err := pve.client.User(context.Background(), username.ToString())
|
||||||
|
|||||||
+529
@@ -0,0 +1,529 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
common "access-manager-api/app/common"
|
||||||
|
ldap "access-manager-api/app/ldap"
|
||||||
|
"access-manager-api/app/localdb"
|
||||||
|
pve "access-manager-api/app/pve"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
paas "proxmoxaas-common-lib"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
uuid "github.com/nu7hatch/gouuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func POST_Ticket(c *gin.Context) {
|
||||||
|
body := common.Login{}
|
||||||
|
err := c.ShouldBindJSON(&body)
|
||||||
|
if err != nil { // bad request from binding
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to parse username
|
||||||
|
body.Username, err = paas.ParseUsername(body.UsernameRaw)
|
||||||
|
if err != nil { // username format incorrect
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"auth": false, "error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handler := Realms[body.Username.Realm].Type
|
||||||
|
|
||||||
|
userbackends := UserSession{}
|
||||||
|
|
||||||
|
// always bind proxmox backend
|
||||||
|
PVEClient, code, err := pve.NewClientFromCredentials(Config.PVE, body.Username, body.Password)
|
||||||
|
if err != nil { // pve client failed to bind
|
||||||
|
c.JSON(code, gin.H{"auth": false, "error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userbackends.PVE = PVEClient
|
||||||
|
|
||||||
|
// bind backend by type
|
||||||
|
switch handler {
|
||||||
|
case "pve":
|
||||||
|
case "ldap":
|
||||||
|
config := Realms[body.Username.Realm].Config.(common.LDAPConfig)
|
||||||
|
LDAPClient, code, err := ldap.NewClientFromCredentials(config, body.Username, body.Password)
|
||||||
|
if err != nil { // ldap client failed to bind
|
||||||
|
c.JSON(code, gin.H{"auth": false, "error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userbackends.Realm.Name = body.Username.Realm
|
||||||
|
userbackends.Realm.Handler = LDAPClient
|
||||||
|
default:
|
||||||
|
c.JSON(code, gin.H{"auth": false, "error": fmt.Errorf("user realm %s is not supported", body.Username.Realm)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// failing to load lcoaldb is a fatal error
|
||||||
|
userbackends.DB, code, err = localdb.NewClientFromCredentials(Config.LocalDB, body.Username, body.Password)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"auth": false, "error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// successful binding at this point
|
||||||
|
// create new session
|
||||||
|
session := sessions.Default(c)
|
||||||
|
// create random uuid to map user to backends
|
||||||
|
uuid, _ := uuid.NewV4()
|
||||||
|
// set uuid mapping in session
|
||||||
|
session.Set("SessionUUID", uuid.String())
|
||||||
|
// set uuid mapping in LDAPSessions
|
||||||
|
UserSessions[uuid.String()] = &userbackends
|
||||||
|
// save the session
|
||||||
|
err = session.Save()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"auth": false, "error": err.Error()})
|
||||||
|
} else {
|
||||||
|
// return successful auth
|
||||||
|
c.JSON(http.StatusOK, gin.H{"auth": true})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DELETE_Ticket(c *gin.Context) {
|
||||||
|
// get session uuid from session cookie
|
||||||
|
session := sessions.Default(c)
|
||||||
|
SessionUUID := session.Get("SessionUUID")
|
||||||
|
if SessionUUID == nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uuid := SessionUUID.(string)
|
||||||
|
|
||||||
|
// delete uuid entry from user sessions
|
||||||
|
delete(UserSessions, uuid) // deletes uuid mapping
|
||||||
|
session.Options(sessions.Options{MaxAge: -1}) // set max age to -1 so session cookie is deleted
|
||||||
|
err := session.Save() // if save somehow fails, it should be ok since the uuid mapping is already deleted
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"auth": false})
|
||||||
|
} else {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"auth": false})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GET_Pool(c *gin.Context) {
|
||||||
|
poolid, ok := c.Params.Get("poolid")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pool, code, err := GetPool(backends, poolid)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.JSON(http.StatusOK, gin.H{"pool": pool})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func POST_Pool(c *gin.Context) {
|
||||||
|
poolid, ok := c.Params.Get("poolid")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// read request body
|
||||||
|
pool := common.Pool{}
|
||||||
|
err = c.ShouldBindJSON(&pool)
|
||||||
|
if err != nil { // failed binding, usually missing user field
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, code, _ = GetPool(backends, poolid) // test pool existence
|
||||||
|
|
||||||
|
if code == http.StatusNotFound { // pool does not already exist, create new pool
|
||||||
|
code, err = NewPool(backends, poolid, pool)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
code, err = ModPool(backends, poolid, pool)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DELETE_Pool(c *gin.Context) {
|
||||||
|
poolid, ok := c.Params.Get("poolid")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err = DelPool(backends, poolid)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GET_Group(c *gin.Context) {
|
||||||
|
groupname_str, ok := c.Params.Get("groupname")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupname, err := common.ParseGroupname(groupname_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
group, code, err := GetGroup(backends, groupname)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.JSON(http.StatusOK, gin.H{"group": group})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func POST_Group(c *gin.Context) {
|
||||||
|
groupname_str, ok := c.Params.Get("groupname")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupname, err := paas.ParseGroupname(groupname_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// read request body
|
||||||
|
group := common.Group{}
|
||||||
|
err = c.ShouldBindJSON(&group)
|
||||||
|
if err != nil { // failed binding, usually missing user field
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, code, _ = GetGroup(backends, groupname) // test group existence
|
||||||
|
|
||||||
|
if code == http.StatusNotFound { // group does not already exist, create new group
|
||||||
|
code, err = NewGroup(backends, groupname, group)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
code, err = ModGroup(backends, groupname, group)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DELETE_Group(c *gin.Context) {
|
||||||
|
groupname_str, ok := c.Params.Get("groupname")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupname, err := paas.ParseGroupname(groupname_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err = DelGroup(backends, groupname)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func POST_Pool_Group(c *gin.Context) {
|
||||||
|
poolid, ok := c.Params.Get("poolid")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupname_str, ok := c.Params.Get("groupname")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupname, err := paas.ParseGroupname(groupname_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err = AddGroupToPool(backends, groupname, poolid)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DELETE_Pool_Group(c *gin.Context) {
|
||||||
|
poolid, ok := c.Params.Get("poolid")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupname_str, ok := c.Params.Get("groupname")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupname, err := paas.ParseGroupname(groupname_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err = DelGroupFromPool(backends, groupname, poolid)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GET_User(c *gin.Context) {
|
||||||
|
username_str, ok := c.Params.Get("username")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter poolid")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username, err := common.ParseUsername(username_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, code, err := GetUser(backends, username)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.JSON(http.StatusOK, gin.H{"user": user})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func POST_User(c *gin.Context) {
|
||||||
|
username_str, ok := c.Params.Get("username")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username, err := paas.ParseUsername(username_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// read request body
|
||||||
|
user := common.User{}
|
||||||
|
err = c.ShouldBindJSON(&user)
|
||||||
|
if err != nil { // failed binding, usually missing user field
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, code, _ = GetUser(backends, username) // test user existence
|
||||||
|
|
||||||
|
if code == http.StatusNotFound { // user does not already exist, create new user
|
||||||
|
code, err = NewUser(backends, username, user)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
code, err = ModUser(backends, username, user)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DELETE_User(c *gin.Context) {
|
||||||
|
username_str, ok := c.Params.Get("username")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username, err := paas.ParseUsername(username_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err = DelUser(backends, username)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func POST_Group_User(c *gin.Context) {
|
||||||
|
groupname_str, ok := c.Params.Get("groupname")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username_str, ok := c.Params.Get("username")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter username")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupname, err := paas.ParseGroupname(groupname_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username, err := paas.ParseUsername(username_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err = AddUserToGroup(backends, username, groupname)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DELETE_Group_User(c *gin.Context) {
|
||||||
|
groupname_str, ok := c.Params.Get("groupname")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter groupname")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username_str, ok := c.Params.Get("username")
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Errorf("missing required path parameter username")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupname, err := paas.ParseGroupname(groupname_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username, err := paas.ParseUsername(username_str)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backends, code, err := GetUserSessionFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err = DelUserFromGroup(backends, username, groupname)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(code, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
localdb "access-manager-api/app/localdb"
|
|
||||||
pve "access-manager-api/app/pve"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Realm struct {
|
|
||||||
Type string
|
|
||||||
Config any
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserSession struct {
|
|
||||||
PVE *pve.ProxmoxClient
|
|
||||||
Realm struct {
|
|
||||||
Name string
|
|
||||||
Handler any
|
|
||||||
}
|
|
||||||
DB *localdb.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -16,5 +16,8 @@
|
|||||||
"uuid": "<secret-uuid>"
|
"uuid": "<secret-uuid>"
|
||||||
},
|
},
|
||||||
"paas-client-role": "<PAAS Client Role>"
|
"paas-client-role": "<PAAS Client Role>"
|
||||||
|
},
|
||||||
|
"localdb": {
|
||||||
|
"path": "localdb.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user