Search code examples
databaseloopbackjsstrongloop

I receive a 401 error when I access a custom method inside my Models folder


I'm using Loopback to access/update/make changes to my database. I've created a method called deleteSingleHearingTest in my common/models folder:

Account.deleteSingleHearingTest = function (req, callback) {
    console.log('accounts.js: deleteSingleHearingTest: are we being reached????', req)
    Account.findById(req.accessToken.userId)
        .then(account => {
            if (!account) {
                throw new Error('Cannot find user');
            }
            console.log('account.js: deleteSingleHearingTest: req: ', req);
            return app.models.HearingTest.updateAll({ accountId: account.id, id: req.body.hearingTestId }, { isDeleted: new Date() });

        })
        .then(() => {
            callback(null);
        })
        .catch(error => {
            callback(error);
        });
}

after which I created a remote method:

Account.remoteMethod(
        'deleteSingleHearingTest', {
            http: {
                path: '/deleteSingleHearingTest',
                verb: 'post'
            },
            accepts: [
                { arg: 'req', type: 'object', http: { source: 'req' } }
            ],
            returns: { "wtf": "wtf" }
        });

When I attempt to use this via fetch inside a method called deleteSingleHearingTest in my actions folder(redux) I receive a 401 status error message:

export const deleteSingleHearingTest = (hearingTestNumber) => {
    return (dispatch, getState) => {
        let state = getState();
        if (!state.user || !state.user.accessToken || !state.user.accessToken.id || !state.user.accessToken.userId) {
            console.debug('writeTestResult', state.user);
            // TODO: ERROR
            return;
        }
        dispatch({
            type: DELETE_SINGLE_REPORT_REQUEST
        });
        console.log('here is your access token', state.user.accessToken);

        fetch(SERVERCONFIG.BASEURL + '/api/Accounts/deleteSingleHearingTest?access_token=' + state.user.accessToken.id, {
            method: 'POST',
            headers: SERVERCONFIG.HEADERS,
            body: JSON.stringify({ "hearingTestId": hearingTestNumber })


        })
            .then(response => {
                if (response.status === 200) {
                    console.log('actions/user.js deleteSingleReport were in the pipe 5x5', response.json());
                }
                console.log('actions/user.js failed to delete item: response: ', response)
            })
    }
};

Two errors I'm noticing:
1. the Account.deleteSingleHearingTest never gets reached. I know this because the console.log never shows up in the window where I ran node .

  1. I get a 401 status error message on the front end.

Here is a picture of my StrongLoop gui. enter image description here

here is the picture of my account.json file

Here's a picture of a method that a previous developer created that currently works with no status errors. deleteSingleHearingTest is nearly identical.

enter image description here enter image description here


Solution

  • From what I can tell by looking at your API Explorer screenshot, your Account model is extending LoopBack's built-in User model. The User model has ACLs configured to deny access to all methods except few explicitly allowed ones. You can find the ACL configuration in common/models/user.json.

    "acls": [
      {
        "principalType": "ROLE",
        "principalId": "$everyone",
        "permission": "DENY"
      },
      {
        "principalType": "ROLE",
        "principalId": "$everyone",
        "permission": "ALLOW",
        "property": "create"
      },
      {
        "principalType": "ROLE",
        "principalId": "$owner",
        "permission": "ALLOW",
        "property": "deleteById"
      },
      {
        "principalType": "ROLE",
        "principalId": "$everyone",
        "permission": "ALLOW",
        "property": "login"
      },
      // etc.
    ]
    

    The first entry denies access to all methods, the subsequent entries allow access to certain methods for certain groups of users. For example, anybody ($everyone) can create a new user or invoke a login method, but the details of a User model can be retrieved only by the user themselves ($owner).

    Please refer to Controlling data access and Understanding the built-in User model in LoopBack's documentation to learn more.

    In order to allow your users to execute Account.deleteSingleHearingTest method, you need to add a new ACL entry to your Account configuration. I believe LoopBack can merge ACL defined in the base model (User) with the additional ACL entries defined in a subclassing model (Account) as of pull request #1289. If that's the case then you need to simply add a new ACL entry in your common/models/account.json file. Assuming any logged-in user can invoke deleteSingleHearingTest, but anonymous (unauthenticated) users cannot:

    {
      "name": "Account",
      "base": "User",
      // ...
      "acls": [
        {
          "principalType": "ROLE",
          "principalId": "$authenticated",
          "permission": "ALLOW",
          "property": "deleteSingleHearingTest"
        }
      ]
    }