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 be get applied for newly created instances. All subclasses have public read and write enabled by default.
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({"role:Admin"=>{"read"=>true, "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.
142 143 144 145 146 |
# File 'lib/parse/model/acl.rb', line 142 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.
118 |
# File 'lib/parse/model/acl.rb', line 118 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.
130 131 132 |
# File 'lib/parse/model/acl.rb', line 130 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.
153 154 155 156 157 |
# File 'lib/parse/model/acl.rb', line 153 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.
175 176 177 |
# File 'lib/parse/model/acl.rb', line 175 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.
166 167 168 |
# File 'lib/parse/model/acl.rb', line 166 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" => } },
{ "_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:
* {Parse::Query::ACLReadableByConstraint} and
{Parse::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.
212 213 214 |
# File 'lib/parse/model/acl.rb', line 212 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
359 360 361 |
# File 'lib/parse/model/acl.rb', line 359 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.
223 224 225 |
# File 'lib/parse/model/acl.rb', line 223 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.
260 261 262 263 |
# File 'lib/parse/model/acl.rb', line 260 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.
432 433 434 435 436 437 |
# File 'lib/parse/model/acl.rb', line 432 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.
454 455 456 457 458 459 |
# File 'lib/parse/model/acl.rb', line 454 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.
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/parse/model/acl.rb', line 314 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.
346 347 348 349 |
# File 'lib/parse/model/acl.rb', line 346 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
388 389 390 |
# File 'lib/parse/model/acl.rb', line 388 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
368 369 370 |
# File 'lib/parse/model/acl.rb', line 368 def attributes .select { |k, v| v.present? }.as_json end |
#delete(object) ⇒ Object #delete(id) ⇒ Object
Removes a permission for an objectId or user.
287 288 289 290 291 292 293 |
# File 'lib/parse/model/acl.rb', line 287 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).
810 811 812 |
# File 'lib/parse/model/acl.rb', line 810 def empty? .empty? || .values.none? { |v| v.present? } end |
#everyone(read, write) ⇒ Hash Also known as: world
Set the public read and write permissions.
269 270 271 272 |
# File 'lib/parse/model/acl.rb', line 269 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.
410 411 412 413 |
# File 'lib/parse/model/acl.rb', line 410 def master_key_only! will_change! @permissions = {} end |
#no_read! ⇒ Array
Denies read permission on all existing users and roles attached to this object.
476 477 478 479 480 481 |
# File 'lib/parse/model/acl.rb', line 476 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).
697 698 699 |
# File 'lib/parse/model/acl.rb', line 697 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.
498 499 500 501 502 503 |
# File 'lib/parse/model/acl.rb', line 498 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).
703 704 705 |
# File 'lib/parse/model/acl.rb', line 703 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.
736 737 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 |
# File 'lib/parse/model/acl.rb', line 736 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.
721 722 723 |
# File 'lib/parse/model/acl.rb', line 721 def owners .select { |k, v| v.read && v.write }.keys end |
#present? ⇒ Boolean
Returns true if there are any permissions.
393 394 395 |
# File 'lib/parse/model/acl.rb', line 393 def present? .values.any? { |v| v.present? } end |
#public_read? ⇒ Boolean
Checks if the object has public read access.
685 686 687 |
# File 'lib/parse/model/acl.rb', line 685 def public_read? [PUBLIC]&.read == true end |
#public_write? ⇒ Boolean
Checks if the object has public write access.
691 692 693 |
# File 'lib/parse/model/acl.rb', line 691 def public_write? [PUBLIC]&.write == true end |
#read_only? ⇒ Boolean
Checks if the object is read-only (has read permissions but no write permissions).
709 710 711 |
# File 'lib/parse/model/acl.rb', line 709 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.
507 508 509 |
# File 'lib/parse/model/acl.rb', line 507 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.
530 531 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 |
# File 'lib/parse/model/acl.rb', line 530 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.
278 279 280 |
# File 'lib/parse/model/acl.rb', line 278 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).
715 716 717 |
# File 'lib/parse/model/acl.rb', line 715 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.
513 514 515 |
# File 'lib/parse/model/acl.rb', line 513 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.
610 611 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 |
# File 'lib/parse/model/acl.rb', line 610 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 |