A permissions library for Clojure(Script), inspired by agynamix/permissions.
Library is still undergoing development, so expect frequent changes to the API.
The core API lives in can.can
(not can.core
) and is recommended
to be aliased as can
, i.e., (require '[can.can :as can])
.
Permissions is a map of sets, where the key is the domain, and the
values the rights applicable to that domain. can.can/allow?
takes
the permissions map and an domain/action string to determine whether
the user is allowed to perform the said action:
(def permissions {:support #{:create-ticket :update-ticket}
:printer #{:print}})
(print (can/allow? permissions "support:create-ticket")) ;; outputs "true"
(print (can/allow? permissions "print:clear-spool")) ;; outputs "false"
You could also use :*
to grant permissions:
(def super-support {:support #{:*}})
(print (can/allow? super-support "support:launch-nuclear")) ;; outputs "true"
(print (can/allow? super-support "print:clear-spool")) ;; outputs "false"
And even superuser:
(def superuser {:* #{:*}})
(print (can/allow? superuser "hello:world")) ;; outputs "true"
Unlike permissions, actions cannot use *
, i.e., doing
(can/allow? permissions "*:create-ticket")
or
(can/allow? permissions "admin:*")
will always return false
.
Breaking change in 0.3.0: bitmask-related functions reside in can.bitmask
now, and they are renamed as decode and encode.
can.bitmask/decode
takes the full permissions setup (i.e.,
all available domains and actions available in your application) and
converts a map of domain-bitmask pair into a permissions map.
(def all-permissions {:admin [:create :read :update :delete :approve :reject]
:support [:create-ticket :update-ticket :close-ticket]
:printer [:print :clear-spool]})
(def alice-permissions
(can/decode all-permissions {:admin 7 :printer 1}))
(print alice-permissions) ;; outputs {:admin #{:create :read :update}
;; :printer #{:print}}
Note that the order of the available actions in each domain matters, i.e., new actions should be appended to the end of the action list.
can.bitmask/decode
makes it trivial to implement
access control list/matrix in your application, e.g., the relational
database could have a table that looks like the following:
userid | admin | support | printer |
---|---|---|---|
alice | 7 | 0 | 1 |
bob | 0 | 7 | 2 |
Note that when using bitmasks permissions, the total number of actions per domain cannot exceed 63 on Clojure, and cannot exceed 52 on ClojureScript.
can.bitmask/encode
converts the permissions map into a map of
domain-bitmask pair (somewhat the inverse of can.bitmask/decode
),
making it easy to persist changes back to the datastore:
(print (encode all-permissions alice-permissions)) ;; outputs {:admin 7
;; :support 0
;; :printer 1}
Copyright © 2019 Shaolang Ai
This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.