2023-10-26 20:34:59 +00:00
|
|
|
package ldapSync
|
|
|
|
|
|
|
|
/*
|
|
|
|
this file contains functions to sync ldap users and groups to the database
|
|
|
|
*/
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-03-27 15:00:59 +00:00
|
|
|
"git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/logger"
|
2023-10-26 20:34:59 +00:00
|
|
|
"github.com/pocketbase/dbx"
|
|
|
|
"github.com/pocketbase/pocketbase"
|
|
|
|
"github.com/pocketbase/pocketbase/forms"
|
|
|
|
"github.com/pocketbase/pocketbase/models"
|
2024-11-01 12:39:34 +00:00
|
|
|
"os"
|
|
|
|
"strings"
|
2023-10-26 20:34:59 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// upsertLDAPGroup This function creates / updates a record in the ldap groups table
|
|
|
|
func upsertLDAPGroup(app *pocketbase.PocketBase, ldapGroup *LDAPGroup) error {
|
|
|
|
|
|
|
|
// find ldapGroups table
|
|
|
|
collection, err := app.Dao().FindCollectionByNameOrId(ldapGroupsTableName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var record *models.Record
|
|
|
|
|
|
|
|
// if record exists, update it
|
|
|
|
if res, _ := app.Dao().FindFirstRecordByFilter(
|
|
|
|
ldapGroupsTableName,
|
2024-05-11 11:29:56 +00:00
|
|
|
"objectGUID = {:objectGUID}",
|
|
|
|
dbx.Params{"objectGUID": ldapGroup.objectGUID},
|
2023-10-26 20:34:59 +00:00
|
|
|
); res != nil {
|
|
|
|
record = res
|
|
|
|
} else { // if record does not exist, create it
|
|
|
|
record = models.NewRecord(collection)
|
|
|
|
}
|
|
|
|
|
|
|
|
form := forms.NewRecordUpsert(app, record)
|
|
|
|
|
2024-05-11 11:29:56 +00:00
|
|
|
var memberOfGroupIds []string
|
2023-10-26 20:34:59 +00:00
|
|
|
|
|
|
|
// get group ids from group dns
|
|
|
|
for _, groupDn := range ldapGroup.memberOf {
|
|
|
|
group, err := GetLdapGroupByDN(app, groupDn)
|
|
|
|
if err == nil {
|
2024-05-11 11:29:56 +00:00
|
|
|
memberOfGroupIds = append(memberOfGroupIds, group.Id)
|
2023-10-26 20:34:59 +00:00
|
|
|
} else {
|
|
|
|
logger.LogErrorF("unable to find %s.memberOf: %s", ldapGroup.cn, groupDn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// load data
|
|
|
|
err = form.LoadData(map[string]any{
|
2024-05-11 11:29:56 +00:00
|
|
|
"objectGUID": ldapGroup.objectGUID,
|
2023-10-26 20:34:59 +00:00
|
|
|
"cn": ldapGroup.cn,
|
|
|
|
"dn": ldapGroup.dn,
|
|
|
|
"description": ldapGroup.description,
|
2024-05-11 11:29:56 +00:00
|
|
|
"memberOf": memberOfGroupIds,
|
2023-10-26 20:34:59 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// validate and submit (internally it calls app.Dao().SaveRecord(record) in a transaction)
|
|
|
|
if err := form.Submit(); err != nil {
|
|
|
|
return fmt.Errorf("failed to upsert group with dn: %s - %w", ldapGroup.dn, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// upsertLDAPUser This function creates / updates a record in the ldap users table
|
|
|
|
func upsertLDAPUser(app *pocketbase.PocketBase, ldapUser *LDAPUser) error {
|
|
|
|
|
|
|
|
// find ldapUsers table
|
|
|
|
collection, err := app.Dao().FindCollectionByNameOrId(ldapUsersTableName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var record *models.Record
|
|
|
|
|
|
|
|
// if record exists, update it
|
|
|
|
if res, _ := app.Dao().FindFirstRecordByFilter(
|
|
|
|
ldapUsersTableName,
|
2024-05-11 11:29:56 +00:00
|
|
|
"objectGUID = {:objectGUID}",
|
|
|
|
dbx.Params{"objectGUID": ldapUser.objectGUID},
|
2023-10-26 20:34:59 +00:00
|
|
|
); res != nil {
|
|
|
|
record = res
|
|
|
|
} else { // if record does not exist, create it
|
|
|
|
record = models.NewRecord(collection)
|
2023-11-20 15:32:32 +00:00
|
|
|
// refresh token key only if new record is created
|
|
|
|
// if record is updated, the token key should not be changed because it is used to authenticate the user
|
|
|
|
// if the token key is changed, the user will be logged out
|
|
|
|
if err := record.RefreshTokenKey(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-10-26 20:34:59 +00:00
|
|
|
}
|
|
|
|
|
2024-11-01 12:39:34 +00:00
|
|
|
// calculate accountExpires
|
2023-10-26 20:34:59 +00:00
|
|
|
accountExpires, _ := ldapTimeToUnixTime(ldapUser.accountExpires)
|
|
|
|
|
2024-05-11 11:29:56 +00:00
|
|
|
var memberOfGroupIds []string
|
2024-11-01 12:39:34 +00:00
|
|
|
var isAdmin bool
|
2023-10-26 20:34:59 +00:00
|
|
|
|
|
|
|
// get group ids from group dns
|
|
|
|
for _, groupDn := range ldapUser.memberOf {
|
2024-11-01 12:39:34 +00:00
|
|
|
|
|
|
|
// get group by dn
|
2023-10-26 20:34:59 +00:00
|
|
|
group, err := GetLdapGroupByDN(app, groupDn)
|
2024-11-01 12:39:34 +00:00
|
|
|
|
|
|
|
// check if error
|
|
|
|
if err != nil {
|
2023-10-26 20:34:59 +00:00
|
|
|
logger.LogErrorF("unable to find %s.memberOf: %s", ldapUser.cn, groupDn)
|
2024-11-01 12:39:34 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if user is in admin group
|
|
|
|
if strings.ToLower(group.Get("dn").(string)) == strings.ToLower(os.Getenv("LDAP_ADMIN_GROUP_DN")) {
|
|
|
|
isAdmin = true
|
2023-10-26 20:34:59 +00:00
|
|
|
}
|
2024-11-01 12:39:34 +00:00
|
|
|
|
|
|
|
// add group id to memberOfGroupIds
|
|
|
|
memberOfGroupIds = append(memberOfGroupIds, group.Id)
|
2023-10-26 20:34:59 +00:00
|
|
|
}
|
|
|
|
|
2024-11-01 12:39:34 +00:00
|
|
|
// set data
|
2024-05-11 11:29:56 +00:00
|
|
|
record.Set("objectGUID", ldapUser.objectGUID)
|
2023-10-26 20:34:59 +00:00
|
|
|
record.Set("givenName", ldapUser.givenName)
|
|
|
|
record.Set("sn", ldapUser.sn)
|
|
|
|
record.Set("username", ldapUser.cn)
|
|
|
|
record.Set("accountExpires", accountExpires)
|
|
|
|
record.Set("email", ldapUser.mail)
|
|
|
|
record.Set("emailVisibility", false)
|
|
|
|
record.Set("verified", true)
|
|
|
|
record.Set("dn", ldapUser.dn)
|
|
|
|
record.Set("cn", ldapUser.cn)
|
2024-05-11 11:29:56 +00:00
|
|
|
record.Set("memberOf", memberOfGroupIds)
|
2024-05-14 12:18:20 +00:00
|
|
|
record.Set("REALM", ldapUser.REALM)
|
2024-11-01 12:39:34 +00:00
|
|
|
record.Set("isAdmin", isAdmin)
|
2023-10-26 20:34:59 +00:00
|
|
|
|
|
|
|
if err := app.Dao().SaveRecord(record); err != nil {
|
|
|
|
return fmt.Errorf("failed to upsert user with dn: %s - %w", ldapUser.dn, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-20 15:32:32 +00:00
|
|
|
// GetLdapGroupByDN This function returns a record from the ldap groups table by dn. It also expands the memberOf field.
|
2023-10-26 20:34:59 +00:00
|
|
|
func GetLdapGroupByDN(app *pocketbase.PocketBase, dn string) (*models.Record, error) {
|
2023-11-20 15:32:32 +00:00
|
|
|
record, err := app.Dao().FindFirstRecordByFilter(
|
2023-10-26 20:34:59 +00:00
|
|
|
ldapGroupsTableName,
|
|
|
|
"dn = {:dn}",
|
|
|
|
dbx.Params{"dn": dn},
|
|
|
|
)
|
2023-11-20 15:32:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if errs := app.Dao().ExpandRecord(record, []string{"memberOf"}, nil); len(errs) > 0 {
|
|
|
|
return nil, fmt.Errorf("ldap group - failed to expand: %v", errs)
|
|
|
|
}
|
|
|
|
return record, nil
|
2023-10-26 20:34:59 +00:00
|
|
|
}
|
|
|
|
|
2023-11-20 15:32:32 +00:00
|
|
|
// GetLdapUserByCN This function returns a record from the ldap users table by cn. It also expands the memberOf field.
|
2023-10-26 20:34:59 +00:00
|
|
|
func GetLdapUserByCN(app *pocketbase.PocketBase, cn string) (*models.Record, error) {
|
2023-11-20 15:32:32 +00:00
|
|
|
record, err := app.Dao().FindAuthRecordByUsername(ldapUsersTableName, cn)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if errs := app.Dao().ExpandRecord(record, []string{"memberOf"}, nil); len(errs) > 0 {
|
|
|
|
return nil, fmt.Errorf("ldap user - failed to expand: %v", errs)
|
|
|
|
}
|
|
|
|
return record, nil
|
2023-10-26 20:34:59 +00:00
|
|
|
}
|
2024-05-11 11:29:56 +00:00
|
|
|
|
|
|
|
// GetLdapGroupByObjectGUID This function returns a record from the ldap groups table by objectGUID.
|
|
|
|
// It also expands the memberOf field.
|
|
|
|
func GetLdapGroupByObjectGUID(app *pocketbase.PocketBase, objectGUID string) (*models.Record, error) {
|
|
|
|
record, err := app.Dao().FindFirstRecordByFilter(
|
|
|
|
ldapGroupsTableName,
|
|
|
|
"objectGUID = {:objectGUID}",
|
|
|
|
dbx.Params{"objectGUID": objectGUID},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if errs := app.Dao().ExpandRecord(record, []string{"memberOf"}, nil); len(errs) > 0 {
|
|
|
|
return nil, fmt.Errorf("ldap group - failed to expand: %v", errs)
|
|
|
|
}
|
|
|
|
return record, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetLdapUserByObjectGUID This function returns a record from the ldap users table by objectGUID.
|
|
|
|
// It also expands the memberOf field.
|
|
|
|
func GetLdapUserByObjectGUID(app *pocketbase.PocketBase, objectGUID string) (*models.Record, error) {
|
|
|
|
record, err := app.Dao().FindFirstRecordByFilter(
|
|
|
|
ldapUsersTableName,
|
|
|
|
"objectGUID = {:objectGUID}",
|
|
|
|
dbx.Params{"objectGUID": objectGUID},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if errs := app.Dao().ExpandRecord(record, []string{"memberOf"}, nil); len(errs) > 0 {
|
|
|
|
return nil, fmt.Errorf("ldap user - failed to expand: %v", errs)
|
|
|
|
}
|
|
|
|
return record, nil
|
|
|
|
}
|