From 834f9d908508a41578e84fd129c905b0acac7b7a Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 25 Jan 2023 16:59:16 +0100 Subject: [PATCH] Add role helper functions, add project role barebone, add valid role arr - HasAnyRoles([]string): Checks if user has *one* of the roles - HasAllRoles([]string): Cheks if user has *all* of the roles - HasNotRoles([]string): Checks if user has *none* of the roles - IsValidRole(string): Checks if given string is known valid role --- internal/auth/auth.go | 64 ++++++++++++++++++++++++++++++ internal/auth/users.go | 12 +++--- internal/graph/schema.resolvers.go | 2 +- internal/repository/job.go | 4 +- internal/repository/query.go | 2 +- 5 files changed, 73 insertions(+), 11 deletions(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 22fa0db..b2e781d 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -23,8 +23,11 @@ const ( RoleSupport string = "support" RoleApi string = "api" RoleUser string = "user" + RoleProject string = "project" ) +var validRoles = [5]string{RoleAdmin, RoleSupport, RoleApi, RoleUser, RoleProject} + const ( AuthViaLocalPassword int8 = 0 AuthViaLDAP int8 = 1 @@ -38,6 +41,7 @@ type User struct { Roles []string `json:"roles"` AuthSource int8 `json:"via"` Email string `json:"email"` + Project string `json:"project"` Expiration time.Time } @@ -50,6 +54,66 @@ func (u *User) HasRole(role string) bool { return false } +// Role-Arrays are short: performance not impacted by nested loop +func (u *User) HasAnyRole(queryroles []string) bool { + for _, ur := range u.Roles { + for _, qr := range queryroles { + if ur == qr { + return true + } + } + } + return false +} + +// Role-Arrays are short: performance not impacted by nested loop +func (u *User) HasAllRoles(queryroles []string) bool { + target := len(queryroles) + matches := 0 + for _, ur := range u.Roles { + for _, qr := range queryroles { + if ur == qr { + matches += 1 + break + } + } + } + + if matches == target { + return true + } else { + return false + } +} + +// Role-Arrays are short: performance not impacted by nested loop +func (u *User) HasNotRoles(queryroles []string) bool { + matches := 0 + for _, ur := range u.Roles { + for _, qr := range queryroles { + if ur == qr { + matches += 1 + break + } + } + } + + if matches == 0 { + return true + } else { + return false + } +} + +func IsValidRole(role string) bool { + for _, r := range validRoles { + if r == role { + return true + } + } + return false +} + func GetUser(ctx context.Context) *User { x := ctx.Value(ContextUserKey) if x == nil { diff --git a/internal/auth/users.go b/internal/auth/users.go index aea5eb3..edcf9bb 100644 --- a/internal/auth/users.go +++ b/internal/auth/users.go @@ -120,14 +120,12 @@ func (auth *Authentication) AddRole( return err } - if role != RoleAdmin && role != RoleApi && role != RoleUser && role != RoleSupport { + if !IsValidRole(role) { return fmt.Errorf("invalid user role: %#v", role) } - for _, r := range user.Roles { - if r == role { - return fmt.Errorf("user %#v already has role %#v", username, role) - } + if user.HasRole(role) { + return fmt.Errorf("user %#v already has role %#v", username, role) } roles, _ := json.Marshal(append(user.Roles, role)) @@ -143,7 +141,7 @@ func (auth *Authentication) RemoveRole(ctx context.Context, username string, rol return err } - if role != RoleAdmin && role != RoleApi && role != RoleUser && role != RoleSupport { + if !IsValidRole(role) { return fmt.Errorf("invalid user role: %#v", role) } @@ -170,7 +168,7 @@ func (auth *Authentication) RemoveRole(ctx context.Context, username string, rol func FetchUser(ctx context.Context, db *sqlx.DB, username string) (*model.User, error) { me := GetUser(ctx) - if me != nil && !me.HasRole(RoleAdmin) && !me.HasRole(RoleSupport) && me.Username != username { + if me != nil && me.Username != username && me.HasNotRoles([]string{RoleAdmin, RoleSupport}) { return nil, errors.New("forbidden") } diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index 1aa8a04..61d8643 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -152,7 +152,7 @@ func (r *queryResolver) Job(ctx context.Context, id string) (*schema.Job, error) return nil, err } - if user := auth.GetUser(ctx); user != nil && !user.HasRole(auth.RoleAdmin) && !user.HasRole(auth.RoleSupport) && job.User != user.Username { + if user := auth.GetUser(ctx); user != nil && job.User != user.Username && user.HasNotRoles([]string{auth.RoleAdmin, auth.RoleSupport}){ return nil, errors.New("you are not allowed to see this job") } diff --git a/internal/repository/job.go b/internal/repository/job.go index f305cd0..b7bba51 100644 --- a/internal/repository/job.go +++ b/internal/repository/job.go @@ -423,7 +423,7 @@ func (r *JobRepository) FindJobOrUser(ctx context.Context, searchterm string) (j user := auth.GetUser(ctx) if id, err := strconv.Atoi(searchterm); err == nil { qb := sq.Select("job.id").From("job").Where("job.job_id = ?", id) - if user != nil && !user.HasRole(auth.RoleAdmin) && !user.HasRole(auth.RoleSupport) { + if user != nil && user.HasNotRoles([]string{auth.RoleAdmin, auth.RoleSupport}) { qb = qb.Where("job.user = ?", user.Username) } @@ -435,7 +435,7 @@ func (r *JobRepository) FindJobOrUser(ctx context.Context, searchterm string) (j } } - if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { + if user == nil || user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport}) { err := sq.Select("job.user").Distinct().From("job"). Where("job.user = ?", searchterm). RunWith(r.stmtCache).QueryRow().Scan(&username) diff --git a/internal/repository/query.go b/internal/repository/query.go index fad6091..a58af17 100644 --- a/internal/repository/query.go +++ b/internal/repository/query.go @@ -94,7 +94,7 @@ func (r *JobRepository) CountJobs( func SecurityCheck(ctx context.Context, query sq.SelectBuilder) sq.SelectBuilder { user := auth.GetUser(ctx) - if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleApi) || user.HasRole(auth.RoleSupport) { + if user == nil || user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleApi, auth.RoleSupport}) { return query }