summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeiko Bernloehr <Heiko.Bernloehr@FreeIT.de>2017-03-01 22:28:20 +0100
committerHeiko Bernloehr <Heiko.Bernloehr@FreeIT.de>2017-03-01 22:28:20 +0100
commitf346740451f931acbfce34be450b9d0499cabc70 (patch)
tree918b821c56bc372a47404cff0f0cd49081c40f4f
parent8d7481a2502ec136233c9d3a6eb404d428920bf0 (diff)
downloadecs-f346740451f931acbfce34be450b9d0499cabc70.tar.gz
ecs-f346740451f931acbfce34be450b9d0499cabc70.zip
Initial models.
-rw-r--r--app/models/auth.rb57
-rw-r--r--app/models/community.rb36
-rw-r--r--app/models/community_message.rb22
-rw-r--r--app/models/ev_type.rb21
-rw-r--r--app/models/event.rb94
-rw-r--r--app/models/identity.rb36
-rw-r--r--app/models/logging_observer.rb15
-rw-r--r--app/models/membership.rb120
-rw-r--r--app/models/membership_message.rb126
-rw-r--r--app/models/message.rb324
-rw-r--r--app/models/organization.rb32
-rw-r--r--app/models/participant.rb169
-rw-r--r--app/models/ressource.rb50
-rw-r--r--app/models/subparticipant.rb137
14 files changed, 1239 insertions, 0 deletions
diff --git a/app/models/auth.rb b/app/models/auth.rb
new file mode 100644
index 0000000..ca52bf6
--- /dev/null
+++ b/app/models/auth.rb
@@ -0,0 +1,57 @@
+class Auth < ActiveRecord::Base
+ belongs_to :message
+
+ #named_scope :hash, lambda {|hash| {
+ # :joins => {:membership_messages => {:membership => :participant}},
+ # :order => "id ASC",
+ # :conditions => {:participants => {:id => participant.id}}}}
+
+
+ # if valid time window return true
+ def test_validation_window
+ b = JSON.parse(message.body)
+ sov = Time.parse(b["sov"])
+ eov = Time.parse(b["eov"])
+ if eov > Time.now
+ true
+ else
+ false
+ end
+ end
+
+ # garbage collect outtimed authorization tokens
+ def self.gc_outtimed
+ gc_sys_auths_lock= "#{Rails.root}/tmp/gc_sys_auths.lock"
+ if File.exists?(gc_sys_auths_lock)
+ logtext= "GC: there seems to be already running a ecs:gc_sys_auths process (#{gc_sys_auths_lock}). Aborting."
+ logger.info logtext
+ puts logtext unless Rails.env.test?
+ else
+ begin
+ File.open(gc_sys_auths_lock,"w") do |f|
+ f.puts "#{Process.pid}"
+ end
+ logtext= "GC: Searching for outtimed auths ..."
+ logger.info logtext
+ puts logtext unless Rails.env.test?
+ Auth.all.each do |auth|
+ if ! auth.test_validation_window
+ auth.message.destroy_as_sender
+ logtext= "GC: garbage collect auths token: #{auth.one_touch_hash}"
+ logger.info logtext
+ puts logtext unless Rails.env.test?
+ end
+ end
+ logtext= "GC: Searching for outtimed auths done."
+ logger.info logtext
+ puts logtext unless Rails.env.test?
+ ensure
+ begin
+ File.delete(gc_sys_auths_lock)
+ rescue
+ end
+ end
+ end
+ end
+
+end
diff --git a/app/models/community.rb b/app/models/community.rb
new file mode 100644
index 0000000..2cd3298
--- /dev/null
+++ b/app/models/community.rb
@@ -0,0 +1,36 @@
+# Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Community < ActiveRecord::Base
+ has_many :memberships, :order => :id
+ has_many :participants, :through => :memberships
+ has_many :community_messages, :dependent => :destroy
+ has_many :messages, :through => :community_messages
+ validates_presence_of :name
+ validates_uniqueness_of :name
+
+ named_scope :for_participant, lambda { |participant| {
+ :joins => [:memberships => :participant],
+ :conditions => { :participants => { :id => participant.id }}}}
+
+ named_scope :for_message, lambda { |message| {
+ :joins => [:memberships => {:membership_messages => :message}],
+ :conditions => { :messages => { :id => message.id }}}}
+
+
+end
diff --git a/app/models/community_message.rb b/app/models/community_message.rb
new file mode 100644
index 0000000..b0e6033
--- /dev/null
+++ b/app/models/community_message.rb
@@ -0,0 +1,22 @@
+# Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class CommunityMessage < ActiveRecord::Base
+ belongs_to :message
+ belongs_to :community
+end
diff --git a/app/models/ev_type.rb b/app/models/ev_type.rb
new file mode 100644
index 0000000..87c87ba
--- /dev/null
+++ b/app/models/ev_type.rb
@@ -0,0 +1,21 @@
+# Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class EvType < ActiveRecord::Base
+ has_many :events
+end
diff --git a/app/models/event.rb b/app/models/event.rb
new file mode 100644
index 0000000..4885f8f
--- /dev/null
+++ b/app/models/event.rb
@@ -0,0 +1,94 @@
+# Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Event < ActiveRecord::Base
+ belongs_to :ev_type
+ belongs_to :participant
+ belongs_to :message
+
+ def ===(y)
+ participant_id == y.participant_id and message_id == y.message_id and ev_type_id == y.ev_type_id
+ end
+
+private
+
+ # if count <0 then list all events otherwise maximal count events
+ named_scope :for_participant, lambda { |participant_id,count| {
+ :conditions => { :participant_id => participant_id },
+ :order => "id ASC",
+ count<0 ? :readonly : :limit => count }}
+ named_scope :for_participant_and_message_desc_order, lambda { |participant_id,message_id| {
+ :conditions => { :participant_id => participant_id, :message_id => message_id },
+ :order => "id DESC",
+ :limit => 1 }}
+
+ def self.make(options)
+ options.assert_valid_keys(:event_type_name, :membership_message, :participant, :message)
+ message = options[:membership_message] ? options[:membership_message].message : options[:message]
+ participant= options[:membership_message] ? options[:membership_message].membership.participant : options[:participant]
+ return if not (message.ressource.events? and participant.events?)
+ event = Event.new
+ event.participant_id = participant.id
+ event.message_id = message.id
+ case options[:event_type_name]
+ when "created"
+ event.ev_type_id = EvType.find_by_name("created").id
+ when "destroyed"
+ event.ev_type_id = EvType.find_by_name("destroyed").id
+ when "updated"
+ event.ev_type_id = EvType.find_by_name("updated").id
+ else event.ev_type_id = 7777
+ end
+ if unique_or_notlast?(event) and event.save
+ event
+ else
+ # There is already a pending event (the last one) describing a change of
+ # the message. So don't create another one. Instead only touch the
+ # "updated_at" attribute of the event.
+ iev= Event.for_participant_and_message_desc_order(event.participant_id, event.message_id)[0]
+ iev.updated_at= Time.now.to_s(:db)
+ iev.save
+ nil
+ end
+ end
+
+ def self.unique_or_notlast?(event)
+ # Normally there should/could only be multiple update events more than
+ # once. The testing code would also handle all other events correctly.
+ mid= event.message_id
+ pid= event.participant_id
+ etid= event.ev_type_id
+ case initial_event(pid, mid, etid)
+ when nil
+ # its a unique event
+ return true
+ when Event.for_participant_and_message_desc_order(pid, mid)[0]
+ # there is already such an event for that message and its the last in the queue
+ # for case equality see also overridden === case operator
+ return false
+ else
+ # there is such an event but it's not the last in the queue so create a new one
+ return true
+ end
+ end
+
+ def self.initial_event(pid, mid, etid)
+ Event.find_by_participant_id_and_message_id_and_ev_type_id(pid, mid, etid)
+ end
+
+end
diff --git a/app/models/identity.rb b/app/models/identity.rb
new file mode 100644
index 0000000..f6f3cb7
--- /dev/null
+++ b/app/models/identity.rb
@@ -0,0 +1,36 @@
+# Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Identity < ActiveRecord::Base
+
+ require 'securerandom'
+
+ belongs_to :participant
+
+ # TODO validate :participant_id
+ # it's possible in rails 2.3.6 with :inverse_of
+ validates_presence_of :name
+ validates_uniqueness_of :name
+
+
+ def self.randomized_authid
+ SecureRandom.hex
+ end
+
+end
+
diff --git a/app/models/logging_observer.rb b/app/models/logging_observer.rb
new file mode 100644
index 0000000..d920666
--- /dev/null
+++ b/app/models/logging_observer.rb
@@ -0,0 +1,15 @@
+class LoggingObserver < ActiveRecord::Observer
+ observe Event
+
+ def after_create(model)
+ case model
+ when Event
+ msgpath= "#{model.message.ressource.namespace}/#{model.message.ressource.ressource}/#{model.message.id}"
+ evreceiver_pid= model.participant.id
+ evreceiver_mid= (Membership.receiver(evreceiver_pid, model.message.id)).id
+ evtype= model.ev_type.name
+ model.logger.info("**#{model.message.ressource.namespace}** Event: Type:#{evtype} -- MsgPath:#{msgpath} -- ReceiverPid:#{evreceiver_pid} -- ReceiverMid:#{evreceiver_mid}")
+ end
+ end
+
+end
diff --git a/app/models/membership.rb b/app/models/membership.rb
new file mode 100644
index 0000000..fda3093
--- /dev/null
+++ b/app/models/membership.rb
@@ -0,0 +1,120 @@
+# Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Membership < ActiveRecord::Base
+ belongs_to :participant
+ belongs_to :community
+ belongs_to :community_with_reduced_attributes,
+ :class_name => "Community",
+ :foreign_key => "community_id",
+ :select => "name, description, id"
+ has_many :messages, :through => :membership_messages
+ has_many :membership_messages, :dependent => :destroy
+
+ after_create :postroute
+
+ # returns memberships of the relation between a participant and a message
+ # if no relationship then returns empty array.
+ named_scope :receiver, lambda { |participant_id,message_id| {
+ :joins => [:participant, {:membership_messages => :message}],
+ :conditions => { :participants => { :id => participant_id }, :messages => { :id => message_id } } } }
+
+ named_scope :receivers, lambda { |message_id| {
+ :joins => [:membership_messages => :message],
+ :select => :memberships.to_s+".id" + ", community_id, participant_id",
+ :conditions => { :messages => { :id => message_id } } } }
+
+ named_scope :for_participant_id, lambda { |participant_id| {
+ :joins => [:participant],
+ :conditions => { :participants => { :id => participant_id } } } }
+
+ named_scope :for_participant_id_and_community_id, lambda { |participant_id,community_id| {
+ :joins => [:participant, :community],
+ :conditions => { :participants => { :id => participant_id }, :communities => { :id => community_id } } } }
+
+ def self.senders(participant, message)
+ sender_mids=[]
+ Community.for_participant(participant).for_message(message).uniq.each do |comm|
+ sender_mids << Membership.find_by_participant_id_and_community_id(participant.id,comm.id)
+ end
+ if sender_mids.empty?
+ []
+ else
+ sender_mids.flatten
+ end
+ end
+
+ def self.memberships(participant,itsyou=false,filter=nil)
+ memberships = []
+ Membership.for_participant_id(participant.id).each do |membership|
+ community= lambda { |memb|
+ attribs = memb.community_with_reduced_attributes.attributes
+ id = attribs["id"]; attribs.delete("id"); attribs["cid"] = id
+ attribs
+ }.call(membership)
+ logger.debug "**** Membership::memberships: community: #{community.inspect}"
+ if itsyou
+ participants_with_reduced_attribs= membership.community.participants.itsyou(participant.id).without_anonymous.reduced_attributes
+ logger.debug "**** Membership::memberships: participants_with_reduced_attribs: #{participants_with_reduced_attribs.inspect}"
+ else
+ participants_with_reduced_attribs= case
+ when filter[:all]
+ membership.community.participants.order_id_asc.reduced_attributes
+ when filter[:mainparticipants]
+ membership.community.participants.mainparticipants_with_reduced_attributes
+ when filter[:subparticipants]
+ membership.community.participants.subparticipants_with_reduced_attributes
+ when filter[:anonymous]
+ membership.community.participants.anonymous_participants_with_reduced_attributes
+ else
+ membership.community.participants.mainparticipants_with_reduced_attributes
+ end
+ end
+ participants= participants_with_reduced_attribs.map do |p|
+ attribs = p.attributes
+ attribs["mid"] = Membership.for_participant_id_and_community_id(p.id, membership.community.id).first.id
+ attribs["org"] = {"name" => p.organization.name, "abbr" => p.organization.abrev}
+ attribs["itsyou"] = p.id == participant.id
+ attribs["pid"] = p.id
+ attribs["type"] = p.ptype
+ attribs.delete("id")
+ attribs.delete("organization_id")
+ attribs.delete("ptype")
+ attribs
+ end
+ logger.debug "**** Membership::memberships: participants: #{participants.inspect}"
+ memberships <<
+ { :community => community,
+ :participants => participants
+ }
+ end
+ memberships
+ end
+
+private
+
+ # generate created events for all messages connected to this community membership
+ def postroute
+ community.messages.map{|m| m.ressource.postroute ? m : nil}.compact.each do |msg|
+ messages << msg
+ Event.make(:event_type_name => EvType.find_by_name("created").name, :participant => participant, :message => msg)
+ logger.info "**** postrouting message.id=#{msg.id} to participant:#{participant.name} (pid:#{participant.id})"
+ end
+ end
+
+end
diff --git a/app/models/membership_message.rb b/app/models/membership_message.rb
new file mode 100644
index 0000000..e196902
--- /dev/null
+++ b/app/models/membership_message.rb
@@ -0,0 +1,126 @@
+# Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class MembershipMessage < ActiveRecord::Base
+ belongs_to :membership
+ belongs_to :message
+
+ # Populate the memberships_messages jointable if sender joins community with receiver
+ def self.populate_jointable(record, x_ecs_receiver_memberships, x_ecs_receiver_communities, sender_participant)
+ rec_mids = extract_x_ecs_receiver_memberships(x_ecs_receiver_memberships)
+ rec_cids = extract_x_ecs_receiver_communities(x_ecs_receiver_communities)
+ if rec_cids.blank? and rec_mids.blank?
+ raise Ecs::MissingReceiverHeaderException,
+ "You must at least specify one of X-EcsReceiverMemberships or X-EcsReceiverCommunities header\r\n"
+ end
+
+ pop_succ = false
+ memberships_for_sender_participant_id = Membership.for_participant_id(sender_participant.id)
+
+ rec_mids.each do |rmid|
+ # Test if sender joins same community as receiver
+ if memberships_for_sender_participant_id.map{|m| m.community.id}.include?(Membership.find(rmid).community.id)
+ Membership.find(rmid).messages << record unless MembershipMessage.find_by_membership_id_and_message_id(rmid, record.id)
+ pop_succ = true
+ end
+ end
+
+ rec_cids.each do |rcid|
+ # Test if sender joins same community as receiver
+ if memberships_for_sender_participant_id.map{|m| m.community.id}.include?(rcid)
+ Community.find(rcid).memberships.each do |membership|
+ if !MembershipMessage.find_by_membership_id_and_message_id(membership.id, record.id) and # relation already made
+ (sender_participant.community_selfrouting or # address sender through community
+ membership.participant.id != sender_participant.id) # address sender through community
+ membership.messages << record
+ end
+ end
+ pop_succ = true
+ end
+ end
+
+ unless pop_succ
+ raise Ecs::AuthorizationException,
+ "You are not joining at least one of the community to which you are addressing.\r\n" +
+ "or\r\n" +
+ "You are not joining at least one of the same community as the receiving membership.\r\n"
+ end
+ rescue ActiveRecord::RecordNotFound
+ raise Ecs::InvalidMessageException,
+ "Membership id in X-EcsReceiverMemberships header not found."
+ end
+
+ # Depopulate the memberships_messages jointable
+ def self.de_populate_jointable(record)
+ record.membership_messages.each do |mm|
+ mm.destroy
+ end
+ end
+
+ def self.extract_x_ecs_receiver_communities(erc)
+ receiver_communities= []
+ erc.split(',').map {|e| e.strip}.each do |comm_str|
+ if comm_str =~ /\d{#{comm_str.length}}/
+ # comm_str has only digits
+ receiver_communities << comm_str.to_i
+ else
+ # comm_str should be a community name
+ comm= Community.find_by_name(comm_str)
+ if comm == nil then
+ raise Ecs::InvalidMessageException, "community id/name in X-EcsReceiverCommunities header not found: #{comm_str}"
+ end
+ receiver_communities << comm.id
+ end
+ end unless erc.blank?
+ receiver_communities.uniq!
+ receiver_communities
+ end
+
+ def self.extract_x_ecs_receiver_memberships(erm)
+ receiver_memberships= []
+ erm.split(',').map {|e| e.strip}.each do |memb_str|
+ if memb_str =~ /\d{#{memb_str.length}}/
+ # memb_str has only digits
+ #receiver_memberships.concat Membership.find(memb_str.to_i).community.memberships.map{ |m| m.id }
+ receiver_memberships << memb_str.to_i if Membership.find(memb_str.to_i)
+ else
+ # memb_str is invalid, because it's not an integer value
+ # raise Exception
+ end
+ end unless erm.blank?
+ receiver_memberships.uniq!
+ receiver_memberships
+ end
+
+
+private
+
+ # Deletes all records with relations between a record and the given
+ # memberships or all record with relation to the given message
+ # (memberships=nil)
+ def self.delete_relations(message, memberships=nil)
+ if memberships
+ memberships.each do |m|
+ destroy_all ["membership_id = ? and message_id = ?", m.id, message.id]
+ end
+ else
+ destroy_all ["message_id = ?", message.id]
+ end
+ end
+
+end
diff --git a/app/models/message.rb b/app/models/message.rb
new file mode 100644
index 0000000..24eb296
--- /dev/null
+++ b/app/models/message.rb
@@ -0,0 +1,324 @@
+# Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Message < ActiveRecord::Base
+
+ require 'exceptions'
+ require 'json/add/rails'
+
+ has_many :memberships, :through => :membership_messages
+ has_many :membership_messages
+ has_many :events, :dependent => :destroy
+ has_many :community_messages, :dependent => :destroy
+ has_many :communities, :through => :community_messages
+ has_one :auth, :dependent => :destroy
+ belongs_to :ressource
+
+ named_scope :for_participant_receiver, lambda {|participant| {
+ :joins => {:membership_messages => {:membership => :participant}},
+ :order => "id ASC",
+ :conditions => {:participants => {:id => participant.id}},
+ :readonly => false}}
+
+ named_scope :for_participant_sender, lambda {|participant| {
+ :order => "id ASC",
+ :conditions => {:sender => participant.id},
+ :readonly => false}}
+
+ named_scope :for_not_removed, lambda { {
+ :order => "id ASC",
+ :conditions => {:removed => false}}}
+
+ named_scope :for_removed, lambda { {
+ :order => "id ASC",
+ :conditions => {:removed => true}}}
+
+ named_scope :for_resource, lambda {|namespace, name| {
+ :joins => :ressource,
+ :order => "id ASC",
+ :conditions => {:ressources => {:namespace => namespace, :ressource => name}}}}
+
+ def self.create__(request, app_namespace, ressource_name, participant)
+ transaction do
+ message = create! do |arm|
+ arm.create_update_helper(request, app_namespace, ressource_name, participant.id)
+ end
+ MembershipMessage.extract_x_ecs_receiver_communities(request.headers["X-EcsReceiverCommunities"]).each do |cid|
+ message.communities << Community.find(cid)
+ end
+ MembershipMessage.populate_jointable(message,
+ request.headers["X-EcsReceiverMemberships"],
+ request.headers["X-EcsReceiverCommunities"],
+ participant)
+ Participant.for_message(message).uniq.each do |p|
+ Event.make(:event_type_name => EvType.find(1).name, :participant => p, :message => message)
+ end if message.ressource.events
+ if app_namespace == 'sys' and ressource_name == 'auths'
+ message.post_create_auths_resource(participant)
+ end
+ message
+ end
+ rescue ActiveRecord::RecordInvalid
+ raise Ecs::InvalidMessageException, $!.to_s
+ end
+
+ def update__(request, app_namespace, ressource_name, participant)
+ raise(Ecs::AuthorizationException, "You are not the original sender of the message.") unless participant.sender?(self)
+ transaction do
+ create_update_helper(request, app_namespace, ressource_name, participant.id)
+ save!
+ receivers_old = Participant.for_message(self).uniq
+ MembershipMessage.de_populate_jointable(self)
+ MembershipMessage.populate_jointable(self,
+ request.headers["X-EcsReceiverMemberships"],
+ request.headers["X-EcsReceiverCommunities"],
+ participant)
+ receivers_new = Participant.for_message(self).uniq
+ # TODO: if there are only the headers X-EcsReceiverMemberships and
+ # X-EcsReceiverCommunities are updated, then we have to generate events only
+ # for these new and removed receivers. To distinguish if the message body
+ # is untouched we can use the ETag functionality.
+ (receivers_new & receivers_old).each do |p|
+ # generate updated events
+ Event.make(:event_type_name => EvType.find(3).name, :participant => p, :message => self)
+ end if self.ressource.events
+ (receivers_old - receivers_new).each do |p|
+ # generate destroyed events
+ Event.make(:event_type_name => EvType.find(2).name, :participant => p, :message => self)
+ end if self.ressource.events
+ (receivers_new - receivers_old).each do |p|
+ # generate created events
+ Event.make(:event_type_name => EvType.find(1).name, :participant => p, :message => self)
+ end if self.ressource.events
+ if app_namespace == 'sys' and ressource_name == 'auths'
+ post_create_auths_resource(participant)
+ end
+ self
+ end
+ rescue ActiveRecord::RecordInvalid
+ raise Ecs::InvalidMessageException, $!.to_s
+ end
+
+ def validate
+ if content_type.blank? then
+ errors.add_to_base("*** You must povide a \"Content-Type\" header. ")
+ end
+ if body.blank? then
+ errors.add_to_base("*** You have to provide a \"http body\". *** ")
+ end
+ if sender.blank? then
+ errors.add_to_base("*** There is no \"sender\"; this is a fatal error; please report this to ecs@freeit.de. *** ")
+ end
+ end
+
+ # return first messages from fifo/lifo queue
+ def self.fifo_lifo_rest(namespace, ressource, participant_id, options={:queue_type => :fifo})
+ m=find(:all, :readonly => false, :lock => true,
+ :select => "messages.id",
+ :joins => [:ressource, { :membership_messages => { :membership => :participant } }],
+ :conditions => { :participants => { :id => participant_id },
+ :ressources => { :namespace => namespace, :ressource => ressource } },
+ :order => :messages.to_s+".id #{(options[:queue_type]==:fifo)?'ASC':'DESC'}")
+ if m.empty? then nil else find(m[0]) end
+ end
+
+ # get a record out of the message table
+ def self.get_record(msg_id, app_namespace, ressource_name)
+ outdated_auth_token = nil
+ ressource = Ressource.find_by_namespace_and_ressource(app_namespace, ressource_name)
+ raise(Ecs::InvalidRessourceUriException, "*** ressource uri error ***") unless ressource
+ if app_namespace == 'sys' and ressource_name == 'auths'
+ # processing a auths resource
+ if msg_id =~ /\D/
+ # asking a one touch token with the hash key
+ auth = Auth.find_by_one_touch_hash(msg_id)
+ if auth
+ record = auth.message
+ else
+ raise ActiveRecord::RecordNotFound, "Invalid auths hash"
+ end
+ else
+ unless record = find_by_id_and_ressource_id(msg_id.to_i, ressource.id)
+ raise ActiveRecord::RecordNotFound, "Invalid auths id"
+ end
+ end
+ else
+ record = find_by_id_and_ressource_id(msg_id.to_i, ressource.id)
+ end
+ if !record or record.removed
+ raise ActiveRecord::RecordNotFound, "Invalid resource id"
+ else
+ [record, outdated_auth_token]
+ end
+ end
+
+ def filter(action_name, app_namespace, ressource_name, params)
+ d="filter/#{app_namespace}/#{ressource_name}/#{action_name}/*"
+ filters=Dir[d].collect{|f| File.directory?(f) ? f : nil}.compact
+ return if filters.empty?
+ FILTER_API.params= params
+ FILTER_API.record= self
+ filters.sort!
+ filters.each do |f|
+ files= Dir[f+'/*.rb']
+ next if files.empty?
+ EcsFilter.constants.each {|c| EcsFilter.instance_eval { remove_const c.to_sym } }
+ files.each do |e|
+ EcsFilter.module_eval IO.read(e)
+ end
+ eval "EcsFilter::Filter.start"
+ end
+ rescue Exception
+ logger.error "Filter Exception: "+$!.class.to_s+": "+$!.backtrace[0]
+ logger.error "Filter Exception: "+$!.message
+ end
+
+ # Request body has to be in json format.
+ # Preprocess request body if it's a /sys/auths resource.
+ # Generate a one touch token (hash)
+ def post_create_auths_resource(participant)
+ ttl_min = 5.seconds
+ ttl = ttl_min + 60.seconds
+ unless Mime::Type.lookup(self.content_type).to_sym == :json
+ raise Ecs::InvalidMimetypeException, "Body format has to be in JSON"
+ end
+ begin
+ b = JSON.parse(self.body)
+ rescue JSON::ParserError
+ raise Ecs::InvalidMessageException, "Invalid JSON body"
+ end
+ bks = b.keys
+
+ # NOTE Assures that there are at least url or realm set -> backward compatibility
+ unless bks.include?("url") or bks.include?("realm")
+ raise Ecs::InvalidMessageException, "You have to provide realm or url attribute"
+ end
+
+ #msg_id = URI.split(b["url"])[5][1..-1].sub(/[^\/]*\/[^\/]*\/(.*)/, '\1').to_i
+ #begin
+ # Message.find(msg_id)
+ #rescue ActiveRecord::RecordNotFound
+ # raise Ecs::InvalidMessageException, $!.to_s
+ #end
+ case
+ when (!bks.include?("sov") and !bks.include?("eov"))
+ b["sov"] = Time.now.xmlschema
+ b["eov"] = (Time.now + ttl).xmlschema
+ when (bks.include?("sov") and !bks.include?("eov"))
+ if Time.parse(b["sov"]) < Time.now
+ raise Ecs::InvalidMessageException, 'sov time is younger then current time'
+ end
+ b["eov"] = (Time.parse(b["sov"]) + ttl).xmlschema
+ when (!bks.include?("sov") and bks.include?("eov"))
+ if Time.parse(b["eov"]) < (Time.now + ttl_min)
+ raise Ecs::InvalidMessageException, 'eov time is too young'
+ end
+ b["sov"] = Time.now.xmlschema
+ when (bks.include?("sov") and bks.include?("eov"))
+ if (Time.parse(b["eov"]) < Time.now) or (Time.parse(b["eov"]) < Time.parse(b["sov"]))
+ raise Ecs::InvalidMessageException, 'invalid times either in sov or eov'
+ end
+ end
+ b["abbr"] = participant.organization.abrev
+ one_touch_token_hash = Digest::SHA1.hexdigest(rand.to_s+Time.now.to_s)
+ b["hash"] = one_touch_token_hash
+ b["pid"] = participant.id
+ self.body = JSON.pretty_generate(b)
+ self.auth = Auth.new :one_touch_hash => one_touch_token_hash
+ save!
+ self
+ end
+
+ # If the record has zero relations to memberships and is not tagged for
+ # postrouting it will be deleted.
+ def destroy_as_receiver(participant=nil)
+ memberships= Membership.receiver(participant.id, self.id)
+ if memberships.empty?
+ raise Ecs::NoReceiverOfMessageException,
+ "you are not a receiver of " +
+ "\"#{self.ressource.namespace}/#{self.ressource.ressource}/#{self.id.to_s}\""
+ end
+ if participant
+ MembershipMessage.delete_relations(self, memberships)
+ end
+ destroy_or_tag_as_removed if membership_messages.blank? and !ressource.postroute
+ end
+ alias destroy_unlinked_and_not_postrouted destroy_as_receiver
+
+
+ # Delete a message and send appropriate events. It will only be "fully"
+ # deleted when there are no references from any events otherwise it will be
+ # tagged as deleted.
+ def destroy_as_sender
+ participants = Participant.for_message(self).uniq
+ participants.each do |participant|
+ Event.make(:event_type_name => EvType.find(2).name, :participant => participant, :message => self)
+ end if ressource.events
+ MembershipMessage.delete_relations(self)
+ destroy_or_tag_as_removed
+ end
+ alias destroy_ destroy_as_sender
+
+ def outtimed_auths_resource_by_non_owner?(app_namespace, resource_name, participant)
+ memberships= Membership.receiver(participant.id, self.id)
+ app_namespace == 'sys' and
+ resource_name == 'auths' and
+ !memberships.empty? and
+ !participant.sender?(self) and
+ !auth.test_validation_window
+ end
+
+ def valid_auths_resource_fetched_by_non_owner?(app_namespace, resource_name, memberships, participant)
+ app_namespace == 'sys' and
+ resource_name == 'auths' and
+ !memberships.empty? and
+ !participant.sender?(self) and
+ auth.test_validation_window
+ end
+
+ def valid_no_auths_resource_fetched_by_non_owner?(app_namespace, resource_name, memberships, participant)
+ app_namespace != 'sys' and
+ ressource_name != 'auths' and
+ !memberships.empty? and
+ !participant.sender?(self)
+ end
+
+ # Helper function for create and update
+ def create_update_helper(request, app_namespace, ressource_name, participant_id)
+ ressource = Ressource.find_by_namespace_and_ressource(app_namespace, ressource_name)
+ raise(Ecs::InvalidRessourceUriException, "*** ressource uri error ***") unless ressource
+ self.ressource_id = ressource.id
+ self.content_type = request.headers["CONTENT_TYPE"]
+ self.sender = participant_id
+ self.body = request.raw_post
+ end
+
+private
+
+ # Deletes the message if there are no references from events otherwise it
+ # will be tagged as deleted.
+ def destroy_or_tag_as_removed
+ if self.events.blank?
+ destroy
+ else
+ self.removed = true
+ save!
+ end
+ end
+
+end
diff --git a/app/models/organization.rb b/app/models/organization.rb
new file mode 100644
index 0000000..2fcbb3c
--- /dev/null
+++ b/app/models/organization.rb
@@ -0,0 +1,32 @@
+# Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Organization < ActiveRecord::Base
+ # TODO: warn the user about possible deletions of participants.
+ has_many :participants, :dependent => :destroy
+
+ validates_presence_of :name, :abrev
+ validates_uniqueness_of :name, :abrev
+
+ def orgtext
+ "#{name} (#{abrev})"
+ end
+ def orgtext=(name)
+ return
+ end
+end
diff --git a/app/models/participant.rb b/app/models/participant.rb
new file mode 100644
index 0000000..5fc363c
--- /dev/null
+++ b/app/models/participant.rb
@@ -0,0 +1,169 @@
+# Copyright (C) 2007, 2008, 2009, 2010, 2014 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Participant < ActiveRecord::Base
+ TTL = 1.hour # how long an anonymous participant lives
+ TYPE={ :main => "main", :sub => "sub", :anonym => "anonym" }
+
+ after_destroy :delete_messages
+
+ belongs_to :organization
+ # TODO: warn user about possible deletions of messages.
+ has_many :memberships, :dependent => :destroy
+ has_many :communities, :through => :memberships
+ has_many :identities, :dependent => :destroy
+ has_many :events, :dependent => :destroy
+ has_many :childs,
+ :order => "id ASC",
+ :class_name => "Subparticipant",
+ :foreign_key => "parent_id",
+ :dependent => :destroy
+ has_one :subparticipant, :dependent => :destroy
+
+ validates_presence_of :name, :organization_id
+ validates_uniqueness_of :name
+
+ accepts_nested_attributes_for :identities, :allow_destroy => true, :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
+ accepts_nested_attributes_for :communities, :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
+ accepts_nested_attributes_for :subparticipant, :allow_destroy => true
+
+ named_scope :order_id_asc, :order => "participants.id ASC"
+ named_scope :without_anonymous, :conditions => { :participants => { :anonymous => false } }
+ named_scope :anonymous, :conditions => { :participants => { :anonymous => true } }
+ named_scope :for_message, lambda { |message| {
+ :joins => [:memberships => {:membership_messages => :message}],
+ :conditions => {:messages => {:id => message.id}}}}
+ named_scope :for_community, lambda { |community| {
+ :joins => [:memberships => :community],
+ :conditions => { :communities => { :id => community.id }}}}
+ named_scope :for_subparticipants
+ named_scope :itsyou, lambda { |itsyoupid| { :conditions => { :participants => { :id => itsyoupid } } } }
+ named_scope :only_subparticipants, :conditions => { :participants => { :ptype => TYPE[:sub] } }
+ named_scope :only_mainparticipants, :conditions => { :participants => { :ptype => TYPE[:main] } }
+ named_scope :only_anonymous, :conditions => { :participants => { :ptype => TYPE[:anonym] } }
+
+ def self.reduced_attributes
+ find :all, :select => "participants.id, participants.name, participants.description, participants.email, participants.dns, participants.organization_id, participants.ptype"
+ end
+
+ def self.mainparticipants_with_reduced_attributes
+ only_mainparticipants.order_id_asc.reduced_attributes
+ end
+
+ def self.subparticipants_with_reduced_attributes
+ only_subparticipants.order_id_asc.reduced_attributes
+ end
+
+ def self.anonymous_participants_with_reduced_attributes
+ only_anonymous.order_id_asc.reduced_attributes
+ end
+
+ def destroy_receiver_messages
+ Message.for_participant_receiver(self).each do |m|
+ m.destroy_as_receiver(self)
+ end
+ end
+
+ def destroy_sender_messages
+ Message.for_participant_sender(self).each do |m|
+ m.destroy_as_sender
+ end
+ end
+
+ def destroy_events
+ self.events.each do |e|
+ e.destroy
+ end
+ end
+
+ def mainparticipant?
+ if not anonymousparticipant? and subparticipant.nil?
+ true
+ else
+ false
+ end
+ end
+
+ def subparticipant?
+ if not anonymousparticipant? and not subparticipant.nil?
+ true
+ else
+ false
+ end
+ end
+
+ def anonymousparticipant?
+ anonymous?
+ end
+
+ # test if the participant is the initial sender of the message in question.
+ def sender?(message)
+ if message.sender == id
+ true
+ else
+ false
+ end
+ end
+
+ def receiver?(message)
+ not Membership.receiver(id, message.id).empty?
+ end
+
+ def events?
+ self.events_.blank? ? false : true
+ end
+
+ def anonymous?
+ anonymous
+ end
+
+ def self.generate_anonymous_participant
+ cookie = Digest::SHA1.hexdigest('something secret'+Time.now.to_s+rand.to_s)
+ params = {
+ "name"=> "#{cookie}",
+ "identities_attributes"=>{"0"=>{"name"=>"#{cookie}", "description"=>"Cookie Identifier"}},
+ "community_ids"=>[Community.find_by_name("public").id],
+ "description"=>"Anonymous Participant",
+ "dns"=>"N/A",
+ "organization_id"=>Organization.find_by_name("not available").id,
+ "email"=>"N/A",
+ "ttl"=> DateTime.now.utc + TTL,
+ "anonymous"=>true,
+ "ptype"=>TYPE[:anonym]
+ }
+ ap = new(params)
+ ap.save!
+ return ap, cookie
+ end
+
+ def self.touch_ttl(participant)
+ participant.ttl = DateTime.now.utc + TTL
+ participant.save
+ end
+
+ def mid(community)
+ Membership.for_participant_id_and_community_id(self, community.id).first.id
+ end
+
+private
+
+ def delete_messages
+ Message.destroy_all(["sender = ?", self.id])
+ end
+
+end
diff --git a/app/models/ressource.rb b/app/models/ressource.rb
new file mode 100644
index 0000000..0a741dd
--- /dev/null
+++ b/app/models/ressource.rb
@@ -0,0 +1,50 @@
+# Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Ressource < ActiveRecord::Base
+ has_many :messages, :dependent => :destroy
+ validates_presence_of :namespace, :ressource
+ after_save :rebuild_routes
+ after_destroy :rebuild_routes
+
+ named_scope :list, :order => "namespace, ressource ASC"
+
+ def self.validates_ressource_path(namespace, ressource)
+ r = Ressource.find_by_namespace_and_ressource(namespace, ressource)
+ raise(Ecs::InvalidRessourceUriException, "*** ressource uri error ***") unless r
+ if namespace.blank? or r.namespace.blank?
+ raise Ecs::InvalidRessourceUriException, "*** namespace error ***"
+ end
+ if ressource.blank? or r.ressource.blank?
+ raise Ecs::InvalidRessourceUriException, "*** ressource name error ***"
+ end
+ return r
+ end
+
+ def events?
+ self.events.blank? ? false : true
+ end
+
+private
+
+ def rebuild_routes
+ logger.info("rebuild routes")
+ ActionController::Routing::Routes.reload!
+ end
+
+end
diff --git a/app/models/subparticipant.rb b/app/models/subparticipant.rb
new file mode 100644
index 0000000..2efc504
--- /dev/null
+++ b/app/models/subparticipant.rb
@@ -0,0 +1,137 @@
+# Copyright (C) 2014 Heiko Bernloehr (FreeIT.de).
+#
+# This file is part of ECS.
+#
+# ECS is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# ECS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with ECS. If not, see <http://www.gnu.org/licenses/>.
+
+class Subparticipant < ActiveRecord::Base
+
+ TTL = 3600 # seconds, how long a subparticipant lives, after last
+ # communication with ECS
+
+ require 'securerandom'
+
+ belongs_to :parent,
+ :class_name => "Participant",
+ :foreign_key => "parent_id"
+
+ belongs_to :participant
+
+
+ def self.generate(parent, json_data)
+ auth_id= Identity.randomized_authid
+ data = process_json_data(parent, json_data)
+ check_valid_communities(parent ,data[:community_ids])
+ params = {
+ "name" => "Subparticipant (\##{SecureRandom.hex}) from #{parent.name}",
+ "identities_attributes" => {"0"=>{"name"=>"#{auth_id}", "description"=>"Randomized authid"}},
+ "community_ids" => data[:community_ids],
+ "description" => "",
+ "dns" => "N/A",
+ "organization_id" => parent.organization.id,
+ "email" => parent.email,
+ "ttl" => nil,
+ "anonymous" => false,
+ "ptype" => Participant::TYPE[:sub],
+ "community_selfrouting" => data[:community_selfrouting],
+ "events_" => data[:events],
+ "subparticipant_attributes" => { :realm => data[:realm] }
+ }
+ participant = Participant.new(params)
+ participant.save!
+ subp= participant.subparticipant
+ subp.parent= parent
+ subp.save!
+ participant.name= "Subparticipant (id:#{subp.id})"
+ participant.description= "Created from \"#{parent.name}\" (pid:#{parent.id})"
+ participant.save!
+ subp
+ end
+
+ def update__(parent, json_data, subparticipant)
+ participant= subparticipant.participant
+ auth_id= "dummy"
+ data= process_json_data(parent, json_data)
+ Subparticipant::check_valid_communities(parent, data[:community_ids])
+ params = {
+ "community_selfrouting" => data[:community_selfrouting],
+ "community_ids" => data[:community_ids],
+ "events_" => data[:events],
+ "subparticipant_attributes" => { :id => self.id.to_s, :realm => data[:realm] }
+ }
+ participant.update_attributes(params)
+ end
+
+private
+
+ def process_json_data(sender, json_data)
+ Subparticipant::process_json_data(sender, json_data)
+ end
+
+ def self.process_json_data(sender, json_data)
+ json_data= {} unless json_data.class == Hash
+ realm= json_data["realm"] ||= nil
+ community_selfrouting= json_data["community_selfrouting"] || false
+ events= json_data["events"] ||= true
+ if json_data["communities"]
+ community_ids= json_data["communities"].map do |comm|
+ case
+ when comm.class == Fixnum
+ if Community.find_by_id(comm)
+ comm
+ else
+ raise Ecs::InvalidMessageException, comm.to_s
+ end
+ when comm.class == String
+ if (c= Community.find_by_name(comm))
+ c.id
+ else
+ raise Ecs::InvalidMessageException, comm.to_s
+ end
+ else
+ nil
+ end
+ end
+ end
+ community_ids ||= []
+ community_ids.compact!
+ { :realm => realm, :community_selfrouting => community_selfrouting, :events => events,
+ :community_ids => community_ids }
+ rescue Ecs::InvalidMessageException
+ errortext= <<-END
+You provided at least one unknown community for a subparticipant creation.
+Following community is unknown (either a cid or a community name): #{$!}
+ END
+ raise Ecs::InvalidMessageException, errortext
+ end
+
+ def self.check_valid_communities(parent, community_ids)
+ if community_ids.blank?
+ logger.debug "Subparticipant#check_valid_communities: empty community_ids"
+ return
+ end
+ parent_community_ids= parent.communities.map{|c| c.id}
+ logger.debug "Subparticipant#check_valid_communities: parent community ids = [#{parent_community_ids.join(', ')}]"
+ logger.debug "Subparticipant#check_valid_communities: subparticipant community ids = [#{community_ids.join(', ')}]"
+ logger.debug "Subparticipant#check_valid_communities: Difference between subparticipant community ids and parent community ids = [#{(community_ids - parent_community_ids).join(', ')}]"
+ unless (community_ids - parent_community_ids).blank?
+ errortext= <<-END
+The subparticipant's communities must be a subset of its parent.
+Following communities are not allowed: #{(community_ids - parent_community_ids).map{|cid|Community.find(cid).name}.join(', ')}
+ END
+ raise Ecs::InvalidMessageException, errortext
+ end
+ end
+
+end