Skip to content
6 changes: 6 additions & 0 deletions sdm/src/main/java/com/sap/cds/sdm/constants/SDMConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ private SDMConstants() {
public static final String VIRUS_ERROR = "%s contains potential malware and cannot be uploaded.";
public static final String REPOSITORY_ERROR = "Failed to get repository info.";
public static final String NOT_FOUND_ERROR = "Failed to read document.";
public static final String NAME_CONSTRAINT_WARNING_MESSAGE =
"Enter a valid file name for %s. The following characters are not supported: /, \\";
public static final String SDM_MISSING_ROLES_EXCEPTION_MSG =
"You do not have the required permissions to rename attachments. Kindly contact the admin";
public static final String SDM_ROLES_ERROR_MESSAGE =
"Unable to rename the file due to an error at the server";
public static final String SDM_ENV_NAME = "sdm";

public static final String SDM_TOKEN_EXCHANGE_DESTINATION = "sdm-token-exchange-flow";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.sap.cds.sdm.model.SDMCredentials;
import com.sap.cds.sdm.service.SDMService;
import com.sap.cds.sdm.utilities.SDMUtils;
import com.sap.cds.services.ServiceException;
import com.sap.cds.services.authentication.AuthenticationInfo;
import com.sap.cds.services.authentication.JwtTokenAuthenticationInfo;
import com.sap.cds.services.cds.ApplicationService;
Expand Down Expand Up @@ -98,9 +99,23 @@ private void processAttachment(
cmisDocument.setFileName(filenameInRequest);
cmisDocument.setObjectId(objectId);
int responseCode = sdmService.renameAttachments(jwtToken, sdmCredentials, cmisDocument);
if (responseCode == 409) {
duplicateFileNameList.add(filenameInRequest);
attachment.replace("fileName", fileNameInSDM);
switch (responseCode) {
case 403:
// SDM Roles for user are missing
throw new ServiceException(SDMConstants.SDM_MISSING_ROLES_EXCEPTION_MSG, null);

case 409:
duplicateFileNameList.add(filenameInRequest);
attachment.replace("fileName", fileNameInSDM);
break;

case 200:
case 201:
// Success cases, do nothing
break;

default:
throw new ServiceException(SDMConstants.SDM_ROLES_ERROR_MESSAGE, null);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.sap.cds.sdm.persistence.DBQuery;
import com.sap.cds.sdm.service.SDMService;
import com.sap.cds.sdm.utilities.SDMUtils;
import com.sap.cds.services.ServiceException;
import com.sap.cds.services.authentication.AuthenticationInfo;
import com.sap.cds.services.authentication.JwtTokenAuthenticationInfo;
import com.sap.cds.services.cds.ApplicationService;
Expand Down Expand Up @@ -99,7 +100,7 @@ private void processAttachments(
}
}

private void processAttachment(
public void processAttachment(
Optional<CdsEntity> attachmentEntity,
CdsUpdateEventContext context,
Map<String, Object> attachment,
Expand All @@ -126,9 +127,23 @@ private void processAttachment(
context.getAuthenticationInfo().as(JwtTokenAuthenticationInfo.class).getToken(),
TokenHandler.getSDMCredentials(),
cmisDocument);
if (responseCode == 409) {
duplicateFileNameList.add(filenameInRequest);
attachment.replace("fileName", fileNameInSDM);
switch (responseCode) {
case 403:
// SDM Roles for user are missing
throw new ServiceException(SDMConstants.SDM_MISSING_ROLES_EXCEPTION_MSG, null);

case 409:
duplicateFileNameList.add(filenameInRequest);
attachment.replace("fileName", fileNameInSDM);
break;

case 200:
case 201:
// Success cases, do nothing
break;

default:
throw new ServiceException(SDMConstants.SDM_ROLES_ERROR_MESSAGE, null);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package unit.com.sap.cds.sdm.handler.applicationservice;

import static com.sap.cds.sdm.utilities.SDMUtils.isFileNameDuplicateInDrafts;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
Expand All @@ -16,6 +18,7 @@
import com.sap.cds.sdm.service.SDMService;
import com.sap.cds.sdm.service.SDMServiceImpl;
import com.sap.cds.sdm.utilities.SDMUtils;
import com.sap.cds.services.ServiceException;
import com.sap.cds.services.authentication.AuthenticationInfo;
import com.sap.cds.services.authentication.JwtTokenAuthenticationInfo;
import com.sap.cds.services.cds.CdsCreateEventContext;
Expand Down Expand Up @@ -218,6 +221,91 @@ public void testRenameWithConflictResponseCode() throws IOException {
.warn("The following files could not be renamed as they already exist:\nfile1.txt\n");
}

@Test
public void testCreateAttachmentWithNoSDMRoles() throws IOException {
// Mock the data structure to simulate the attachments
List<CdsData> data = new ArrayList<>();
Map<String, Object> entity = new HashMap<>();
List<Map<String, Object>> attachments = new ArrayList<>();
Map<String, Object> attachment = spy(new HashMap<>());
attachment.put("fileName", "file1.txt");
attachment.put("url", "objectId");
attachment.put("ID", "test-id"); // assuming there's an ID field
attachments.add(attachment);
entity.put("attachments", attachments);
CdsData mockCdsData = mock(CdsData.class);
when(mockCdsData.get("attachments")).thenReturn(attachments);
data.add(mockCdsData);

// Mock the authentication context
when(context.getAuthenticationInfo()).thenReturn(authInfo);
when(authInfo.as(JwtTokenAuthenticationInfo.class)).thenReturn(jwtTokenInfo);
when(jwtTokenInfo.getToken()).thenReturn("jwtToken");

// Mock the static TokenHandler
when(TokenHandler.getSDMCredentials()).thenReturn(mockCredentials);

// Mock the SDM service responses
when(sdmService.getObject(any(), any(), any()))
.thenReturn("file-sdm.txt"); // Mock a different file name in SDM to trigger renaming
when(sdmService.renameAttachments(
anyString(), any(SDMCredentials.class), any(CmisDocument.class)))
.thenReturn(403); // Mock conflict response code

when(sdmService.renameAttachments(
anyString(), any(SDMCredentials.class), any(CmisDocument.class)))
.thenReturn(403); // Mock conflict response code

ServiceException exception =
assertThrows(
ServiceException.class,
() -> {
handler.updateName(context, data);
});

assertEquals(SDMConstants.SDM_MISSING_ROLES_EXCEPTION_MSG, exception.getMessage());
}

@Test
public void testCreateAttachmentWith500Error() throws IOException {
// Mock the data structure to simulate the attachments
List<CdsData> data = new ArrayList<>();
Map<String, Object> entity = new HashMap<>();
List<Map<String, Object>> attachments = new ArrayList<>();
Map<String, Object> attachment = spy(new HashMap<>());
attachment.put("fileName", "file1.txt");
attachment.put("url", "objectId");
attachment.put("ID", "test-id"); // assuming there's an ID field
attachments.add(attachment);
entity.put("attachments", attachments);
CdsData mockCdsData = mock(CdsData.class);
when(mockCdsData.get("attachments")).thenReturn(attachments);
data.add(mockCdsData);

// Mock the authentication context
when(context.getAuthenticationInfo()).thenReturn(authInfo);
when(authInfo.as(JwtTokenAuthenticationInfo.class)).thenReturn(jwtTokenInfo);
when(jwtTokenInfo.getToken()).thenReturn("jwtToken");

// Mock the static TokenHandler
when(TokenHandler.getSDMCredentials()).thenReturn(mockCredentials);

// Mock the SDM service responses
when(sdmService.getObject(any(), any(), any()))
.thenReturn("file-sdm.txt"); // Mock a different file name in SDM to trigger renaming
when(sdmService.renameAttachments(
anyString(), any(SDMCredentials.class), any(CmisDocument.class)))
.thenReturn(500); // Mock conflict response code
ServiceException exception =
assertThrows(
ServiceException.class,
() -> {
handler.updateName(context, data);
});

assertEquals(SDMConstants.SDM_ROLES_ERROR_MESSAGE, exception.getMessage());
}

@Test
public void testRenameWith200ResponseCode() throws IOException {
// Mock the data structure to simulate the attachments
Expand Down Expand Up @@ -271,21 +359,35 @@ public void testRenameWithRestrictedCharacters() throws IOException {
fileNameWithRestrictedChars.add("file/2.txt");
fileNameWithRestrictedChars.add("file\\3.txt");

// Mock the CdsEntity and setup context
CdsEntity attachmentDraftEntity = mock(CdsEntity.class);
when(context.getTarget()).thenReturn(attachmentDraftEntity);
when(context.getAuthenticationInfo()).thenReturn(authInfo);
when(authInfo.as(JwtTokenAuthenticationInfo.class)).thenReturn(jwtTokenInfo);
when(jwtTokenInfo.getToken()).thenReturn("jwtToken");

when(context.getMessages()).thenReturn(messages);

sdmUtilsMockedStatic = mockStatic(SDMUtils.class);
// Mock SDMUtils to simulate restricted characters
MockedStatic<SDMUtils> sdmUtilsMockedStatic = mockStatic(SDMUtils.class);
sdmUtilsMockedStatic
.when(() -> SDMUtils.isRestrictedCharactersInName(anyString()))
.thenCallRealMethod();
.thenAnswer(
invocation -> {
String filename = invocation.getArgument(0);
return filename.contains("/") || filename.contains("\\");
});

// Mock the SDM service object retrieval
when(sdmService.getObject(anyString(), anyString(), any())).thenReturn("file-in-sdm");

// Ensure renameAttachments behaves as expected
when(sdmService.renameAttachments(anyString(), any(), any(CmisDocument.class)))
.thenReturn(200); // or a desired response code

// Act
handler.updateName(context, data);

// Verify warning message about restricted characters
verify(messages, times(1))
.warn(SDMConstants.nameConstraintMessage(fileNameWithRestrictedChars, "Rename"));

Expand All @@ -296,40 +398,61 @@ public void testRenameWithRestrictedCharacters() throws IOException {
for (Map<String, Object> attachment : attachments) {
String filename = (String) attachment.get("fileName");
if (filename.equals("file/2.txt") || filename.equals("file\\3.txt")) {
// Ensure the filename is replaced
verify(attachment).replace("fileName", "file-in-sdm");
}
}
}

// Close the mocked static method
sdmUtilsMockedStatic.close();
}

@Test
public void testWarnOnRestrictedCharacters() throws IOException {
// Prepare the sample data with restricted characters
List<CdsData> data = prepareMockAttachmentData("file1.txt", "file/2.txt", "file3\\abc.txt");
CdsEntity attachmentDraftEntity = mock(CdsEntity.class);
List<String> fileNameWithRestrictedChars = new ArrayList<>();
fileNameWithRestrictedChars.add("file/2.txt");
fileNameWithRestrictedChars.add("file3\\abc.txt");

// Mock context and related authentication methods
CdsEntity attachmentDraftEntity = mock(CdsEntity.class);
when(context.getTarget()).thenReturn(attachmentDraftEntity);
when(context.getAuthenticationInfo()).thenReturn(authInfo);
when(authInfo.as(JwtTokenAuthenticationInfo.class)).thenReturn(jwtTokenInfo);
when(jwtTokenInfo.getToken()).thenReturn("jwtToken");
// Mock the static TokenHandler
when(TokenHandler.getSDMCredentials()).thenReturn(mockCredentials);

// Mock the SDM service responses
when(TokenHandler.getSDMCredentials()).thenReturn(mockCredentials);
when(sdmService.getObject(anyString(), anyString(), any())).thenReturn("file-in-sdm");

// Mock message handling
when(context.getMessages()).thenReturn(messages);

// No duplicate filenames, simulate restricted characters only.
handler.updateName(context, data);

// Verify the warning message for restricted filenames
verify(messages, times(1))
.warn(SDMConstants.nameConstraintMessage(fileNameWithRestrictedChars, "Rename"));

// Verify no error message is issued
verify(messages, never()).error(anyString());
// Mock SDMUtils restricted character check
try (MockedStatic<SDMUtils> sdmUtilsMockedStatic = mockStatic(SDMUtils.class)) {
sdmUtilsMockedStatic
.when(() -> SDMUtils.isRestrictedCharactersInName(anyString()))
.thenAnswer(
invocation -> {
String filename = invocation.getArgument(0);
return filename.contains("/") || filename.contains("\\");
});

// Mock renameAttachments implementation to avoid ServiceExceptions for testing
when(sdmService.renameAttachments(any(String.class), any(), any(CmisDocument.class)))
.thenReturn(200); // assuming successful rename

// Act by invoking the handler updateName method with the context and data
handler.updateName(context, data);

// Verify the warning for restricted filenames is correctly handled
verify(messages, times(1))
.warn(SDMConstants.nameConstraintMessage(fileNameWithRestrictedChars, "Rename"));

// Ensure no error messages are appearing unexpectedly
verify(messages, never()).error(anyString());
}
}

private List<CdsData> prepareMockAttachmentData(String... fileNames) {
Expand Down
Loading
Loading