Переглянути джерело

teams: refactor so that you can only delete teams if you are team admin

Hugo Häggmark 6 роки тому
батько
коміт
53c74fa2f5

+ 7 - 6
pkg/models/team.go

@@ -73,12 +73,13 @@ type SearchTeamsQuery struct {
 }
 
 type TeamDTO struct {
-	Id          int64  `json:"id"`
-	OrgId       int64  `json:"orgId"`
-	Name        string `json:"name"`
-	Email       string `json:"email"`
-	AvatarUrl   string `json:"avatarUrl"`
-	MemberCount int64  `json:"memberCount"`
+	Id          int64          `json:"id"`
+	OrgId       int64          `json:"orgId"`
+	Name        string         `json:"name"`
+	Email       string         `json:"email"`
+	AvatarUrl   string         `json:"avatarUrl"`
+	MemberCount int64          `json:"memberCount"`
+	Permission  PermissionType `json:"permission"`
 }
 
 type SearchTeamQueryResult struct {

+ 16 - 3
pkg/services/sqlstore/team.go

@@ -23,13 +23,25 @@ func init() {
 	bus.AddHandler("sql", GetTeamMembers)
 }
 
+func getTeamSearchSqlBase() string {
+	return `SELECT
+		team.id as id,
+		team.org_id,
+		team.name as name,
+		team.email as email,
+		(SELECT COUNT(*) from team_member where team_member.team_id = team.id) as member_count,
+		team_member.permission
+		FROM team as team
+		INNER JOIN team_member on team.id = team_member.team_id AND team_member.user_id = ? `
+}
+
 func getTeamSelectSqlBase() string {
 	return `SELECT
 		team.id as id,
 		team.org_id,
 		team.name as name,
 		team.email as email,
-		(SELECT COUNT(*) from team_member where team_member.team_id = team.id) as member_count
+		(SELECT COUNT(*) from team_member where team_member.team_id = team.id) as member_count 
 		FROM team as team `
 }
 
@@ -146,10 +158,11 @@ func SearchTeams(query *m.SearchTeamsQuery) error {
 	var sql bytes.Buffer
 	params := make([]interface{}, 0)
 
-	sql.WriteString(getTeamSelectSqlBase())
 	if query.UserIdFilter > 0 {
-		sql.WriteString(`INNER JOIN team_member on team.id = team_member.team_id AND team_member.user_id = ?`)
+		sql.WriteString(getTeamSearchSqlBase())
 		params = append(params, query.UserIdFilter)
+	} else {
+		sql.WriteString(getTeamSelectSqlBase())
 	}
 	sql.WriteString(` WHERE team.org_id = ?`)
 

+ 5 - 2
public/app/features/teams/TeamList.tsx

@@ -6,7 +6,7 @@ import { DeleteButton } from '@grafana/ui';
 import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
 import { NavModel, Team, OrgRole } from 'app/types';
 import { loadTeams, deleteTeam, setSearchQuery } from './state/actions';
-import { getSearchQuery, getTeams, getTeamsCount } from './state/selectors';
+import { getSearchQuery, getTeams, getTeamsCount, isPermissionTeamAdmin } from './state/selectors';
 import { getNavModel } from 'app/core/selectors/navModel';
 import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
 import { config } from 'app/core/config';
@@ -43,7 +43,10 @@ export class TeamList extends PureComponent<Props, any> {
   };
 
   renderTeam(team: Team) {
+    const { editorsCanAdmin, signedInUser } = this.props;
+    const permission = team.permission;
     const teamUrl = `org/teams/edit/${team.id}`;
+    const canDelete = isPermissionTeamAdmin({ permission, editorsCanAdmin, signedInUser });
 
     return (
       <tr key={team.id}>
@@ -62,7 +65,7 @@ export class TeamList extends PureComponent<Props, any> {
           <a href={teamUrl}>{team.memberCount}</a>
         </td>
         <td className="text-right">
-          <DeleteButton onConfirm={() => this.deleteTeam(team)} />
+          <DeleteButton onConfirm={() => this.deleteTeam(team)} disabled={!canDelete} />
         </td>
       </tr>
     );

+ 2 - 0
public/app/features/teams/__mocks__/teamMocks.ts

@@ -9,6 +9,7 @@ export const getMultipleMockTeams = (numberOfTeams: number): Team[] => {
       avatarUrl: 'some/url/',
       email: `test-${i}@test.com`,
       memberCount: i,
+      permission: TeamPermissionLevel.Member,
     });
   }
 
@@ -22,6 +23,7 @@ export const getMockTeam = (): Team => {
     avatarUrl: 'some/url/',
     email: 'test@test.com',
     memberCount: 1,
+    permission: TeamPermissionLevel.Member,
   };
 };
 

+ 7 - 0
public/app/features/teams/__snapshots__/TeamList.test.tsx.snap

@@ -133,6 +133,7 @@ exports[`Render should render teams table 1`] = `
               className="text-right"
             >
               <DeleteButton
+                disabled={false}
                 onConfirm={[Function]}
               />
             </td>
@@ -183,6 +184,7 @@ exports[`Render should render teams table 1`] = `
               className="text-right"
             >
               <DeleteButton
+                disabled={false}
                 onConfirm={[Function]}
               />
             </td>
@@ -233,6 +235,7 @@ exports[`Render should render teams table 1`] = `
               className="text-right"
             >
               <DeleteButton
+                disabled={false}
                 onConfirm={[Function]}
               />
             </td>
@@ -283,6 +286,7 @@ exports[`Render should render teams table 1`] = `
               className="text-right"
             >
               <DeleteButton
+                disabled={false}
                 onConfirm={[Function]}
               />
             </td>
@@ -333,6 +337,7 @@ exports[`Render should render teams table 1`] = `
               className="text-right"
             >
               <DeleteButton
+                disabled={false}
                 onConfirm={[Function]}
               />
             </td>
@@ -458,6 +463,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on and signedin us
               className="text-right"
             >
               <DeleteButton
+                disabled={true}
                 onConfirm={[Function]}
               />
             </td>
@@ -583,6 +589,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on and signedin us
               className="text-right"
             >
               <DeleteButton
+                disabled={true}
                 onConfirm={[Function]}
               />
             </td>

+ 2 - 1
public/app/features/teams/state/navModel.ts

@@ -1,4 +1,4 @@
-import { Team, NavModelItem, NavModel } from 'app/types';
+import { Team, NavModelItem, NavModel, TeamPermissionLevel } from 'app/types';
 import config from 'app/core/config';
 
 export function buildNavModel(team: Team): NavModelItem {
@@ -47,6 +47,7 @@ export function getTeamLoadingNav(pageName: string): NavModel {
     name: 'Loading',
     email: 'loading',
     memberCount: 0,
+    permission: TeamPermissionLevel.Member,
   });
 
   let node: NavModelItem;

+ 18 - 4
public/app/features/teams/state/selectors.ts

@@ -37,10 +37,24 @@ export interface Config {
 }
 
 export const isSignedInUserTeamAdmin = (config: Config): boolean => {
-  const userInMembers = config.members.find(m => m.userId === config.signedInUser.id);
-  const isAdmin = config.signedInUser.isGrafanaAdmin || config.signedInUser.orgRole === OrgRole.Admin;
-  const userIsTeamAdmin = userInMembers && userInMembers.permission === TeamPermissionLevel.Admin;
+  const { members, signedInUser, editorsCanAdmin } = config;
+  const userInMembers = members.find(m => m.userId === signedInUser.id);
+  const permission = userInMembers ? userInMembers.permission : TeamPermissionLevel.Member;
+
+  return isPermissionTeamAdmin({ permission, signedInUser, editorsCanAdmin });
+};
+
+export interface PermissionConfig {
+  permission: TeamPermissionLevel;
+  editorsCanAdmin: boolean;
+  signedInUser: User;
+}
+
+export const isPermissionTeamAdmin = (config: PermissionConfig): boolean => {
+  const { permission, signedInUser, editorsCanAdmin } = config;
+  const isAdmin = signedInUser.isGrafanaAdmin || signedInUser.orgRole === OrgRole.Admin;
+  const userIsTeamAdmin = permission === TeamPermissionLevel.Admin;
   const isSignedInUserTeamAdmin = isAdmin || userIsTeamAdmin;
 
-  return isSignedInUserTeamAdmin || !config.editorsCanAdmin;
+  return isSignedInUserTeamAdmin || !editorsCanAdmin;
 };

+ 3 - 0
public/app/types/teams.ts

@@ -1,9 +1,12 @@
+import { TeamPermissionLevel } from './acl';
+
 export interface Team {
   id: number;
   name: string;
   avatarUrl: string;
   email: string;
   memberCount: number;
+  permission: TeamPermissionLevel;
 }
 
 export interface TeamMember {