passbolt/passbolt_api · warning · BadRequestException

The property is visible by administrators only.

Error message

The property {0} is visible by administrators only.

What it means

IsMfaEnabledQueryService::decorateAndFilterForIndex() throws BadRequestException when a users-index query requests the is_mfa_enabled property or filter but the caller is not an administrator. The MFA status of other users is admin-only data.

Solutions

  1. Perform the query with administrator credentials.
  2. Remove the is_mfa_enabled contain/filter for non-admin callers.
  3. Gate the client-side feature requesting this property behind role === admin.
  4. If needed for self, use account settings endpoints instead of the users index.

Example fix

// before: unconditionally add contain
$query = $usersTable->find()->contain('IsMfaEnabled');
// after: only for admins
if ($uac->isAdmin()) {
    $query = $usersTable->find()->contain('IsMfaEnabled');
} else {
    $query = $usersTable->find();
}
Defensive patterns

Strategy: validation

Validate before calling

if (!$uac->isAdmin() && (isset($options['contain']['is_mfa_enabled']) || isset($options['filter']['has-mfa']))) {
    // drop the property/filter before the query or reject in the UI
    unset($options['contain']['is_mfa_enabled'], $options['filter']['has-mfa']);
}

Try / catch

try {
    $users = $this->UsersIndex->find($uac, $options);
} catch (BadRequestException $e) {
    return $this->forbidden('is_mfa_enabled is admin-only');
}

Prevention

When it happens

Trigger: GET /users.json?contain[is_mfa_enabled]=1 or ?filter[has-mfa]=... performed by a non-admin (role user/group admin) UAC.

Common situations: Scripts or UIs built against admin sessions reused with regular user credentials; API consumers adding the contain unconditionally for all users.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/677671b7f507f47a. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltCe/MultiFactorAuthentication/src/Service/Query/IsMfaEnabledQueryService.php:47

{
    public const IS_MFA_ENABLED_FILTER_NAME = 'is-mfa-enabled';
    public const IS_MFA_ENABLED_PROPERTY = 'is_mfa_enabled';
    public const MFA_SETTINGS_PROPERTY = 'mfa_settings';

    /**
     * @param \Cake\ORM\Query\SelectQuery $query Query
     * @param \App\Utility\UserAccessControl $uac User Access Control
     * @param array $options Query Options
     * @return void
     * @throws \Cake\Http\Exception\BadRequestException if a non-admin requests the is_mfa_enabled contain
     * @throws \Cake\Http\Exception\BadRequestException if the filter is applied and is_mfa_enabled not contained
     */
    public function decorateAndFilterForIndex(SelectQuery $query, UserAccessControl $uac, array $options): void
    {
        $queryContainsIsMfaEnabled = $this->queryContainsIsMfaEnabled($options);

        if ($queryContainsIsMfaEnabled && !$uac->isAdmin()) {
            throw new BadRequestException(
                __('The property {0} is visible by administrators only.', self::IS_MFA_ENABLED_PROPERTY)
            );
        }

        $isMfaEnabledFilter = $options['filter'][self::IS_MFA_ENABLED_FILTER_NAME] ?? null;
        if (!is_null($isMfaEnabledFilter)) {
            $this->filterByMfaEnabled($query, $isMfaEnabledFilter, $queryContainsIsMfaEnabled);
        } elseif ($queryContainsIsMfaEnabled) {
            $this->addIsMfaEnabledPropertyToQuery($query);
        }
    }

    /**
     * Contain the "is_mfa_enabled" property if the request is made
     *  - by an admin
     *  - or the user being viewed
     *
     * @param \Cake\ORM\Query\SelectQuery $query Query

View on GitHub (pinned to 31c1bbc10f)