Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit baec302

Browse files
committed
refactor: permission check for member suggestions
ref issue #14
1 parent 72cfe63 commit baec302

File tree

7 files changed

+33
-161
lines changed

7 files changed

+33
-161
lines changed

src/constants/index.js

-27
Original file line numberDiff line numberDiff line change
@@ -222,30 +222,3 @@ export const STATUS_OPTIONS = [
222222
{ value: "closed", label: "closed" },
223223
{ value: "cancelled", label: "cancelled" },
224224
];
225-
226-
/*
227-
* TopCoder user roles
228-
*/
229-
export const ROLE_TOPCODER_USER = "Topcoder User";
230-
export const ROLE_CONNECT_COPILOT = "Connect Copilot";
231-
export const ROLE_CONNECT_MANAGER = "Connect Manager";
232-
export const ROLE_CONNECT_ACCOUNT_MANAGER = "Connect Account Manager";
233-
export const ROLE_CONNECT_ADMIN = "Connect Admin";
234-
export const ROLE_ADMINISTRATOR = "administrator";
235-
export const ROLE_CONNECT_COPILOT_MANAGER = "Connect Copilot Manager";
236-
export const ROLE_BUSINESS_DEVELOPMENT_REPRESENTATIVE =
237-
"Business Development Representative";
238-
export const ROLE_PRESALES = "Presales";
239-
export const ROLE_ACCOUNT_EXECUTIVE = "Account Executive";
240-
export const ROLE_PROGRAM_MANAGER = "Program Manager";
241-
export const ROLE_SOLUTION_ARCHITECT = "Solution Architect";
242-
export const ROLE_PROJECT_MANAGER = "Project Manager";
243-
244-
// User roles that can see suggestions when adding new members to project
245-
export const SEE_SUGGESTION_ROLES = [
246-
ROLE_ADMINISTRATOR,
247-
ROLE_CONNECT_ADMIN,
248-
ROLE_CONNECT_MANAGER,
249-
ROLE_CONNECT_ACCOUNT_MANAGER,
250-
ROLE_CONNECT_COPILOT_MANAGER,
251-
];

src/constants/permissions.js

+26
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export const TOPCODER_ROLE = {
5757
CONNECT_MANAGER: "Connect Manager",
5858
ADMINISTRATOR: "administrator",
5959
TOPCODER_USER: "Topcoder User",
60+
CONNECT_ADMIN: "Connect Admin",
61+
COPILOT_MANAGER: "Connect Copilot Manager",
6062
};
6163

6264
/**
@@ -75,6 +77,9 @@ export const PROJECT_ROLE = {
7577
*/
7678

