package checker import ( "context" "fmt" "strings" sdk "git.happydns.org/checker-sdk-go/checker" ) // Rules returns the full list of CheckRules exposed by the LDAP checker. // Each rule covers one concern (SRV discovery, StartTLS posture, anonymous // access, …) and produces its CheckStates by scanning raw LDAPData fields // directly -- there is no shared pre-derived Issues slice in between. func Rules() []sdk.CheckRule { return []sdk.CheckRule{ &srvDiscoveryRule{}, &endpointReachableRule{}, &encryptedTransportRule{}, &startTLSSupportedRule{}, &ldapsHandshakeRule{}, &startTLSOnLDAPSRule{}, &refusesPlainBindRule{}, &anonymousSearchBlockedRule{}, &rootDSEReadableRule{}, &saslMechanismsRule{}, &protocolVersionRule{}, &ipv6ReachableRule{}, &bindCredentialsRule{}, &baseDNReadRule{}, &tlsQualityRule{}, } } // loadLDAPData fetches the LDAP observation. On error, returns a CheckState // the caller should emit to short-circuit its rule. func loadLDAPData(ctx context.Context, obs sdk.ObservationGetter) (*LDAPData, *sdk.CheckState) { var data LDAPData if err := obs.Get(ctx, ObservationKeyLDAP, &data); err != nil { return nil, &sdk.CheckState{ Status: sdk.StatusError, Message: fmt.Sprintf("failed to load LDAP observation: %v", err), Code: "ldap.observation_error", } } return &data, nil } // critState builds a StatusCrit state with an optional fix hint encoded in // Meta["fix"] so report.go's fixesFromStates can surface it. func critState(code, message, subject, fix string) sdk.CheckState { return stateWithFix(sdk.StatusCrit, code, message, subject, fix) } func warnState(code, message, subject, fix string) sdk.CheckState { return stateWithFix(sdk.StatusWarn, code, message, subject, fix) } func infoState(code, message, subject, fix string) sdk.CheckState { return stateWithFix(sdk.StatusInfo, code, message, subject, fix) } func stateWithFix(status sdk.Status, code, message, subject, fix string) sdk.CheckState { st := sdk.CheckState{ Status: status, Message: message, Code: code, Subject: subject, } if fix != "" { st.Meta = map[string]any{"fix": fix} } return st } func passState(code, message string) sdk.CheckState { return sdk.CheckState{ Status: sdk.StatusOK, Message: message, Code: code, } } func notTestedState(code, message string) sdk.CheckState { return sdk.CheckState{ Status: sdk.StatusUnknown, Message: message, Code: code, } } func optString(opts sdk.CheckerOptions, key string) string { v, _ := opts[key].(string) return strings.TrimSpace(v) } // ldapsReachable reports whether at least one LDAPS endpoint accepted TCP. // Used to soften severity of unreachable plain-LDAP endpoints when modern // clients would use LDAPS anyway. func ldapsReachable(data *LDAPData) bool { for _, ep := range data.Endpoints { if ep.Mode == ModeLDAPS && ep.TCPConnected { return true } } return false }