passbolt/passbolt_api · error · UnauthorizedException

Only admin can create or update subscription information.

Error message

Only admin can create or update subscription information.

What it means

SubscriptionsTable::beforeSave() enforces that only an admin UserAccessControl can create or update a subscription record; it throws UnauthorizedException when the 'uac' save option is missing, empty, or not an admin. This guards the subscription table from non-privileged writes.

Solutions

  1. Always pass a UAC in save options: `$table->save($entity, ['uac' => $uac])` or use SubscriptionsTable::createOrUpdate($uac, $key).
  2. Ensure the UAC is built for an admin user (role id of 'admin') — e.g. via UserAccessControl for an administrator identity.
  3. In CLI/root contexts, construct a UAC from an admin user record instead of skipping it.

Example fix

// before
$this->Subscriptions->save($subscriptionEntity);
// after
$this->Subscriptions->save($subscriptionEntity, ['uac' => $uac]); // $uac->isAdmin() === true
Defensive patterns

Strategy: validation

Validate before calling

if (!$uac->isAdmin()) {
    throw new \Cake\Http\Exception\ForbiddenException('Admin required to save subscription');
}
$table->save($entity, ['uac' => $uac]);

Try / catch

try {
    $this->Subscriptions->createOrUpdate($uac, $key);
} catch (\Cake\Http\Exception\UnauthorizedException $e) {
    // caller lacks admin UAC or uac option missing
}

Prevention

When it happens

Trigger: Calling SubscriptionsTable::save()/createOrUpdate()/update() without passing 'uac' in the save options, or with a UAC belonging to a non-admin (e.g. a user role) — $options['uac'] is null or !isAdmin().

Common situations: Custom scripts or plugins saving to Subscriptions directly and forgetting the uac option; running subscription updates with a logged-in non-admin session; CLI tasks that build a UAC with the wrong role; calling save() on the entity from another plugin without compact('uac').

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/9e761c9e4f4f1934. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/Subscription/src/Model/Table/SubscriptionsTable.php:104

        $query->where([
            $this->aliasField('property_id') => $this->getPropertyId(),
        ]);
    }

    /**
     * Ensure that an administration is provided in options before saving.
     *
     * @param \Cake\Event\Event $event the event
     * @param \Passbolt\Subscription\Model\Entity\Subscription $entity entity
     * @param \ArrayObject $options options
     * @return void
     */
    public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $options): void
    {
        /** @var \App\Utility\UserAccessControl $uac */
        $uac = $options['uac'] ?? null;
        if (empty($uac) || !$uac->isAdmin()) {
            throw new UnauthorizedException(__('Only admin can create or update subscription information.'));
        }
        if ($entity->isNew()) {
            $entity->set('created_by', $uac->getId());
        }
        $entity->set('modified_by', $uac->getId());
    }

    /**
     * Fields property and property_id are fixed.
     *
     * @param \Cake\Event\Event $event the event
     * @param \ArrayObject $data data
     * @param \ArrayObject $options options
     * @return void
     */
    public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options): void
    {
        $data['property'] = $this->getProperty();

View on GitHub (pinned to 31c1bbc10f)