Website : rimsha.abasa.com
backdoor
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
var
/
canvas
/
lib
/
microsoft_sync
/
Filename :
graph_service_helpers.rb
back
Copy
# frozen_string_literal: true # # Copyright (C) 2021 - 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/>. # # # Wraps GraphService, which provides lower-level access to the Microsoft Graph API, with # functions specific to canvas models and the particular Microsoft API fields we use. # module MicrosoftSync class GraphServiceHelpers attr_reader :graph_service class UnexpectedResponseError < Errors::PublicError def self.public_message I18n.t("Unexpected response from Microsoft API. This is likely a bug. " \ "Please contact support.") end end MAX_MAIL_NICKNAME_LENGTH = 64 GET_GROUP_USERS_BATCH_SIZE = 999 # Max batch size to minimize API calls def initialize(tenant, extra_statsd_tags) @graph_service = GraphService.new(tenant, extra_statsd_tags) end def list_education_classes_for_course(course) graph_service.education_classes.list(filter: { externalId: course.uuid }) end # Returns the hash of the new course, including the 'id' key def create_education_class(course) graph_service.education_classes.create( description: course.public_description.presence&.truncate(1024), displayName: course.name, externalId: course.uuid, externalName: course.name, externalSource: "manual", mailNickname: mail_nickname_for(course) ) end def update_group_with_course_data(ms_group_id, course) graph_service.groups.update( ms_group_id, microsoft_EducationClassLmsExt: { ltiContextId: course.lti_context_id || Lti::Asset.opaque_identifier_for(course), lmsCourseId: course.uuid, lmsCourseName: course.name, lmsCourseDescription: course.public_description&.truncate(256), }, microsoft_EducationClassSisExt: { sisCourseId: course.sis_source_id, } ) end USERS_ULUVS_TO_AADS_BATCH_SIZE = 15 # Max number of "OR"s in filter clause # Returns a hash from ULUV -> AAD. Accepts 15 at a time. A ULUV (User # LookUp Value) is a value we use to look up users. It is derived from # something in Canvas (e.g. a user's email address, username, or SIS ID # -- see UsersUluvsFinder). We expect the ULUV to correspond to the # property of the Microsoft user indicated by the `remote_attribute` # argument: e.g. userPrincipalName (default if nil is passed) or # mailNickname. # We then return a hash of ULUV -> AAD object ID. An AAD [Azure Active # Directory] object ID, referred to here as just an "aad", is the ID for # the user on the Microsoft side, which is what Microsoft references in # their groups/teams. # # The properties on Microsoft's user objects are case-insensitive, so this # method downcases and uniqs the ULUVs before requesting them from # Microsoft. But whatever casing the Microsoft response uses, this function # makes sure the keys in the return hash match the case of the ULUVs that # were passed in. def users_uluvs_to_aads(remote_attribute, uluvs) remote_attribute ||= "userPrincipalName" downcased_uniqued = uluvs.map(&:downcase).uniq if downcased_uniqued.length > USERS_ULUVS_TO_AADS_BATCH_SIZE raise ArgumentError, "Can't look up #{uluvs.length} ULUVs at once" end uluvs_downcased_to_given_forms = uluvs.group_by(&:downcase) unexpected = [] result_hash = {} graph_service.users.list( select: ["id", remote_attribute], filter: { remote_attribute => downcased_uniqued } ).each do |user_object| given_forms = uluvs_downcased_to_given_forms[user_object[remote_attribute].downcase] if given_forms given_forms.each do |given_form| result_hash[given_form] = user_object["id"] end else unexpected << user_object[remote_attribute] end end if unexpected.present? raise UnexpectedResponseError, "/users returned users with unexpected #{remote_attribute} values " \ "#{unexpected.inspect}, asked for #{downcased_uniqued}" end result_hash end def get_group_users_aad_ids(group_id, owners: false) method = owners ? :list_owners : :list_members [].tap do |aad_ids| graph_service.groups.send( method, group_id, select: ["id"], top: GET_GROUP_USERS_BATCH_SIZE ) do |users| aad_ids.concat(users.pluck("id")) end end end private def mail_nickname_for(course) prefix = "Course_" postfix = "-#{course.uuid.first(13)}" safe_course_code = course.course_code.strip.parameterize.underscore.first( MAX_MAIL_NICKNAME_LENGTH - (postfix.length + prefix.length) ) "#{prefix}#{safe_course_code}#{postfix}" end end end