spacer
  • CiviCRM
  • CRM-10667

Users with ACLs & participant based smart groups cannot see groups on groups tab.

  • Log In
  • Views
    • XML
    • Word
    • Printable

Details

  • Type: spacer Bug
  • Status: spacer Closed
  • Priority: spacer Minor
  • Resolution: Fixed/Completed
  • Affects Version/s: 4.2.0
  • Fix Version/s: 4.3
  • Component/s: Core CiviCRM
  • Labels:
    None
  • Is MIH?:
    No
  • Code Sprint:
    Yes

Description

This is a variant of a problem I have been struggling to pin down - but in this case I can consistently replicate it on 4.2. In order to determine which groups can be viewed by an ACL-permissioned user a query which resolves viewable smart groups is used. In this case the query contains an inner join on the event table and no data is returned due to the inner join.

The situation is that the user does not have 'view all contacts permission' but they DO have permission to see contacts via an ACL hook.

One of the groups they are permitted to see is a smart group of all participants in a particular event. This results in them not being able to see groups of any contact who is not a participant wherever they are rendered by the function.

I am using the multisite module here - although the problem would affect any usage of ACLs based on their event participation

CRM_Contact_BAO_GroupContact::getContactGroup();

ie. on the group tab of the dashboard the group list incorrectly shows as no groups (CRM_Contact_Page_View_GroupContact::browse()). The query that doesn't show the groups is called by
    $in = CRM_Contact_BAO_GroupContact::getContactGroup($this->_contactId, 'Added');

$permission = CRM_Core_Permission::whereClause(CRM_Core_Permission::VIEW, $tables, $whereTables);

&&

$from = CRM_Contact_BAO_Query::fromClause($tables);

The first query identifies the civicrm_event table as one of the tables for the from clause. The from clause eventually gets the join to civicrm_event from CRM_Event_BAO_Query::from();

THE JOIN IT USES IS AN INNER JOIN & hence the query is empty.

The query generated is below (NB this example is pretty modest - but on sites with a large number of smart groups this query can become very long & large)

SELECT COUNT(DISTINCT civicrm_group_contact.id)
FROM civicrm_contact contact_a
LEFT JOIN civicrm_address ON (contact_a.id = civicrm_address.contact_id AND civicrm_address.is_primary = 1)
LEFT JOIN civicrm_state_province ON civicrm_address.state_province_id = civicrm_state_province.id
LEFT JOIN civicrm_country ON civicrm_address.country_id = civicrm_country.id
LEFT JOIN civicrm_email ON (contact_a.id = civicrm_email.contact_id AND civicrm_email.is_primary = 1)
LEFT JOIN civicrm_phone ON (contact_a.id = civicrm_phone.contact_id AND civicrm_phone.is_primary = 1)
LEFT JOIN civicrm_im ON (contact_a.id = civicrm_im.contact_id AND civicrm_im.is_primary = 1)
LEFT JOIN civicrm_group_contact ON contact_a.id = civicrm_group_contact.contact_id
LEFT JOIN civicrm_group ON civicrm_group.id = civicrm_group_contact.group_id
LEFT JOIN civicrm_subscription_history ON civicrm_group_contact.contact_id = civicrm_subscription_history.contact_id AND civicrm_group_contact.group_id = civicrm_subscription_history.group_id
LEFT JOIN civicrm_participant ON civicrm_participant.contact_id = contact_a.id
INNER JOIN civicrm_event ON civicrm_participant.event_id = civicrm_event.id
LEFT JOIN civicrm_worldregion ON civicrm_country.region_id = civicrm_worldregion.id
LEFT JOIN civicrm_value_media_info_34 ON civicrm_value_media_info_34.entity_id = contact_a.id
LEFT JOIN civicrm_option_group option_group_gender ON (option_group_gender.name = 'gender')
LEFT JOIN civicrm_option_value gender ON (contact_a.gender_id = gender.value AND option_group_gender.id = gender.option_group_id)
LEFT JOIN civicrm_option_group option_group_prefix ON (option_group_prefix.name = 'individual_prefix')
LEFT JOIN civicrm_option_value individual_prefix ON (contact_a.prefix_id = individual_prefix.value AND option_group_prefix.id = individual_prefix.option_group_id)
LEFT JOIN civicrm_option_group option_group_suffix ON (option_group_suffix.name = 'individual_suffix')
LEFT JOIN civicrm_option_value individual_suffix ON (contact_a.suffix_id = individual_suffix.value AND option_group_suffix.id = individual_suffix.option_group_id)
WHERE contact_a.id = 10590 AND civicrm_group.is_active = 1 AND civicrm_group.is_hidden = 0 AND civicrm_group_contact.status = 'Added' AND ((civicrm_group_contact.group_id IN (41, 45, 46, 66, 70, 71, 78, 81, 82, 83, 88, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 131) AND civicrm_group_contact.status = 'Added') OR (LOWER(civicrm_worldregion.name) = 'europe' AND contact_a.contact_type IN ('Organization') AND ((contact_a.contact_sub_type LIKE '%Party%'))) OR (civicrm_value_media_info_34.media_type_283 = 'agency') OR (civicrm_event.id = 20 AND civicrm_participant.status_id = 1 AND civicrm_participant.is_test = 0) OR (civicrm_event.id = 20 AND civicrm_participant.status_id = 1 AND civicrm_participant.is_test = 0))


