Skip to content

Issue20141

Salif Faustino edited this page Apr 28, 2024 · 11 revisions

A duplicated entry can be saved even if a unique field shares the same value as the original one #20141

Issue Description

This report details an inconsistency observed in Strapi's behavior when a custom field is set to be unique. The unique constraint aims to prevent duplicate entries based on the value in that specific field.

Observed Behavior: Direct Duplicates Saved: If a user attempts to save a new record that's an exact copy of an existing record (including the same value in the unique field), Strapi bypasses the constraint and allows the duplicate entry. This bypasses the intended functionality of the unique constraint.

Screenshot 2024-04-27 at 19 19 44
                                Figure 1: Unique.

Actual Behavior: Duplicate entries are being saved despite the unique constraint on the isbn field.

issue
                                Figure 2: Behaviour.
list
                                Figure 3: Unwanted duplication.

Expected Behavior: Upon attempting to save a new record with a duplicate value in the isbn field, the application should throw an error or prevent the save operation from completing.

Root Cause

Steps to reproduce the behavior

  • Create a Content-Type with a unique field in it
  • Create an entry and fill the unique field
  • Go to the List View
  • Duplicate the entry
  • Click "Save"

Version

This issue happened using:

  • Node.js version: 18.14.2
  • NPM version: 9.8.1
  • Strapi version: 4.20.3
  • Database: sqlite

Requirement

As a user I am supposed to see an error on the unique field and I should not be allowed to save it.

Investigation

After inspecting the page using the browser's developer tools, it was possible to locate the script collection-types.ts where some crud operations is taking place. First, there needed to exist some debugging in different areas of the script in order to be able to pinpoint the exact place where the issue was.

Actions Taken

After investigating, the method responsible was found: async clone(ctx: any) and it was also settled that there was no validation taking place to check the issue stated above. The following necessary checks were added :

Before:

async clone(ctx: any) {
    const { userAbility, user } = ctx.state;
    const { model, sourceId: id } = ctx.params;
    const { body } = ctx.request;
    const entityManager = getService('entity-manager');
    const permissionChecker = getService('permission-checker').create({ userAbility, model });
}

After:

async clone(ctx: any) {
    const { userAbility, user } = ctx.state;
    const { model, sourceId: id } = ctx.params;
    const { body } = ctx.request;

    const prohibitedFields = getProhibitedCloningFields(model);

    if (prohibitedFields.length > 0) {
        return ctx.badRequest(
            'Entity could not be cloned as it has unique and/or relational fields. ' +
            'Please edit those fields manually and save to complete the cloning.',
            {
                prohibitedFields,
            }
        );
    }

    const entityManager = getService('entity-manager');
    const permissionChecker = getService('permission-checker').create({ userAbility, model });
}

image

This diagram simplifies the clone function into a sequential process of checking prohibited fields, handling cases where they exist or not, and then either returning an error or performing the cloning operation.

Using the function getProhibitedCloningFields, which accepts a model and checks if there are prohibited fields - if there are more than 1 it will throw the error. Despite successfully validating the changes, locating the file within the core of the application proved challenging. This led to a deeper exploration of Strapi's architecture and functionality.

Changes to SourceCode files

This shows the before and after Screenshot 2024-04-27 at 23 17 23

                                Figure 4: Code Changes to Strapi's source code.

Insights

In Strapi, modifying the behavior of built-in admin panel pages, such as the Fields updates, isn't straightforward through the addition of custom JavaScript code snippets. Strapi utilizes a plugin system for extending functionalities, but these plugins typically do not involve directly injecting scripts into the admin panel.

Identification

During the search through the codebase, I tried using the search on my IDE but all my effort proved abortive. What make it easy was the fact the name of the file was collection-type and in the view it was under collection types. This made it easy to locate the file

Solution

Despite attempts to modify these components, the changes did not reflect in the interface. Recognizing that it was a React application, the decision was made to rebuild it before retesting the modifications. This approach aims to ensure that the updated file with validation functionality will be more prominent, contributing to a more intuitive and user-friendly experience for the users, as you can see the error displayed with a good user experience below.

afterupdate
                                 Figure 5: Validation displaying error with a good user experience.

Conclusion

The fix was published as a Pull Request on Strapi’s GitHub thread and is currently being evaluated by the contributors of the project as seen below.

Screenshot 2024-04-27 at 21 03 57
                                Figure 6: Pull Request issue.

Clone this wiki locally