7779
export const PERMISSIONS = {
80+
/**
81+
* Resource Booking
82+
*/
7883
EDIT_RESOURCE_BOOKING: {
7984
meta: {
8085
group: "Resource Booking",
@@ -91,11 +96,32 @@ export const PERMISSIONS = {
9196
topcoderRoles: [TOPCODER_ROLE.BOOKING_MANAGER, TOPCODER_ROLE.ADMINISTRATOR],
9297
},
9398

99+
/**
100+
* Job
101+
*/
94102
EDIT_JOB_STATUS: {
95103
meta: {
96104
group: "Job",
97105
title: 'Edit Job "status"',
98106
},
99107
topcoderRoles: [TOPCODER_ROLE.BOOKING_MANAGER, TOPCODER_ROLE.ADMINISTRATOR],
100108
},
109+
110+
/**
111+
* Team
112+
*/
113+
SEE_MEMBER_SUGGESTIONS: {
114+
meta: {
115+
group: "Team",
116+
title: "See Member Suggestions",
117+
description: "When entering user handle in the invite field.",
118+
},
119+
topcoderRoles: [
120+
TOPCODER_ROLE.BOOKING_MANAGER,
121+
TOPCODER_ROLE.ADMINISTRATOR,
122+
TOPCODER_ROLE.CONNECT_ADMIN,
123+
TOPCODER_ROLE.CONNECT_MANAGER,
124+
TOPCODER_ROLE.COPILOT_MANAGER,
125+
],
126+
},
101127
};

src/hoc/withAuthentication/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import _ from "lodash";
88
import { getAuthUserTokens, login } from "@topcoder/micro-frontends-navbar-app";
99
import LoadingIndicator from "../../components/LoadingIndicator";
1010
import { authUserSuccess, authUserError } from "./actions";
11-
import { decodeToken } from "utils/helpers";
11+
import { decodeToken } from "tc-auth-lib";
1212
import { useDispatch, useSelector } from "react-redux";
1313

1414
export default function withAuthentication(Component) {

src/routes/TeamAccess/components/AddModal/index.jsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Button from "components/Button";
88
import BaseModal from "components/BaseModal";
99
import ReactSelect from "components/ReactSelect";
1010
import "./styles.module.scss";
11+
import { formatPlural } from "utils/format";
1112

1213
// Minimum length of input for suggestions to trigger
1314
const SUGGESTION_TRIGGER_LENGTH = 3;
@@ -101,10 +102,9 @@ const AddModal = ({ open, onClose, teamId, validateAdds, showSuggestions }) => {
101102
const { success, failed } = res.value;
102103
if (success.length) {
103104
const numAdds = success.length;
104-
const plural = numAdds !== 1 ? "s" : "";
105105
toastr.success(
106106
"Members Added",
107-
`Successfully added ${numAdds} member${plural}`
107+
`Successfully added ${numAdds} ${formatPlural(numAdds, 'member')}`
108108
);
109109
}
110110

@@ -125,7 +125,7 @@ const AddModal = ({ open, onClose, teamId, validateAdds, showSuggestions }) => {
125125
if (!!err.response) {
126126
setResponseErrors([err.message]);
127127
} else {
128-
setResponseErrors(["Error occured when adding members"]);
128+
setResponseErrors(["Error occurred when adding members"]);
129129
}
130130
});
131131
}, [dispatch, selectedMembers, teamId]);

src/routes/TeamAccess/components/AddModalContainer/index.jsx

+3-27
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
import React, { useCallback } from "react";
66
import _ from "lodash";
77
import PT from "prop-types";
8-
import { SEE_SUGGESTION_ROLES } from "constants";
98
import AddModal from "../AddModal";
10-
import { useTCRoles } from "../../hooks/useTCRoles";
9+
import { hasPermission } from "utils/permissions";
10+
import { PERMISSIONS } from "constants/permissions";
1111

1212
/**
1313
* Checks if a member to be added is already on the team
@@ -28,33 +28,13 @@ const checkForMatches = (newMember, memberList) => {
2828
return memberList.find((member) => member.handle === label);
2929
};
3030

31-
/**
32-
* Checks if member has any of the allowed roles
33-
* @param {string[]} memberRoles A list of the member's roles
34-
* @param {string[]} neededRoles A list of allowed roles
35-
*
36-
* @returns {boolean} true if member has at least one allowed role, false otherwise
37-
*/
38-
const hasRequiredRole = (memberRoles, neededRoles) => {
39-
return _.some(memberRoles, (role) => {
40-
const loweredRole = role.toLowerCase();
41-
return neededRoles.find((needed) => {
42-
const lowNeeded = needed.toLowerCase();
43-
console.log(loweredRole, lowNeeded);
44-
return loweredRole === lowNeeded;
45-
});
46-
});
47-
};
48-
4931
const AddModalContainer = ({
5032
members,
5133
invitees,
5234
teamId,
5335
addOpen,
5436
setAddOpen,
5537
}) => {
56-
const roles = useTCRoles();
57-
5838
const validateAdds = useCallback(
5939
(newMembers) => {
6040
return _.some(newMembers, (newMember) => {
@@ -67,17 +47,13 @@ const AddModalContainer = ({
6747
[members, invitees]
6848
);
6949

70-
const shouldShowSuggestions = useCallback(() => {
71-
return hasRequiredRole(roles, SEE_SUGGESTION_ROLES);
72-
}, [roles]);
73-
7450
return (
7551
<AddModal
7652
open={addOpen}
7753
onClose={() => setAddOpen(false)}
7854
teamId={teamId}
7955
validateAdds={validateAdds}
80-
showSuggestions={shouldShowSuggestions()}
56+
showSuggestions={hasPermission(PERMISSIONS.SEE_MEMBER_SUGGESTIONS)}
8157
/>
8258
);
8359
};

src/routes/TeamAccess/hooks/useTCRoles.js

-32
This file was deleted.

src/utils/helpers.js

-71
Original file line numberDiff line numberDiff line change
@@ -17,74 +17,3 @@ export const delay = (duration) =>
1717
new Promise((resolve) => {
1818
setTimeout(resolve, duration);
1919
});
20-
21-
/**
22-
* Decode URL Base64 string
23-
*
24-
* This method was taken from https://github.com/topcoder-platform/tc-auth-lib/blob/dev/src/token.js
25-
*
26-
* @param {string} str URL base 64 string
27-
*
28-
* @returns {string} JSON string
29-
*/
30-
function urlBase64Decode(str) {
31-
let output = str.replace(/-/g, "+").replace(/_/g, "/");
32-
33-
switch (output.length % 4) {
34-
case 0:
35-
break;
36-
37-
case 2:
38-
output += "==";
39-
break;
40-
41-
case 3:
42-
output += "=";
43-
break;
44-
45-
default:
46-
throw new Error("Illegal base64url string!");
47-
}
48-
return decodeURIComponent(escape(atob(output))); //polyfill https://github.com/davidchambers/Base64.js
49-
}
50-
51-
/**
52-
* Decode data of the bearer user token
53-
*
54-
* This method was taken from https://github.com/topcoder-platform/tc-auth-lib/blob/dev/src/token.js
55-
*
56-
* @param {string} token Bearer user token
57-
*/
58-
export function decodeToken(token) {
59-
const parts = token.split(".");
60-
61-
if (parts.length !== 3) {
62-
throw new Error("The token is invalid");
63-
}
64-
65-
const decoded = urlBase64Decode(parts[1]);
66-
67-
if (!decoded) {
68-
throw new Error("Cannot decode the token");
69-
}
70-
71-
// covert base64 token in JSON object
72-
let t = JSON.parse(decoded);
73-
74-
// tweaking for custom claim for RS256
75-
t.userId = _.parseInt(
76-
_.find(t, (value, key) => {
77-
return key.indexOf("userId") !== -1;
78-
})
79-
);
80-
81-
t.handle = _.find(t, (value, key) => {
82-
return key.indexOf("handle") !== -1;
83-
});
84-
85-
t.roles = _.find(t, (value, key) => {
86-
return key.indexOf("roles") !== -1;
87-
});
88-
89-
return t;
90-
}

0 commit comments

Comments
 (0)