authz

import "github.com/infrapi/kayra/internal/authz"

Resolves a user id to their directory attributes and group membership, against either a real LDAP directory or the dev: local: fixture the spec calls out for testing without one, and caches the result.

Authorizer#

type UserInfo struct {
	ID         string
	Attributes map[string]string
	Groups     []string
}

type Authorizer interface {
	ResolveUser(userID string) (UserInfo, error)
}

Every authorizations.<name> entry’s full contract. Authorization decisions elsewhere (see rbac.CanManageNamespaceRBAC) are made against the groups a caller’s own token already carries, never against a separately synced member list — so resolving a group’s membership independently of a user is deliberately not part of this package’s job.

authorizer, err := authz.New(cfg) // cfg: config.AuthorizationBackend

Picks LDAPAuthorizer or DevLocalAuthorizer depending on which of cfg.Ldap/cfg.Local is set.

type PasswordAuthenticator interface {
	Login(username, password string) (UserInfo, error)
}

Both backends implement this too: verify a username/password against the real backend, return the same UserInfo ResolveUser would — the password-bind half of crypto.MintSelfJWT, minting a self-issued JWT from a real login instead of trusting whatever groups an operator types in. There is no CLI command built on this anymore (see CLI); it remains a library-level capability for whatever bootstraps a self-signed credential.

LDAPAuthorizer#

Dials per operation rather than holding a long-lived connection (LDAP lookups are infrequent — the result is cached — so there’s no idle connection to babysit). Login does the standard “search then bind”: locate the user’s entry with the service account, then re-bind the same connection as that entry’s DN with the submitted password.

member_regex filters the raw memberOf-style DN list down to the groups Kayra cares about: a DN that doesn’t match is dropped, one that does contributes its first capture group (or the whole match, with no capture group) as the resolved group name. options.lowercase_usernames and allow_username_or_email_login normalize the submitted id before searching; retry_empty_result_with_codes retries a search that comes back empty with one of those LDAP result codes.

DevLocalAuthorizer#

Reads a YAML fixture (examples/kayra-users.yaml is a real one) standing in for that same directory:

users:
  - id: jdoe
    password: "s3cret" # dev/test only, plaintext
    attributes:
      email: jdoe@example.com
    groups:
      - "cn=sandbox-core-sysadmin,cn=groups,cn=accounts,dc=example,dc=com"

The same optional member_regex applies, with identical semantics to the real LDAP backend — so a fixture authored with raw DN-shaped strings exercises the same filtering a real directory would.

Cached#

cached := authz.NewCached(backend, s, keys, name)
info, err := cached.ResolveUser(userID, ttl)

Wraps any Authorizer with the store-backed cache the spec describes: “cache user’s info for ’exp’ duration … to avoid multiple LDAP calls during a session.” Scoped by name (the owning authorizations.<name>), so two providers resolving the same user id never collide. See kayra cache clean --scope users.