Files
grafana/pkg/services/accesscontrol/database/database.go
T
Eric Leijonmarck 1e8f8dff4b Auth: Adds validation and ingestion of conflict file (#53014)
* add users-manager command

* add users-manager command

* rename files

* refactor: imports and renaming

* Command: add conflict merge user command

- MergeUser will
	- replace all user_ids from conflicting users to the chosen userId
	- delete users whose user_ids are not the chosen user
- SameIdentification will
	- update chosen user with chosen email,login details
	- delete users whose user_ids are not the chosen user

* refactor: clean up

* refactor: create structure for read, validate, ingest

* feat: ls and generate-file for conflicting users

* remove usagestats

* added back pkg/services/login/authinfoservice/database/stats.go

* Revert "added back pkg/services/login/authinfoservice/database/stats.go"

This reverts commit 2ba6e3c4d6.

* Revert "remove usagestats"

This reverts commit 1e3fa97810.

* cherry pick

* Revert "cherry pick"

This reverts commit 461626c306.

* validation of picked merge user

* fix test

* make lint

* make test run

* tests for ingest working

* clean up and refactored to align with downstream refactoring

* formatting

* refactor: name list instead of ls

* fix: static lint error use trimprefix

* WIP: permissions for validation

* fix: remove unused functions in sqlstore

* fix: remove unused function

* handling of multiple users and resolve discarded users

* fix tests

* fix: bug that did not exclude the blocks

* ioutil is blacklisted

* WIP: validation

* tests for merging a user working

* add latest changes to output print

* refactor: removed conflictEmail and conflictLogin that was not used

* refactor: code clean up, showChanges working

* test and linting fixes

* test and linting fixes

* refactor: removed logging of config and added more info for vlidation command

* refactor: fix order of code

* fix time now

* refactor: no longer need for check casesensitive login/email

* removed unnessecary loop

* refactor: move functions around

* test: working

* docs: add docuemntationf for file

* Add failing test for generating the conflict login block

* Fix regex

* Fix some stuff/tests

Co-authored-by: eleijonmarck <eric.leijonmarck@gmail.com>

* add: docs for conflict file

* add: conflict_email, conflict_login fields

* add: conflict_email, conflict_login fields

* WIP

* fix: tests working as intended

* Update pkg/cmd/grafana-cli/commands/conflict_user_command.go

Co-authored-by: linoman <2051016+linoman@users.noreply.github.com>

* review comments

* Update pkg/cmd/grafana-cli/commands/conflict_user_command.go

Co-authored-by: Misi <mgyongyosi@users.noreply.github.com>

* Update pkg/cmd/grafana-cli/commands/conflict_user_command.go

Co-authored-by: Misi <mgyongyosi@users.noreply.github.com>

* missspelling

* trailing new line

* update to use userimpl store

* remove newline

* remove newline

* refactor: initializing of resolver for conflicts

* fix: test sqlStore

* refactor: removed lines

* refactor: remove TODOs

Co-authored-by: Mihaly Gyongyosi <mgyongyosi@users.noreply.github.com>
Co-authored-by: linoman <2051016+linoman@users.noreply.github.com>
2022-09-29 14:26:24 +02:00

180 lines
4.9 KiB
Go

package database
import (
"context"
"strconv"
"strings"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
const (
globalOrgID = 0
)
func ProvideService(sql sqlstore.Store) *AccessControlStore {
return &AccessControlStore{sql}
}
type AccessControlStore struct {
sql sqlstore.Store
}
func (s *AccessControlStore) GetUserPermissions(ctx context.Context, query accesscontrol.GetUserPermissionsQuery) ([]accesscontrol.Permission, error) {
result := make([]accesscontrol.Permission, 0)
err := s.sql.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
if query.UserID == 0 && len(query.TeamIDs) == 0 && len(query.Roles) == 0 {
// no permission to fetch
return nil
}
filter, params := userRolesFilter(query.OrgID, query.UserID, query.TeamIDs, query.Roles)
q := `
SELECT
permission.action,
permission.scope
FROM permission
INNER JOIN role ON role.id = permission.role_id
` + filter
if len(query.Actions) > 0 {
q += " WHERE permission.action IN("
if len(query.Actions) > 0 {
q += "?" + strings.Repeat(",?", len(query.Actions)-1)
}
q += ")"
for _, a := range query.Actions {
params = append(params, a)
}
}
q += `
ORDER BY permission.scope
`
if err := sess.SQL(q, params...).Find(&result); err != nil {
return err
}
return nil
})
return result, err
}
func userRolesFilter(orgID, userID int64, teamIDs []int64, roles []string) (string, []interface{}) {
var params []interface{}
builder := strings.Builder{}
// This is an additional security. We should never have permissions granted to userID 0.
// Only allow real users to get user/team permissions (anonymous/apikeys)
if userID > 0 {
builder.WriteString(`
SELECT ur.role_id
FROM user_role AS ur
WHERE ur.user_id = ?
AND (ur.org_id = ? OR ur.org_id = ?)
`)
params = []interface{}{userID, orgID, globalOrgID}
}
if len(teamIDs) > 0 {
if builder.Len() > 0 {
builder.WriteString("UNION")
}
builder.WriteString(`
SELECT tr.role_id FROM team_role as tr
WHERE tr.team_id IN(?` + strings.Repeat(", ?", len(teamIDs)-1) + `)
AND tr.org_id = ?
`)
for _, id := range teamIDs {
params = append(params, id)
}
params = append(params, orgID)
}
if len(roles) != 0 {
if builder.Len() > 0 {
builder.WriteString("UNION")
}
builder.WriteString(`
SELECT br.role_id FROM builtin_role AS br
WHERE br.role IN (?` + strings.Repeat(", ?", len(roles)-1) + `)
AND (br.org_id = ? OR br.org_id = ?)
`)
for _, role := range roles {
params = append(params, role)
}
params = append(params, orgID, globalOrgID)
}
return "INNER JOIN (" + builder.String() + ") as all_role ON role.id = all_role.role_id", params
}
func (s *AccessControlStore) DeleteUserPermissions(ctx context.Context, orgID, userID int64) error {
err := s.sql.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
roleDeleteQuery := "DELETE FROM user_role WHERE user_id = ?"
roleDeleteParams := []interface{}{roleDeleteQuery, userID}
if orgID != accesscontrol.GlobalOrgID {
roleDeleteQuery += " AND org_id = ?"
roleDeleteParams = []interface{}{roleDeleteQuery, userID, orgID}
}
// Delete user role assignments
if _, err := sess.Exec(roleDeleteParams...); err != nil {
return err
}
// only delete scopes to user if all permissions is removed (i.e. user is removed)
if orgID == accesscontrol.GlobalOrgID {
// Delete permissions that are scoped to user
if _, err := sess.Exec("DELETE FROM permission WHERE scope = ?", accesscontrol.Scope("users", "id", strconv.FormatInt(userID, 10))); err != nil {
return err
}
}
roleQuery := "SELECT id FROM role WHERE name = ?"
roleParams := []interface{}{accesscontrol.ManagedUserRoleName(userID)}
if orgID != accesscontrol.GlobalOrgID {
roleQuery += " AND org_id = ?"
roleParams = []interface{}{accesscontrol.ManagedUserRoleName(userID), orgID}
}
var roleIDs []int64
if err := sess.SQL(roleQuery, roleParams...).Find(&roleIDs); err != nil {
return err
}
if len(roleIDs) == 0 {
return nil
}
permissionDeleteQuery := "DELETE FROM permission WHERE role_id IN(? " + strings.Repeat(",?", len(roleIDs)-1) + ")"
permissionDeleteParams := make([]interface{}, 0, len(roleIDs)+1)
permissionDeleteParams = append(permissionDeleteParams, permissionDeleteQuery)
for _, id := range roleIDs {
permissionDeleteParams = append(permissionDeleteParams, id)
}
// Delete managed user permissions
if _, err := sess.Exec(permissionDeleteParams...); err != nil {
return err
}
managedRoleDeleteQuery := "DELETE FROM role WHERE id IN(? " + strings.Repeat(",?", len(roleIDs)-1) + ")"
managedRoleDeleteParams := []interface{}{managedRoleDeleteQuery}
for _, id := range roleIDs {
managedRoleDeleteParams = append(managedRoleDeleteParams, id)
}
// Delete managed user roles
if _, err := sess.Exec(managedRoleDeleteParams...); err != nil {
return err
}
return nil
})
return err
}