feat: implement T01-T04 — Go module, canonical model, LDAP validator, error taxonomy
- T01: Go module (keycape), full directory skeleton, Makefile, CI workflow
- T02: spec/canonical-model.yaml with 6 entities + Go domain types
- T03: spec/ldap-schema.yaml + validator binary with structural/semantic rules
- T04: Error taxonomy — 4 stable error types, JSON format, HTTP helpers
28 tests pass, go vet clean, go build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 01:27:54 +01:00
|
|
|
// lldap-export exports the LLDAP directory as a canonical YAML snapshot
|
|
|
|
|
// for use with the validator and migration tools.
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
2026-03-13 02:08:03 +01:00
|
|
|
"context"
|
|
|
|
|
"flag"
|
feat: implement T01-T04 — Go module, canonical model, LDAP validator, error taxonomy
- T01: Go module (keycape), full directory skeleton, Makefile, CI workflow
- T02: spec/canonical-model.yaml with 6 entities + Go domain types
- T03: spec/ldap-schema.yaml + validator binary with structural/semantic rules
- T04: Error taxonomy — 4 stable error types, JSON format, HTTP helpers
28 tests pass, go vet clean, go build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 01:27:54 +01:00
|
|
|
"fmt"
|
|
|
|
|
"os"
|
2026-03-13 02:08:03 +01:00
|
|
|
|
|
|
|
|
"keycape/internal/adapters/lldap"
|
|
|
|
|
"keycape/internal/migration/lldapexport"
|
|
|
|
|
"keycape/internal/server/telemetry"
|
|
|
|
|
"keycape/internal/validator"
|
|
|
|
|
|
|
|
|
|
"github.com/rs/zerolog"
|
feat: implement T01-T04 — Go module, canonical model, LDAP validator, error taxonomy
- T01: Go module (keycape), full directory skeleton, Makefile, CI workflow
- T02: spec/canonical-model.yaml with 6 entities + Go domain types
- T03: spec/ldap-schema.yaml + validator binary with structural/semantic rules
- T04: Error taxonomy — 4 stable error types, JSON format, HTTP helpers
28 tests pass, go vet clean, go build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 01:27:54 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func main() {
|
2026-03-13 02:08:03 +01:00
|
|
|
// Flags.
|
|
|
|
|
url := flag.String("url", "ldap://localhost:389", "LLDAP server URL (ldap:// or ldaps://)")
|
|
|
|
|
bindDN := flag.String("bind-dn", "", "Service account bind DN (required)")
|
|
|
|
|
bindPW := flag.String("bind-pw", "", "Service account password (required)")
|
|
|
|
|
baseDN := flag.String("base-dn", "", "LDAP search base DN (required)")
|
|
|
|
|
output := flag.String("output", "canonical-export.yaml", "Output file path")
|
|
|
|
|
tlsSkip := flag.Bool("tls-skip-verify", false, "Skip TLS certificate verification (dev only)")
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
|
|
if *bindDN == "" || *baseDN == "" {
|
|
|
|
|
fmt.Fprintln(os.Stderr, "lldap-export: --bind-dn and --base-dn are required")
|
|
|
|
|
flag.Usage()
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log := zerolog.New(os.Stderr).With().Timestamp().Logger()
|
|
|
|
|
emitter := telemetry.NewLogEmitter(log)
|
|
|
|
|
|
|
|
|
|
cfg := lldap.Config{
|
|
|
|
|
URL: *url,
|
|
|
|
|
BindDN: *bindDN,
|
|
|
|
|
BindPW: *bindPW,
|
|
|
|
|
BaseDN: *baseDN,
|
|
|
|
|
TLSSkipVerify: *tlsSkip,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repo := lldap.New(cfg)
|
|
|
|
|
exp := lldapexport.New(repo, validator.ModeProvisioning, emitter)
|
|
|
|
|
|
|
|
|
|
result, err := exp.Export(context.Background(), *output)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "lldap-export: export failed: %v\n", err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-07 08:45:50 +02:00
|
|
|
fmt.Fprintf(os.Stdout, "Exported %d users, %d groups to %s (group enumeration: %s)\n",
|
|
|
|
|
len(result.Users), len(result.Groups), *output, result.GroupEnumeration)
|
|
|
|
|
|
|
|
|
|
if result.GroupEnumeration != lldapexport.EnumerationDirectory {
|
|
|
|
|
fmt.Fprintln(os.Stderr,
|
|
|
|
|
"lldap-export: groups were derived from user memberships; groups with no members are absent")
|
|
|
|
|
}
|
2026-03-13 02:08:03 +01:00
|
|
|
|
|
|
|
|
if len(result.IncompatibilityReport) > 0 {
|
|
|
|
|
fmt.Fprintln(os.Stderr, "Incompatibility report:")
|
|
|
|
|
for _, item := range result.IncompatibilityReport {
|
|
|
|
|
fmt.Fprintln(os.Stderr, " -", item)
|
|
|
|
|
}
|
|
|
|
|
os.Exit(2) // partial success: exported with warnings
|
|
|
|
|
}
|
feat: implement T01-T04 — Go module, canonical model, LDAP validator, error taxonomy
- T01: Go module (keycape), full directory skeleton, Makefile, CI workflow
- T02: spec/canonical-model.yaml with 6 entities + Go domain types
- T03: spec/ldap-schema.yaml + validator binary with structural/semantic rules
- T04: Error taxonomy — 4 stable error types, JSON format, HTTP helpers
28 tests pass, go vet clean, go build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 01:27:54 +01:00
|
|
|
}
|