diff --git a/pkg/services/sqlstore/migrations/ualert/permissions.go b/pkg/services/sqlstore/migrations/ualert/permissions.go index 764d064f582..c37e1b28b5f 100644 --- a/pkg/services/sqlstore/migrations/ualert/permissions.go +++ b/pkg/services/sqlstore/migrations/ualert/permissions.go @@ -133,11 +133,80 @@ func (m *migration) setACL(orgID int64, dashboardID int64, items []*dashboardAcl if dashboardID <= 0 { return fmt.Errorf("folder id must be greater than zero for a folder permission") } + + // userPermissionsMap is a map keeping the highest permission per user + // for handling conficting inherited (folder) and non-inherited (dashboard) user permissions + userPermissionsMap := make(map[int64]*dashboardAcl, len(items)) + // teamPermissionsMap is a map keeping the highest permission per team + // for handling conficting inherited (folder) and non-inherited (dashboard) team permissions + teamPermissionsMap := make(map[int64]*dashboardAcl, len(items)) + for _, item := range items { + if item.UserID != 0 { + acl, ok := userPermissionsMap[item.UserID] + if !ok { + userPermissionsMap[item.UserID] = item + } else { + if item.Permission > acl.Permission { + // the higher permission wins + userPermissionsMap[item.UserID] = item + } + } + } + + if item.TeamID != 0 { + acl, ok := teamPermissionsMap[item.TeamID] + if !ok { + teamPermissionsMap[item.TeamID] = item + } else { + if item.Permission > acl.Permission { + // the higher permission wins + teamPermissionsMap[item.TeamID] = item + } + } + } + } + + type keyType struct { + UserID int64 `xorm:"user_id"` + TeamID int64 `xorm:"team_id"` + Role roleType + Permission permissionType + } + // seen keeps track of inserted perrmissions to avoid duplicates (due to inheritance) + seen := make(map[keyType]struct{}, len(items)) for _, item := range items { if item.UserID == 0 && item.TeamID == 0 && (item.Role == nil || !item.Role.IsValid()) { return models.ErrDashboardAclInfoMissing } + // ignore duplicate user permissions + if item.UserID != 0 { + acl, ok := userPermissionsMap[item.UserID] + if ok { + if acl.Id != item.Id { + continue + } + } + } + + // ignore duplicate team permissions + if item.TeamID != 0 { + acl, ok := teamPermissionsMap[item.TeamID] + if ok { + if acl.Id != item.Id { + continue + } + } + } + + key := keyType{UserID: item.UserID, TeamID: item.TeamID, Role: "", Permission: item.Permission} + if item.Role != nil { + key.Role = *item.Role + } + if _, ok := seen[key]; ok { + continue + } + // unset Id so that the new record will get a different one item.Id = 0 item.OrgID = orgID @@ -149,6 +218,7 @@ func (m *migration) setACL(orgID int64, dashboardID int64, items []*dashboardAcl if _, err := m.sess.Insert(item); err != nil { return err } + seen[key] = struct{}{} } // Update dashboard HasAcl flag