Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

group access management #412

Merged
merged 1 commit into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions backend/pkg/auth/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import (
)

type Handler struct {
basePath string
basePath string
groupClaim string
}

func NewHandler(basePath string) *Handler {
return &Handler{basePath: basePath}
func NewHandler(basePath, groupClaim string) *Handler {
return &Handler{basePath: basePath, groupClaim: groupClaim}
}

func (h *Handler) Callback(ctx *gin.Context) {
Expand All @@ -25,8 +26,11 @@ func (h *Handler) Callback(ctx *gin.Context) {
return
}

profile := NewProfile(user)
profile.AssignGroups(mapGroups(user, h.groupClaim))

session := sessions.Default(ctx)
session.Set("profile", NewProfile(user))
session.Set("profile", profile)

if err := session.Save(); err != nil {
zap.L().Error("failed to save session", zap.Error(err))
Expand All @@ -44,8 +48,11 @@ func (h *Handler) Login(ctx *gin.Context) {
return
}

profile := NewProfile(user)
profile.AssignGroups(mapGroups(user, h.groupClaim))

session := sessions.Default(ctx)
session.Set("profile", NewProfile(user))
session.Set("profile", profile)

ctx.Redirect(http.StatusTemporaryRedirect, h.basePath)
}
Expand Down
31 changes: 31 additions & 0 deletions backend/pkg/auth/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package auth

import (
"github.com/markbates/goth"
)

func mapGroups(user goth.User, claim string) []string {
groups := make([]string, 0)

if claim == "" {
return groups
}

rawGroups, ok := user.RawData[claim]
if !ok {
return groups
}

mapped, ok := rawGroups.([]any)
if !ok {
return groups
}

for _, group := range mapped {
if g, ok := group.(string); ok {
groups = append(groups, g)
}
}

return groups
}
7 changes: 5 additions & 2 deletions backend/pkg/auth/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func Provider(provider string) gin.HandlerFunc {
}
}

func Valid(basePath string) gin.HandlerFunc {
func Valid(basePath, groupClaim string) gin.HandlerFunc {
return func(ctx *gin.Context) {
providerName, err := gothic.GetProviderName(ctx.Request)
if err != nil {
Expand Down Expand Up @@ -71,7 +71,10 @@ func Valid(basePath string) gin.HandlerFunc {
return
}

session.Set("profile", NewProfile(user))
newProfile := NewProfile(user)
newProfile.AssignGroups(mapGroups(user, groupClaim))

session.Set("profile", newProfile)
if err := session.Save(); err != nil {
zap.L().Error("failed to save profile session", zap.Error(err))
}
Expand Down
5 changes: 5 additions & 0 deletions backend/pkg/auth/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Profile struct {
Firstname string `json:"firstname"`
Name string `json:"name"`
Email string `json:"email"`
Groups []string `json:"groups"`
RefreshToken string `json:"-"`
AccessToken string `json:"-"`
IDToken string `json:"-"`
Expand All @@ -34,6 +35,10 @@ func (p *Profile) GetName() string {
return p.Email
}

func (p *Profile) AssignGroups(groups []string) {
p.Groups = groups
}

func NewProfile(user goth.User) Profile {
return Profile{
ID: user.UserID,
Expand Down
19 changes: 15 additions & 4 deletions backend/pkg/auth/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,33 @@ import (
"strings"

"github.com/gin-gonic/gin"

"github.com/kyverno/policy-reporter-ui/pkg/utils"
)

type AccessControl struct {
Emails []string
Groups []string
}

type Permissions struct {
AccessControl AccessControl `json:"-"`
}

func (p Permissions) AllowedEmail(email string) bool {
if len(p.AccessControl.Emails) == 0 {
func (p Permissions) Allowed(profile *Profile) bool {
if len(p.AccessControl.Emails) == 0 && len(p.AccessControl.Groups) == 0 {
return true
}

if len(p.AccessControl.Emails) > 0 && slices.Contains(p.AccessControl.Emails, profile.Email) {
return true
}

if len(p.AccessControl.Groups) > 0 && utils.Some(p.AccessControl.Groups, profile.Groups) {
return true
}

return slices.Contains(p.AccessControl.Emails, email)
return false
}

func ClusterPermissions(permissions map[string]Permissions) gin.HandlerFunc {
Expand All @@ -38,7 +49,7 @@ func ClusterPermissions(permissions map[string]Permissions) gin.HandlerFunc {
}

if profile := ProfileFrom(ctx); profile != nil {
if !permissions[cluster].AllowedEmail(profile.Email) {
if !permissions[cluster].Allowed(profile) {
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}
Expand Down
4 changes: 2 additions & 2 deletions backend/pkg/auth/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

const SessionKey = "auth-session"

func Setup(engine *gin.Engine, basePath, provider, tempDir string) {
func Setup(engine *gin.Engine, basePath, groupKey, provider, tempDir string) {
gob.Register(Profile{})
gob.Register(map[string]any{})

Expand All @@ -28,7 +28,7 @@ func Setup(engine *gin.Engine, basePath, provider, tempDir string) {

engine.Use(sessions.Sessions(SessionKey, NewStore(authStore)))

handler := NewHandler(basePath)
handler := NewHandler(basePath, groupKey)

engine.GET("/login", Provider(provider), handler.Login)
engine.GET("/logout", Provider(provider), handler.Logout)
Expand Down
10 changes: 10 additions & 0 deletions backend/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type OpenIDConnect struct {
CallbackURL string `mapstructure:"callbackUrl"`
ClientID string `mapstructure:"clientId"`
ClientSecret string `mapstructure:"clientSecret"`
GroupClaim string `mapstructure:"groupClaim"`
Scopes []string `mapstructure:"scopes"`
}

Expand Down Expand Up @@ -191,6 +192,7 @@ type Source struct {

type AccessControl struct {
Emails []string `mapstructure:"emails"`
Groups []string `mapstructure:"groups"`
}

type Boards struct {
Expand Down Expand Up @@ -243,3 +245,11 @@ func (c *Config) AuthBasePath() string {

return c.OpenIDConnect.BasePath()
}

func (c *Config) AuthGroupClaim() string {
if c.OAuth.Enabled {
return ""
}

return c.OpenIDConnect.GroupClaim
}
8 changes: 2 additions & 6 deletions backend/pkg/config/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ func MapConfig(c *Config) *api.Config {
Banner: c.UI.Banner,
Boards: api.Boards{
Permissions: auth.Permissions{
AccessControl: auth.AccessControl{
Emails: c.Boards.AccessControl.Emails,
},
AccessControl: auth.AccessControl(c.Boards.AccessControl),
},
},
Sources: utils.Map(c.Sources, func(s Source) api.Source {
Expand Down Expand Up @@ -81,9 +79,7 @@ func MapCustomBoards(customBoards []CustomBoard) map[string]api.CustomBoard {
List: c.Sources.List,
},
Permissions: auth.Permissions{
AccessControl: auth.AccessControl{
Emails: c.AccessControl.Emails,
},
AccessControl: auth.AccessControl(c.AccessControl),
},
PolicyReports: api.PolicyReports{
Selector: c.PolicyReports.Selector,
Expand Down
6 changes: 3 additions & 3 deletions backend/pkg/config/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ func (r *Resolver) SetupOAuth(ctx context.Context, engine *gin.Engine) ([]gin.Ha
}

goth.UseProviders(provider)
auth.Setup(engine, r.config.OAuth.BasePath(), config.Provider, r.config.TempDir)
auth.Setup(engine, r.config.OAuth.BasePath(), r.config.AuthGroupClaim(), config.Provider, r.config.TempDir)

return []gin.HandlerFunc{auth.Provider(r.config.OAuth.Provider), auth.Auth(r.config.OAuth.BasePath())}, nil
}
Expand All @@ -272,7 +272,7 @@ func (r *Resolver) SetupOIDC(ctx context.Context, engine *gin.Engine) ([]gin.Han

goth.UseProviders(provider)

auth.Setup(engine, r.config.OpenIDConnect.BasePath(), "openid-connect", r.config.TempDir)
auth.Setup(engine, r.config.OpenIDConnect.BasePath(), r.config.AuthGroupClaim(), "openid-connect", r.config.TempDir)

return []gin.HandlerFunc{auth.Provider("openid-connect"), auth.Auth(r.config.OpenIDConnect.BasePath())}, nil
}
Expand Down Expand Up @@ -369,7 +369,7 @@ func (r *Resolver) Server(ctx context.Context) (*server.Server, error) {
if !r.config.UI.Disabled {
var uiMiddleware []gin.HandlerFunc
if r.config.AuthEnabled() {
uiMiddleware = append(uiMiddleware, auth.Valid(r.config.AuthBasePath()))
uiMiddleware = append(uiMiddleware, auth.Valid(r.config.AuthBasePath(), r.config.AuthGroupClaim()))
}

zap.L().Info("register UI", zap.String("path", r.config.UI.Path))
Expand Down
14 changes: 7 additions & 7 deletions backend/pkg/server/api/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (h *Handler) Config(ctx *gin.Context) {

clusters := make([]Cluster, 0, len(h.config.Clusters))
for _, cl := range h.config.Clusters {
access := cl.AllowedEmail(profile.Email)
access := cl.Allowed(profile)
if access {
clusters = append(clusters, cl)
}
Expand Down Expand Up @@ -69,7 +69,7 @@ func (h *Handler) ListCustomBoards(ctx *gin.Context) {

func (h *Handler) ListPolicySources(ctx *gin.Context) {
if profile := auth.ProfileFrom(ctx); profile != nil {
if !h.config.Boards.AllowedEmail(profile.Email) {
if !h.config.Boards.Allowed(profile) {
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}
Expand Down Expand Up @@ -153,7 +153,7 @@ func (h *Handler) GetCustomBoard(ctx *gin.Context) {
}

if profile := auth.ProfileFrom(ctx); profile != nil {
if !config.AllowedEmail(profile.Email) {
if !config.Allowed(profile) {
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}
Expand Down Expand Up @@ -252,14 +252,14 @@ func (h *Handler) Layout(ctx *gin.Context) {
boards = make(map[string]CustomBoard, len(h.customBoards))

for key, board := range h.customBoards {
if !board.AllowedEmail(profile.Email) {
if !board.Allowed(profile) {
continue
}

boards[key] = board
}

if !h.config.Boards.AllowedEmail(profile.Email) {
if !h.config.Boards.Allowed(profile) {
sources = nil
}
} else {
Expand All @@ -277,7 +277,7 @@ func (h *Handler) Layout(ctx *gin.Context) {

func (h *Handler) Dashboard(ctx *gin.Context) {
if profile := auth.ProfileFrom(ctx); profile != nil {
if !h.config.Boards.AllowedEmail(profile.Email) {
if !h.config.Boards.Allowed(profile) {
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}
Expand Down Expand Up @@ -334,7 +334,7 @@ func (h *Handler) Dashboard(ctx *gin.Context) {

func (h *Handler) Policies(ctx *gin.Context) {
if profile := auth.ProfileFrom(ctx); profile != nil {
if !h.config.Boards.AllowedEmail(profile.Email) {
if !h.config.Boards.Allowed(profile) {
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}
Expand Down
4 changes: 3 additions & 1 deletion backend/pkg/server/api/model.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package api

import "github.com/kyverno/policy-reporter-ui/pkg/auth"
import (
"github.com/kyverno/policy-reporter-ui/pkg/auth"
)

type Policy struct {
Source string `json:"source,omitempty"`
Expand Down
12 changes: 12 additions & 0 deletions backend/pkg/utils/contains.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,15 @@ func Contains[T comparable](list []T, item T) bool {

return false
}

func Some[T comparable](list []T, items []T) bool {
for _, i := range list {
for _, j := range items {
if i == j {
return true
}
}
}

return false
}
Loading