Files
Bertrand Gouny 02b5f0354d feat(bootstrap): configure management ACLs and align feature names
Expose management hidden attribute lists through bootstrap environment defaults and keep ACL rendering in the LDIF template. Align management assets with database-readonly naming and drop the MONITOR and AUDITLOG flag suffixes.

Update documentation, migration notes and integration coverage for custom hidden attributes, empty effective lists, audit logging and monitoring.
2026-10-27 11:10:13 +01:00

129 lines
4.8 KiB
Go

package tests
import (
"context"
_ "embed"
"fmt"
"strings"
"dagger.io/dagger"
)
// DataManagement checks optional tree creation and the scoped password-reset account.
func DataManagement(ctx context.Context, client *dagger.Client, image *dagger.Container) error {
passwords, err := rootPasswords(ctx, image)
if err != nil {
return err
}
base := merge(passwords, env{
"OPENLDAP_BOOTSTRAP_DATA_TREE": "true",
"OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT": "true",
"OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT_PASSWORD_HASHED": passwords["OPENLDAP_BOOTSTRAP_DATA_ROOT_PASSWORD_HASHED"],
})
for _, mode := range []string{"disabled", "tree", "plain", "policy", "custom", "supplied", "hidden", "policy-hidden", "policy-only", "empty", "policy-disabled"} {
if err := func() error {
usersDN := "ou=users," + suffix
groupsDN := "ou=groups," + suffix
servicesDN := "ou=services," + suffix
managementDN := "cn=management," + servicesDN
settings := merge(base)
nodeImage := image
switch mode {
case "disabled":
settings["OPENLDAP_BOOTSTRAP_DATA_TREE"] = "false"
settings["OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT"] = "false"
case "tree":
settings["OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT"] = "false"
case "hidden":
settings["OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT_HIDDEN_ATTRIBUTES"] = "userPassword,shadowLastChange,mail"
case "policy-hidden", "policy-only", "empty":
settings["OPENLDAP_BOOTSTRAP_PPOLICY"] = "true"
settings["OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT_HIDDEN_PPOLICY_ATTRIBUTES"] = "pwdHistory,mail"
if mode == "policy-only" || mode == "empty" {
settings["OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT_HIDDEN_ATTRIBUTES"] = ""
}
if mode == "empty" {
settings["OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT_HIDDEN_PPOLICY_ATTRIBUTES"] = ""
}
case "policy-disabled":
settings["OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT_HIDDEN_PPOLICY_ATTRIBUTES"] = "pwdHistory,mail"
case "policy":
settings = merge(settings, env{
"OPENLDAP_BOOTSTRAP_PPOLICY": "true",
"OPENLDAP_BOOTSTRAP_MEMBEROF": "true",
"OPENLDAP_BOOTSTRAP_REFINT": "true",
"OPENLDAP_BOOTSTRAP_REFINT_NOTHING_DN": "cn=empty-group," + servicesDN,
"OPENLDAP_BOOTSTRAP_UNIQUE": "true",
"OPENLDAP_BOOTSTRAP_AUDITLOG": "true",
"OPENLDAP_BOOTSTRAP_DATA_READONLY": "true",
"OPENLDAP_BOOTSTRAP_DATA_READONLY_PASSWORD_HASHED": passwords["OPENLDAP_BOOTSTRAP_DATA_ROOT_PASSWORD_HASHED"],
})
case "custom":
usersDN = "ou=people," + suffix
groupsDN = "ou=roles," + suffix
servicesDN = "ou=machines," + suffix
managementDN = "cn=sso," + servicesDN
case "supplied":
settings["OPENLDAP_BOOTSTRAP_DATA_TREE"] = "false"
nodeImage = image.WithNewFile("/container/services/openldap-bootstrap/assets/ldif/data/custom/30-custom-tree.ldif", fmt.Sprintf(`dn: %s
objectClass: organizationalUnit
ou: users
dn: %s
objectClass: organizationalUnit
ou: groups
dn: %s
objectClass: organizationalUnit
ou: services
`, usersDN, groupsDN, servicesDN))
}
settings = merge(settings, env{
"OPENLDAP_BOOTSTRAP_DATA_TREE_USERS_DN": usersDN,
"OPENLDAP_BOOTSTRAP_DATA_TREE_GROUPS_DN": groupsDN,
"OPENLDAP_BOOTSTRAP_DATA_TREE_SERVICES_DN": servicesDN,
"OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT_DN": managementDN,
})
if mode == "empty" {
_, err := expect(ctx, withEnv(nodeImage, settings), false,
[]string{"Management hidden attributes must not be empty"}, "container", "--skip-process")
return err
}
node := "management-" + mode
service, err := startNode(ctx, nodeImage, node, settings, nil, nil)
if err != nil {
return err
}
defer stopNodes(ctx, []*dagger.Service{service})
return check(ctx, image, nil, env{
"TEST_NODES": node,
"TEST_MANAGEMENT_MODE": mode,
"TEST_USERS_DN": usersDN,
"TEST_GROUPS_DN": groupsDN,
"TEST_SERVICES_DN": servicesDN,
"TEST_MANAGEMENT_DN": managementDN,
}, dataManagementCheck)
}(); err != nil {
return fmt.Errorf("data management %s: %w", mode, err)
}
}
generated, err := expect(ctx, withEnv(image, merge(base, env{"OPENLDAP_BOOTSTRAP_DATA_MANAGEMENT_PASSWORD_HASHED": ""})), true,
[]string{"Generated database management user password:", "config file testing succeeded"}, "container", "--skip-process")
if err != nil {
return err
}
data, err := generated.container.WithExec([]string{"container", "run", "--", "slapcat", "-n", "1", "-a", "(cn=management)"}).Stdout(ctx)
if err != nil {
return err
}
if !strings.Contains(data, "cn: management") || !strings.Contains(data, "userPassword:: ") {
return fmt.Errorf("generated management account/password missing")
}
return nil
}
//go:embed testdata/data-management.sh
var dataManagementCheck string