1e8f8dff4b
* 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 commit2ba6e3c4d6. * Revert "remove usagestats" This reverts commit1e3fa97810. * cherry pick * Revert "cherry pick" This reverts commit461626c306. * 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>
180 lines
4.9 KiB
Go
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
|
|
}
|