2022-02-01 14:57:26 +01:00
package accesscontrol
import (
"fmt"
"strconv"
"time"
"xorm.io/xorm"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
2022-08-10 11:56:48 +02:00
"github.com/grafana/grafana/pkg/services/org"
2022-02-01 14:57:26 +01:00
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
)
const (
TeamsMigrationID = "teams permissions migration"
)
func AddTeamMembershipMigrations ( mg * migrator . Migrator ) {
mg . AddMigration ( TeamsMigrationID , & teamPermissionMigrator { editorsCanAdmin : mg . Cfg . EditorsCanAdmin } )
}
var _ migrator . CodeMigration = new ( teamPermissionMigrator )
type teamPermissionMigrator struct {
2022-03-03 15:05:47 +01:00
permissionMigrator
2022-02-01 14:57:26 +01:00
editorsCanAdmin bool
}
func ( p * teamPermissionMigrator ) SQL ( dialect migrator . Dialect ) string {
return "code migration"
}
func ( p * teamPermissionMigrator ) Exec ( sess * xorm . Session , migrator * migrator . Migrator ) error {
p . sess = sess
p . dialect = migrator . Dialect
return p . migrateMemberships ( )
}
// setRolePermissions sets the role permissions deleting any team related ones before inserting any.
func ( p * teamPermissionMigrator ) setRolePermissions ( roleID int64 , permissions [ ] accesscontrol . Permission ) error {
// First drop existing permissions
if _ , errDeletingPerms := p . sess . Exec ( "DELETE FROM permission WHERE role_id = ? AND (action LIKE ? OR action LIKE ?)" , roleID , "teams:%" , "teams.permissions:%" ) ; errDeletingPerms != nil {
return errDeletingPerms
}
// Then insert new permissions
var newPermissions [ ] accesscontrol . Permission
now := time . Now ( )
for _ , permission := range permissions {
permission . RoleID = roleID
permission . Created = now
permission . Updated = now
newPermissions = append ( newPermissions , permission )
}
if _ , errInsertPerms := p . sess . InsertMulti ( & newPermissions ) ; errInsertPerms != nil {
return errInsertPerms
}
return nil
}
2022-04-21 14:31:02 +02:00
// mapPermissionToRBAC translates the legacy membership (Member or Admin) into RBAC permissions
func ( p * teamPermissionMigrator ) mapPermissionToRBAC ( permission models . PermissionType , teamID int64 ) [ ] accesscontrol . Permission {
2022-02-01 14:57:26 +01:00
teamIDScope := accesscontrol . Scope ( "teams" , "id" , strconv . FormatInt ( teamID , 10 ) )
switch permission {
case 0 :
return [ ] accesscontrol . Permission { { Action : "teams:read" , Scope : teamIDScope } }
case models . PERMISSION_ADMIN :
return [ ] accesscontrol . Permission {
{ Action : "teams:delete" , Scope : teamIDScope } ,
{ Action : "teams:read" , Scope : teamIDScope } ,
{ Action : "teams:write" , Scope : teamIDScope } ,
{ Action : "teams.permissions:read" , Scope : teamIDScope } ,
{ Action : "teams.permissions:write" , Scope : teamIDScope } ,
}
default :
return [ ] accesscontrol . Permission { }
}
}
func ( p * teamPermissionMigrator ) getUserRoleByOrgMapping ( ) ( map [ int64 ] map [ int64 ] string , error ) {
2023-01-09 14:39:53 +01:00
var orgUsers [ ] * org . OrgUserDTO
2022-02-01 14:57:26 +01:00
if err := p . sess . SQL ( ` SELECT * FROM org_user ` ) . Cols ( "org_user.org_id" , "org_user.user_id" , "org_user.role" ) . Find ( & orgUsers ) ; err != nil {
return nil , err
}
userRolesByOrg := map [ int64 ] map [ int64 ] string { }
// Loop through users and organise them by organization ID
for _ , orgUser := range orgUsers {
2023-01-09 14:39:53 +01:00
orgRoles , initialized := userRolesByOrg [ orgUser . OrgID ]
2022-02-01 14:57:26 +01:00
if ! initialized {
orgRoles = map [ int64 ] string { }
}
2023-01-09 14:39:53 +01:00
orgRoles [ orgUser . UserID ] = orgUser . Role
userRolesByOrg [ orgUser . OrgID ] = orgRoles
2022-02-01 14:57:26 +01:00
}
return userRolesByOrg , nil
}
// migrateMemberships generate managed permissions for users based on their memberships to teams
func ( p * teamPermissionMigrator ) migrateMemberships ( ) error {
// Fetch user roles in each org
userRolesByOrg , err := p . getUserRoleByOrgMapping ( )
if err != nil {
return err
}
// Fetch team memberships
teamMemberships := [ ] * models . TeamMember { }
if err := p . sess . SQL ( "SELECT * FROM team_member" ) . Find ( & teamMemberships ) ; err != nil {
return err
}
// No need to create any roles if there is no team members
if len ( teamMemberships ) == 0 {
return nil
}
// Loop through memberships and generate associated permissions
// Downgrade team permissions if needed - only admins or editors (when editorsCanAdmin option is enabled)
// can access team administration endpoints
userPermissionsByOrg , errGen := p . generateAssociatedPermissions ( teamMemberships , userRolesByOrg )
if errGen != nil {
return errGen
}
// Sort roles that:
2022-03-24 18:06:44 +01:00
// * need to be created and assigned (rolesToCreate)
2022-02-01 14:57:26 +01:00
// * are already created and assigned (rolesByOrg)
2022-03-24 18:06:44 +01:00
rolesToCreate , rolesByOrg , errOrganizeRoles := p . sortRolesToAssign ( userPermissionsByOrg )
2022-02-01 14:57:26 +01:00
if errOrganizeRoles != nil {
return errOrganizeRoles
}
// Create missing roles
createdRoles , errCreate := p . bulkCreateRoles ( rolesToCreate )
if errCreate != nil {
return errCreate
}
// Populate rolesMap with the newly created roles
for i := range createdRoles {
2022-03-03 15:05:47 +01:00
rolesByOrg [ createdRoles [ i ] . OrgID ] [ createdRoles [ i ] . Name ] = createdRoles [ i ]
2022-02-01 14:57:26 +01:00
}
// Assign newly created roles
2022-03-24 18:06:44 +01:00
if errAssign := p . bulkAssignRoles ( createdRoles ) ; errAssign != nil {
2022-02-01 14:57:26 +01:00
return errAssign
}
// Set all roles teams related permissions
return p . setRolePermissionsForOrgs ( userPermissionsByOrg , rolesByOrg )
}
2022-03-03 15:05:47 +01:00
func ( p * teamPermissionMigrator ) setRolePermissionsForOrgs ( userPermissionsByOrg map [ int64 ] map [ int64 ] [ ] accesscontrol . Permission , rolesByOrg map [ int64 ] map [ string ] * accesscontrol . Role ) error {
2022-02-01 14:57:26 +01:00
for orgID , userPermissions := range userPermissionsByOrg {
for userID , permissions := range userPermissions {
2022-03-03 15:05:47 +01:00
role , ok := rolesByOrg [ orgID ] [ fmt . Sprintf ( "managed:users:%d:permissions" , userID ) ]
2022-02-01 14:57:26 +01:00
if ! ok {
2022-03-03 15:05:47 +01:00
return & ErrUnknownRole { fmt . Sprintf ( "managed:users:%d:permissions" , userID ) }
2022-02-01 14:57:26 +01:00
}
if errSettingPerms := p . setRolePermissions ( role . ID , permissions ) ; errSettingPerms != nil {
return errSettingPerms
}
}
}
return nil
}
2022-03-24 18:06:44 +01:00
func ( p * teamPermissionMigrator ) sortRolesToAssign ( userPermissionsByOrg map [ int64 ] map [ int64 ] [ ] accesscontrol . Permission ) ( [ ] * accesscontrol . Role , map [ int64 ] map [ string ] * accesscontrol . Role , error ) {
2022-02-01 14:57:26 +01:00
var rolesToCreate [ ] * accesscontrol . Role
2022-03-03 15:05:47 +01:00
rolesByOrg := map [ int64 ] map [ string ] * accesscontrol . Role { }
2022-02-01 14:57:26 +01:00
for orgID , userPermissions := range userPermissionsByOrg {
for userID := range userPermissions {
roleName := fmt . Sprintf ( "managed:users:%d:permissions" , userID )
role , errFindingRoles := p . findRole ( orgID , roleName )
if errFindingRoles != nil {
2022-03-24 18:06:44 +01:00
return nil , nil , errFindingRoles
2022-02-01 14:57:26 +01:00
}
2022-03-03 15:05:47 +01:00
if rolesByOrg [ orgID ] == nil {
rolesByOrg [ orgID ] = map [ string ] * accesscontrol . Role { }
}
2022-02-01 14:57:26 +01:00
if role . ID != 0 {
2022-03-03 15:05:47 +01:00
rolesByOrg [ orgID ] [ roleName ] = & role
2022-02-01 14:57:26 +01:00
} else {
2022-03-24 18:06:44 +01:00
rolesToCreate = append ( rolesToCreate , & accesscontrol . Role { Name : roleName , OrgID : orgID } )
2022-02-01 14:57:26 +01:00
}
}
}
2022-03-24 18:06:44 +01:00
return rolesToCreate , rolesByOrg , nil
2022-02-01 14:57:26 +01:00
}
func ( p * teamPermissionMigrator ) generateAssociatedPermissions ( teamMemberships [ ] * models . TeamMember ,
userRolesByOrg map [ int64 ] map [ int64 ] string ) ( map [ int64 ] map [ int64 ] [ ] accesscontrol . Permission , error ) {
userPermissionsByOrg := map [ int64 ] map [ int64 ] [ ] accesscontrol . Permission { }
for _ , m := range teamMemberships {
// Downgrade team permissions if needed:
// only admins or editors (when editorsCanAdmin option is enabled)
// can access team administration endpoints
if m . Permission == models . PERMISSION_ADMIN {
2022-08-10 11:56:48 +02:00
if userRolesByOrg [ m . OrgId ] [ m . UserId ] == string ( org . RoleViewer ) || ( userRolesByOrg [ m . OrgId ] [ m . UserId ] == string ( org . RoleEditor ) && ! p . editorsCanAdmin ) {
2022-02-01 14:57:26 +01:00
m . Permission = 0
if _ , err := p . sess . Cols ( "permission" ) . Where ( "org_id=? and team_id=? and user_id=?" , m . OrgId , m . TeamId , m . UserId ) . Update ( m ) ; err != nil {
return nil , err
}
}
}
userPermissions , initialized := userPermissionsByOrg [ m . OrgId ]
if ! initialized {
userPermissions = map [ int64 ] [ ] accesscontrol . Permission { }
}
2022-04-21 14:31:02 +02:00
userPermissions [ m . UserId ] = append ( userPermissions [ m . UserId ] , p . mapPermissionToRBAC ( m . Permission , m . TeamId ) ... )
2022-02-01 14:57:26 +01:00
userPermissionsByOrg [ m . OrgId ] = userPermissions
}
return userPermissionsByOrg , nil
}