diff --git a/.circleci/config.yml b/.circleci/config.yml index f158f16a88d..67c0a948633 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -871,6 +871,7 @@ commands: export FEATURE_FLAG_COAST_GUARD_EMPLID=true export FEATURE_FLAG_MOVE_LOCK=false export FEATURE_FLAG_OKTA_DODID_INPUT=false + export FEATURE_FLAG_HEADQUARTERS_ROLE=false export FEATURE_FLAG_SAFETY_MOVE=false export FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false @@ -906,6 +907,7 @@ commands: FEATURE_FLAG_COAST_GUARD_EMPLID: 'true' FEATURE_FLAG_MOVE_LOCK: 'false' FEATURE_FLAG_OKTA_DODID_INPUT: 'false' + FEATURE_FLAG_HEADQUARTERS_ROLE: 'false' FEATURE_FLAG_SAFETY_MOVE: 'false' FEATURE_FLAG_MANAGE_SUPPORTING_DOCS: 'false' command: | diff --git a/.envrc b/.envrc index 469b8aa9abe..a37faa98f6a 100644 --- a/.envrc +++ b/.envrc @@ -133,6 +133,8 @@ export FEATURE_FLAG_MOVE_LOCK=false export FEATURE_FLAG_OKTA_DODID_INPUT=false export FEATURE_FLAG_SAFETY_MOVE=true +# Feature flag to disable/enable headquarters role +export FEATURE_FLAG_HEADQUARTERS_ROLE=true # Feature flag for additional supporting documents uploaded by customer export FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=true diff --git a/config/env/demo.app-client-tls.env b/config/env/demo.app-client-tls.env index 08d06a17154..79d364694fd 100644 --- a/config/env/demo.app-client-tls.env +++ b/config/env/demo.app-client-tls.env @@ -38,5 +38,6 @@ FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false FEATURE_FLAG_COAST_GUARD_EMPLID=false FEATURE_FLAG_MOVE_LOCK=false FEATURE_FLAG_OKTA_DODID_INPUT=false +FEATURE_FLAG_HEADQUARTERS_ROLE=false FEATURE_FLAG_SAFETY_MOVE=false FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false diff --git a/config/env/demo.app.env b/config/env/demo.app.env index b862ee8b2ff..ba745bd144e 100644 --- a/config/env/demo.app.env +++ b/config/env/demo.app.env @@ -44,5 +44,6 @@ FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false FEATURE_FLAG_COAST_GUARD_EMPLID=false FEATURE_FLAG_MOVE_LOCK=false FEATURE_FLAG_OKTA_DODID_INPUT=false +FEATURE_FLAG_HEADQUARTERS_ROLE=false FEATURE_FLAG_SAFETY_MOVE=false FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false diff --git a/config/env/exp.app-client-tls.env b/config/env/exp.app-client-tls.env index 374d8703065..6c4b7529d61 100644 --- a/config/env/exp.app-client-tls.env +++ b/config/env/exp.app-client-tls.env @@ -38,5 +38,6 @@ FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false FEATURE_FLAG_COAST_GUARD_EMPLID=false FEATURE_FLAG_MOVE_LOCK=false FEATURE_FLAG_OKTA_DODID_INPUT=false +FEATURE_FLAG_HEADQUARTERS_ROLE=false FEATURE_FLAG_SAFETY_MOVE=false FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false diff --git a/config/env/exp.app.env b/config/env/exp.app.env index fc28cfa0279..4a724476a71 100644 --- a/config/env/exp.app.env +++ b/config/env/exp.app.env @@ -44,5 +44,6 @@ FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false FEATURE_FLAG_COAST_GUARD_EMPLID=false FEATURE_FLAG_MOVE_LOCK=false FEATURE_FLAG_OKTA_DODID_INPUT=false +FEATURE_FLAG_HEADQUARTERS_ROLE=false FEATURE_FLAG_SAFETY_MOVE=false FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false diff --git a/config/env/loadtest.app-client-tls.env b/config/env/loadtest.app-client-tls.env index 75653d1c217..6193f09f1f1 100644 --- a/config/env/loadtest.app-client-tls.env +++ b/config/env/loadtest.app-client-tls.env @@ -36,5 +36,6 @@ FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false FEATURE_FLAG_COAST_GUARD_EMPLID=false FEATURE_FLAG_MOVE_LOCK=false FEATURE_FLAG_OKTA_DODID_INPUT=false +FEATURE_FLAG_HEADQUARTERS_ROLE=false FEATURE_FLAG_SAFETY_MOVE=false FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false diff --git a/config/env/loadtest.app.env b/config/env/loadtest.app.env index 229b4625373..14bebecf623 100644 --- a/config/env/loadtest.app.env +++ b/config/env/loadtest.app.env @@ -42,5 +42,6 @@ FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false FEATURE_FLAG_COAST_GUARD_EMPLID=false FEATURE_FLAG_MOVE_LOCK=false FEATURE_FLAG_OKTA_DODID_INPUT=false +FEATURE_FLAG_HEADQUARTERS_ROLE=false FEATURE_FLAG_SAFETY_MOVE=false FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false diff --git a/config/env/prd.app-client-tls.env b/config/env/prd.app-client-tls.env index 3a1054eb475..990eb2861c8 100644 --- a/config/env/prd.app-client-tls.env +++ b/config/env/prd.app-client-tls.env @@ -35,5 +35,6 @@ FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false FEATURE_FLAG_COAST_GUARD_EMPLID=false FEATURE_FLAG_MOVE_LOCK=false FEATURE_FLAG_OKTA_DODID_INPUT=false +FEATURE_FLAG_HEADQUARTERS_ROLE=false FEATURE_FLAG_SAFETY_MOVE=false FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false diff --git a/config/env/prd.app.env b/config/env/prd.app.env index 4ca8b3bd756..8914cca5e36 100644 --- a/config/env/prd.app.env +++ b/config/env/prd.app.env @@ -43,5 +43,6 @@ FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false FEATURE_FLAG_COAST_GUARD_EMPLID=false FEATURE_FLAG_MOVE_LOCK=false FEATURE_FLAG_OKTA_DODID_INPUT=false +FEATURE_FLAG_HEADQUARTERS_ROLE=false FEATURE_FLAG_SAFETY_MOVE=false FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false diff --git a/config/env/stg.app-client-tls.env b/config/env/stg.app-client-tls.env index 9b14e4d8028..448e777fbea 100644 --- a/config/env/stg.app-client-tls.env +++ b/config/env/stg.app-client-tls.env @@ -37,5 +37,6 @@ FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false FEATURE_FLAG_COAST_GUARD_EMPLID=false FEATURE_FLAG_MOVE_LOCK=false FEATURE_FLAG_OKTA_DODID_INPUT=false +FEATURE_FLAG_HEADQUARTERS_ROLE=false FEATURE_FLAG_SAFETY_MOVE=false FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false diff --git a/config/env/stg.app.env b/config/env/stg.app.env index 1f00064f3c0..e74bb3c1def 100644 --- a/config/env/stg.app.env +++ b/config/env/stg.app.env @@ -44,5 +44,6 @@ FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false FEATURE_FLAG_COAST_GUARD_EMPLID=false FEATURE_FLAG_MOVE_LOCK=false FEATURE_FLAG_OKTA_DODID_INPUT=false +FEATURE_FLAG_HEADQUARTERS_ROLE=false FEATURE_FLAG_SAFETY_MOVE=false FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false diff --git a/config/flipt/storage/development.features.yaml b/config/flipt/storage/development.features.yaml index e06b68a7df3..32c48c75263 100644 --- a/config/flipt/storage/development.features.yaml +++ b/config/flipt/storage/development.features.yaml @@ -1,6 +1,14 @@ version: "1.2" namespace: development flags: +- key: headquarters_role + name: Headquarters Role feature flag + type: BOOLEAN_FLAG_TYPE + enabled: true + rollouts: + - segment: + key: mil-app + value: true - key: safety_move name: Safety Move feature flag type: BOOLEAN_FLAG_TYPE diff --git a/pkg/handlers/authentication/devlocal.go b/pkg/handlers/authentication/devlocal.go index 345613fd966..e3cf32b15dd 100644 --- a/pkg/handlers/authentication/devlocal.go +++ b/pkg/handlers/authentication/devlocal.go @@ -1141,7 +1141,8 @@ func loginUser(h devlocalAuthHandler, user *models.User, userType string, w http } func isOfficeUser(userType string) bool { - if userType == TOOOfficeUserType || userType == TIOOfficeUserType || userType == ServicesCounselorOfficeUserType || userType == QaeOfficeUserType || userType == CustomerServiceRepresentativeOfficeUserType || userType == HQOfficeUserType { + if userType == TOOOfficeUserType || userType == TIOOfficeUserType || userType == ServicesCounselorOfficeUserType || + userType == QaeOfficeUserType || userType == CustomerServiceRepresentativeOfficeUserType || userType == HQOfficeUserType { return true } return false diff --git a/pkg/handlers/authentication/permissions.go b/pkg/handlers/authentication/permissions.go index ab4e56de2ba..907e8746d38 100644 --- a/pkg/handlers/authentication/permissions.go +++ b/pkg/handlers/authentication/permissions.go @@ -124,7 +124,7 @@ var CustomerServiceRepresentative = RolePermissions{ }, } -var AllRolesPermissions = []RolePermissions{TOO, TIO, ServicesCounselor, QAE, HQ, CustomerServiceRepresentative} +var AllRolesPermissions = []RolePermissions{TOO, TIO, ServicesCounselor, QAE, CustomerServiceRepresentative, HQ} // check if a [user.role] has permissions on a given object func checkUserPermission(appCtx appcontext.AppContext, session *auth.Session, permission string) (bool, error) { diff --git a/src/components/Office/RequestAccountForm/RequestAccountForm.test.jsx b/src/components/Office/RequestAccountForm/RequestAccountForm.test.jsx index a0239602bb2..f684a6a0968 100644 --- a/src/components/Office/RequestAccountForm/RequestAccountForm.test.jsx +++ b/src/components/Office/RequestAccountForm/RequestAccountForm.test.jsx @@ -90,10 +90,6 @@ describe('RequestAccountForm component', () => { const qsaCheckbox = screen.getByTestId('qualityAssuranceEvaluatorCheckBox'); expect(qsaCheckbox).toBeInstanceOf(HTMLInputElement); expect(qsaCheckbox).not.toBeChecked(false); - - const hqCheckbox = screen.getByTestId('headquartersCheckBox'); - expect(hqCheckbox).toBeInstanceOf(HTMLInputElement); - expect(hqCheckbox).not.toBeChecked(false); }); it('cancels requesting office account when cancel button is clicked', async () => { @@ -149,51 +145,6 @@ describe('RequestAccountForm component', () => { expect(testProps.onSubmit).toHaveBeenCalled(); }); - it('submits Headquarters office account form when submit button is clicked', async () => { - const mockOfficeId = '3210a533-19b8-4805-a564-7eb452afce10'; - const mockHeadquartersOffice = { - address: { - city: 'Test City', - country: 'United States', - id: 'a13806fc-0e7d-4dc3-91ca-b802d9da50f1', - postalCode: '85309', - state: 'AZ', - streetAddress1: '7383 N Litchfield Rd', - streetAddress2: 'Rm 1122', - }, - created_at: '2018-05-28T14:27:39.198Z', - gbloc: 'KKFA', - id: mockOfficeId, - name: 'Tester', - phone_lines: [], - updated_at: '2018-05-28T14:27:39.198Z', - }; - - const mockSearchHeadquartersOfficesOpen = () => Promise.resolve([mockHeadquartersOffice]); - searchTransportationOfficesOpen.mockImplementation(mockSearchHeadquartersOfficesOpen); - - renderWithRouter(); - - await userEvent.type(screen.getByLabelText('First Name'), 'Bob'); - await userEvent.type(screen.getByLabelText('Last Name'), 'Banks'); - await userEvent.type(screen.getByLabelText('Email'), 'banks@gmail.com'); - await userEvent.type(screen.getByLabelText('Telephone'), '333-333-3333'); - await userEvent.type(screen.getByTestId('officeAccountRequestEdipi'), '1111111111'); - await userEvent.type(screen.getByTestId('officeAccountRequestOtherUniqueId'), '1111111111'); - - const transportationOfficeInput = screen.getByLabelText('Transportation Office'); - fireEvent.change(transportationOfficeInput, { target: { value: 'Tester' } }); - await act(() => selectEvent.select(transportationOfficeInput, /Tester/)); - - const tooCheckbox = screen.getByTestId('headquartersCheckBox'); - await userEvent.click(tooCheckbox); - - const submitButton = await screen.getByTestId('requestOfficeAccountSubmitButton'); - await userEvent.click(submitButton); - - expect(testProps.onSubmit).toHaveBeenCalled(); - }); - it('shows policy error when both TOO and TIO checkboxes are both selected, and goes away after unselecting one of them', async () => { renderWithRouter(); diff --git a/src/components/form/OfficeAccountRequestFields/OfficeAccountRequestFields.jsx b/src/components/form/OfficeAccountRequestFields/OfficeAccountRequestFields.jsx index 7ad5bd51f49..a992197e4bb 100644 --- a/src/components/form/OfficeAccountRequestFields/OfficeAccountRequestFields.jsx +++ b/src/components/form/OfficeAccountRequestFields/OfficeAccountRequestFields.jsx @@ -96,12 +96,6 @@ export const OfficeAccountRequestFields = ({ render }) => { name="customerSupportRepresentativeCheckBox" label="Customer Support Representative" /> - , )} diff --git a/src/constants/userRoles.js b/src/constants/userRoles.js index 9429c6b0228..245e8542c3a 100644 --- a/src/constants/userRoles.js +++ b/src/constants/userRoles.js @@ -6,9 +6,9 @@ export const roleTypes = { CONTRACTING_OFFICER: 'contracting_officer', SERVICES_COUNSELOR: 'services_counselor', PRIME_SIMULATOR: 'prime_simulator', + QAE: 'qae', HQ: 'headquarters', CUSTOMER_SERVICE_REPRESENTATIVE: 'customer_service_representative', - QAE: 'qae', }; export const adminOfficeRoles = [ @@ -18,9 +18,9 @@ export const adminOfficeRoles = [ { roleType: 'contracting_officer', name: 'Contracting Officer' }, { roleType: 'services_counselor', name: 'Services Counselor' }, { roleType: 'prime_simulator', name: 'Prime Simulator' }, + { roleType: 'qae', name: 'Quality Assurance Evaluator' }, { roleType: 'headquarters', name: 'Headquarters' }, { roleType: 'customer_service_representative', name: 'Customer Service Representative' }, - { roleType: 'qae', name: 'Quality Assurance Evaluator' }, ]; export const officeRoles = [ @@ -28,7 +28,7 @@ export const officeRoles = [ roleTypes.TIO, roleTypes.SERVICES_COUNSELOR, roleTypes.PRIME_SIMULATOR, + roleTypes.QAE, roleTypes.HQ, roleTypes.CUSTOMER_SERVICE_REPRESENTATIVE, - roleTypes.QAE, ]; diff --git a/src/pages/Office/RequestAccount/RequestAccount.jsx b/src/pages/Office/RequestAccount/RequestAccount.jsx index 124f2dc3705..b2ed206b36c 100644 --- a/src/pages/Office/RequestAccount/RequestAccount.jsx +++ b/src/pages/Office/RequestAccount/RequestAccount.jsx @@ -62,12 +62,6 @@ export const RequestAccount = ({ setFlashMessage }) => { roleType: 'qae', }); } - if (values.headquartersCheckBox) { - requestedRoles.push({ - name: 'Headquarters', - roleType: 'headquarters', - }); - } if (values.customerSupportRepresentativeCheckBox) { requestedRoles.push({ name: 'Customer Service Representative', diff --git a/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx b/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx index 24d09541f48..5887bc53021 100644 --- a/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx +++ b/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx @@ -1,13 +1,22 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { CheckboxGroupInput } from 'react-admin'; -import { adminOfficeRoles } from 'constants/userRoles'; -import { officeUserPrivileges } from 'constants/userPrivileges'; +import { adminOfficeRoles, roleTypes } from 'constants/userRoles'; +import { officeUserPrivileges, elevatedPrivilegeTypes } from 'constants/userPrivileges'; +import { isBooleanFlagEnabled } from 'utils/featureFlags'; const RolesPrivilegesCheckboxInput = (props) => { let rolesSelected = []; let privilegesSelected = []; + const [isHeadquartersRoleFF, setHeadquartersRoleFF] = useState(false); + + useEffect(() => { + isBooleanFlagEnabled('headquarters_role')?.then((enabled) => { + setHeadquartersRoleFF(enabled); + }); + }, []); + const makeRoleTypeArray = (roles) => { if (!roles || roles.length === 0) { rolesSelected = []; @@ -15,7 +24,9 @@ const RolesPrivilegesCheckboxInput = (props) => { } return roles.reduce((rolesArray, role) => { if (role.roleType) { - rolesArray.push(role.roleType); + if (isHeadquartersRoleFF || (!isHeadquartersRoleFF && role.roleType !== roleTypes.HQ)) { + rolesArray.push(role.roleType); + } } rolesSelected = rolesArray; @@ -24,16 +35,35 @@ const RolesPrivilegesCheckboxInput = (props) => { }; const parseRolesCheckboxInput = (input) => { - if (privilegesSelected.includes('supervisor') || privilegesSelected.includes('safety')) { + if ( + privilegesSelected.includes(elevatedPrivilegeTypes.SUPERVISOR) || + privilegesSelected.includes(elevatedPrivilegeTypes.SAFETY) + ) { var index; - if (input.includes('customer')) { - index = input.indexOf('customer'); + if (input.includes(roleTypes.CUSTOMER)) { + index = input.indexOf(roleTypes.CUSTOMER); if (index !== -1) { input.splice(index, 1); } } - if (input.includes('contracting_officer')) { - index = input.indexOf('contracting_officer'); + if (input.includes(roleTypes.CONTRACTING_OFFICER)) { + index = input.indexOf(roleTypes.CONTRACTING_OFFICER); + if (index !== -1) { + input.splice(index, 1); + } + } + } + + if (!isHeadquartersRoleFF && input.includes(roleTypes.HQ)) { + if (input.includes(roleTypes.HQ)) { + index = input.indexOf(roleTypes.HQ); + if (index !== -1) { + input.splice(index, 1); + } + } + } else if (isHeadquartersRoleFF && privilegesSelected.includes(elevatedPrivilegeTypes.SAFETY)) { + if (input.includes(roleTypes.HQ)) { + index = input.indexOf(roleTypes.HQ); if (index !== -1) { input.splice(index, 1); } @@ -62,17 +92,26 @@ const RolesPrivilegesCheckboxInput = (props) => { }; const parsePrivilegesCheckboxInput = (input) => { - if (rolesSelected.includes('customer') || rolesSelected.includes('contracting_officer')) { + if (rolesSelected.includes(roleTypes.CUSTOMER) || rolesSelected.includes(roleTypes.CONTRACTING_OFFICER)) { var index; - if (input.includes('supervisor')) { - index = input.indexOf('supervisor'); + if (input.includes(elevatedPrivilegeTypes.SUPERVISOR)) { + index = input.indexOf(elevatedPrivilegeTypes.SUPERVISOR); if (index !== -1) { input.splice(index, 1); } } - if (input.includes('safety')) { - index = input.indexOf('safety'); + if (input.includes(elevatedPrivilegeTypes.SAFETY)) { + index = input.indexOf(elevatedPrivilegeTypes.SAFETY); + if (index !== -1) { + input.splice(index, 1); + } + } + } + + if (isHeadquartersRoleFF && rolesSelected.includes(roleTypes.HQ)) { + if (input.includes(elevatedPrivilegeTypes.SAFETY)) { + index = input.indexOf(elevatedPrivilegeTypes.SAFETY); if (index !== -1) { input.splice(index, 1); }