Website : rimsha.abasa.com
backdoor
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
var
/
canvas
/
lib
/
Filename :
turnitin.rb
back
Copy
# frozen_string_literal: true # # Copyright (C) 2011 - 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/>. # module Turnitin def self.state_from_similarity_score(similarity_score) return "none" if similarity_score == 0 return "acceptable" if similarity_score < 25 return "warning" if similarity_score < 50 return "problem" if similarity_score < 75 "failure" end class Client attr_accessor :endpoint, :account_id, :shared_secret, :host, :testing def initialize(account_id, shared_secret, host = nil, testing = false) @host = host || "api.turnitin.com" @endpoint = "/api.asp" raise "Account ID required" unless account_id raise "Shared secret required" unless shared_secret @account_id = account_id @shared_secret = shared_secret @testing = testing @functions = { create_user: "1", # instructor or student create_course: "2", # instructor only enroll_student: "3", # student only create_assignment: "4", # instructor only submit_paper: "5", # student or teacher generate_report: "6", show_paper: "7", delete_paper: "8", change_password: "9", list_papers: "10", check_user_paper: "11", view_admin_statistics: "12", view_grade_mark: "13", report_turnaround_times: "14", submission_scores: "15", login_user: "17", logout_user: "18", } end def id(obj) if @testing "test_#{obj.asset_string}" elsif obj.respond_to?(:turnitin_id) obj.turnitin_asset_string else "#{account_id}_#{obj.asset_string}" end end def email(item) # emails @example.com are, guaranteed by RFCs, to be like /dev/null :) email = if item.is_a?(User) item.email elsif item.respond_to?(:turnitin_id) "#{item.turnitin_asset_string}@null.instructure.example.com" end email || "#{item.asset_string}@null.instructure.example.com" end TurnitinUser = Struct.new(:asset_string, :first_name, :last_name, :name) def testSettings user = TurnitinUser.new("admin_test", "Admin", "Test", "Admin Test") res = createTeacher(user) res.success? end def createStudent(user) sendRequest(:create_user, 2, user:, utp: "1") end def createTeacher(user) sendRequest(:create_user, 2, user:, utp: "2") end def createCourse(course) sendRequest(:create_course, 2, course:, user: course, utp: "2") end def enrollStudent(course, student) sendRequest(:enroll_student, 2, user: student, course:, utp: "1", tem: email(course)) end def self.default_assignment_turnitin_settings { originality_report_visibility: "immediate", s_paper_check: "1", internet_check: "1", journal_check: "1", exclude_biblio: "1", exclude_quoted: "1", exclude_type: "0", exclude_value: "", submit_papers_to: "1" } end def self.normalize_assignment_turnitin_settings(settings) unless settings.nil? valid_keys = Turnitin::Client.default_assignment_turnitin_settings.keys valid_keys << :created settings = settings.slice(*valid_keys) settings[:originality_report_visibility] = "immediate" unless %w[immediate after_grading after_due_date never].include?(settings[:originality_report_visibility]) settings[:s_view_report] = determine_student_visibility(settings[:originality_report_visibility]) %i[s_paper_check internet_check journal_check exclude_biblio exclude_quoted submit_papers_to].each do |key| bool = Canvas::Plugin.value_to_boolean(settings[key]) settings[key] = bool ? "1" : "0" end exclude_value = settings[:exclude_value].to_i settings[:exclude_type] = "0" unless %w[0 1 2].include?(settings[:exclude_type]) settings[:exclude_value] = case settings[:exclude_type] when "0" then "" when "1" then [exclude_value, 1].max.to_s when "2" then (0..100).cover?(exclude_value) ? exclude_value.to_s : "0" end end settings end def self.determine_student_visibility(originality_report_visibility) case originality_report_visibility when "immediate", "after_grading", "after_due_date" "1" when "never" "0" end end def createOrUpdateAssignment(assignment, settings) course = assignment.context # turnitin generally expects the timezone to be set the same as # the Turnitin account is set up as. today = course.time_zone.today settings = Turnitin::Client.normalize_assignment_turnitin_settings(settings) # institution_check - 1/0, check institution # submit_papers_to - 0=none, 1=standard, 2=institution response = sendRequest(:create_assignment, settings.delete(:created) ? "3" : "2", settings.merge!({ user: course, course:, assignment:, utp: "2", dtstart: "#{today.strftime} 00:00:00", dtdue: "#{today.strftime} 00:00:00", dtpost: "#{today.strftime} 00:00:00", late_accept_flag: "1", post: true })) response.success? ? { assignment_id: response.assignment_id } : response.error_hash end # if asset_string is passed in, only submit that attachment def submitPaper(submission, asset_string = nil) student = submission.user assignment = submission.assignment course = assignment.context opts = { post: true, utp: "1", user: student, course:, assignment:, tem: email(course) } responses = {} if submission.submission_type == "online_upload" attachments = submission.attachments.select { |a| a.turnitinable? && (asset_string.nil? || a.asset_string == asset_string) } attachments.each do |a| responses[a.asset_string] = sendRequest(:submit_paper, "2", { ptl: a.display_name, pdata: a.open, ptype: "2" }.merge!(opts)) end elsif submission.submission_type == "online_text_entry" && (asset_string.nil? || submission.asset_string == asset_string) responses[submission.asset_string] = sendRequest(:submit_paper, "2", { ptl: assignment.title, pdata: submission.plaintext_body, ptype: "1" }.merge!(opts)) else raise "Unsupported submission type for turnitin integration: #{submission.submission_type}" end responses.transform_values! do |res| res.success? ? { object_id: res.returned_object_id } : res.error_hash end responses end def generateReport(submission, asset_string) assignment = submission.assignment course = assignment.context object_id = submission.turnitin_data[asset_string][:object_id] rescue nil res = nil res = sendRequest(:generate_report, 2, oid: object_id, utp: "2", user: course, course:, assignment:) if object_id data = {} if res data[:similarity_score] = res.css("originalityscore").first.try(:content) data[:web_overlap] = res.css("web_overlap").first.try(:content) data[:publication_overlap] = res.css("publication_overlap").first.try(:content) data[:student_overlap] = res.css("student_paper_overlap").first.try(:content) end data end def submissionReportUrl(submission, asset_string) assignment = submission.assignment course = assignment.context object_id = submission.turnitin_data[asset_string][:object_id] rescue nil sendRequest(:generate_report, 1, oid: object_id, utp: "2", user: course, course:, assignment:) end def submissionStudentReportUrl(submission, asset_string) user = submission.user assignment = submission.assignment course = assignment.context object_id = submission.turnitin_data[asset_string][:object_id] rescue nil sendRequest(:generate_report, 1, oid: object_id, utp: "1", user:, course:, assignment:, tem: email(course)) end def submissionPreviewUrl(submission, asset_string) user = submission.user assignment = submission.assignment course = assignment.context object_id = submission.turnitin_data[asset_string][:object_id] rescue nil sendRequest(:show_paper, 1, oid: object_id, utp: "1", user:, course:, assignment:, tem: email(course)) end def submissionDownloadUrl(submission, asset_string) user = submission.user assignment = submission.assignment course = assignment.context object_id = submission.turnitin_data[asset_string][:object_id] rescue nil sendRequest(:show_paper, 1, oid: object_id, utp: "1", user:, course:, assignment:, tem: email(course)) end def listSubmissions(assignment) course = assignment.context sendRequest(:list_papers, 2, assignment:, course:, user: course, utp: "1", tem: email(course)) end # From the turnitin api docs: To calculate the MD5, concatenate the data # values associated with the URL variables of ALL variables being sent, in # alphabetical order according to variable name, being sure to include at # least the following: # # aid + diagnostic + encrypt + fcmd + fid + gmtime + uem + ufn + uln + utp + shared secret key # # The shared secret key is added to the end of the parameters. # # From our testing, turnitin appears to be unescaping parameters and then # calculating MD5, so our MD5 should be calculated before parameters are # escaped def request_md5(params) keys_used = [] str = "" keys = %i[aid assign assignid cid cpw ctl diagnostic dis dtdue dtstart dtpost encrypt fcmd fid gmtime newassign newupw oid pfn pln ptl ptype said tem uem ufn uid uln upw utp] keys.each do |key| keys_used << key if params[key].present? str += params[key] || "" end str += @shared_secret Digest::MD5.hexdigest(str) end def escape_params(params) escaped_params = {} params.each do |key, value| escaped_params[key] = if value.is_a?(String) CGI.escape(value).gsub("+", "%20") # turnitin uses %20 to encode spaces (instead of +) else value end end escaped_params end def prepare_params(command, fcmd, args) user = args.delete :user course = args.delete :course assignment = args.delete :assignment post = args.delete :post params = args.merge({ gmtime: Time.now.utc.strftime("%Y%m%d%H%M")[0, 11], fid: @functions[command], fcmd: fcmd.to_s, encrypt: "0", aid: @account_id, src: "15", dis: "1" }) if user params[:uid] = id(user) params[:uem] = email(user) if user.is_a?(Course) params[:ufn] = user.name params[:uln] = "Course" else params[:ufn] = user.first_name params[:uln] = user.last_name params[:uln] = "Student" if params[:uln].empty? end end if course params[:cid] = id(course) params[:ctl] = course.name end if assignment params[:assign] = "#{assignment.title} - #{assignment.id}" params[:assignid] = id(assignment) end params[:diagnostic] = "1" if @testing params[:md5] = request_md5(params) params = escape_params(params) if post params end def sendRequest(command, fcmd, args) require "net/http" post = args[:post] # gets deleted in prepare_params params = prepare_params(command, fcmd, args) if post query, headers = LegacyMultipart::Post.prepare_query(params) http = Net::HTTP.new(@host, 443) http.use_ssl = true http_response = http.start do |con| req = Net::HTTP::Post.new(@endpoint, headers) con.read_timeout = 30 begin con.request(req, query) rescue => e Rails.logger.error("Turnitin API error for account_id #{@account_id}: POSTING FAILED") Rails.logger.error(params.to_json) Canvas::Errors.capture(e, { tags: { type: "turnitin_api_unreachable", host: @host, endpoint: @endpoint } }) raise e end end else requestParams = "" params.each do |key, value| next if value.nil? requestParams += "&#{URI::DEFAULT_PARSER.escape(key.to_s)}=#{CGI.escape(value.to_s)}" end if params[:fcmd] == "1" return "https://#{@host}#{@endpoint}?#{requestParams}" else http = Net::HTTP.new(@host, 443) http.use_ssl = true http_response = http.start do |conn| conn.get("#{@endpoint}?#{requestParams}") end end end return nil if @testing response = Turnitin::Response.new(http_response) if response.error? Rails.logger.error("Turnitin API error for account_id #{@account_id}: error #{response.return_code}") Rails.logger.error(params.to_json) Rails.logger.error(http_response.body) end response end end end