Class: Parse::ACL

Inherits:
DataType show all
Defined in:
lib/parse/model/acl.rb

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

Class Method Summary collapse

Instance Method Summary collapse

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.

Parameters:

  • acls (Hash) (defaults to: nil)

    a Parse-compatible hash acl format.

  • owner (Parse::Object) (defaults to: nil)

    a delegate to receive notifications of acl changes. This delegate must support receiving ‘acl_will_change!` method.



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

#delegateObject

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

#permissionsHash

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.

Examples:

object.acl.permissions
# => { "*": { "read": true }, "3KmCvT7Zsb": {  "read": true, "write": true } }

Returns:

  • (Hash)

    a hash of permissions.



130
131
132
# File 'lib/parse/model/acl.rb', line 130

def permissions
  @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.

Parameters:

  • read (Boolean) (defaults to: true)

    the read permissions for PUBLIC (default: true)

  • write (Boolean) (defaults to: true)

    the write permissions for PUBLIC (default: true)

Version:

  • 1.7.0



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.

Parameters:

  • read (Boolean)

    the read permission value

  • write (Boolean) (defaults to: nil)

    the write permission value.

Returns:

See Also:



175
176
177
# File 'lib/parse/model/acl.rb', line 175

def self.permission(read, write = nil)
  ACL::Permission.new(read, write)
end

.privateParse::ACL

Create a new private ACL with no public access. Objects with this ACL can only be accessed with the master key.

Examples:

acl = Parse::ACL.private
acl.as_json # => {}

Returns:

  • (Parse::ACL)

    an empty ACL with no permissions.

Version:

  • 3.1.3



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" => 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:

* {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.

Examples:

permissions = ["*", user.id] + user.acl_roles.to_a.map { |n| "role:#{n}" }
pipeline << { "$match" => Parse::ACL.read_predicate(permissions) }

Parameters:

  • permissions (Array<String>)

    permission strings — user objectIds, “role:RoleName”, or “*” for public.

  • include_public (Boolean) (defaults to: true)

    when true (default), “*” is appended to permissions if missing. Set false for a strict-permissions check (e.g., :ACL.readable_by => “none” semantics).

Returns:

  • (Hash)

    a MongoDB $or subexpression suitable for use inside a $match stage.



212
213
214
# File 'lib/parse/model/acl.rb', line 212

def self.read_predicate(permissions, include_public: true)
  permission_predicate("_rperm", permissions, include_public: include_public)
end

.typecast(value, delegate = nil) ⇒ ACL

Used for object conversion when formatting the input/output value in Parse::Object properties

Parameters:

  • value (Hash)

    a Parse ACL hash to construct a Parse::ACL instance.

  • delegate (Object) (defaults to: nil)

    any object that would need to be notified of changes.

Returns:

See Also:



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.

Parameters:

  • permissions (Array<String>)

    permission strings.

  • include_public (Boolean) (defaults to: true)

    whether to append “*”.

Returns:

  • (Hash)

    a MongoDB $or subexpression.



223
224
225
# File 'lib/parse/model/acl.rb', line 223

def self.write_predicate(permissions, include_public: true)
  permission_predicate("_wperm", permissions, 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.

Examples:

acl = Parse::ACL.new
# create a public read-only ACL
acl.apply :public, true, false
acl.as_json # => {'*' => { "read" => true }}

create a second instance with similar privileges
acl2 = Parse::ACL.everyone(true, false)
acl2.as_json # => {'*' => { "read" => true }}

acl == acl2 # => true
acl == {'*' => { "read" => true }} # hash ok too

acl2.apply_role 'Admin', true, true # rw for Admins
acl == acl2 # => false

Returns:

  • (Boolean)

    whether two ACLs have the same set of privileges.



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.

Examples:

object.acl
#    { "*":          { "read" : true },
#      "3KmCvT7Zsb": { "read" : true, "write": true },
#      "role:Admins": { "write": true }
#     }
object.acl.all_read!
# Outcome:
#    { "*":          { "read" : true },
#      "3KmCvT7Zsb": { "read" : true, "write": true },
#      "role:Admins": { "read" : true, "write": true}
#     }

Returns:

  • (Array)

    list of ACL keys

Version:

  • 1.7.2



432
433
434
435
436
437
# File 'lib/parse/model/acl.rb', line 432

def all_read!
  will_change!
  permissions.keys.each do |perm|
    permissions[perm].read! true
  end
end

#all_write!Array

Grants write permission on all existing users and roles attached to this object.

Examples:

object.acl
#    { "*":          { "read" : true },
#      "3KmCvT7Zsb": { "read" : true, "write": true },
#      "role:Admins": { "write": true }
#     }
object.acl.all_write!
# Outcome:
#    { "*":          { "read" : true, "write": true },
#      "3KmCvT7Zsb": { "read" : true, "write": true },
#      "role:Admins": { "write": true }
#     }

Returns:

  • (Array)

    list of ACL keys

Version:

  • 1.7.2



454
455
456
457
458
459
# File 'lib/parse/model/acl.rb', line 454

def all_write!
  will_change!
  permissions.keys.each do |perm|
    permissions[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.

Overloads:

  • #apply(user, read = nil, write = nil) ⇒ Hash

    Set the read and write permissions for this user on this ACL.

    Parameters:

    • user (Parse::User)

      the user object.

    • read (Boolean) (defaults to: nil)

      the read permission.

    • write (Boolean) (defaults to: nil)

      the write permission.

  • #apply(role, read = nil, write = nil) ⇒ Hash

    Set the read and write permissions for this role object on this ACL.

    Parameters:

    • role (Parse::Role)

      the role object.

    • read (Boolean) (defaults to: nil)

      the read permission.

    • write (Boolean) (defaults to: nil)

      the write permission.

  • #apply(id, read = nil, write = nil) ⇒ Hash

    Set the read and write permissions for this objectId on this ACL.

    Parameters:

    • id (String|:public)

      the objectId for a User. If :public is passed, then the PUBLIC read and write permissions will be modified.

    • read (Boolean) (defaults to: nil)

      the read permission.

    • write (Boolean) (defaults to: nil)

      the write permission.

Returns:

  • (Hash)

    the current set of permissions.

See Also:



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
  permission = ACL.permission(read, write)
  # if the input is already an Permission object, then set it directly
  permission = read if read.is_a?(Parse::ACL::Permission)
  if permission.is_a?(ACL::Permission)
    if permissions[id.to_s] != permission
      will_change! # dirty track
      permissions[id.to_s] = permission
    end
  end

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

Overloads:

  • #apply_role(role, read = nil, write = nil) ⇒ Object

    Parameters:

    • role (Parse::Role)

      the role object.

    • read (Boolean) (defaults to: nil)

      the read permission.

    • write (Boolean) (defaults to: nil)

      the write permission.

  • #apply_role(role_name, read = nil, write = nil) ⇒ Object

    Parameters:

    • role_name (String)

      the name of the role.

    • read (Boolean) (defaults to: nil)

      the read permission.

    • write (Boolean) (defaults to: nil)

      the write permission.



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

Returns:



388
389
390
# File 'lib/parse/model/acl.rb', line 388

def as_json(*args)
  permissions.select { |k, v| v.present? }.as_json
end

#attributesHash

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

Returns:



368
369
370
# File 'lib/parse/model/acl.rb', line 368

def attributes
  permissions.select { |k, v| v.present? }.as_json
end

#delete(object) ⇒ Object #delete(id) ⇒ Object

Removes a permission for an objectId or user.

Overloads:

  • #delete(object) ⇒ Object

    Parameters:

    • object (Parse::User)

      the user to revoke permissions.

  • #delete(id) ⇒ Object

    Parameters:

    • id (String)

      the objectId to revoke permissions.



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? && permissions.has_key?(id)
    will_change!
    permissions.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).

Returns:

  • (Boolean)

    true if no permissions exist



810
811
812
# File 'lib/parse/model/acl.rb', line 810

def empty?
  permissions.empty? || permissions.values.none? { |v| v.present? }
end

#everyone(read, write) ⇒ Hash Also known as: world

Set the public read and write permissions.

Parameters:

  • read (Boolean)

    the read permission state.

  • write (Boolean)

    the write permission state.

Returns:

  • (Hash)

    the current public permissions.



269
270
271
272
# File 'lib/parse/model/acl.rb', line 269

def everyone(read, write)
  apply(PUBLIC, read, write)
  permissions[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.

Examples:

object.acl
#    { "*":          { "read" : true },
#      "3KmCvT7Zsb": { "read" : true, "write": true },
#      "role:Admins": { "write": true }
#     }
object.acl.master_key_only!
# Outcome:
#    { }

Returns:

  • (Hash)

    The cleared permissions hash

Version:

  • 1.7.2



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.

Examples:

object.acl
#    { "*":          { "read" : true },
#      "3KmCvT7Zsb": { "read" : true, "write": true },
#      "role:Admins": { "write": true }
#     }
object.acl.no_read!
# Outcome:
#    { "*":          nil,
#      "3KmCvT7Zsb": { "write": true },
#      "role:Admins": { "write": true }
#     }

Returns:

  • (Array)

    list of ACL keys

Version:

  • 1.7.2



476
477
478
479
480
481
# File 'lib/parse/model/acl.rb', line 476

def no_read!
  will_change!
  permissions.keys.each do |perm|
    permissions[perm].read! false
  end
end

#no_read?Boolean

Checks if the object has no read permissions for anyone (master key only).

Returns:

  • (Boolean)

    true if no one has read access



697
698
699
# File 'lib/parse/model/acl.rb', line 697

def no_read?
  permissions.values.none? { |v| v.read }
end

#no_write!Array

Denies write permission on all existing users and roles attached to this object.

Examples:

object.acl
#    { "*":          { "read" : true },
#      "3KmCvT7Zsb": { "read" : true, "write": true },
#      "role:Admins": { "write": true }
#     }
object.acl.no_write!
# Outcome:
#    { "*":          { "read" : true },
#      "3KmCvT7Zsb": { "read" : true },
#      "role:Admins": nil
#     }

Returns:

  • (Array)

    list of ACL keys

Version:

  • 1.7.2



498
499
500
501
502
503
# File 'lib/parse/model/acl.rb', line 498

def no_write!
  will_change!
  permissions.keys.each do |perm|
    permissions[perm].write! false
  end
end

#no_write?Boolean

Checks if the object has no write permissions for anyone (master key only).

Returns:

  • (Boolean)

    true if no one has write access



703
704
705
# File 'lib/parse/model/acl.rb', line 703

def no_write?
  permissions.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.

Examples:

acl.owner?("user123")                          # Check single user ID
acl.owner?("Admin")                            # Check single role name
acl.owner?(user_object)                        # Check user + their roles
acl.owner?(user_pointer)                       # Check user pointer + their roles
acl.owner?(["user123", "Admin"])               # Check array (OR logic)

Parameters:

Returns:

  • (Boolean)

    true if the user/role (or any in the array) has both read and write access



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 = normalize_permission_key(item)
             next false unless key
             perm = permissions[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")
      permissions_to_check = []

      # Add the user ID from the pointer
      user_id = user_or_role.respond_to?(:id) ? user_or_role.id : nil
      permissions_to_check << 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|
            permissions_to_check << "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?(permissions_to_check) if permissions_to_check.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))
    permissions_to_check = []

    # Add the user ID
    permissions_to_check << 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|
          permissions_to_check << "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?(permissions_to_check) if permissions_to_check.any?
    return false
  end

  # Single string value - check directly
  key = normalize_permission_key(user_or_role)
  return false unless key
  perm = permissions[key]
  perm&.read == true && perm&.write == true
end

#ownersArray<String>

Returns an array of all user IDs and role names that have both read and write access.

Returns:

  • (Array<String>)

    list of user IDs and role names with full access



721
722
723
# File 'lib/parse/model/acl.rb', line 721

def owners
  permissions.select { |k, v| v.read && v.write }.keys
end

#present?Boolean

Returns true if there are any permissions.

Returns:

  • (Boolean)

    true if there are any permissions.



393
394
395
# File 'lib/parse/model/acl.rb', line 393

def present?
  permissions.values.any? { |v| v.present? }
end

#public_read?Boolean

Checks if the object has public read access.

Returns:

  • (Boolean)

    true if public can read this object



685
686
687
# File 'lib/parse/model/acl.rb', line 685

def public_read?
  permissions[PUBLIC]&.read == true
end

#public_write?Boolean

Checks if the object has public write access.

Returns:

  • (Boolean)

    true if public can write to this object



691
692
693
# File 'lib/parse/model/acl.rb', line 691

def public_write?
  permissions[PUBLIC]&.write == true
end

#read_only?Boolean

Checks if the object is read-only (has read permissions but no write permissions).

Returns:

  • (Boolean)

    true if object has read access but no write access



709
710
711
# File 'lib/parse/model/acl.rb', line 709

def read_only?
  permissions.values.any? { |v| v.read } && permissions.values.none? { |v| v.write }
end

#readable_byArray<String>

Returns an array of all user IDs and role names that have read access to this object.

Returns:

  • (Array<String>)

    list of user IDs and role names (e.g., [“*”, “user123”, “role:Admin”])



507
508
509
# File 'lib/parse/model/acl.rb', line 507

def readable_by
  permissions.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.

Examples:

acl.readable_by?("user123")                    # Check single user ID
acl.readable_by?("Admin")                      # Check single role name
acl.readable_by?(user_object)                  # Check user + their roles
acl.readable_by?(user_pointer)                 # Check user pointer + their roles
acl.readable_by?(["user123", "Admin"])         # Check array (OR logic)

Parameters:

Returns:

  • (Boolean)

    true if the user/role (or any in the array) has read access



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 = normalize_permission_key(item)
             key && permissions[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")
      permissions_to_check = []

      # Add the user ID from the pointer
      user_id = user_or_role.respond_to?(:id) ? user_or_role.id : nil
      permissions_to_check << 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|
            permissions_to_check << "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?(permissions_to_check) if permissions_to_check.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))
    permissions_to_check = []

    # Add the user ID
    permissions_to_check << 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|
          permissions_to_check << "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?(permissions_to_check) if permissions_to_check.any?
    return false
  end

  # Single string value - check directly
  key = normalize_permission_key(user_or_role)
  return false unless key
  permissions[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).

Returns:

  • (Boolean)

    true if object has write access but no read access



715
716
717
# File 'lib/parse/model/acl.rb', line 715

def write_only?
  permissions.values.any? { |v| v.write } && permissions.values.none? { |v| v.read }
end

#writeable_byArray<String> Also known as: writable_by

Returns an array of all user IDs and role names that have write access to this object.

Returns:

  • (Array<String>)

    list of user IDs and role names (e.g., [“*”, “user123”, “role:Admin”])



513
514
515
# File 'lib/parse/model/acl.rb', line 513

def writeable_by
  permissions.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.

Examples:

acl.writeable_by?("user123")                   # Check single user ID
acl.writeable_by?("Admin")                     # Check single role name
acl.writeable_by?(user_object)                 # Check user + their roles
acl.writeable_by?(user_pointer)                # Check user pointer + their roles
acl.writeable_by?(["user123", "Admin"])        # Check array (OR logic)

Parameters:

Returns:

  • (Boolean)

    true if the user/role (or any in the array) has write access



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 = normalize_permission_key(item)
             key && permissions[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")
      permissions_to_check = []

      # Add the user ID from the pointer
      user_id = user_or_role.respond_to?(:id) ? user_or_role.id : nil
      permissions_to_check << 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|
            permissions_to_check << "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?(permissions_to_check) if permissions_to_check.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))
    permissions_to_check = []

    # Add the user ID
    permissions_to_check << 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|
          permissions_to_check << "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?(permissions_to_check) if permissions_to_check.any?
    return false
  end

  # Single string value - check directly
  key = normalize_permission_key(user_or_role)
  return false unless key
  permissions[key]&.write == true
end