# SocialStream provides a sophisticated and powerful system of permissions based on the {Relation relations} # of the social network. # # = Permissions and Relations # # Permissions are assigned to {Relation Relations}, and through relations, to {Tie Ties}. # When a sender establishes a {Tie} with a receiver, she is granting to the receiver # the permissions assigned to {Relation} of the {Tie} she has just established. # # For example, when _Alice_ establishes a _friend_ tie to _Bob_, she is granting # him the permissions associated with her _friend_ relation. Alice's _friend_ relation may # have different permissions from Bob's _friend_ relation. # # = Permissions description # # Permissions are composed by *action*, *objective* and *function*. Action and objective # are typical in content management systems, e.g. create activity, # update tie, read post. *function* is a new parameter for social networks # # == Actions # # Current available actions are: # # +create+:: add a new instance of something (activity, tie, post, etc) # +read+:: view something # +update+:: modify something # +destroy+:: delete something # +follow+:: subscribe to activity updates from the receiver of the tie # +represent+:: give the receiver rights to act as if he were me. # # == Objectives # # +activity+:: all the objects in a wall: posts, comments # # Other objectives currently not implemented could be +tie+, +post+, +comment+ or +message+ # # == Functions # # Function is a novel feature. It supports applying the permission to other related ties. # It is required because the set of ties changes along with the establish of contacts # in the website, besides {SocialStream::Models::Subject subjects} can describe and # customize their own relations and permissions. # # Available functions are: # # +tie+:: apply the permission to the established tie only. # # Example: if the _friend_ relation has the permission # create activity tie, the _friend_ can create activities # attached to this tie only. _Bob_ can create activities only at _Alice_'s # _friend_ level. # # +weak_ties+:: apply the permission to all the related ties with a relation weaker # or equal than this. When a subject establishes a strong ties, # their related ties are established at the same time. # # Example: if the _member_ relation of a group has the permission # create activity weak_ties, its members # can also create activities attached to the weaker ties of # _acquaintance_ and _public_. # This means than a group _member_ can create activities at different # levels of strength hierarchy, and therefore, with different levels of # access. # # +star_ties+:: the permission applies to all the ties at the same level of strength, # that is, ties with the same sender and the same relation but # different receivers. # # Example: the _public_ relation has the permission # read activity star_ties. If _Alice_ has a _public_ tie with # _Bob_, she is granting him access to activities attached to other ties # from _Alice_ to the rest of her _public_ contacts. # # +weak_star_ties+:: apply the permission to weak and star ties. This is the union of # the former. # # Example: group's _admin relation has the permission # destroy activity weak_star_ties # This means that _admins_ can destroy activities from other # _members_, _acquaintances_ and _public_. # class Permission < ActiveRecord::Base has_many :relation_permissions, :dependent => :destroy has_many :relations, :through => :relation_permissions %w(represent follow).each do |p| scope p, where(:action => p) # scope :represent, where(:action => 'represent') end # The SQL and ARel conditions for permission queries ParameterConditions = { :table => { 'tie' => "ties_as.sender_id = ties.sender_id AND ties_as.receiver_id = ties.receiver_id AND ties_as.relation_id = ties.relation_id", 'weak_ties' => "ties_as.sender_id = ties.sender_id AND ties_as.receiver_id = ties.receiver_id AND relations.lft BETWEEN relations_as.lft AND relations_as.rgt", 'star_ties' => "ties_as.sender_id = ties.sender_id AND ties_as.relation_id = ties.relation_id", 'weak_star_ties' => "ties_as.sender_id = ties.sender_id AND relations.lft BETWEEN relations_as.lft AND relations_as.rgt" }, :arel => { 'tie' => lambda { |as, t| # The same sender, receiver and relation as[:sender_id].eq(t.sender_id).and( as[:receiver_id].eq(t.receiver_id)).and( as[:relation_id].eq(t.relation_id)) }, 'weak_ties' => lambda { |as, t| # The same sender and receiver, but a stronger or equal relation as[:sender_id].eq(t.sender_id).and( as[:receiver_id].eq(t.receiver_id)).and( as[:relation_id].in(t.relation.stronger_or_equal.map(&:id))) }, 'star_ties' => lambda { |as, t| # The same receiver and relation as[:sender_id].eq(t.sender_id).and( as[:relation_id].eq(t.relation_id)) }, 'weak_star_ties' => lambda { |as, t| # The same receiver with stronger or equal relations as[:sender_id].eq(t.sender_id).and( as[:relation_id].in(t.relation.stronger_or_equal.map(&:id))) } } } class << self # Builds SQL conditions based on {ParameterConditions} def parameter_conditions(tie = nil) if tie.present? ParameterConditions[:arel].inject([]) { |conditions, h| # Add the condition 'permissions.function = key' # to all arel ParameterConditions conditions << h.last.call(Tie.arel_table, tie).and(arel_table[:function].eq(h.first)) }.inject(nil){ |result, pc| # Join all ParameterConditions with OR result.nil? ? pc : result.or(pc) } else ParameterConditions[:table].inject([]){ |result, pc| result << sanitize_sql([ "#{ pc.last } AND permissions.function = ?", pc.first ]) }.join(" OR ") end end end end