// Package oidc signs users in through an OpenID Connect provider and turns the // groups it reports into the access terdut grants. // // The package knows nothing about the database or HTTP handlers: Grants is a // pure function of configuration and groups, and Provider is the protocol. The // api package joins them to users, teams and sessions. package oidc import ( "git.ryuvia.com/niklas/terdut-server/internal/config" ) // Role names match models.RoleOwner and RoleMember. They are restated here so // the package stays free of the models import; config.Validate has already // refused anything else. const ( roleOwner = "owner" roleMember = "member" ) // Grants is the access a set of groups confers. type Grants struct { // Admitted is false when AllowedGroups is set and the user is in none of // them. Nothing else in the struct means anything then. Admitted bool // Admin is whether the user is in the admin group. Admin bool // Teams maps team name to role. Where several groups grant the same team the // highest role wins, so belonging to both a members group and an owners // group makes somebody an owner rather than whichever mapping came last. Teams map[string]string } // ComputeGrants evaluates the configured mappings against groups. func ComputeGrants(cfg config.OIDC, groups []string) Grants { in := make(map[string]bool, len(groups)) for _, g := range groups { in[g] = true } g := Grants{Teams: map[string]string{}} g.Admitted = len(cfg.AllowedGroups) == 0 for _, allowed := range cfg.AllowedGroups { if in[allowed] { g.Admitted = true break } } if !g.Admitted { return g } g.Admin = cfg.AdminGroup != "" && in[cfg.AdminGroup] for _, m := range cfg.GroupMappings { if !in[m.Group] { continue } if rank(m.Role) > rank(g.Teams[m.Team]) { g.Teams[m.Team] = m.Role } } return g } // rank orders roles; an unknown or absent role ranks lowest. func rank(role string) int { switch role { case roleOwner: return 2 case roleMember: return 1 } return 0 } // HigherRole reports whether role a outranks role b. func HigherRole(a, b string) bool { return rank(a) > rank(b) }