The saved_search entry looks like

INSERT INTO `civicrm_saved_search` (`id`, `form_values`, `mapping_id`, `search_custom_id`, `where_clause`, `select_tables`, `where_tables`) VALUES (37, 'a:127:{s:5:"qfKey";s:37:"fa6ad47e5bc8d14b6994a495cd0956b9_5745";s:12:"hidden_basic";s:1:"1";s:12:"contact_type";a:0:{}s:5:"group";a:0:{}s:12:"contact_tags";a:0:{}s:9:"sort_name";s:0:"";s:5:"email";s:0:"";s:14:"contact_source";s:0:"";s:9:"job_title";s:0:"";s:7:"uf_user";s:0:"";s:11:"uf_group_id";s:0:"";s:14:"component_mode";s:1:"3";s:8:"operator";s:3:"AND";s:25:"display_relationship_type";s:0:"";s:7:"privacy";a:6:{s:12:"do_not_phone";s:0:"";s:12:"do_not_email";s:0:"";s:11:"do_not_mail";s:0:"";s:10:"do_not_sms";s:0:"";s:12:"do_not_trade";s:0:"";s:13:"do_not_toggle";s:0:"";}s:13:"email_on_hold";a:1:{s:7:"on_hold";s:0:"";}s:30:"preferred_communication_method";a:5:{i:1;s:0:"";i:2;s:0:"";i:3;s:0:"";i:4;s:0:"";i:5;s:0:"";}s:18:"preferred_language";s:0:"";s:13:"hidden_custom";s:1:"1";s:10:"custom_149";s:0:"";s:10:"custom_150";s:0:"";s:10:"custom_151";s:0:"";s:10:"custom_152";s:0:"";s:14:"custom_58_from";s:0:"";s:12:"custom_58_to";s:0:"";s:14:"custom_61_from";s:0:"";s:12:"custom_61_to";s:0:"";s:14:"custom_62_from";s:0:"";s:12:"custom_62_to";s:0:"";s:9:"custom_64";s:0:"";s:9:"custom_66";a:0:{}s:9:"custom_67";s:0:"";s:9:"custom_68";s:0:"";s:9:"custom_69";a:0:{}s:9:"custom_70";s:0:"";s:9:"custom_79";s:0:"";s:9:"custom_72";s:0:"";i:1;s:0:"";s:10:"custom_148";a:1:{i:1;s:0:"";}s:10:"custom_153";s:0:"";s:10:"custom_154";s:0:"";s:10:"custom_155";s:0:"";s:10:"custom_156";s:0:"";s:10:"custom_157";s:0:"";s:10:"custom_158";s:0:"";s:10:"custom_159";s:0:"";s:10:"custom_160";s:0:"";s:10:"custom_161";s:0:"";s:10:"custom_162";s:0:"";s:10:"custom_164";s:0:"";s:10:"custom_165";s:0:"";s:10:"custom_166";s:0:"";s:10:"custom_167";s:0:"";s:10:"custom_168";s:0:"";s:10:"custom_170";s:0:"";s:10:"custom_172";s:0:"";s:10:"custom_173";s:0:"";s:15:"custom_174_from";s:0:"";s:13:"custom_174_to";s:0:"";s:10:"custom_208";s:0:"";s:10:"custom_209";s:0:"";s:10:"custom_210";s:0:"";s:10:"custom_211";s:0:"";s:10:"custom_212";s:0:"";s:10:"custom_213";s:0:"";s:10:"custom_214";s:0:"";s:10:"custom_215";s:0:"";s:10:"custom_216";s:0:"";s:10:"custom_217";s:0:"";s:10:"custom_218";s:0:"";s:10:"custom_221";s:0:"";s:10:"custom_219";s:0:"";s:8:"amendors";s:0:"";s:9:"delegates";s:0:"";s:7:"intsect";s:0:"";s:13:"CiviCRM_OP_OR";s:0:"";s:10:"custom_253";a:4:{s:8:"amendors";s:0:"";s:9:"delegates";s:0:"";s:7:"intsect";s:0:"";s:13:"CiviCRM_OP_OR";s:0:"";}s:16:"hidden_CiviEvent";s:1:"1";s:10:"event_name";s:35:"Copenhagen EGP Council Meeting 2012";s:10:"event_type";s:0:"";s:21:"participant_fee_level";s:0:"";s:8:"event_id";s:2:"20";s:13:"event_type_id";s:0:"";s:18:"participant_fee_id";s:0:"";s:20:"event_start_date_low";s:0:"";s:19:"event_end_date_high";s:0:"";s:21:"participant_status_id";a:1:{i:1;s:1:"1";}s:26:"participant_fee_amount_low";s:0:"";s:27:"participant_fee_amount_high";s:0:"";s:9:"custom_78";s:0:"";s:1:"a";s:0:"";s:9:"custom_73";a:1:{s:1:"a";s:0:"";}s:9:"custom_74";s:0:"";s:15:"custom_224_from";s:0:"";s:13:"custom_224_to";s:0:"";s:15:"custom_226_from";s:0:"";s:13:"custom_226_to";s:0:"";s:15:"custom_259_from";s:0:"";s:20:"custom_259_from_time";s:0:"";s:13:"custom_259_to";s:0:"";s:18:"custom_259_to_time";s:0:"";s:15:"custom_260_from";s:0:"";s:20:"custom_260_from_time";s:0:"";s:13:"custom_260_to";s:0:"";s:18:"custom_260_to_time";s:0:"";s:10:"custom_262";s:0:"";s:10:"custom_263";s:0:"";s:3:"yes";s:0:"";s:10:"custom_264";a:1:{s:3:"yes";s:0:"";}s:10:"custom_275";s:0:"";s:10:"custom_276";a:1:{s:3:"yes";s:0:"";}s:10:"custom_277";s:0:"";s:10:"custom_279";s:0:"";s:10:"custom_280";s:0:"";s:10:"custom_270";s:0:"";s:15:"custom_271_from";s:0:"";s:20:"custom_271_from_time";s:0:"";s:13:"custom_271_to";s:0:"";s:18:"custom_271_to_time";s:0:"";s:15:"custom_272_from";s:0:"";s:20:"custom_272_from_time";s:0:"";s:13:"custom_272_to";s:0:"";s:18:"custom_272_to_time";s:0:"";s:10:"custom_273";s:0:"";s:10:"custom_274";a:1:{s:3:"yes";s:0:"";}s:4:"task";s:2:"13";s:8:"radio_ts";s:6:"ts_all";}', NULL, NULL, ' ( civicrm_event.id = 20 AND civicrm_participant.status_id = 1 AND civicrm_participant.is_test = 0 ) ', 'a:13:{s:15:"civicrm_contact";i:1;s:15:"civicrm_address";i:1;s:22:"civicrm_state_province";i:1;s:15:"civicrm_country";i:1;s:13:"civicrm_email";i:1;s:13:"civicrm_phone";i:1;s:10:"civicrm_im";i:1;s:19:"civicrm_participant";i:1;s:13:"civicrm_event";i:1;s:19:"civicrm_worldregion";i:1;s:6:"gender";i:1;s:17:"individual_prefix";i:1;s:17:"individual_suffix";i:1;}', 'a:3:{s:15:"civicrm_contact";i:1;s:19:"civicrm_participant";i:1;s:13:"civicrm_event";i:1;}');


