luserx0 / unix-permissions

Swiss Army knife for Unix permissions

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

downloads last commit license Coverage Status travis npm node JavaScript Style Guide eslint-config-standard-prettier-fp Gitter

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 to u+rw.
  • inverting. For example a umask of 117 means new files will be created with 661 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.

Examples (JavaScript)

// 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' })

Examples (CLI)

$ stat -c "%a" /etc/passwd
644
$ unix-permissions convert.symbolic "$(stat -c "%a" /etc/passwd)"
u=rw,go=r

Installation

$ npm install unix-permissions

Usage (JavaScript)

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.

Usage (CLI)

$ 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.

Types

You can use any of the following permission types as input. You can also convert() between them.

octal

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='

number

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'

stat

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'

symbolic

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'

object

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

Methods

convert.octal(permission)

convert.number(permission)

convert.stat(permission)

convert.symbolic(permission)

convert.object(permission)

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 - or false).

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'

type(permission)

Returns the permission's type or 'invalid'.

type('1') // 'octal'
type(1) // 'number'
type(0.5) // 'invalid'
type('a+x') // 'symbolic'

normalize(permission)

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

positive(permission)

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'

contain(permission, permissions...)

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`

equal(permission, permissions...)

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`

set(permission, permissions...)

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'

not(permission)

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'

invert(permission)

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'

min(permissions...)

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'

max(permissions...)

Inverse of min().

max('404', '440', '402') // '0446'

About

Swiss Army knife for Unix permissions

License:Apache License 2.0


Languages

Language:JavaScript 100.0%