| @@ -18,8 +18,13 @@ | ||
| 18 | 18 | */ |
| 19 | 19 | class CustomerController extends Controller |
| 20 | 20 | { |
| 21 | 21 | /** |
| 22 | + * Maximum number of customers accepted by a single bulk-delete request. | |
| 23 | + */ | |
| 24 | + const BULK_DELETE_LIMIT = 100; | |
| 25 | + | |
| 26 | + /** | |
| 22 | 27 | * index method will return the list of customers |
| 23 | 28 | * @param Request $request |
| 24 | 29 | * @param Customer $customer |
| 25 | 30 | * @return array |
| @@ -222,10 +227,14 @@ | ||
| 222 | 227 | $customerIds = array_filter($customerIds, function ($id) { |
| 223 | 228 | return $id > 0; |
| 224 | 229 | }); |
| 225 | 230 | |
| 231 | + $customerIds = array_values(array_unique($customerIds)); | |
| 232 | + | |
| 233 | + // Each id fans out into a full cascade delete (tickets, conversations, | |
| 234 | + // attachments), so an unbounded batch means an unbounded request. | |
| 226 | 235 | $this->validate(['customer_ids' => $customerIds], [ |
| 227 | - 'customer_ids' => 'required|array|min:1', | |
| 236 | + 'customer_ids' => 'required|array|min:1|max:' . self::BULK_DELETE_LIMIT, | |
| 228 | 237 | 'customer_ids.*' => 'required|integer|exists:fs_persons,id' |
| 229 | 238 | ]); |
| 230 | 239 | |
| 231 | 240 | return $customer->bulkDeleteCustomers($customerIds); |
| @@ -252,15 +261,19 @@ | ||
| 252 | 261 | |
| 253 | 262 | /** |
| 254 | 263 | * resetAvatar method will restore a customer avatar |
| 255 | 264 | * For a successful upload it's required to send file object, customer id and the user type(customer) |
| 256 | - * @param Request $request | |
| 257 | - * @param $id | |
| 265 | + * | |
| 266 | + * No Customer type-hint here: route-model binding resolves inside the | |
| 267 | + * permission callback, before any policy runs, which lets unauthenticated | |
| 268 | + * callers probe customer ID existence (FS-PERM-001). Resolve after auth. | |
| 269 | + * @param int|string $customer | |
| 258 | 270 | * @return array |
| 259 | 271 | */ |
| 260 | - public function resetAvatar(Customer $customer) | |
| 272 | + public function resetAvatar($customer) | |
| 261 | 273 | { |
| 262 | 274 | try { |
| 275 | + $customer = Customer::findOrFail((int) $customer); | |
| 263 | 276 | $customer->restoreAvatar(); |
| 264 | 277 | |
| 265 | 278 | return [ |
| 266 | 279 | 'message' => __('Customer avatar reset to gravatar default', 'fluent-support'), |
| @@ -276,10 +289,10 @@ | ||
| 276 | 289 | { |
| 277 | 290 | $search = trim($request->getSafe('search', 'sanitize_text_field')); |
| 278 | 291 | |
| 279 | 292 | // '*' is a WP_User_Query wildcard and survives sanitize_text_field, so a |
| 280 | - // lone '*' would list every user on the site (FS-SEC-014). Stripping it | |
| 281 | - // leaves WP_User_Query doing an exact match. | |
| 293 | + // lone '*' would list every user on the site. Stripping it leaves | |
| 294 | + // WP_User_Query doing an exact match. | |
| 282 | 295 | $search = trim(str_replace('*', '', $search)); |
| 283 | 296 | |
| 284 | 297 | if (!$search) { |
| 285 | 298 | return $this->sendError([ |