refusal = ''; if ( ! is_object( $user ) || ! isset( $user->ID ) ) { return; } $uuid = is_array( $item ) && isset( $item['uuid'] ) ? (string) $item['uuid'] : ''; if ( '' === $uuid ) { return; } $agent = $this->agents->find_by_app_password_uuid( $uuid ); if ( null === $agent ) { return; } // The bind endpoint only offers the linked user's own passwords, so // a mismatch means the link moved afterward. Refused rather than // ignored: running the request as an agent whose capabilities the // authenticated user never had is the one outcome worth a 401. if ( $agent->wp_user_id !== (int) $user->ID ) { $this->refusal = 'mismatch'; return; } // An administrator-run agent is bound only by an extension // (AF-25.15): a password that reached its row anyway is refused, not // left to run as untracked automation with administrator rights. if ( $agent->runs_as_admin() ) { $this->refusal = 'admin_agent'; return; } // Disabling an agent has to stop it. Falling through here would demote // it to untracked automation, which keeps writing and only leaves a // ledger note - the opposite of what disabling means. if ( 'active' !== $agent->status ) { $this->refusal = 'inactive'; return; } // A token presented alongside the password already won: the context // may only be established once, and the token is the stronger claim. if ( AgentContext::is_agent_request() ) { return; } AgentContext::establish( $agent ); } /** * Report a refused binding as a 401. * * @param \WP_Error|true|null $result Result from earlier handlers. * @return \WP_Error|true|null */ public function rest_authentication_errors( $result ) { if ( null !== $result ) { return $result; } if ( 'inactive' === $this->refusal ) { return new \WP_Error( 'adaf_agent_not_active', __( 'The agent bound to this application password is disabled or revoked. Retrying will not help; ask a site administrator to re-enable the agent or unbind the password.', 'agentic-daisy-ai-agent-firewall' ), array( 'status' => 401, 'reason' => 'agent_not_active', 'retryable' => false, ) ); } if ( 'admin_agent' === $this->refusal ) { return new \WP_Error( 'adaf_admin_agent_no_credential', __( 'This application password is bound to an agent that runs as an administrator, which only an add-on may bind. Retrying will not help; ask a site administrator to unbind the password.', 'agentic-daisy-ai-agent-firewall' ), array( 'status' => 401, 'reason' => 'admin_agent_no_credential', 'retryable' => false, ) ); } if ( 'mismatch' === $this->refusal ) { return new \WP_Error( 'adaf_binding_mismatch', __( 'This application password is bound to an agent linked to a different WordPress user. Retrying will not help; ask a site administrator to rebind it.', 'agentic-daisy-ai-agent-firewall' ), array( 'status' => 401, 'reason' => 'binding_mismatch', 'retryable' => false, ) ); } return $result; } }