urn-schema
v0.2.1
Published
URN schema validation
Downloads
592
Readme
URN Schema
This library handles URN schemas, similar to AWS ARN's, useful for access control.
Features:
- [x] Variable interpolation
urn:${some.dataset}
- [x] Wildcarding
urn:*
- [x] Precompilation for performance
- [x] Uri validation
urn:this/${is.a}/*/uri?with&a&query
- [x] ACL's
- [x] Conforms to URN
import UrnSchema, { UriValidator } from 'urn-schema'
const schema = new UrnSchema('version:method:scope:uri', {
uri: UriValidator,
})
const acl = schema.createAcl({
group_a: [
"urn:1.0:POST:testing:products/*/items/*"
]
})
acl.validate('group_a', {
version : '1.0',
method : 'POST',
scope : 'testing',
uri : 'products/22/items'
}) // returns { valid: true, group: 'group_a' }
acl.validate('group_a', {
version : '2.0',
method : 'GET',
scope : 'testing',
uri : 'products/22/items'
}) // returns { valid: false, group: 'group_a' }
In the basic example above we have defined a schema which matches predefined properties, including parsing the uri
appropriately.
Below is a more advanced example.
const acl = schema.createAcl({
group_a: [
"urn:${versions}:GET:${scopes}:products/${user['!~validIds~!']}/*?direction&order"
]
})
const data = {
versions: [ '1.0', '2.0' ],
scopes: [ 'testing', 'staging' ],
user: {
"!~validIds~!": [ 22, 24, 77 ]
}
}
acl.validate('group_a', {
version : '2.0',
method : 'GET',
scope : 'testing',
uri : 'products/22/items?order=size'
}, data) // returns { valid: true, group: 'group_a' }
In the above example the variables defined in group_a
's first urn are interpolated from the data
object.
These uri's would also pass:
products/24/
products/27/something?direction=asc
And so too would version 1.0
and scope staging
.
It's easy to parse a full URL into something you can use against the schema.
Imagine the below originalUrl
is /2.0/testing/products/22
.
app.use((req, res, next) => {
const { method, originalUrl, auth } = req
// Take off the first 2 parts
let [ version, scope, ...uri ] = originalUrl
.replace(/^\/|\/$/g, '') // Remove trailing & leading slashes
.split('/')
uri = uri.join('/') // Join it back up
const aclCheck = acl.validate(auth.scope, {
version, scope, method, uri
})
if ( aclCheck.valid )
return next()
return next( new ForbiddenError("Permission denied!!!") )
})
Interpolation Mechanics
When a variable is interpolated, the type matters.
- If
${some.data}
resolves to an array[1, 2]
, then the value can match either of them, as a string comparison - If
${some.data}
resolves to any other type, it will be stringified and compared against the value