mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Login: Remove single admin team restriction (#54534)
* Remove single member team restriction * Add label when permissions list is empty * Fix unit tests * Add co-author. Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
This commit is contained in:
parent
4bba3223a0
commit
d2bb72fb3c
@ -433,12 +433,6 @@ func updateTeamMember(sess *DBSession, orgID, teamID, userID int64, permission m
|
|||||||
|
|
||||||
if permission != models.PERMISSION_ADMIN {
|
if permission != models.PERMISSION_ADMIN {
|
||||||
permission = 0 // make sure we don't get invalid permission levels in store
|
permission = 0 // make sure we don't get invalid permission levels in store
|
||||||
|
|
||||||
// protect the last team admin
|
|
||||||
_, err := isLastAdmin(sess, orgID, teamID, userID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
member.Permission = permission
|
member.Permission = permission
|
||||||
@ -464,11 +458,6 @@ func removeTeamMember(sess *DBSession, cmd *models.RemoveTeamMemberCommand) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := isLastAdmin(sess, cmd.OrgId, cmd.TeamId, cmd.UserId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawSQL = "DELETE FROM team_member WHERE org_id=? and team_id=? and user_id=?"
|
var rawSQL = "DELETE FROM team_member WHERE org_id=? and team_id=? and user_id=?"
|
||||||
res, err := sess.Exec(rawSQL, cmd.OrgId, cmd.TeamId, cmd.UserId)
|
res, err := sess.Exec(rawSQL, cmd.OrgId, cmd.TeamId, cmd.UserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -482,29 +471,6 @@ func removeTeamMember(sess *DBSession, cmd *models.RemoveTeamMemberCommand) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func isLastAdmin(sess *DBSession, orgId int64, teamId int64, userId int64) (bool, error) {
|
|
||||||
rawSQL := "SELECT user_id FROM team_member WHERE org_id=? and team_id=? and permission=?"
|
|
||||||
userIds := []*int64{}
|
|
||||||
err := sess.SQL(rawSQL, orgId, teamId, models.PERMISSION_ADMIN).Find(&userIds)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
isAdmin := false
|
|
||||||
for _, adminId := range userIds {
|
|
||||||
if userId == *adminId {
|
|
||||||
isAdmin = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isAdmin && len(userIds) == 1 {
|
|
||||||
return true, models.ErrLastTeamAdmin
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserTeamMemberships return a list of memberships to teams granted to a user
|
// GetUserTeamMemberships return a list of memberships to teams granted to a user
|
||||||
// If external is specified, only memberships provided by an external auth provider will be listed
|
// If external is specified, only memberships provided by an external auth provider will be listed
|
||||||
// This function doesn't perform any accesscontrol filtering.
|
// This function doesn't perform any accesscontrol filtering.
|
||||||
|
@ -251,25 +251,18 @@ func TestIntegrationTeamCommandsAndQueries(t *testing.T) {
|
|||||||
require.Equal(t, len(q2.Result), 0)
|
require.Equal(t, len(q2.Result), 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should never remove the last admin of a team", func(t *testing.T) {
|
t.Run("Should have empty teams", func(t *testing.T) {
|
||||||
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
|
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("A user should not be able to remove the last admin", func(t *testing.T) {
|
t.Run("A user should be able to remove the admin permission for the last admin", func(t *testing.T) {
|
||||||
err = sqlStore.RemoveTeamMember(context.Background(), &models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0]})
|
|
||||||
require.Equal(t, err, models.ErrLastTeamAdmin)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("A user should be able to remove an admin if there are other admins", func(t *testing.T) {
|
|
||||||
err = sqlStore.AddTeamMember(userIds[1], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = sqlStore.RemoveTeamMember(context.Background(), &models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[1]})
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("A user should not be able to remove the admin permission for the last admin", func(t *testing.T) {
|
|
||||||
err = sqlStore.UpdateTeamMember(context.Background(), &models.UpdateTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], Permission: 0})
|
err = sqlStore.UpdateTeamMember(context.Background(), &models.UpdateTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], Permission: 0})
|
||||||
require.Error(t, err, models.ErrLastTeamAdmin)
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("A user should be able to remove the last member", func(t *testing.T) {
|
||||||
|
err = sqlStore.RemoveTeamMember(context.Background(), &models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0]})
|
||||||
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("A user should be able to remove the admin permission if there are other admins", func(t *testing.T) {
|
t.Run("A user should be able to remove the admin permission if there are other admins", func(t *testing.T) {
|
||||||
|
@ -26,6 +26,7 @@ type Type = 'users' | 'teams' | 'builtInRoles';
|
|||||||
export type Props = {
|
export type Props = {
|
||||||
title?: string;
|
title?: string;
|
||||||
buttonLabel?: string;
|
buttonLabel?: string;
|
||||||
|
emptyLabel?: string;
|
||||||
addPermissionTitle?: string;
|
addPermissionTitle?: string;
|
||||||
resource: string;
|
resource: string;
|
||||||
resourceId: ResourceId;
|
resourceId: ResourceId;
|
||||||
@ -35,6 +36,7 @@ export type Props = {
|
|||||||
export const Permissions = ({
|
export const Permissions = ({
|
||||||
title = 'Permissions',
|
title = 'Permissions',
|
||||||
buttonLabel = 'Add a permission',
|
buttonLabel = 'Add a permission',
|
||||||
|
emptyLabel = 'There are no permissions',
|
||||||
resource,
|
resource,
|
||||||
resourceId,
|
resourceId,
|
||||||
canSetPermissions,
|
canSetPermissions,
|
||||||
@ -145,6 +147,15 @@ export const Permissions = ({
|
|||||||
onCancel={() => setIsAdding(false)}
|
onCancel={() => setIsAdding(false)}
|
||||||
/>
|
/>
|
||||||
</SlideDown>
|
</SlideDown>
|
||||||
|
{items.length === 0 && (
|
||||||
|
<table className="filter-table gf-form-group">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>{emptyLabel}</th>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)}
|
||||||
<PermissionList
|
<PermissionList
|
||||||
title="Role"
|
title="Role"
|
||||||
items={builtInRoles}
|
items={builtInRoles}
|
||||||
|
@ -21,6 +21,7 @@ const TeamPermissions = (props: TeamPermissionsProps) => {
|
|||||||
title=""
|
title=""
|
||||||
addPermissionTitle="Add member"
|
addPermissionTitle="Add member"
|
||||||
buttonLabel="Add member"
|
buttonLabel="Add member"
|
||||||
|
emptyLabel="There are no members in this team or you do not have the permissions to list the current members."
|
||||||
resource="teams"
|
resource="teams"
|
||||||
resourceId={props.team.id}
|
resourceId={props.team.id}
|
||||||
canSetPermissions={canSetPermissions}
|
canSetPermissions={canSetPermissions}
|
||||||
|
Loading…
Reference in New Issue
Block a user