Documentation
¶
Overview ¶
Package ldap is the enterprise auth provider — connects to a corporate LDAP/AD directory, authenticates users with their domain credentials, and resolves their group memberships into Coremetry roles via an admin-configurable mapping.
Designed for enterprise-style on-prem deployments:
- LDAPS (port 636) is the default; StartTLS (389→TLS upgrade) is supported for legacy AD setups.
- Custom CA paste field for internal CAs; SkipVerify toggle as last-resort escape hatch for self-signed certs.
- Group→role mapping is the primary provisioning path. Pre- provisioned users (admin pinned a row) override the group map.
- Bind password is stored in plaintext in system_settings — that was an explicit deployment-time decision; an env-keyed encrypt path can be bolted on later if needed.
Index ¶
- type AuthResult
- type Config
- type GroupRoleMapping
- type LDAPUser
- type Service
- func (s *Service) Authenticate(ctx context.Context, username, password string) (*AuthResult, error)
- func (s *Service) Configure(incoming Config)
- func (s *Service) Enabled() bool
- func (s *Service) LoadPersisted(ctx context.Context, store SettingsStore) error
- func (s *Service) SavePersisted(ctx context.Context, store SettingsStore, c Config) error
- func (s *Service) Search(ctx context.Context, query string, limit int) ([]LDAPUser, error)
- func (s *Service) Snapshot() Config
- func (s *Service) StartConfigRefresh(ctx context.Context, store SettingsStore, interval time.Duration)
- func (s *Service) TestConnection(ctx context.Context, override *Config) error
- type SettingsStore
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AuthResult ¶
AuthResult bundles the authenticated user + the role we resolved from their group memberships.
type Config ¶
type Config struct {
Enabled bool `json:"enabled"`
// Connection
Host string `json:"host"`
Port int `json:"port"`
UseTLS bool `json:"useTLS"` // direct ldaps:// (default port 636)
StartTLS bool `json:"startTLS"` // upgrade plain → TLS on 389
SkipVerify bool `json:"skipVerify"`
CACert string `json:"caCert"` // PEM bundle for internal CA
// Service account used to look up users / groups.
BindDN string `json:"bindDN"`
BindPassword string `json:"bindPassword"`
// Search
BaseDN string `json:"baseDN"`
UserSearchFilter string `json:"userSearchFilter"` // {{username}} placeholder
UserAttribute string `json:"userAttribute"` // sAMAccountName | uid | mail
EmailAttribute string `json:"emailAttribute"`
DisplayAttribute string `json:"displayAttribute"`
// Group lookup
GroupSearchBase string `json:"groupSearchBase"`
GroupFilter string `json:"groupFilter"` // {{userDN}} placeholder
// SkipMemberOfFetch drops `memberOf` from the user-search
// attribute list. AD enforces MaxValRange (default 1500)
// and MaxReceiveBuffer (1MB on some configs); a senior
// user with thousands of nested group memberships trips
// these and the login fails with LDAP_ADMIN_LIMIT_EXCEEDED
// or a 1MB-cap error. Skipping memberOf moves the
// authoritative membership lookup to the separate
// GroupSearchBase + GroupFilter pass (LDAP_MATCHING_RULE_IN_CHAIN
// recurses through nested groups cleanly), which pulls
// only DN values without the per-user attribute bloat.
// Required pre-req: GroupSearchBase must be set; otherwise
// the auth fall-through has nothing to derive roles from.
SkipMemberOfFetch bool `json:"skipMemberOfFetch"`
// Role assignment
DefaultRole string `json:"defaultRole"` // role for users without group match
GroupRoleMap []GroupRoleMapping `json:"groupRoleMap"` // first match wins (admin > editor > viewer)
}
Config is the persisted LDAP connection + mapping definition.
Defaults are filled in by Normalize() so the config struct can be built from a half-empty PUT body and still produce sensible probes.
type GroupRoleMapping ¶
type LDAPUser ¶
type LDAPUser struct {
DN string `json:"dn"`
Username string `json:"username"`
Email string `json:"email"`
DisplayName string `json:"displayName"`
// Department / Company — directory org info (v0.8.266, operator:
// "organizasyon, ad soyad, ekip bilgisi de gelsin"). AD names them
// department/company; inetOrgPerson uses ou/o — dirText resolves
// the fallback. Department feeds the users.team column on login,
// Company the org column.
Department string `json:"department,omitempty"`
Company string `json:"company,omitempty"`
Groups []string `json:"groups,omitempty"`
// Photo — raw thumbnailPhoto (AD) / jpegPhoto (inetOrgPerson) bytes
// (v0.8.238). Never serialized: the directory-search UI JSON must
// not ship images; the login path persists it to the users row and
// the photo endpoints serve it from there.
Photo []byte `json:"-"`
}
LDAPUser is the lightweight projection of a directory entry that the UI consumes (search results + provisioning picker).
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service holds the live config; mutates safely under RWMutex so the admin Settings PUT can swap config while a login is in flight.
func (*Service) Authenticate ¶
Authenticate runs the standard "search-then-bind" auth pattern:
- Service-bind (admin lookup credentials).
- Find the user by username (or email — `username` may be either, the configured UserSearchFilter handles it).
- Re-bind with the user's DN + entered password — that's the actual credential check.
- Resolve groups → role via the configured GroupRoleMap; fall back to DefaultRole.
func (*Service) Configure ¶
Configure swaps the live config. If incoming.BindPassword == "" but a password is already saved, the old one is preserved (matches the "leave empty to keep current" UX).
func (*Service) LoadPersisted ¶
func (s *Service) LoadPersisted(ctx context.Context, store SettingsStore) error
func (*Service) SavePersisted ¶
func (*Service) Search ¶
Search looks up users matching `query` (substring on username, email or displayName). Used by the admin "pick a user to provision" flow. Returns at most `limit` entries.
func (*Service) StartConfigRefresh ¶ added in v0.5.324
func (s *Service) StartConfigRefresh(ctx context.Context, store SettingsStore, interval time.Duration)
StartConfigRefresh — v0.5.324. Background goroutine that re-reads the persisted LDAP config from the shared store every `interval`. Closes the multi-pod gap where one pod wrote new settings but other pods kept serving stale in-memory cfg until restart. interval ≤ 0 → 30s.