@stonyx/orm 0.3.2-alpha.94 → 0.3.2-alpha.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -323,15 +323,22 @@ export default class OwnerAccess {
|
|
|
323
323
|
|
|
324
324
|
access(request) {
|
|
325
325
|
// `access` runs after route matching, so `request.params` is populated and
|
|
326
|
-
// `id` has already been URL-decoded. Authorize on it.
|
|
326
|
+
// `id` has already been URL-decoded. Authorize on it, never on a URL.
|
|
327
327
|
const { id } = request.params;
|
|
328
328
|
|
|
329
|
+
// `id` is still raw client text. Normalise it the way the record lookup
|
|
330
|
+
// does, or your predicate and the lookup disagree — see "Numeric ids" below.
|
|
331
|
+
// No radix on parseInt: that is deliberate, and it must stay that way.
|
|
332
|
+
const recordId = id === undefined ? undefined : (isNaN(id) ? id : parseInt(id));
|
|
333
|
+
|
|
329
334
|
// Returning false explicitly denies access to this record
|
|
330
|
-
if (
|
|
335
|
+
if (recordId === 'angela') return false;
|
|
331
336
|
|
|
332
337
|
// No `id` means the collection route. Returning a function plugs it in to
|
|
333
|
-
// the response object as a filter
|
|
334
|
-
|
|
338
|
+
// the response object as a filter. NOTE: a function return authorizes the
|
|
339
|
+
// request outright — the operations list below is not consulted — so this
|
|
340
|
+
// branch permits POST /owners as well as reads.
|
|
341
|
+
if (recordId === undefined) return record => record.id !== 'angela';
|
|
335
342
|
|
|
336
343
|
// Returning a list of operations allows full access to everything else
|
|
337
344
|
return ['read', 'create', 'update', 'delete'];
|
|
@@ -355,6 +362,41 @@ form that is safe to match on — `request.baseUrl` is the mount text as the
|
|
|
355
362
|
client spelled it (`/OWNERS`), not the model. A class may still list several
|
|
356
363
|
models in `models` when they share one rule.
|
|
357
364
|
|
|
365
|
+
**Numeric ids: normalise before you compare.** `request.params.id` is raw text
|
|
366
|
+
from the client. When it looks numeric the ORM coerces it — `isNaN(id) ? id :
|
|
367
|
+
parseInt(id)` — *before* it resolves the record, so `7`, `007`, `7.0`, `7.9`,
|
|
368
|
+
`7e0`, `0x7`, `+7`, `%207` (a leading space), `%09 7` (a tab) and `7%0A` (a
|
|
369
|
+
trailing newline) all address record `7`, while a `===` against the raw text
|
|
370
|
+
matches only the one spelling you wrote down. Every other spelling falls through
|
|
371
|
+
to whatever your method returns next — which, in the shape above, is a full CRUD
|
|
372
|
+
grant. All of them are plain address-bar requests.
|
|
373
|
+
|
|
374
|
+
Two details are load-bearing. `parseInt` is called with **no radix**, so `0x7`
|
|
375
|
+
is `7` and not `0`; writing `parseInt(id, 10)` in your predicate re-opens the
|
|
376
|
+
hex spelling. And the coercion applies only when the id looks numeric, so a
|
|
377
|
+
model with string ids (like `owner` above) is unaffected — which is exactly why
|
|
378
|
+
this is easy to miss. Normalise the same way the lookup does:
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
export default class AnimalAccess {
|
|
382
|
+
models = ['animal'];
|
|
383
|
+
|
|
384
|
+
access(request) {
|
|
385
|
+
const { id } = request.params;
|
|
386
|
+
|
|
387
|
+
// Agrees with the lookup for every spelling of 7 above. Compare the
|
|
388
|
+
// coerced value, which for a numeric-id model is a number, not a string.
|
|
389
|
+
const recordId = id === undefined ? undefined : (isNaN(id) ? id : parseInt(id));
|
|
390
|
+
|
|
391
|
+
if (recordId === 7) return false;
|
|
392
|
+
|
|
393
|
+
if (recordId === undefined) return record => record.id !== 7;
|
|
394
|
+
|
|
395
|
+
return ['read', 'create', 'update', 'delete'];
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
358
400
|
The sample above is executed verbatim against a live server by
|
|
359
401
|
`test/integration/readme-access/`, which reads it out of this file: the request
|
|
360
402
|
`DELETE /owners/angela` is asserted to return 403 with the record intact, along
|