230 lines
7.5 KiB
Ruby
230 lines
7.5 KiB
Ruby
#
|
|
# Copyright (C) 2014 - 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/>.
|
|
#
|
|
|
|
# @API User Observees
|
|
# API for accessing information about the users a user is observing.
|
|
|
|
class UserObserveesController < ApplicationController
|
|
before_action :require_user
|
|
|
|
before_action :self_or_admin_permission_check, except: [:update]
|
|
before_action :admin_permission_check, only: [:update]
|
|
|
|
# @API List observees
|
|
#
|
|
# List the users that the given user is observing.
|
|
#
|
|
# *Note:* all users are allowed to list their own observees. Administrators can list
|
|
# other users' observees.
|
|
#
|
|
# @argument include[] [String, "avatar_url"]
|
|
# - "avatar_url": Optionally include avatar_url.
|
|
#
|
|
# @example_request
|
|
# curl https://<canvas>/api/v1/users/<user_id>/observees \
|
|
# -X GET \
|
|
# -H 'Authorization: Bearer <token>'
|
|
#
|
|
# @returns [User]
|
|
def index
|
|
includes = params[:include] || []
|
|
observed_users = user.observed_users.active_user_observers.active.order_by_sortable_name
|
|
observed_users = Api.paginate(observed_users, self, api_v1_user_observees_url)
|
|
render json: users_json(observed_users, @current_user, session,includes )
|
|
end
|
|
|
|
# @API Add an observee with credentials
|
|
#
|
|
# Register the given user to observe another user, given the observee's credentials.
|
|
#
|
|
# *Note:* all users are allowed to add their own observees, given the observee's
|
|
# credentials or access token are provided. Administrators can add observees given credentials, access token or
|
|
# the {api:UserObserveesController#update observee's id}.
|
|
#
|
|
# @argument observee[unique_id] [Optional, String]
|
|
# The login id for the user to observe. Required if access_token is omitted.
|
|
#
|
|
# @argument observee[password] [Optional, String]
|
|
# The password for the user to observe. Required if access_token is omitted.
|
|
#
|
|
# @argument access_token [Optional, String]
|
|
# The access token for the user to observe. Required if <tt>observee[unique_id]</tt> or <tt>observee[password]</tt> are omitted.
|
|
#
|
|
# @example_request
|
|
# curl https://<canvas>/api/v1/users/<user_id>/observees \
|
|
# -X POST \
|
|
# -H 'Authorization: Bearer <token>' \
|
|
# -F 'observee[unique_id]=UNIQUE_ID' \
|
|
# -F 'observee[password]=PASSWORD'
|
|
#
|
|
# @returns User
|
|
def create
|
|
# verify target observee exists and is in an account with the observer
|
|
if params[:access_token]
|
|
verified_token = AccessToken.authenticate(params[:access_token])
|
|
if verified_token.nil?
|
|
render json: {errors: [{'message' => 'Unknown observee.'}]}, status: 422
|
|
return
|
|
end
|
|
observee_user = verified_token.user
|
|
else
|
|
observee_pseudonym = @domain_root_account.pseudonyms.active.by_unique_id(params[:observee][:unique_id]).first
|
|
if observee_pseudonym.nil? || common_accounts_for(user, observee_pseudonym.user).empty?
|
|
render json: {errors: [{'message' => 'Unknown observee.'}]}, status: 422
|
|
return
|
|
end
|
|
|
|
|
|
# if using external auth, save off form information then send to external
|
|
# login form. remainder of adding observee happens in response to that flow
|
|
if @domain_root_account.parent_registration?
|
|
session[:parent_registration] = {}
|
|
session[:parent_registration][:user_id] = @current_user.id
|
|
session[:parent_registration][:observee] = params[:observee]
|
|
session[:parent_registration][:observee_only] = true
|
|
render(json: {redirect: saml_observee_path})
|
|
return
|
|
end
|
|
|
|
# verify provided password
|
|
unless Pseudonym.authenticate(params[:observee] || {}, [@domain_root_account.id] + @domain_root_account.trusted_account_ids)
|
|
render json: {errors: [{'message' => 'Invalid credentials provided.'}]}, status: :unauthorized
|
|
return
|
|
end
|
|
|
|
# add observer
|
|
observee_user = observee_pseudonym.user
|
|
end
|
|
add_observee(observee_user)
|
|
render json: user_json(observee_user, @current_user, session)
|
|
end
|
|
|
|
# @API Show an observee
|
|
#
|
|
# Gets information about an observed user.
|
|
#
|
|
# *Note:* all users are allowed to view their own observees.
|
|
#
|
|
# @example_request
|
|
# curl https://<canvas>/api/v1/users/<user_id>/observees/<observee_id> \
|
|
# -X GET \
|
|
# -H 'Authorization: Bearer <token>'
|
|
#
|
|
# @returns User
|
|
def show
|
|
raise ActiveRecord::RecordNotFound unless has_observee?(observee)
|
|
|
|
render json: user_json(observee, @current_user, session)
|
|
end
|
|
|
|
# @API Add an observee
|
|
#
|
|
# Registers a user as being observed by the given user.
|
|
#
|
|
# @example_request
|
|
# curl https://<canvas>/api/v1/users/<user_id>/observees/<observee_id> \
|
|
# -X PUT \
|
|
# -H 'Authorization: Bearer <token>'
|
|
#
|
|
# @returns User
|
|
def update
|
|
raise ActiveRecord::RecordNotFound unless can_manage_observers_for?(user, observee)
|
|
|
|
add_observee(observee)
|
|
render json: user_json(observee, @current_user, session)
|
|
end
|
|
|
|
# @API Remove an observee
|
|
#
|
|
# Unregisters a user as being observed by the given user.
|
|
#
|
|
# @example_request
|
|
# curl https://<canvas>/api/v1/users/<user_id>/observees/<observee_id> \
|
|
# -X DELETE \
|
|
# -H 'Authorization: Bearer <token>'
|
|
#
|
|
# @returns User
|
|
def destroy
|
|
raise ActiveRecord::RecordNotFound unless has_observee?(observee)
|
|
|
|
user.user_observees.active.where(user_id: observee).destroy_all
|
|
render json: user_json(observee, @current_user, session)
|
|
end
|
|
|
|
private
|
|
|
|
def user
|
|
@user ||= params[:user_id].nil? ? @current_user : api_find(User.active, params[:user_id])
|
|
end
|
|
|
|
def observee
|
|
@observee ||= api_find(User.active, params[:observee_id])
|
|
end
|
|
|
|
def add_observee(observee)
|
|
@current_user.shard.activate do
|
|
unless has_observee?(observee)
|
|
user.user_observees.create_or_restore(user_id: observee)
|
|
user.touch
|
|
end
|
|
end
|
|
end
|
|
|
|
def has_observee?(observee)
|
|
user.user_observees.active.where(user_id: observee).exists?
|
|
end
|
|
|
|
def self_or_admin_permission_check
|
|
return true if user == @current_user
|
|
admin_permission_check
|
|
end
|
|
|
|
def admin_permission_check
|
|
return true if can_manage = can_manage_observers_for?(user)
|
|
|
|
if can_manage.nil?
|
|
raise ActiveRecord::RecordNotFound
|
|
else
|
|
render_unauthorized_action
|
|
end
|
|
end
|
|
|
|
def common_accounts_for(*users)
|
|
shards = users.map(&:associated_shards).reduce(:&)
|
|
Shard.with_each_shard(shards) do
|
|
user_ids = users.map(&:id)
|
|
Account.where(id: UserAccountAssociation.
|
|
joins(:account).where(accounts: {parent_account_id: nil}).
|
|
where(user_id: user_ids).
|
|
group(:account_id).
|
|
having("count(*) = #{user_ids.length}"). # user => account is unique for user_account_associations
|
|
select(:account_id)
|
|
)
|
|
end
|
|
end
|
|
|
|
def can_manage_observers_for?(*users)
|
|
matching_accounts = common_accounts_for(*users)
|
|
return nil if matching_accounts.empty?
|
|
|
|
matching_accounts.any? do |a|
|
|
return true if a.grants_right?(@current_user, :manage_user_observers)
|
|
end
|
|
end
|
|
end
|