mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Move Team service to SignedInUserInterface (#72674)
* move SignedInUser to specific file * add primitive interface for signedInUser
This commit is contained in:
parent
2c26a02b82
commit
30274a4f88
@ -5,7 +5,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sqlIDAcceptList = map[string]struct{}{
|
var sqlIDAcceptList = map[string]struct{}{
|
||||||
@ -33,18 +33,19 @@ type SQLFilter struct {
|
|||||||
// Filter creates a where clause to restrict the view of a query based on a users permissions
|
// Filter creates a where clause to restrict the view of a query based on a users permissions
|
||||||
// Scopes that exists for all actions will be parsed and compared against the supplied sqlID
|
// Scopes that exists for all actions will be parsed and compared against the supplied sqlID
|
||||||
// Prefix parameter is the prefix of the scope that we support (e.g. "users:id:")
|
// Prefix parameter is the prefix of the scope that we support (e.g. "users:id:")
|
||||||
func Filter(user *user.SignedInUser, sqlID, prefix string, actions ...string) (SQLFilter, error) {
|
func Filter(user identity.Requester, sqlID, prefix string, actions ...string) (SQLFilter, error) {
|
||||||
if _, ok := sqlIDAcceptList[sqlID]; !ok {
|
if _, ok := sqlIDAcceptList[sqlID]; !ok {
|
||||||
return denyQuery, errors.New("sqlID is not in the accept list")
|
return denyQuery, errors.New("sqlID is not in the accept list")
|
||||||
}
|
}
|
||||||
if user == nil || user.Permissions == nil || user.Permissions[user.OrgID] == nil {
|
|
||||||
|
if user == nil || user.IsNil() {
|
||||||
return denyQuery, errors.New("missing permissions")
|
return denyQuery, errors.New("missing permissions")
|
||||||
}
|
}
|
||||||
|
|
||||||
wildcards := 0
|
wildcards := 0
|
||||||
result := make(map[interface{}]int)
|
result := make(map[interface{}]int)
|
||||||
for _, a := range actions {
|
for _, a := range actions {
|
||||||
ids, hasWildcard := ParseScopes(prefix, user.Permissions[user.OrgID][a])
|
ids, hasWildcard := ParseScopes(prefix, user.GetPermissions(user.GetOrgID())[a])
|
||||||
if hasWildcard {
|
if hasWildcard {
|
||||||
wildcards += 1
|
wildcards += 1
|
||||||
continue
|
continue
|
||||||
|
9
pkg/services/auth/identity/requester.go
Normal file
9
pkg/services/auth/identity/requester.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package identity
|
||||||
|
|
||||||
|
type Requester interface {
|
||||||
|
GetIsGrafanaAdmin() bool
|
||||||
|
GetLogin() string
|
||||||
|
GetOrgID() int64
|
||||||
|
GetPermissions(orgID int64) map[string][]string
|
||||||
|
IsNil() bool
|
||||||
|
}
|
@ -4,10 +4,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/kinds/team"
|
"github.com/grafana/grafana/pkg/kinds/team"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Typed errors
|
// Typed errors
|
||||||
@ -70,7 +72,7 @@ type DeleteTeamCommand struct {
|
|||||||
type GetTeamByIDQuery struct {
|
type GetTeamByIDQuery struct {
|
||||||
OrgID int64
|
OrgID int64
|
||||||
ID int64
|
ID int64
|
||||||
SignedInUser *user.SignedInUser
|
SignedInUser identity.Requester
|
||||||
HiddenUsers map[string]struct{}
|
HiddenUsers map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +82,7 @@ const FilterIgnoreUser int64 = 0
|
|||||||
type GetTeamsByUserQuery struct {
|
type GetTeamsByUserQuery struct {
|
||||||
OrgID int64
|
OrgID int64
|
||||||
UserID int64 `json:"userId"`
|
UserID int64 `json:"userId"`
|
||||||
SignedInUser *user.SignedInUser
|
SignedInUser identity.Requester
|
||||||
}
|
}
|
||||||
|
|
||||||
type SearchTeamsQuery struct {
|
type SearchTeamsQuery struct {
|
||||||
@ -89,7 +91,7 @@ type SearchTeamsQuery struct {
|
|||||||
Limit int
|
Limit int
|
||||||
Page int
|
Page int
|
||||||
OrgID int64 `xorm:"org_id"`
|
OrgID int64 `xorm:"org_id"`
|
||||||
SignedInUser *user.SignedInUser
|
SignedInUser identity.Requester
|
||||||
HiddenUsers map[string]struct{}
|
HiddenUsers map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
@ -37,14 +37,14 @@ type xormStore struct {
|
|||||||
cfg *setting.Cfg
|
cfg *setting.Cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFilteredUsers(signedInUser *user.SignedInUser, hiddenUsers map[string]struct{}) []string {
|
func getFilteredUsers(signedInUser identity.Requester, hiddenUsers map[string]struct{}) []string {
|
||||||
filteredUsers := make([]string, 0, len(hiddenUsers))
|
filteredUsers := make([]string, 0, len(hiddenUsers))
|
||||||
if signedInUser == nil || signedInUser.IsGrafanaAdmin {
|
if signedInUser == nil || signedInUser.IsNil() || signedInUser.GetIsGrafanaAdmin() {
|
||||||
return filteredUsers
|
return filteredUsers
|
||||||
}
|
}
|
||||||
|
|
||||||
for u := range hiddenUsers {
|
for u := range hiddenUsers {
|
||||||
if u == signedInUser.Login {
|
if u == signedInUser.GetLogin() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
filteredUsers = append(filteredUsers, u)
|
filteredUsers = append(filteredUsers, u)
|
||||||
|
@ -461,9 +461,10 @@ func TestIntegrationSQLStore_SearchTeams(t *testing.T) {
|
|||||||
assert.Len(t, queryResult.Teams, tt.expectedTeamCount)
|
assert.Len(t, queryResult.Teams, tt.expectedTeamCount)
|
||||||
assert.Equal(t, queryResult.TotalCount, int64(tt.expectedTeamCount))
|
assert.Equal(t, queryResult.TotalCount, int64(tt.expectedTeamCount))
|
||||||
|
|
||||||
if !hasWildcardScope(tt.query.SignedInUser, ac.ActionTeamsRead) {
|
castSignedInUser := tt.query.SignedInUser.(*user.SignedInUser)
|
||||||
|
if !hasWildcardScope(castSignedInUser, ac.ActionTeamsRead) {
|
||||||
for _, team := range queryResult.Teams {
|
for _, team := range queryResult.Teams {
|
||||||
assert.Contains(t, tt.query.SignedInUser.Permissions[tt.query.SignedInUser.OrgID][ac.ActionTeamsRead], fmt.Sprintf("teams:id:%d", team.ID))
|
assert.Contains(t, castSignedInUser.Permissions[castSignedInUser.OrgID][ac.ActionTeamsRead], fmt.Sprintf("teams:id:%d", team.ID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
119
pkg/services/user/identity.go
Normal file
119
pkg/services/user/identity.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/models/roletype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SignedInUser struct {
|
||||||
|
UserID int64 `xorm:"user_id"`
|
||||||
|
OrgID int64 `xorm:"org_id"`
|
||||||
|
OrgName string
|
||||||
|
OrgRole roletype.RoleType
|
||||||
|
Login string
|
||||||
|
Name string
|
||||||
|
Email string
|
||||||
|
AuthenticatedBy string
|
||||||
|
ApiKeyID int64 `xorm:"api_key_id"`
|
||||||
|
IsServiceAccount bool `xorm:"is_service_account"`
|
||||||
|
IsGrafanaAdmin bool
|
||||||
|
IsAnonymous bool
|
||||||
|
IsDisabled bool
|
||||||
|
HelpFlags1 HelpFlags1
|
||||||
|
LastSeenAt time.Time
|
||||||
|
Teams []int64
|
||||||
|
// Permissions grouped by orgID and actions
|
||||||
|
Permissions map[int64]map[string][]string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) ShouldUpdateLastSeenAt() bool {
|
||||||
|
return u.UserID > 0 && time.Since(u.LastSeenAt) > time.Minute*5
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) NameOrFallback() string {
|
||||||
|
if u.Name != "" {
|
||||||
|
return u.Name
|
||||||
|
}
|
||||||
|
if u.Login != "" {
|
||||||
|
return u.Login
|
||||||
|
}
|
||||||
|
return u.Email
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) ToUserDisplayDTO() *UserDisplayDTO {
|
||||||
|
return &UserDisplayDTO{
|
||||||
|
ID: u.UserID,
|
||||||
|
Login: u.Login,
|
||||||
|
Name: u.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) HasRole(role roletype.RoleType) bool {
|
||||||
|
if u.IsGrafanaAdmin {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.OrgRole.Includes(role)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRealUser returns true if the user is a real user and not a service account
|
||||||
|
func (u *SignedInUser) IsRealUser() bool {
|
||||||
|
// backwards compatibility
|
||||||
|
// checking if userId the user is a real user
|
||||||
|
// previously we used to check if the UserId was 0 or -1
|
||||||
|
// and not a service account
|
||||||
|
return u.UserID > 0 && !u.IsServiceAccountUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) IsApiKeyUser() bool {
|
||||||
|
return u.ApiKeyID > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsServiceAccountUser returns true if the user is a service account
|
||||||
|
func (u *SignedInUser) IsServiceAccountUser() bool {
|
||||||
|
return u.IsServiceAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) HasUniqueId() bool {
|
||||||
|
return u.IsRealUser() || u.IsApiKeyUser() || u.IsServiceAccountUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) GetCacheKey() (string, error) {
|
||||||
|
if u.IsRealUser() {
|
||||||
|
return fmt.Sprintf("%d-user-%d", u.OrgID, u.UserID), nil
|
||||||
|
}
|
||||||
|
if u.IsApiKeyUser() {
|
||||||
|
return fmt.Sprintf("%d-apikey-%d", u.OrgID, u.ApiKeyID), nil
|
||||||
|
}
|
||||||
|
if u.IsServiceAccountUser() { // not considered a real user
|
||||||
|
return fmt.Sprintf("%d-service-%d", u.OrgID, u.UserID), nil
|
||||||
|
}
|
||||||
|
return "", ErrNoUniqueID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) GetIsGrafanaAdmin() bool {
|
||||||
|
return u.IsGrafanaAdmin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) GetLogin() string {
|
||||||
|
return u.Login
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) GetOrgID() int64 {
|
||||||
|
return u.OrgID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *SignedInUser) GetPermissions(orgID int64) map[string][]string {
|
||||||
|
if u.Permissions == nil {
|
||||||
|
return make(map[string][]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.Permissions[orgID]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: remove this method once all services are using an interface
|
||||||
|
func (u *SignedInUser) IsNil() bool {
|
||||||
|
return u == nil
|
||||||
|
}
|
@ -5,8 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models/roletype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type HelpFlags1 uint64
|
type HelpFlags1 uint64
|
||||||
@ -201,27 +199,6 @@ type AnalyticsSettings struct {
|
|||||||
IntercomIdentifier string
|
IntercomIdentifier string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignedInUser struct {
|
|
||||||
UserID int64 `xorm:"user_id"`
|
|
||||||
OrgID int64 `xorm:"org_id"`
|
|
||||||
OrgName string
|
|
||||||
OrgRole roletype.RoleType
|
|
||||||
Login string
|
|
||||||
Name string
|
|
||||||
Email string
|
|
||||||
AuthenticatedBy string
|
|
||||||
ApiKeyID int64 `xorm:"api_key_id"`
|
|
||||||
IsServiceAccount bool `xorm:"is_service_account"`
|
|
||||||
IsGrafanaAdmin bool
|
|
||||||
IsAnonymous bool
|
|
||||||
IsDisabled bool
|
|
||||||
HelpFlags1 HelpFlags1
|
|
||||||
LastSeenAt time.Time
|
|
||||||
Teams []int64
|
|
||||||
// Permissions grouped by orgID and actions
|
|
||||||
Permissions map[int64]map[string][]string `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) NameOrFallback() string {
|
func (u *User) NameOrFallback() string {
|
||||||
if u.Name != "" {
|
if u.Name != "" {
|
||||||
return u.Name
|
return u.Name
|
||||||
@ -251,74 +228,6 @@ type UserDisplayDTO struct {
|
|||||||
AvatarURL string `json:"avatarUrl"`
|
AvatarURL string `json:"avatarUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------
|
|
||||||
// DTO & Projections
|
|
||||||
|
|
||||||
func (u *SignedInUser) ShouldUpdateLastSeenAt() bool {
|
|
||||||
return u.UserID > 0 && time.Since(u.LastSeenAt) > time.Minute*5
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *SignedInUser) NameOrFallback() string {
|
|
||||||
if u.Name != "" {
|
|
||||||
return u.Name
|
|
||||||
}
|
|
||||||
if u.Login != "" {
|
|
||||||
return u.Login
|
|
||||||
}
|
|
||||||
return u.Email
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *SignedInUser) ToUserDisplayDTO() *UserDisplayDTO {
|
|
||||||
return &UserDisplayDTO{
|
|
||||||
ID: u.UserID,
|
|
||||||
Login: u.Login,
|
|
||||||
Name: u.Name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *SignedInUser) HasRole(role roletype.RoleType) bool {
|
|
||||||
if u.IsGrafanaAdmin {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return u.OrgRole.Includes(role)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRealUser returns true if the user is a real user and not a service account
|
|
||||||
func (u *SignedInUser) IsRealUser() bool {
|
|
||||||
// backwards compatibility
|
|
||||||
// checking if userId the user is a real user
|
|
||||||
// previously we used to check if the UserId was 0 or -1
|
|
||||||
// and not a service account
|
|
||||||
return u.UserID > 0 && !u.IsServiceAccountUser()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *SignedInUser) IsApiKeyUser() bool {
|
|
||||||
return u.ApiKeyID > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsServiceAccountUser returns true if the user is a service account
|
|
||||||
func (u *SignedInUser) IsServiceAccountUser() bool {
|
|
||||||
return u.IsServiceAccount
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *SignedInUser) HasUniqueId() bool {
|
|
||||||
return u.IsRealUser() || u.IsApiKeyUser() || u.IsServiceAccountUser()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *SignedInUser) GetCacheKey() (string, error) {
|
|
||||||
if u.IsRealUser() {
|
|
||||||
return fmt.Sprintf("%d-user-%d", u.OrgID, u.UserID), nil
|
|
||||||
}
|
|
||||||
if u.IsApiKeyUser() {
|
|
||||||
return fmt.Sprintf("%d-apikey-%d", u.OrgID, u.ApiKeyID), nil
|
|
||||||
}
|
|
||||||
if u.IsServiceAccountUser() { // not considered a real user
|
|
||||||
return fmt.Sprintf("%d-service-%d", u.OrgID, u.UserID), nil
|
|
||||||
}
|
|
||||||
return "", ErrNoUniqueID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *ErrCaseInsensitiveLoginConflict) Unwrap() error {
|
func (e *ErrCaseInsensitiveLoginConflict) Unwrap() error {
|
||||||
return ErrCaseInsensitive
|
return ErrCaseInsensitive
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user