Access Modifiers – Weaker API and Security Support in Ruby?

When first coding in Java in 1996, it took me a while to appreciate the newest access modifier – default or package.  Then the norm was exposing almost everything as public, including fields, unless there was a good reason to restrict.  Joshua Bloch’s Effective Java later reversed that, utilizing access modifiers appropriately to enhance security and scope API’s.

Default vs. public access was particularly important.  A component of functionality (i.e., Java package) allowed all the containing classes to access the equivalent of their public methods to accomplish its responsibility solely within the package.  However, it could prevent access to all these from the outside world and limit clients strictly to an API.  This API was designated by applying the public access modifier to a limited subset of these classes and methods.  This provided a number of benefits:

  1. Changing the underlying implementation without backward compatibility concerns, including consumers coupling their code to an internal implementation detail.  Not because consumers don’t use restricted items, but because they are unable to access them
  2. Explicit API classes and methods which are more easily JavaDoc’d
  3. Restricting sub-classing only within the package
  4. Enhanced security

This was a powerful encapsulation capability, and one that seems to be sorely missing in Ruby with its limited three access modifiers:  public, protected, and private.

Access Modifiers

Ruby protected and private modifiers are radically different than Java.

Private

Like Java, a private method can only be invoked within the methods defined in the class it is declared in.

In Ruby, however, private access is less restrictive.  The methods can also be accessed by any sub-class, not just the class defined within.  In addition, the private methods can be overridden in sub-classes.  This means that I cannot enforce the functionality of any method for any class remaining the same; it can always be altered by sub-classes, circumventing my attempts to enforce invariants in my system.  In addition, all functionality can be directly accessed (and coupled) by clients via sub-classing.  Effectively, all functionality can be directly accessed and altered in Ruby, and there is nothing you can do to prevent it.

Protected

Protected methods can invoked and overridden by sub-classes.  Like private methods, protected methods can only be invoked within the methods defined in the class it is declared in.  However, they can also be invoked on other instances of the same class or sub-class only.  Note that public methods fully lend themselves to being overridden as well.

Member Fields Automatically Private

One thing I do like about Ruby is that member fields are automatically private; the accessors are used to expose the equivalent of getters and setters.

One thing I am not wild about is how the methods always default to public.

Final

I am still trying to find something that is equivalent to Java’s final keyword in Ruby.  Not only do I not see a way to lock down the behavior of public methods in sub-classes, even private and protected methods can be overridden with no ability to limit this.

Package Level Containment

I do not see a Ruby construct equivalent to the Java package.  That is, something that can contain other classes and limit what is exposed outside of it.

I tried an experiment nesting sub-classes within another class, treating that class as a package.  By placing some of the sub-classes beneath the private keyword of the containing class, I had hoped that classes could not be directly accessed and instantiated outside of the containing class.  Unfortunately, this does not work.  Furthermore, I see no mechanism to apply access modifiers to classes.

In my opinion, too much is exposed in Ruby, but perhaps as I learn more about the Ruby and Rails philosophy I will better understand why.

Lab Code

The following is the quick and dirty code I threw together to experiment with the access modifier behavior:

class A
  def a_public_method_invoking_private
    a_private_method
  end

  def a_public_method_invoking_protected
    a_protected_method
  end

  def a_public_method_invoking_other_protected
    a_other = A.new
    a_other.a_protected_method
  end

  def a_public_method_invoking_other_private
    a_other = A.new
    a_other.a_private_method
  end

  def a_public_method
    puts "a_public_method_invoked"
  end  

  protected

  def a_protected_method
    puts "a_protected_method_invoked"
  end  

    private

    def a_private_method
        puts "a_private_method_invoked"
    end   
end

class B < A
  def a_public_method_invoking_parent_private_method
    puts "surprisingly, your can invoke superclass private methods."
    a_private_method
  end  

  def a_public_method_invoking_parent_protected_method
    puts "You can invoke superclass protected methods."
    a_protected_method
  end

  def a_public_method_invoking_other_protected_in_superclass
    a_other = A.new
    a_other.a_protected_method
  end
end

class C < A
  public :a_public_method
  protected :a_protected_method
  private :a_private_method

  def a_public_method
    super
    puts "a_public_method_invoked - subclass C - allowed for public"
  end

  def a_protected_method
    super
    puts "a_protected_method - overridden extra behavior in C"
  end

  def a_private_method
    super
    puts "a_private_method - overridden extra behavior in C"
  end
end

class FooPackage
  class PublicClass
    def public_method
      puts "PublicClass public_method invoked.  About to invoke own private method, then public method of private class."
      private_method
      p = PrivateClass.new
      p.public_method
    end

    private 

    def private_method
      puts "PublicClass private_method invoked" 
    end 
  end

  private

  class PrivateClass
    def public_method
      puts "PrivateClass public_method invoked.  About to invoke own private method."
      private_method
    end

    private 

    def private_method
      puts "PrivateClass private_method invoked" 
    end 
  end
end

a1 = A.new
#a.a_private_method - this is not legal
#a1.a_public_method_invoking_private - this is legal
#a1.a_public_method_invoking_protected - this is legal
#a1.a_public_method_invoking_other_protected - this is legal
#a1.a_public_method_invoking_other_private - this is not legal

b1 = B.new
#b1.a_public_method_invoking_private - this is legal
#b1.a_public_method_invoking_parent_private_method - this is legal
#b1.a_public_method_invoking_parent_protected_method - this is legal
b1.a_public_method_invoking_other_protected_in_superclass

c1 = C.new
#c1.a_public_method_invoking_protected - this is legal
#c1.a_public_method_invoking_private - this is legal
#c1.a_public_method - this is legal

fp = FooPackage::PublicClass.new
#fp.public_method - this is legal and works its way through
fprivate = FooPackage::PrivateClass.new # this is legal, despite my hopes it would not be
fprivate.public_method
Advertisements

One thought on “Access Modifiers – Weaker API and Security Support in Ruby?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s