Skip to content

Commit cf77245

Browse files
authored
fix: project admin feature import permissions (#8421)
1 parent 798b675 commit cf77245

4 files changed

Lines changed: 45 additions & 11 deletions

File tree

api/features/import_export/permissions.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ def has_permission(self, request: Request, view: APIView) -> bool:
1717
environment = Environment.objects.select_related(
1818
"project__organisation",
1919
).get(id=view.kwargs["environment_id"])
20-
organisation = environment.project.organisation
2120

22-
# Since feature imports can be destructive, use org admin.
23-
return request.user.is_organisation_admin(organisation) # type: ignore[union-attr,no-any-return]
21+
# Since feature imports can be destructive, use project admin.
22+
return request.user.is_project_admin(environment.project) # type: ignore[union-attr,no-any-return]
2423

2524

2625
class CreateFeatureExportPermissions(IsAuthenticated):

api/tests/unit/features/import_export/test_unit_features_import_export_views.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,31 @@ def test_feature_import__already_processing__returns_bad_request(
254254
]
255255

256256

257+
def test_feature_import__project_admin__creates_import(
258+
staff_client: APIClient,
259+
environment: Environment,
260+
with_project_permissions: WithProjectPermissionsCallable,
261+
) -> None:
262+
# Given
263+
with_project_permissions(admin=True) # type: ignore[call-arg]
264+
assert FeatureImport.objects.count() == 0
265+
url = reverse(
266+
"api-v1:features:feature-import",
267+
args=[environment.id],
268+
)
269+
270+
file_data = b"[]"
271+
uploaded_file = SimpleUploadedFile("test.23.json", file_data)
272+
data = {"file": uploaded_file, "strategy": OVERWRITE_DESTRUCTIVE}
273+
274+
# When
275+
response = staff_client.post(url, data=data, format="multipart")
276+
277+
# Then
278+
assert response.status_code == 201
279+
assert FeatureImport.objects.count() == 1
280+
281+
257282
def test_feature_import__unauthorized_user__returns_forbidden(
258283
staff_client: APIClient,
259284
environment: Environment,

frontend/web/components/import-export/FeatureImport.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ const FeatureExport: FC<FeatureExportType> = ({ projectId }) => {
127127
setFileData(null)
128128
refetch()
129129
} else {
130-
toast('Failed to import flags')
130+
toast('Failed to import flags', 'danger')
131131
}
132132
})
133133
}

frontend/web/components/import-export/ImportPage.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ import Button from 'components/base/forms/Button'
1313
import PanelSearch from 'components/PanelSearch'
1414
import TabItem from 'components/navigation/TabMenu/TabItem'
1515
import FeatureImport from './FeatureImport'
16-
import AccountStore from 'common/stores/account-store'
1716
import Constants from 'common/constants'
1817
import { useHistory } from 'react-router-dom'
19-
import { OrganisationPermission } from 'common/types/permissions.types'
18+
import { ProjectPermission } from 'common/types/permissions.types'
19+
import { useHasPermission } from 'common/providers/Permission'
2020

2121
type ImportPageType = {
2222
projectId: string
@@ -25,6 +25,12 @@ type ImportPageType = {
2525

2626
const ImportPage: FC<ImportPageType> = ({ projectId, projectName }) => {
2727
const history = useHistory()
28+
const { isLoading: isLoadingPermission, permission: isProjectAdmin } =
29+
useHasPermission({
30+
id: projectId,
31+
level: 'project',
32+
permission: ProjectPermission.ADMIN,
33+
})
2834
const [LDKey, setLDKey] = useState<string>('')
2935
const [importId, setImportId] = useState<number>()
3036
const [isLoading, setIsLoading] = useState<boolean>(false)
@@ -93,15 +99,19 @@ const ImportPage: FC<ImportPageType> = ({ projectId, projectName }) => {
9399
})
94100
}
95101

96-
const isAdmin = AccountStore.isAdmin()
102+
if (isLoadingPermission) {
103+
return (
104+
<div className='text-center mt-4'>
105+
<Loader />
106+
</div>
107+
)
108+
}
97109

98-
if (!isAdmin) {
110+
if (!isProjectAdmin) {
99111
return (
100112
<div
101113
dangerouslySetInnerHTML={{
102-
__html: Constants.organisationPermissions(
103-
OrganisationPermission.ADMIN,
104-
),
114+
__html: Constants.projectPermissions(ProjectPermission.ADMIN),
105115
}}
106116
className='mt-4'
107117
/>

0 commit comments

Comments
 (0)