Unix file permissions
can take many shapes:
symbolic (ug+rw
), octal (660
) or a list of characters (drw-rw----
).
This library enables using any of these (instead of being
limited to a single one) with any Node.js or
CLI command.
This library can also perform operations on Unix permissions such as:
- testing,
setting and
unsetting. Using bitwise operations (
|
,&
,^
,~
) can be tedious and error-prone otherwise. - validating syntax.
- normalizing. For example
u+r,u+w
can be shortened tou+rw
. - inverting. For example a
umask
of117
means new files will be created with661
permissions. - checking the minimal or maximal permissions among a list of them. This can be useful to aggregate all the permissions of several files, e.g. during a directory recursion.
Permissions are manipulated as strings, not as file paths. This means you must
use other utilities (such as
chmod
or
stat
) to get and set file permissions
using those strings.
// Retrieve a file's permission as an object like
// `{ user: { write: false, read: true, ... }, ... }` instead of a number
convert.object(fs.statSync('/etc/passwd').mode)
// Set a file's permission using `symbolic` notation instead of a number
fs.chmod('/etc/passwd', convert.number('a=r'))
// Set a file's permission using `symbolic` notation instead of a number
fs.writeFile('/my/file', content, { mode: convert.number('a=r') })
// Disallow executing new files using `umask`
process.umask(convert.number(invert('a-x')))
// If your library takes Unix permissions as input, using
// `unix-permissions` under the hood lets your users choose their
// favorite Unix permissions type.
myLibrary.method({ mode: 'a-wx' })
myLibrary.method({ mode: '444' })
$ stat -c "%a" /etc/passwd
644
$ unix-permissions convert.symbolic "$(stat -c "%a" /etc/passwd)"
u=rw,go=r
$ npm install unix-permissions
const { convert } = require('unix-permissions')
// `permission` will be set to `rw-rw----`
const permission = convert.stat('660')
Several methods other than convert
are available but they mostly follow the
same pattern.
Permission strings are passed as input and returned as output.
$ unix-permissions convert.stat 660
rw-rw----
The same methods as in JavaScript are available. Exit code will be 1
if an
error occurred, e.g. if the permission syntax is invalid.
You can use any of the following permission types as input. You can also
convert()
between them.
Permission type used by chmod
.
Octal string where each digit represents a user class: user
, group
and
others
. Each digit's is a bitfield
representing read
, write
and execute
. Special permissions
(setuid
,
setgid
and
sticky
) can optionally be
specified by prepending another digit.
An operator can be prepended:
=
(default): unset omitted permissions+
: leave omitted permissions as is-
: unset specified permissions
convert.stat('720') // 'rwx-w----'
convert.stat('7000') // '--S--S--T'
convert.stat('\\720') // 'rwx-w----'
convert.stat('0720') // 'rwx-w----'
convert.stat('0o720') // 'rwx-w----'
convert.symbolic('+720') // 'u+rwx,g+w'
convert.symbolic('-720') // 'u-rwx,g-w'
convert.symbolic('=720') // 'u=rwx,g=w,o='
Permission type used by Node.js
fs.chmod()
.
It is the same as octal
except:
- as a decimal number.
- no operator can be used.
- it can be used as input in JavaScript but not on the
command line, where all numbers should be in
octal
form instead.
convert.stat(0) // '---------'
convert.stat(1) // '--------x'
convert.stat(3) // '-------wx'
convert.stat(8) // '-----x---'
convert.stat(512) // '--------T'
Permission type used by stat
and
ls
.
It is a string where each character represents either the permission (r
, w
,
x
) or no permission (-
). The special permission are indicated with S
,
s
, T
and t
where lowercase implies x
is also present.
Optionally a first character can be specified to indicate the file type (e.g.
d
for directories).
convert.octal('--------x') // '0001'
convert.octal('--x--x--x') // '0111'
convert.octal('--------T') // '1000'
convert.octal('--------t') // '1001'
convert.octal('d--------x') // '0001'
convert.octal('--x --x --x') // '0111'
convert.octal('rwx --- ---') // '0700'
convert.octal('xwr --- ---') // '0700'
Permission type used by chmod
as a
string like gu+rx
.
Starts with the user class (a
for all, u
for user, g
for group, o
for
others) then the operator (+
, -
or =
) and ends with the permissions
characters.
While +
leaves the omitted permissions as is, =
unsets them. For example
o=x
is the same as combining o+x
and o-rwt
.
Several groups can be specified using a comma-separated list like g+x,o+r
.
User classes can be concatenated like go+x
.
convert.octal('o+wx') // '+0003'
convert.octal('o=wx') // '0003'
convert.octal('o-wx') // '-0003'
convert.octal('go+x') // '+0011'
convert.octal('g+x,o+x') // '+0011'
convert.octal('a+x') // '+0111'
convert.octal('+x') // '+0111'
convert.octal('a+s') // '+6000'
convert.octal('o+') // '+0000'
Permission type as an object such as { user: { read: true, write: false } }
.
The full syntax is:
{
"user": { "read": true, "write": true, "execute": true },
"group": { "read": true, "write": true, "execute": true },
"others": { "read": true, "write": true, "execute": true },
"special": { "setuid": true, "setgid": true, "sticky": true }
}
The values can be true
, false
or undefined
. undefined
leaves
permissions as is while false
unsets them.
convert.symbolic({ others: { read: true, execute: true } }) // 'o+rx'
convert.symbolic({ others: { read: true, execute: false } }) // 'o+r,o-x'
convert.symbolic({ others: { read: true, execute: undefined } }) // 'o+r'
convert.symbolic({ all: { read: true } }) // 'a+r'
convert.symbolic({}) // 'a+'
convert.symbolic({ special: { setuid: true, setgid: true, sticky: true } })
// 'ug+s,o+t'
$ unix-permissions convert.symbolic '{ "all": { "read": true } }'
a+r
Returns permission
converted to another type
.
Note that symbolic
and object
distinguish between:
- leaving permissions as is (omitting them or using
undefined
) - unsetting them (using
-
orfalse
).
number
and stat
do not
make this distinction. If you convert between them, you might lose this
information as we assume 0
in number
and -
in
stat
mean "unset permissions". However you can use
positive()
to overcome this issue.
convert.symbolic('111') // 'a=x'
positive(convert.symbolic('111')) // 'a+x'
convert.octal('o+x') // '+0001'
convert.octal('o=x') // '0001'
Returns the permission
's type
or 'invalid'
.
type('1') // 'octal'
type(1) // 'number'
type(0.5) // 'invalid'
type('a+x') // 'symbolic'
Normalize a permission
to its canonical shape.
Throw an exception if permission
is invalid.
normalize('1') // '0001'
normalize('g+x,o+x') // 'go+x'
normalize('d--- --- ---') // '---------'
normalize({ user: { read: undefined, write: true } })
// { user: { write: true } }
normalize('z+x') // Throws an exception
Remove all negative permissions. See convert() for more explanation.
positive('o+x,o-rw') // 'o+x'
positive('o=x') // 'o+x'
positive('660') // '+0660'
invert('660') // '0117'
invert(positive('660')) // '-0660'
Tests whether permission
includes permissions
.
Returns true
or false
or (on the CLI) use the exit code 0
or 1
.
contain('--x--x--x', 'a=x') // `true`
contain('--x--x--x', 'a+x') // `true`
contain('--x--x--x', 'a-x') // `false`
contain('--x--x--x', 'a-w') // `true`
contain('o+x', 'o+x') // `true`
contain('o+x', 'o+x,o+x') // `true`
contain('o+x', 'o=w') // `false`
contain('o+x,o-w', 'o-w,o+x') // `true`
contain('o+x,o-w', 'o-w') // `true`
contain('o+x,o-w', 'o+x', 'o-w') // `true`
Tests whether permission
equals exactly permissions
.
Returns true
or false
or (on the CLI) use the exit code 0
or 1
.
equal('--x--x--x', 'a=x') // `true`
equal('--x--x--x', 'a+x') // `false`
equal('--x--x--x', 'a-x') // `false`
equal('--x--x--x', 'a-w') // `false`
equal('o+x', 'o+x') // `true`
equal('o+x', 'o+x,o+x') // `true`
equal('o+x', 'o=w') // `false`
equal('o+x,o-w', 'o-w,o+x') // `true`
equal('o+x,o-w', 'o-w') // `false`
equal('o+x,o-w', 'o+x', 'o-w') // `false`
Returns the result of setting permissions
on permission
.
This is useful to avoid error-prone bitwise operations (|
, &
, ^
, ~
).
This can also be used to remove special permissions using
set(permission, 'a-st')
since some functions like
umask
do not allow them.
set('---------', 'a+x') // '--x--x--x'
set('---------', 'a+x', 'a+r') // 'r-xr-xr-x'
set('--x--x--x', 'o-x') // '--x--x---'
set('a+x', 'a+r') // 'a+rx'
set('4660', 'a-st') // '0660'
Inverts permission
including special permissions.
This can be used in combination with set()
to unset permissions
instead of
setting them.
not('u+xs') // 'u-xs'
not('u-xs') // 'u+xs'
not('u=x') // 'u=rws'
not('a=x') // 'ug=rws,o=rwt'
not('rws-ws-w-') // '---r--r-t'
not('0660') // '7117'
not('1660') // '6117'
set('rwxrwxrwx', not('a+x')) // 'rw-rw-rw-'
set('---------', not('a-x')) // '--x--x--x'
set('a+xr', not('a+r')) // 'a+x,a-r'
Inverts permission
and removes special permissions.
For example a umask
of 117
means new
files will be created with 661
permissions.
invert('u+xs') // 'u-x'
invert('u-xs') // 'u+x'
invert('u=x') // 'u+rw,u-x'
invert('a=x') // 'a+rw,a-x'
invert('rws-ws-w-') // '---r--r-x'
invert('0660') // '0117'
invert('1660') // '0117'
Retrieve the lowest permissions among all arguments.
This does not return the lowest argument. Instead it returns a combination of the lowest bits of all arguments.
This can be useful if you are looking for the lowest permission of a several files, e.g. during a directory recursion.
min('404', '440', '402') // '0400'
Inverse of min()
.
max('404', '440', '402') // '0446'