>
)}
diff --git a/src/routes/ResourceBookingForm/utils.js b/src/routes/ResourceBookingForm/utils.js
index a373df11..ef1eb210 100644
--- a/src/routes/ResourceBookingForm/utils.js
+++ b/src/routes/ResourceBookingForm/utils.js
@@ -10,6 +10,8 @@ import {
FORM_ROW_TYPE,
FORM_FIELD_TYPE,
} from "../../constants";
+import { hasPermission } from "utils/permissions";
+import { PERMISSIONS } from "constants/permissions";
const EDIT_RESOURCE_BOOKING_ROWS = [
{ type: FORM_ROW_TYPE.SINGLE, fields: ["handle"] },
@@ -43,6 +45,7 @@ export const getEditResourceBookingConfig = (onSubmit) => {
minValue: 0,
placeholder: "Member Rate",
step: 0.01,
+ hidden: !hasPermission(PERMISSIONS.ACCESS_RESOURCE_BOOKING_MEMBER_RATE),
},
{
label: "Start Date",
diff --git a/src/utils/helpers.js b/src/utils/helpers.js
index 6a839add..afbf1a79 100644
--- a/src/utils/helpers.js
+++ b/src/utils/helpers.js
@@ -4,6 +4,7 @@
* This file should contain helper methods which cannot be grouped into a separate file like we did for "format.js".
* If there are multiple methods which could be grouped into a separate file by their meaning they should be extracted from here to not make this file too big.
*/
+import _ from "lodash";
/**
* Delay code for some milliseconds using promise.
@@ -16,3 +17,74 @@ export const delay = (duration) =>
new Promise((resolve) => {
setTimeout(resolve, duration);
});
+
+/**
+ * Decode URL Base64 string
+ *
+ * This method was taken from https://github.com/topcoder-platform/tc-auth-lib/blob/dev/src/token.js
+ *
+ * @param {string} str URL base 64 string
+ *
+ * @returns {string} JSON string
+ */
+function urlBase64Decode(str) {
+ let output = str.replace(/-/g, "+").replace(/_/g, "/");
+
+ switch (output.length % 4) {
+ case 0:
+ break;
+
+ case 2:
+ output += "==";
+ break;
+
+ case 3:
+ output += "=";
+ break;
+
+ default:
+ throw new Error("Illegal base64url string!");
+ }
+ return decodeURIComponent(escape(atob(output))); //polyfill https://github.com/davidchambers/Base64.js
+}
+
+/**
+ * Decode data of the bearer user token
+ *
+ * This method was taken from https://github.com/topcoder-platform/tc-auth-lib/blob/dev/src/token.js
+ *
+ * @param {string} token Bearer user token
+ */
+export function decodeToken(token) {
+ const parts = token.split(".");
+
+ if (parts.length !== 3) {
+ throw new Error("The token is invalid");
+ }
+
+ const decoded = urlBase64Decode(parts[1]);
+
+ if (!decoded) {
+ throw new Error("Cannot decode the token");
+ }
+
+ // covert base64 token in JSON object
+ let t = JSON.parse(decoded);
+
+ // tweaking for custom claim for RS256
+ t.userId = _.parseInt(
+ _.find(t, (value, key) => {
+ return key.indexOf("userId") !== -1;
+ })
+ );
+
+ t.handle = _.find(t, (value, key) => {
+ return key.indexOf("handle") !== -1;
+ });
+
+ t.roles = _.find(t, (value, key) => {
+ return key.indexOf("roles") !== -1;
+ });
+
+ return t;
+}
diff --git a/src/utils/permissions.js b/src/utils/permissions.js
new file mode 100644
index 00000000..319e84c3
--- /dev/null
+++ b/src/utils/permissions.js
@@ -0,0 +1,162 @@
+import _ from "lodash";
+import store from "../store";
+
+/**
+ * Check if user has permission.
+ * - By default this method if logged-in user has `permission` for the project
+ * currently loaded in the Redux Store.
+ * - Optionally, we can check permission for another `project` or `user` by passing them in `entities` argument.
+ *
+ * `permission` may be defined in two ways:
+ * - **Full** way with defined `allowRule` and optional `denyRule`, example:
+ * ```js
+ * {
+ * allowRule: {
+ * projectRoles: [],
+ * topcoderRoles: []
+ * },
+ * denyRule: {
+ * projectRoles: [],
+ * topcoderRoles: []
+ * }
+ * }
+ * ```
+ * If user matches `denyRule` then the access would be dined even if matches `allowRule`.
+ * - **Simplified** way may be used if we only want to define `allowRule`.
+ * We can skip the `allowRule` property and define `allowRule` directly inside `permission` object, example:
+ * ```js
+ * {
+ * projectRoles: [],
+ * topcoderRoles: []
+ * }
+ * ```
+ * This **simplified** permission is equal to a **full** permission:
+ * ```js
+ * {
+ * allowRule: {
+ * projectRoles: [],
+ * topcoderRoles: []
+ * }
+ * }
+ * ```
+ *
+ * @param {Object} permissionRule permission rule
+ * @param {Array} permissionRule.projectRoles the list of project roles of the user
+ * @param {Array} permissionRule.topcoderRoles the list of Topcoder roles of the user
+ * @param {Object} [entities] `project` and `user` which has to be used to check permission
+ * @param {Object} [entities.project] project
+ * @param {Array} entities.project.members list of project members
+ * @param {Object} [entities.user] user
+ * @param {String} entities.user.role user Project Role
+ *
+ * @returns {Boolean} true, if has permission
+ */
+export const hasPermission = (permission, entities = {}) => {
+ const user = entities.user || _.get(store.getState(), "authUser", {});
+ // TODO: at the moment there is no place in Redux Store where we store project, so we have to always pass it manually
+ const project = entities.project || null;
+
+ const allowRule = permission.allowRule ? permission.allowRule : permission;
+ const denyRule = permission.denyRule ? permission.denyRule : null;
+
+ const allow = matchPermissionRule(allowRule, user, project);
+ const deny = matchPermissionRule(denyRule, user, project);
+
+ return allow && !deny;
+};
+
+/**
+ * Check if user match the permission rule.
+ * (Helper method, most likely wouldn't be used directly).
+ *
+ * This method uses permission rule defined in `permissionRule`
+ * and checks that the `user` matches it.
+ *
+ * If we define a rule with `projectRoles` list, we also should provide `projectMembers`
+ * - the list of project members.
+ *
+ * `permissionRule.projectRoles` may be equal to `true` which means user is a project member with any role
+ *
+ * `permissionRule.topcoderRoles` may be equal to `true` which means user is a logged-in user
+ *
+ * @param {Object} permissionRule permission rule
+ * @param {Array|Array