Skip to content

Commit

Permalink
Merge pull request #1819 from akto-api-security/feature/edit_collecti…
Browse files Browse the repository at this point in the history
…on_name

allow custom collection group names to be editable
  • Loading branch information
Ark2307 authored Dec 13, 2024
2 parents 29b4093 + fc400cf commit e0a94a3
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public String fetchCollection() {
private String collectionName;

private boolean isValidApiCollectionName(){
if (this.collectionName == null) {
if (this.collectionName == null || this.collectionName.length() == 0) {
addActionError("Invalid collection name");
return false;
}
Expand Down Expand Up @@ -728,6 +728,35 @@ public String updateEnvType(){
return Action.ERROR.toUpperCase();
}

public String editCollectionName() {
if(!isValidApiCollectionName()){
return ERROR.toUpperCase();
}

ApiCollection apiCollection = ApiCollectionsDao.instance.getMeta(apiCollectionId);
if (apiCollection == null) {
String errorMessage = "API collection not found";
addActionError(errorMessage);
return Action.ERROR.toUpperCase();
}

if (apiCollection.getHostName() != null) {
String errorMessage = "Unable to modify the Traffic API collection";
addActionError(errorMessage);
return Action.ERROR.toUpperCase();
}

ApiCollectionsDao.instance.updateOne(
Filters.eq(ApiCollection.ID, apiCollectionId),
Updates.combine(
Updates.set(ApiCollection.NAME, collectionName),
Updates.set("displayName", collectionName)
)
);

return Action.SUCCESS.toUpperCase();
}

public List<ApiCollection> getApiCollections() {
return this.apiCollections;
}
Expand Down
29 changes: 29 additions & 0 deletions apps/dashboard/src/main/resources/struts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7383,6 +7383,35 @@
</result>
</action>

<action name="api/editCollectionName" class="com.akto.action.ApiCollectionsAction" method="editCollectionName">
<interceptor-ref name="json"/>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="roleAccessInterceptor">
<param name="featureLabel">API_COLLECTIONS</param>
<param name="accessType">READ_WRITE</param>
<param name="actionDescription">User edited collection name</param>
</interceptor-ref>
<result name="FORBIDDEN" type="json">
<param name="statusCode">403</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
<interceptor-ref name="usageInterceptor">
<param name="featureLabel">API_COLLECTIONS</param>
</interceptor-ref>
<result name="SUCCESS" type="json"></result>
<result name="ERROR" type="json">
<param name="statusCode">422</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
<result name="UNAUTHORIZED" type="json">
<param name="statusCode">403</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
</action>

</package>

</struts>
Original file line number Diff line number Diff line change
Expand Up @@ -805,4 +805,14 @@ export default {
})
},

async editCollectionName(apiCollectionId, collectionName) {
return await request({
url: '/api/editCollectionName',
method: 'post',
data: {
apiCollectionId, collectionName
}
})
},

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PageWithMultipleCards from "../../../components/layouts/PageWithMultipleCards"
import { Text, HorizontalStack, Button, Popover, Modal, IndexFiltersMode, VerticalStack, Box, Checkbox } from "@shopify/polaris"
import { Text, HorizontalStack, Button, Popover, Modal, IndexFiltersMode, VerticalStack, Box, Checkbox, TextField } from "@shopify/polaris"
import api from "../api"
import { useEffect, useState } from "react"
import func from "@/util/func"
Expand Down Expand Up @@ -154,9 +154,10 @@ function ApiEndpoints(props) {
const setShowDetails = ObserveStore(state => state.setInventoryFlyout)
const collectionsMap = PersistStore(state => state.collectionsMap)
const allCollections = PersistStore(state => state.allCollections);
const setCollectionsMap = PersistStore(state => state.setCollectionsMap)
const setAllCollections = PersistStore(state => state.setAllCollections)

const pageTitle = collectionsMap[apiCollectionId]

const [ pageTitle, setPageTitle] = useState(collectionsMap[apiCollectionId])
const [apiEndpoints, setApiEndpoints] = useState([])
const [apiInfoList, setApiInfoList] = useState([])
const [unusedEndpoints, setUnusedEndpoints] = useState([])
Expand Down Expand Up @@ -185,6 +186,9 @@ function ApiEndpoints(props) {
const queryParams = new URLSearchParams(location.search);
const selectedUrl = queryParams.get('selected_url')
const selectedMethod = queryParams.get('selected_method')
const [isEditing, setIsEditing] = useState(false);
const [editableTitle, setEditableTitle] = useState(pageTitle);


// the values used here are defined at the server.
const definedTableTabs = apiCollectionId === 111111999 ? ['All', 'New', 'High risk', 'No auth', 'Shadow'] : ( apiCollectionId === 111111120 ? ['All', 'New', 'Sensitive', 'High risk', 'Shadow'] : ['All', 'New', 'Sensitive', 'High risk', 'No auth', 'Shadow'] )
Expand Down Expand Up @@ -905,20 +909,80 @@ function ApiEndpoints(props) {
)
]

function updateCollectionName(list, apiCollectionId, newName) {
list.forEach(item => {
if (item.id === apiCollectionId) {
item.displayName = newName;
item.name = newName;
}
});
}


const handleSaveClick = async () => {
api.editCollectionName(apiCollectionId, editableTitle).then((resp) => {
func.setToast(true, false, 'Collection name updated successfully!')
setPageTitle(editableTitle)
collectionsMap[apiCollectionId] = editableTitle
setCollectionsMap(collectionsMap)
updateCollectionName(allCollections, parseInt(apiCollectionId, 10), editableTitle)
setAllCollections(allCollections)
setIsEditing(false);
}).catch((err) => {
setEditableTitle(pageTitle)
setIsEditing(false);
})

};

const handleTitleChange = (value) => {
setEditableTitle(value);
};

const handleKeyDown = (event) => {
if (event.key === 'Enter') {
handleSaveClick();
} else if (event.key === 'Escape') {
setIsEditing(false);
}
}

return (
<div>
{isQueryPage ? apiEndpointTable :
{isQueryPage ? (
apiEndpointTable
) : (
<PageWithMultipleCards
title={
<Box maxWidth="35vw">
<TooltipText tooltip={pageTitle} text={pageTitle} textProps={{ variant: 'headingLg' }} />
</Box>
isEditing ? (
<Box maxWidth="20vw">
<div onKeyDown={(e) => handleKeyDown(e)}>
<TextField
value={editableTitle}
onChange={handleTitleChange}
autoFocus
autoComplete="off"
maxLength="24"
suffix={(
<Text>{editableTitle.length}/24</Text>
)}
onKeyDown={handleKeyDown}
/>
</div>
</Box>
) : (
<div style={{ cursor: isApiGroup ? 'pointer' : 'default' }} onClick={isApiGroup ? () => {setIsEditing(true);} : undefined}>
<Box maxWidth="35vw">
<TooltipText tooltip={pageTitle} text={pageTitle} textProps={{ variant: 'headingLg' }} />
</Box>
</div>
)
}
backUrl="/dashboard/observe/inventory"
secondaryActions={secondaryActionsComponent}
components={components}
/>
}
)}
</div>

)
Expand Down

0 comments on commit e0a94a3

Please sign in to comment.