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
- Perform the query with administrator credentials.
- Remove the is_mfa_enabled contain/filter for non-admin callers.
- Gate the client-side feature requesting this property behind role === admin.
- 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
- Only request is_mfa_enabled in admin-only UI views.
- Gate client code on the authenticated user's role.
- Document the contain/filter as admin-only in API docs.
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
- You are not allowed to access this location.
- Only admin can create or update organization settings.
- Only admin can create or update subscription information.
- Only administrators are allowed to create/update MFA…
- Only administrators are allowed to create/update MFA…
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 QueryView on GitHub (pinned to 31c1bbc10f)