I'm not too sure the best approach to this - at the multisite module level being able to identify which smart groups should be target groups & which should not would be helpful - in which case i would just not give this group 'type 3' or whatever & it would be identified as listable in things like the group tab but not not requiring resolution.

However, I guess in this situation the issue is that we are trying to identify which groups a contact should be able to see - not the contacts within them so potentially

CRM_Core_Permission_Drupal::group

needs to understand if it is being called in order to find groups or contacts. ie if it is being called in order to determine the viewable groups then it shouldn't be resolving smart groups as they are not material to the viewable group list.

Activity

Ascending order - Click to sort in descending order
    All Comments Work Log History Activity Commits Source Reviews
Hide
Permalink
Eileen McNaughton added a comment -
Related issue issues.civicrm.org/jira/browse/CRM-10658 - the symptoms described there also appear on the situation here (although I have focussed on a single 'form' of the problem which is easy to replicate / see)
Show
Eileen McNaughton added a comment - Related issue issues.civicrm.org/jira/browse/CRM-10658 - the symptoms described there also appear on the situation here (although I have focussed on a single 'form' of the problem which is easy to replicate / see)
Hide
Permalink
Eileen McNaughton added a comment -
OK - so my thinking here is that the function

CRM_Contact_BAO_GroupContact::getContactGroup();

Is apparently intended as

   * function to get the list of groups for contact based on status of membership

Normally the smart groups WOULD NOT be rendered for the group contact tab but when the

However, the function called

CRM_Core_Permission::whereClause

is intended to find the contacts the person can see rather than the groups they can see.

ie. it is adding smart group criteria to the query because it's based on contact permissions. ignoring permissions isn't right either as it will include all groups. We need to filter by permitted groups - but not permitted contacts - in this function - assuming the comment description is correct

Show
Eileen McNaughton added a comment - OK - so my thinking here is that the function CRM_Contact_BAO_GroupContact::getContactGroup(); Is apparently intended as    * function to get the list of groups for contact based on status of membership Normally the smart groups WOULD NOT be rendered for the group contact tab but when the However, the function called CRM_Core_Permission::whereClause is intended to find the contacts the person can see rather than the groups they can see. ie. it is adding smart group criteria to the query because it's based on contact permissions. ignoring permissions isn't right either as it will include all groups. We need to filter by permitted groups - but not permitted contacts - in this function - assuming the comment description is correct
Hide
Permalink
Eileen McNaughton added a comment -
nb - drupal 6 patch brings function into line with d7 - changes are the same
Show
Eileen McNaughton added a comment - nb - drupal 6 patch brings function into line with d7 - changes are the same
Hide