Class: Parse::ACL
Overview
An ACL represents the dirty-trackable Parse Permissions object used for each record. In Parse, it is composed a hash-like object that represent User objectIds and/or a set of Role names. For each entity (ex. User/Role/Public), you can define read/write privileges on a particular record through a Permission instance.
If you want to give privileges for an action (ex. read/write), you set that particular permission it to true. If you want to deny a permission, then you set it to false. Any denied permissions will not be part of the final hash structure that is sent to parse, as omission of a permission means denial.
An ACL is represented by a JSON object with the keys being Parse::User object ids or the special key of "", which indicates the public access permissions. The value of each key in the hash is a Permission object which defines the boolean permission state for read and write. The example below illustrates a Parse ACL JSON object where there is a *public read permission, but public write is prevented. In addition, the user with id "3KmCvT7Zsb" is allowed to both read and write this record, and the "Admins" role is also allowed write access.
{ "*": { "read": true }, "3KmCvT7Zsb": { "read": true, "write": true }, "role:Admins": { "write": true } }
All Parse::Object subclasses have an acl property by default. With this property, you can apply and delete permissions for this particular Parse object record. user = Parse::User.first artist = Artist.first
artist.acl # "*": { "read": true, "write": true }
# apply public read, but no public write artist.acl.everyone true, false
# allow user to have read and write access artist.acl.apply user.id, true, true
# remove all permissions for this user id artist.acl.delete user.id
# allow the 'Admins' role read and write artist.acl.apply_role "Admins", true, true
# remove write from all attached privileges artist.acl.no_write!
# remove all attached privileges artist.acl.master_key_only!
artist.save
You may also set default ACLs for your subclasses by using Object.set_default_acl.
These will get applied for newly created instances. Unless overridden, subclasses
inherit the shipped :owner_else_private policy — records are private
(master-only) until an owner is resolved at save time. Use Object.set_default_acl
or Object.acl_policy to grant broader access.
class AdminData < Parse::Object
# Disable public read and write set_default_acl :public, read: true, write: false
# Allow Admin roles to read/write set_default_acl 'Admin', role: true, read: true, write: true
end
data = AdminData.new data.acl # => ACL("write"=>true})
For more information about Parse record ACLs, see the documentation on Security.
Defined Under Namespace
Classes: Permission
Constant Summary collapse
- PUBLIC =
The key field value for public permissions.
"*".freeze
Instance Attribute Summary collapse
-
#delegate ⇒ Object
The instance object to be notified of changes.
-
#permissions ⇒ Hash
Contains a hash structure of permissions, with keys mapping to either Public '*', a role name or an objectId for a user and values of type Permission.
Class Method Summary collapse
-
.everyone(read = true, write = true) ⇒ Object
Create a new ACL with default Public read/write permissions and any overrides from the input hash format.
-
.permission(read, write = nil) ⇒ ACL::Permission
Create a new ACL::Permission instance with the supplied read and write values.
-
.private ⇒ Parse::ACL
Create a new private ACL with no public access.
-
.read_predicate(permissions, include_public: true) ⇒ Hash
Build a MongoDB +$match+-shaped predicate that matches documents readable by any of +permissions+.
-
.typecast(value, delegate = nil) ⇒ ACL
Used for object conversion when formatting the input/output value in Parse::Object properties.
-
.write_predicate(permissions, include_public: true) ⇒ Hash
Build a MongoDB +$match+-shaped predicate that matches documents writable by any of +permissions+.
Instance Method Summary collapse
-
#==(other_acl) ⇒ Boolean
Determines whether two ACLs or a Parse-ACL hash is equivalent to this object.
-
#all_read! ⇒ Array
Grants read permission on all existing users and roles attached to this object.
-
#all_write! ⇒ Array
Grants write permission on all existing users and roles attached to this object.
-
#apply(id, read = nil, write = nil) ⇒ Hash
(also: #add)
Apply a new permission with a given objectId, tag or :public.
-
#apply_role(name, read = nil, write = nil) ⇒ Object
(also: #add_role)
Apply a Role to this ACL.
- #as_json(*args) ⇒ Hash
-
#attributes ⇒ Hash
Used for JSON serialization.
-
#delete(id) ⇒ Object
Removes a permission for an objectId or user.
-
#empty? ⇒ Boolean
(also: #master_key_only?, #master_only?)
Checks if the ACL has no permissions (master key only access).
-
#everyone(read, write) ⇒ Hash
(also: #world)
Set the public read and write permissions.
-
#initialize(acls = nil, owner: nil) ⇒ ACL
constructor
Create a new ACL with default Public read/write permissions and any overrides from the input hash format.
-
#master_key_only! ⇒ Hash
(also: #clear!)
Removes all ACLs, which only allows requests using the Parse Server master key to query and modify the object.
-
#no_read! ⇒ Array
Denies read permission on all existing users and roles attached to this object.
-
#no_read? ⇒ Boolean
Checks if the object has no read permissions for anyone (master key only).
-
#no_write! ⇒ Array
Denies write permission on all existing users and roles attached to this object.
-
#no_write? ⇒ Boolean
Checks if the object has no write permissions for anyone (master key only).
-
#owner?(user_or_role) ⇒ Boolean
Checks if a specific user or role (or any in an array) has both read and write access to this object.
-
#owners ⇒ Array<String>
Returns an array of all user IDs and role names that have both read and write access.
-
#present? ⇒ Boolean
True if there are any permissions.
-
#public_read? ⇒ Boolean
Checks if the object has public read access.
-
#public_write? ⇒ Boolean
Checks if the object has public write access.
-
#read_only? ⇒ Boolean
Checks if the object is read-only (has read permissions but no write permissions).
-
#readable_by ⇒ Array<String>
Returns an array of all user IDs and role names that have read access to this object.
-
#readable_by?(user_or_role) ⇒ Boolean
(also: #can_read?)
Checks if a specific user or role (or any in an array) has read access to this object.
-
#will_change! ⇒ Object
Calls
acl_will_change!on the delegate when the permissions have changed. -
#write_only? ⇒ Boolean
Checks if the object is write-only (has write permissions but no read permissions).
-
#writeable_by ⇒ Array<String>
(also: #writable_by)
Returns an array of all user IDs and role names that have write access to this object.
-
#writeable_by?(user_or_role) ⇒ Boolean
(also: #writable_by?, #can_write?)
Checks if a specific user or role (or any in an array) has write access to this object.
Constructor Details
#initialize(acls = nil, owner: nil) ⇒ ACL
Create a new ACL with default Public read/write permissions and any overrides from the input hash format.
144 145 146 147 148 |
# File 'lib/parse/model/acl.rb', line 144 def initialize(acls = nil, owner: nil) acls = acls.as_json if acls.is_a?(ACL) self.attributes = acls if acls.is_a?(Hash) @delegate = owner end |
Instance Attribute Details
#delegate ⇒ Object
The instance object to be notified of changes. The delegate must support receiving a Object#acl_will_change! method.
120 |
# File 'lib/parse/model/acl.rb', line 120 attr_writer :permissions |
#permissions ⇒ Hash
Contains a hash structure of permissions, with keys mapping to either Public '*', a role name or an objectId for a user and values of type Permission. If you modify this attribute directly, you should call Object#acl_will_change! on the target object in order for dirty tracking to register changes.
132 133 134 |
# File 'lib/parse/model/acl.rb', line 132 def @permissions ||= {} end |
Class Method Details
.everyone(read = true, write = true) ⇒ Object
Create a new ACL with default Public read/write permissions and any overrides from the input hash format.
155 156 157 158 159 |
# File 'lib/parse/model/acl.rb', line 155 def self.everyone(read = true, write = true) acl = Parse::ACL.new acl.everyone(read, write) acl end |
.permission(read, write = nil) ⇒ ACL::Permission
Create a new ACL::Permission instance with the supplied read and write values.
177 178 179 |
# File 'lib/parse/model/acl.rb', line 177 def self.(read, write = nil) ACL::Permission.new(read, write) end |
.private ⇒ Parse::ACL
Create a new private ACL with no public access. Objects with this ACL can only be accessed with the master key.
168 169 170 |
# File 'lib/parse/model/acl.rb', line 168 def self.private Parse::ACL.new end |
.read_predicate(permissions, include_public: true) ⇒ Hash
Build a MongoDB +$match+-shaped predicate that matches documents readable by any of +permissions+. The canonical shape Parse Server enforces at the REST/SDK layer:
{ "$or" => [ { "_rperm" => { "$in" => permissions } }, { "_rperm" => { "$exists" => false } } ]}
The +$exists: false+ branch is mandatory. Parse Server treats a missing +_rperm+ field as publicly readable (the field is only written when an ACL exists), so dropping that branch silently hides every public document.
Used by:
- Query::ACLReadableByConstraint and Query::ACLReadableByRoleConstraint to compile +:ACL.readable_by+ / +:ACL.readable_by_role+ query constraints into aggregation pipelines.
- Parse::AtlasSearch to enforce ACL on +$search+ pipelines that bypass Parse Server and query MongoDB directly.
214 215 216 |
# File 'lib/parse/model/acl.rb', line 214 def self.read_predicate(, include_public: true) ("_rperm", , include_public: include_public) end |
.typecast(value, delegate = nil) ⇒ ACL
Used for object conversion when formatting the input/output value in Parse::Object properties
361 362 363 |
# File 'lib/parse/model/acl.rb', line 361 def self.typecast(value, delegate = nil) ACL.new(value, owner: delegate) end |
.write_predicate(permissions, include_public: true) ⇒ Hash
Build a MongoDB +$match+-shaped predicate that matches documents writable by any of +permissions+. Mirrors read_predicate on the +_wperm+ field. See read_predicate for the shape and the significance of the +$exists: false+ branch.
225 226 227 |
# File 'lib/parse/model/acl.rb', line 225 def self.write_predicate(, include_public: true) ("_wperm", , include_public: include_public) end |
Instance Method Details
#==(other_acl) ⇒ Boolean
Determines whether two ACLs or a Parse-ACL hash is equivalent to this object.
262 263 264 265 |
# File 'lib/parse/model/acl.rb', line 262 def ==(other_acl) return false unless other_acl.is_a?(self.class) || other_acl.is_a?(Hash) as_json == other_acl.as_json end |
#all_read! ⇒ Array
Grants read permission on all existing users and roles attached to this object.
434 435 436 437 438 439 |
# File 'lib/parse/model/acl.rb', line 434 def all_read! will_change! .keys.each do |perm| [perm].read! true end end |
#all_write! ⇒ Array
Grants write permission on all existing users and roles attached to this object.
456 457 458 459 460 461 |
# File 'lib/parse/model/acl.rb', line 456 def all_write! will_change! .keys.each do |perm| [perm].write! true end end |
#apply(user, read = nil, write = nil) ⇒ Hash #apply(role, read = nil, write = nil) ⇒ Hash #apply(id, read = nil, write = nil) ⇒ Hash Also known as: add
Apply a new permission with a given objectId, tag or :public.
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/parse/model/acl.rb', line 316 def apply(id, read = nil, write = nil) return apply_role(id, read, write) if id.is_a?(Parse::Role) id = id.id if id.is_a?(Parse::Pointer) unless id.present? raise ArgumentError, "Invalid argument applying ACLs: must be either objectId, role or :public" end id = PUBLIC if id.to_sym == :public # create a new Permissions = ACL.(read, write) # if the input is already an Permission object, then set it directly = read if read.is_a?(Parse::ACL::Permission) if .is_a?(ACL::Permission) if [id.to_s] != will_change! # dirty track [id.to_s] = end end end |
#apply_role(role, read = nil, write = nil) ⇒ Object #apply_role(role_name, read = nil, write = nil) ⇒ Object Also known as: add_role
Apply a Role to this ACL.
348 349 350 351 |
# File 'lib/parse/model/acl.rb', line 348 def apply_role(name, read = nil, write = nil) name = name.name if name.is_a?(Parse::Role) apply("role:#{name}", read, write) end |
#as_json(*args) ⇒ Hash
390 391 392 |
# File 'lib/parse/model/acl.rb', line 390 def as_json(*args) .select { |k, v| v.present? }.as_json end |
#attributes ⇒ Hash
Used for JSON serialization. Only if an attribute is non-nil, do we allow it in the Permissions hash, since omission means denial of priviledge. If the permission value has neither read or write, then the entire record has been denied all privileges
370 371 372 |
# File 'lib/parse/model/acl.rb', line 370 def attributes .select { |k, v| v.present? }.as_json end |
#delete(object) ⇒ Object #delete(id) ⇒ Object
Removes a permission for an objectId or user.
289 290 291 292 293 294 295 |
# File 'lib/parse/model/acl.rb', line 289 def delete(id) id = id.id if id.is_a?(Parse::Pointer) if id.present? && .has_key?(id) will_change! .delete(id) end end |
#empty? ⇒ Boolean Also known as: master_key_only?, master_only?
Checks if the ACL has no permissions (master key only access).
812 813 814 |
# File 'lib/parse/model/acl.rb', line 812 def empty? .empty? || .values.none? { |v| v.present? } end |
#everyone(read, write) ⇒ Hash Also known as: world
Set the public read and write permissions.
271 272 273 274 |
# File 'lib/parse/model/acl.rb', line 271 def everyone(read, write) apply(PUBLIC, read, write) [PUBLIC] end |
#master_key_only! ⇒ Hash Also known as: clear!
Removes all ACLs, which only allows requests using the Parse Server master key to query and modify the object.
412 413 414 415 |
# File 'lib/parse/model/acl.rb', line 412 def master_key_only! will_change! @permissions = {} end |
#no_read! ⇒ Array
Denies read permission on all existing users and roles attached to this object.
478 479 480 481 482 483 |
# File 'lib/parse/model/acl.rb', line 478 def no_read! will_change! .keys.each do |perm| [perm].read! false end end |
#no_read? ⇒ Boolean
Checks if the object has no read permissions for anyone (master key only).
699 700 701 |
# File 'lib/parse/model/acl.rb', line 699 def no_read? .values.none? { |v| v.read } end |
#no_write! ⇒ Array
Denies write permission on all existing users and roles attached to this object.
500 501 502 503 504 505 |
# File 'lib/parse/model/acl.rb', line 500 def no_write! will_change! .keys.each do |perm| [perm].write! false end end |
#no_write? ⇒ Boolean
Checks if the object has no write permissions for anyone (master key only).
705 706 707 |
# File 'lib/parse/model/acl.rb', line 705 def no_write? .values.none? { |v| v.write } end |
#owner?(user_or_role) ⇒ Boolean
Checks if a specific user or role (or any in an array) has both read and write access to this object. When passed an array of strings, returns true if ANY of the users/roles have both read and write access (OR logic). When passed a Parse::User object or pointer, automatically fetches and checks the user's roles as well.
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 |
# File 'lib/parse/model/acl.rb', line 738 def owner?(user_or_role) # Handle arrays - check if ANY item in the array is an owner (OR logic) if user_or_role.is_a?(Array) # For arrays, just check each string value directly (no User object expansion) return user_or_role.any? do |item| key = (item) next false unless key perm = [key] perm&.read == true && perm&.write == true end end # Handle Parse::Pointer to User - expand to include user ID and roles if user_or_role.is_a?(Parse::Pointer) || (user_or_role.respond_to?(:parse_class) && user_or_role.respond_to?(:id)) # Check if it's a pointer to a User if user_or_role.respond_to?(:parse_class) && (user_or_role.parse_class == "User" || user_or_role.parse_class == "_User") = [] # Add the user ID from the pointer user_id = user_or_role.respond_to?(:id) ? user_or_role.id : nil << user_id if user_id.present? # Query roles directly using the user pointer (no need to fetch the full user) begin if user_id.present? && defined?(Parse::Role) user_roles = Parse::Role.all(users: user_or_role) user_roles.each do |role| << "role:#{role.name}" if role.respond_to?(:name) && role.name.present? end end rescue # If role fetching fails, continue with just the user ID end # Check if any of the user's permissions (user ID or roles) are owners return owner?() if .any? return false end end # If it's a User object, expand it to include the user ID and all their roles if user_or_role.is_a?(Parse::User) || (user_or_role.respond_to?(:is_a?) && user_or_role.is_a?(Parse::User)) = [] # Add the user ID << user_or_role.id if user_or_role.respond_to?(:id) && user_or_role.id.present? # Fetch and add all the user's roles begin if user_or_role.respond_to?(:id) && user_or_role.id.present? && defined?(Parse::Role) user_roles = Parse::Role.all(users: user_or_role) user_roles.each do |role| << "role:#{role.name}" if role.respond_to?(:name) && role.name.present? end end rescue # If role fetching fails, continue with just the user ID end # Check if any of the user's permissions (user ID or roles) are owners # Use array checking logic (OR) return owner?() if .any? return false end # Single string value - check directly key = (user_or_role) return false unless key perm = [key] perm&.read == true && perm&.write == true end |
#owners ⇒ Array<String>
Returns an array of all user IDs and role names that have both read and write access.
723 724 725 |
# File 'lib/parse/model/acl.rb', line 723 def owners .select { |k, v| v.read && v.write }.keys end |
#present? ⇒ Boolean
Returns true if there are any permissions.
395 396 397 |
# File 'lib/parse/model/acl.rb', line 395 def present? .values.any? { |v| v.present? } end |
#public_read? ⇒ Boolean
Checks if the object has public read access.
687 688 689 |
# File 'lib/parse/model/acl.rb', line 687 def public_read? [PUBLIC]&.read == true end |
#public_write? ⇒ Boolean
Checks if the object has public write access.
693 694 695 |
# File 'lib/parse/model/acl.rb', line 693 def public_write? [PUBLIC]&.write == true end |
#read_only? ⇒ Boolean
Checks if the object is read-only (has read permissions but no write permissions).
711 712 713 |
# File 'lib/parse/model/acl.rb', line 711 def read_only? .values.any? { |v| v.read } && .values.none? { |v| v.write } end |
#readable_by ⇒ Array<String>
Returns an array of all user IDs and role names that have read access to this object.
509 510 511 |
# File 'lib/parse/model/acl.rb', line 509 def readable_by .select { |k, v| v.read }.keys end |
#readable_by?(user_or_role) ⇒ Boolean Also known as: can_read?
Checks if a specific user or role (or any in an array) has read access to this object. When passed an array of strings, returns true if ANY of the users/roles have read access (OR logic). When passed a Parse::User object or pointer, automatically fetches and checks the user's roles as well.
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 |
# File 'lib/parse/model/acl.rb', line 532 def readable_by?(user_or_role) # Handle arrays - check if ANY item in the array has read access (OR logic) if user_or_role.is_a?(Array) # For arrays, just check each string value directly (no User object expansion) return user_or_role.any? do |item| key = (item) key && [key]&.read == true end end # Handle Parse::Pointer to User - expand to include user ID and roles if user_or_role.is_a?(Parse::Pointer) || (user_or_role.respond_to?(:parse_class) && user_or_role.respond_to?(:id)) # Check if it's a pointer to a User if user_or_role.respond_to?(:parse_class) && (user_or_role.parse_class == "User" || user_or_role.parse_class == "_User") = [] # Add the user ID from the pointer user_id = user_or_role.respond_to?(:id) ? user_or_role.id : nil << user_id if user_id.present? # Query roles directly using the user pointer (no need to fetch the full user) begin if user_id.present? && defined?(Parse::Role) user_roles = Parse::Role.all(users: user_or_role) user_roles.each do |role| << "role:#{role.name}" if role.respond_to?(:name) && role.name.present? end end rescue # If role fetching fails, continue with just the user ID end # Check if any of the user's permissions (user ID or roles) have read access return readable_by?() if .any? return false end end # If it's a User object, expand it to include the user ID and all their roles if user_or_role.is_a?(Parse::User) || (user_or_role.respond_to?(:is_a?) && user_or_role.is_a?(Parse::User)) = [] # Add the user ID << user_or_role.id if user_or_role.respond_to?(:id) && user_or_role.id.present? # Fetch and add all the user's roles begin if user_or_role.respond_to?(:id) && user_or_role.id.present? && defined?(Parse::Role) user_roles = Parse::Role.all(users: user_or_role) user_roles.each do |role| << "role:#{role.name}" if role.respond_to?(:name) && role.name.present? end end rescue # If role fetching fails, continue with just the user ID end # Check if any of the user's permissions (user ID or roles) have read access # Use array checking logic (OR) return readable_by?() if .any? return false end # Single string value - check directly key = (user_or_role) return false unless key [key]&.read == true end |
#will_change! ⇒ Object
Calls acl_will_change! on the delegate when the permissions have changed.
All Object subclasses implement this method.
280 281 282 |
# File 'lib/parse/model/acl.rb', line 280 def will_change! @delegate.acl_will_change! if @delegate.respond_to?(:acl_will_change!) end |
#write_only? ⇒ Boolean
Checks if the object is write-only (has write permissions but no read permissions).
717 718 719 |
# File 'lib/parse/model/acl.rb', line 717 def write_only? .values.any? { |v| v.write } && .values.none? { |v| v.read } end |
#writeable_by ⇒ Array<String> Also known as: writable_by
Returns an array of all user IDs and role names that have write access to this object.
515 516 517 |
# File 'lib/parse/model/acl.rb', line 515 def writeable_by .select { |k, v| v.write }.keys end |
#writeable_by?(user_or_role) ⇒ Boolean Also known as: writable_by?, can_write?
Checks if a specific user or role (or any in an array) has write access to this object. When passed an array of strings, returns true if ANY of the users/roles have write access (OR logic). When passed a Parse::User object or pointer, automatically fetches and checks the user's roles as well.
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 |
# File 'lib/parse/model/acl.rb', line 612 def writeable_by?(user_or_role) # Handle arrays - check if ANY item in the array has write access (OR logic) if user_or_role.is_a?(Array) # For arrays, just check each string value directly (no User object expansion) return user_or_role.any? do |item| key = (item) key && [key]&.write == true end end # Handle Parse::Pointer to User - expand to include user ID and roles if user_or_role.is_a?(Parse::Pointer) || (user_or_role.respond_to?(:parse_class) && user_or_role.respond_to?(:id)) # Check if it's a pointer to a User if user_or_role.respond_to?(:parse_class) && (user_or_role.parse_class == "User" || user_or_role.parse_class == "_User") = [] # Add the user ID from the pointer user_id = user_or_role.respond_to?(:id) ? user_or_role.id : nil << user_id if user_id.present? # Query roles directly using the user pointer (no need to fetch the full user) begin if user_id.present? && defined?(Parse::Role) user_roles = Parse::Role.all(users: user_or_role) user_roles.each do |role| << "role:#{role.name}" if role.respond_to?(:name) && role.name.present? end end rescue # If role fetching fails, continue with just the user ID end # Check if any of the user's permissions (user ID or roles) have write access return writeable_by?() if .any? return false end end # If it's a User object, expand it to include the user ID and all their roles if user_or_role.is_a?(Parse::User) || (user_or_role.respond_to?(:is_a?) && user_or_role.is_a?(Parse::User)) = [] # Add the user ID << user_or_role.id if user_or_role.respond_to?(:id) && user_or_role.id.present? # Fetch and add all the user's roles begin if user_or_role.respond_to?(:id) && user_or_role.id.present? && defined?(Parse::Role) user_roles = Parse::Role.all(users: user_or_role) user_roles.each do |role| << "role:#{role.name}" if role.respond_to?(:name) && role.name.present? end end rescue # If role fetching fails, continue with just the user ID end # Check if any of the user's permissions (user ID or roles) have write access # Use array checking logic (OR) return writeable_by?() if .any? return false end # Single string value - check directly key = (user_or_role) return false unless key [key]&.write == true end |