canvas-lms/lib/address_book.rb

93 lines
3.3 KiB
Ruby
Raw Normal View History

#
# Copyright (C) 2016 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas 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, version 3 of the License.
#
# Canvas 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 this program. If not, see <http://www.gnu.org/licenses/>.
AddressBook facade fixes CNVS-29824 refs CNVS-29862, CNVS-29867 test-plan: check for regressions around: * accessing profile#show endpoint, with profiles enabled, is authorized if and only if current user knows profile owner. * eportfolios#show, with profiles enabled, includes link to owner's profile if and only if current user knows owner. * conversations: - filtering of individual recipients when creating a conversation - expansion of context recipients when creating a conversation - restriction of individual recipients to those known via course when creating a conversation in a course - including as an admin over that course - filtering of individual recipients when adding a message to an existing conversation allows existing recipients * searching/browsing address book (conversation creation, link observer to students, due date overrides, etc.): - when loading info about single user (user_id parameter), including combinations of conversation_id and context parameters, returns user data if and only if the target is known to current user under those parameters - users matched: - excludes already listed users - restricts to users known via specified context, if any - including as an admin over that context - finds users with weak associations (e.g. invited student that hasn't logged in for first time yet) when linking observers - doesn't find users with weak associations otherwise - contexts matched: - limited to those known to current user - have count of known users - have counts of known users in synthetic contexts * data returned for known users in various API calls include common course and groups between current user and returned user Change-Id: I66bca0921b298be8d529a713fa982a6dfdcbcc8e Reviewed-on: https://gerrit.instructure.com/84045 Reviewed-by: Jonathan Featherstone <jfeatherstone@instructure.com> Tested-by: Jenkins QA-Review: Deepeeca Soundarrajan <dsoundarrajan@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2016-06-17 00:37:56 +08:00
# manually require since ::MessageableUser satisfies
# AddressBook::MessageableUser and prevents the autoload
require_relative 'address_book/messageable_user'
# see AddressBook::Base for primary documentation of the interface
module AddressBook
STRATEGIES = {
'messageable_user' => { implementation: AddressBook::MessageableUser, label: lambda{ I18n.t('MessageableUser library') } }.freeze,
'microservice' => { implementation: AddressBook::Service, label: lambda{ I18n.t('AddressBook microservice') } }.freeze,
add addressbook performance tap option closes CNVS-34528 another option for the address book strategy chosen in the plugin. combines answers from current MessageableUser implementation with traffic directed to the service as if it were in use. but the service is instructed via `ignore_result` parameter to reply to canvas as quickly as possible, so that if the service does have performance issues, actual canvas performance is not affected. test-plan: - set up canvas to be able to talk to the address book service - have a published course with an active teacher and an active student - do _not_ replicate that data into the address book service; i.e. the service answer should be wrong if used - choose the "AddressBook performance tap" as the address book implementation in the plugin setting - logged in as the teacher, query for the student in the inbox. should find the student (backed by the messageableuser implementation) - check the service request logs; even though the service response wasn't used, there should still be a request made against it - take down the service entirely - repeat the query in the inbox. should still get the result from the messageableuser implementation despite the service being unavailable Change-Id: I031b7c397d2e20d74d7699a81ca8040064a8df75 Reviewed-on: https://gerrit.instructure.com/104112 Reviewed-by: Andrew Huff <ahuff@instructure.com> Tested-by: Jenkins QA-Review: Deepeeca Soundarrajan <dsoundarrajan@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2017-03-07 03:10:42 +08:00
'performance_tap' => { implementation: AddressBook::PerformanceTap, label: lambda{ I18n.t('AddressBook performance tap') } }.freeze,
'empty' => { implementation: AddressBook::Empty, label: lambda{ I18n.t('Empty stub (for testing only)') } }.freeze
}.freeze
DEFAULT_STRATEGY = 'messageable_user'
AddressBook facade fixes CNVS-29824 refs CNVS-29862, CNVS-29867 test-plan: check for regressions around: * accessing profile#show endpoint, with profiles enabled, is authorized if and only if current user knows profile owner. * eportfolios#show, with profiles enabled, includes link to owner's profile if and only if current user knows owner. * conversations: - filtering of individual recipients when creating a conversation - expansion of context recipients when creating a conversation - restriction of individual recipients to those known via course when creating a conversation in a course - including as an admin over that course - filtering of individual recipients when adding a message to an existing conversation allows existing recipients * searching/browsing address book (conversation creation, link observer to students, due date overrides, etc.): - when loading info about single user (user_id parameter), including combinations of conversation_id and context parameters, returns user data if and only if the target is known to current user under those parameters - users matched: - excludes already listed users - restricts to users known via specified context, if any - including as an admin over that context - finds users with weak associations (e.g. invited student that hasn't logged in for first time yet) when linking observers - doesn't find users with weak associations otherwise - contexts matched: - limited to those known to current user - have count of known users - have counts of known users in synthetic contexts * data returned for known users in various API calls include common course and groups between current user and returned user Change-Id: I66bca0921b298be8d529a713fa982a6dfdcbcc8e Reviewed-on: https://gerrit.instructure.com/84045 Reviewed-by: Jonathan Featherstone <jfeatherstone@instructure.com> Tested-by: Jenkins QA-Review: Deepeeca Soundarrajan <dsoundarrajan@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2016-06-17 00:37:56 +08:00
def self.registry
RequestStore.store[:address_books] ||= {}
end
# choose the implementation of address book according to the plugin setting
def self.strategy
strategy = Canvas::Plugin.find('address_book').settings[:strategy]
unless STRATEGIES.has_key?(strategy)
# plugin setting specifies an invalid strategy. (TODO: logger.warn or
# something.) gracefully fall back on default
strategy = DEFAULT_STRATEGY
end
strategy
end
def self.implementation
return STRATEGIES[strategy][:implementation]
end
# instantiates an address book for the sender
AddressBook facade fixes CNVS-29824 refs CNVS-29862, CNVS-29867 test-plan: check for regressions around: * accessing profile#show endpoint, with profiles enabled, is authorized if and only if current user knows profile owner. * eportfolios#show, with profiles enabled, includes link to owner's profile if and only if current user knows owner. * conversations: - filtering of individual recipients when creating a conversation - expansion of context recipients when creating a conversation - restriction of individual recipients to those known via course when creating a conversation in a course - including as an admin over that course - filtering of individual recipients when adding a message to an existing conversation allows existing recipients * searching/browsing address book (conversation creation, link observer to students, due date overrides, etc.): - when loading info about single user (user_id parameter), including combinations of conversation_id and context parameters, returns user data if and only if the target is known to current user under those parameters - users matched: - excludes already listed users - restricts to users known via specified context, if any - including as an admin over that context - finds users with weak associations (e.g. invited student that hasn't logged in for first time yet) when linking observers - doesn't find users with weak associations otherwise - contexts matched: - limited to those known to current user - have count of known users - have counts of known users in synthetic contexts * data returned for known users in various API calls include common course and groups between current user and returned user Change-Id: I66bca0921b298be8d529a713fa982a6dfdcbcc8e Reviewed-on: https://gerrit.instructure.com/84045 Reviewed-by: Jonathan Featherstone <jfeatherstone@instructure.com> Tested-by: Jenkins QA-Review: Deepeeca Soundarrajan <dsoundarrajan@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2016-06-17 00:37:56 +08:00
def self.for(sender)
registry[sender] ||= implementation.new(sender)
AddressBook facade fixes CNVS-29824 refs CNVS-29862, CNVS-29867 test-plan: check for regressions around: * accessing profile#show endpoint, with profiles enabled, is authorized if and only if current user knows profile owner. * eportfolios#show, with profiles enabled, includes link to owner's profile if and only if current user knows owner. * conversations: - filtering of individual recipients when creating a conversation - expansion of context recipients when creating a conversation - restriction of individual recipients to those known via course when creating a conversation in a course - including as an admin over that course - filtering of individual recipients when adding a message to an existing conversation allows existing recipients * searching/browsing address book (conversation creation, link observer to students, due date overrides, etc.): - when loading info about single user (user_id parameter), including combinations of conversation_id and context parameters, returns user data if and only if the target is known to current user under those parameters - users matched: - excludes already listed users - restricts to users known via specified context, if any - including as an admin over that context - finds users with weak associations (e.g. invited student that hasn't logged in for first time yet) when linking observers - doesn't find users with weak associations otherwise - contexts matched: - limited to those known to current user - have count of known users - have counts of known users in synthetic contexts * data returned for known users in various API calls include common course and groups between current user and returned user Change-Id: I66bca0921b298be8d529a713fa982a6dfdcbcc8e Reviewed-on: https://gerrit.instructure.com/84045 Reviewed-by: Jonathan Featherstone <jfeatherstone@instructure.com> Tested-by: Jenkins QA-Review: Deepeeca Soundarrajan <dsoundarrajan@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2016-06-17 00:37:56 +08:00
end
# partitions the list of recipients into user ids and context asset strings
def self.partition_recipients(recipients)
users = ::MessageableUser.individual_recipients(recipients)
contexts = ::MessageableUser.context_recipients(recipients)
return users, contexts
end
# filters the list of users to only those that are "available" (but not
# necessarily known to any particular sender)
def self.available(users)
Shackles.activate(:slave) do
::MessageableUser.available.where(id: users).to_a
end
end
unify admin view handling in address book fixes CNVS-35217, CNVS-35799, CNVS-35800 - allow Services::AddressBook.known_in_context to take a whitelist of user_ids. enables... - allow address_book.known_users to take a context parameter (replaces include_context; see note below). enables... - use known_users(users, context: ...) when normalizing recipients in the conversations controller, in place of post-filtering results from known_in_context(context, maybe_admin). - remove is_admin flag from address_book.known_in_context; admin view never utilized for this method - also remove is_admin flag from address_book.search_users; admin view is determined implicitly when context parameter present - move admin_context checks from conversations controller and search controller inside the address book - don't apply read_as_admin check when evaluating params[:context] for visible contexts in search results (it wasn't intended for that, only for @admin_context, which is gone) - finally, make passing of contexts consistent; all of known_users, known_in_context, and search_users allow asset_strings _or_ context objects note on include_context: include_context was intended to force inclusion of participants in an otherwise unconnected context for an externally vetted admin, without restricting other contexts. the context parameter is instead intended to restrict to participants known either through that context, or as an admin over that context while other contexts are ignored. this is more consistent with how the call was being used. meanwhile, whether an admin view is appropriate for that context is determined within the address book instead of by the caller. when determining whether a context is eligible for admin view within known_users or search_users, the sender must have read_as_admin permission on the context _and_ not be a participant in the context's course (if any). this allows restrictions due to the course participation to override read_as_admin, as they should. test-plan: [CNVS-35217] - have an account with an account admin - have a course in that account with two teachers (one section-limited, one not), and a student in a different section than the section-limited teacher (all active, published, etc.) - ingest the data into the address book service and configure canvas to use the service - as the account admin, search for the student in the inbox in the context of the course; should find them. - as the non-section-limited teacher, search for the student in the inbox in the context of the course; should find them. - as the section-limited teacher, search for the student in the inbox in the context of the course; should not find them. [CNVS-35799, CNVS-35800] - add two more students to the same course, then remove one from the course and deactivate the other - re-ingest the data into the address book service (or have had the kinesis stream hooked up during the change) - as the non-section-limited teacher, search for the student that was added but then removed; should not find them - as the non-section-limited teacher, search for the student that was deactivated; should not find them - as the account admin, search for the student that was deactivated; should not find them Change-Id: I3233d8d45394c9233adc9888bfaf477e73bfde11 Reviewed-on: https://gerrit.instructure.com/107053 Tested-by: Jenkins Reviewed-by: Andrew Huff <ahuff@instructure.com> QA-Review: Deepeeca Soundarrajan <dsoundarrajan@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2017-03-31 05:31:28 +08:00
def self.decompose_context(context_code)
context_code &&
context_code =~ ::MessageableUser::Calculator::CONTEXT_RECIPIENT &&
Regexp.last_match.to_a[1..-1]
end
AddressBook facade fixes CNVS-29824 refs CNVS-29862, CNVS-29867 test-plan: check for regressions around: * accessing profile#show endpoint, with profiles enabled, is authorized if and only if current user knows profile owner. * eportfolios#show, with profiles enabled, includes link to owner's profile if and only if current user knows owner. * conversations: - filtering of individual recipients when creating a conversation - expansion of context recipients when creating a conversation - restriction of individual recipients to those known via course when creating a conversation in a course - including as an admin over that course - filtering of individual recipients when adding a message to an existing conversation allows existing recipients * searching/browsing address book (conversation creation, link observer to students, due date overrides, etc.): - when loading info about single user (user_id parameter), including combinations of conversation_id and context parameters, returns user data if and only if the target is known to current user under those parameters - users matched: - excludes already listed users - restricts to users known via specified context, if any - including as an admin over that context - finds users with weak associations (e.g. invited student that hasn't logged in for first time yet) when linking observers - doesn't find users with weak associations otherwise - contexts matched: - limited to those known to current user - have count of known users - have counts of known users in synthetic contexts * data returned for known users in various API calls include common course and groups between current user and returned user Change-Id: I66bca0921b298be8d529a713fa982a6dfdcbcc8e Reviewed-on: https://gerrit.instructure.com/84045 Reviewed-by: Jonathan Featherstone <jfeatherstone@instructure.com> Tested-by: Jenkins QA-Review: Deepeeca Soundarrajan <dsoundarrajan@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2016-06-17 00:37:56 +08:00
def self.valid_context?(context_code)
unify admin view handling in address book fixes CNVS-35217, CNVS-35799, CNVS-35800 - allow Services::AddressBook.known_in_context to take a whitelist of user_ids. enables... - allow address_book.known_users to take a context parameter (replaces include_context; see note below). enables... - use known_users(users, context: ...) when normalizing recipients in the conversations controller, in place of post-filtering results from known_in_context(context, maybe_admin). - remove is_admin flag from address_book.known_in_context; admin view never utilized for this method - also remove is_admin flag from address_book.search_users; admin view is determined implicitly when context parameter present - move admin_context checks from conversations controller and search controller inside the address book - don't apply read_as_admin check when evaluating params[:context] for visible contexts in search results (it wasn't intended for that, only for @admin_context, which is gone) - finally, make passing of contexts consistent; all of known_users, known_in_context, and search_users allow asset_strings _or_ context objects note on include_context: include_context was intended to force inclusion of participants in an otherwise unconnected context for an externally vetted admin, without restricting other contexts. the context parameter is instead intended to restrict to participants known either through that context, or as an admin over that context while other contexts are ignored. this is more consistent with how the call was being used. meanwhile, whether an admin view is appropriate for that context is determined within the address book instead of by the caller. when determining whether a context is eligible for admin view within known_users or search_users, the sender must have read_as_admin permission on the context _and_ not be a participant in the context's course (if any). this allows restrictions due to the course participation to override read_as_admin, as they should. test-plan: [CNVS-35217] - have an account with an account admin - have a course in that account with two teachers (one section-limited, one not), and a student in a different section than the section-limited teacher (all active, published, etc.) - ingest the data into the address book service and configure canvas to use the service - as the account admin, search for the student in the inbox in the context of the course; should find them. - as the non-section-limited teacher, search for the student in the inbox in the context of the course; should find them. - as the section-limited teacher, search for the student in the inbox in the context of the course; should not find them. [CNVS-35799, CNVS-35800] - add two more students to the same course, then remove one from the course and deactivate the other - re-ingest the data into the address book service (or have had the kinesis stream hooked up during the change) - as the non-section-limited teacher, search for the student that was added but then removed; should not find them - as the non-section-limited teacher, search for the student that was deactivated; should not find them - as the account admin, search for the student that was deactivated; should not find them Change-Id: I3233d8d45394c9233adc9888bfaf477e73bfde11 Reviewed-on: https://gerrit.instructure.com/107053 Tested-by: Jenkins Reviewed-by: Andrew Huff <ahuff@instructure.com> QA-Review: Deepeeca Soundarrajan <dsoundarrajan@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2017-03-31 05:31:28 +08:00
decompose_context(context_code).present?
end
def self.load_context(context_code)
context_type, context_id = decompose_context(context_code)
return nil unless context_id
context_class =
case context_type
when 'course' then Course
when 'section' then CourseSection
when 'group' then Group
end
context_class.find(context_id)
AddressBook facade fixes CNVS-29824 refs CNVS-29862, CNVS-29867 test-plan: check for regressions around: * accessing profile#show endpoint, with profiles enabled, is authorized if and only if current user knows profile owner. * eportfolios#show, with profiles enabled, includes link to owner's profile if and only if current user knows owner. * conversations: - filtering of individual recipients when creating a conversation - expansion of context recipients when creating a conversation - restriction of individual recipients to those known via course when creating a conversation in a course - including as an admin over that course - filtering of individual recipients when adding a message to an existing conversation allows existing recipients * searching/browsing address book (conversation creation, link observer to students, due date overrides, etc.): - when loading info about single user (user_id parameter), including combinations of conversation_id and context parameters, returns user data if and only if the target is known to current user under those parameters - users matched: - excludes already listed users - restricts to users known via specified context, if any - including as an admin over that context - finds users with weak associations (e.g. invited student that hasn't logged in for first time yet) when linking observers - doesn't find users with weak associations otherwise - contexts matched: - limited to those known to current user - have count of known users - have counts of known users in synthetic contexts * data returned for known users in various API calls include common course and groups between current user and returned user Change-Id: I66bca0921b298be8d529a713fa982a6dfdcbcc8e Reviewed-on: https://gerrit.instructure.com/84045 Reviewed-by: Jonathan Featherstone <jfeatherstone@instructure.com> Tested-by: Jenkins QA-Review: Deepeeca Soundarrajan <dsoundarrajan@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2016-06-17 00:37:56 +08:00
end
end