add declared_user_type

closes FOO-2318, FOO-2319, FOO-2320

test plan:
 * verify that /api/v1/users/self/logins includes the declared_user_type
 * use PUT /api/v1/users/self/logins/:id to update the declared_user_type;
   verify it changes

Change-Id: I1e43ab6ead5515b113b1949bdda544b2b5c6834f
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/273647
Reviewed-by: Simon Williams <simon@instructure.com>
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
QA-Review: Cody Cutrer <cody@instructure.com>
Product-Review: Cody Cutrer <cody@instructure.com>
This commit is contained in:
Cody Cutrer 2021-09-15 12:37:24 -06:00
parent f65c11a10b
commit 6e04277c3a
6 changed files with 96 additions and 14 deletions

View File

@ -42,6 +42,7 @@ class PseudonymsController < ApplicationController
# @response_field authentication_provider_type The type of the authentication
# provider that this login is associated with
# @response_field workflow_state The current status of the login
# @response_field declared_user_type The declared intention for this user's role
#
# @example_response
# [
@ -53,7 +54,8 @@ class PseudonymsController < ApplicationController
# "user_id": 2,
# "authentication_provider_id": 1,
# "authentication_provider_type": "facebook",
# "workflow_state": "active"
# "workflow_state": "active",
# "declared_user_type": null,
# }
# ]
def index
@ -218,6 +220,21 @@ class PseudonymsController < ApplicationController
# provider, or the type of the provider (in which case, it will find the
# first matching provider).
#
# @argument login[declared_user_type] [String]
# The declared intention of the user type. This can be set, but does
# not change any Canvas functionality with respect to their access.
# A user can still be a teacher, admin, student, etc. in any particular
# context without regard to this setting. This can be used for
# administrative purposes for integrations to be able to more easily
# identify why the user was created.
# Valid values are:
# * administrative
# * observer
# * staff
# * student
# * student_other
# * teacher
#
# @example_request
#
# #create a facebook login for user with ID 123
@ -310,6 +327,21 @@ class PseudonymsController < ApplicationController
# @argument login[workflow_state] [String, "active"|"suspended"]
# Used to suspend or re-activate a login.
#
# @argument login[declared_user_type] [String]
# The declared intention of the user type. This can be set, but does
# not change any Canvas functionality with respect to their access.
# A user can still be a teacher, admin, student, etc. in any particular
# context without regard to this setting. This can be used for
# administrative purposes for integrations to be able to more easily
# identify why the user was created.
# Valid values are:
# * administrative
# * observer
# * staff
# * student
# * student_other
# * teacher
#
# @example_request
# curl https://<canvas>/api/v1/accounts/:account_id/logins/:login_id \
# -H "Authorization: Bearer <ACCESS-TOKEN>" \
@ -326,6 +358,7 @@ class PseudonymsController < ApplicationController
# "integration_id": null,
# "authentication_provider_id": null,
# "workflow_state": "active",
# "declared_user_type": "teacher"
# }
def update
if api_request?
@ -423,6 +456,7 @@ class PseudonymsController < ApplicationController
:authentication_provider_id,
:integration_id,
:workflow_state,
:declared_user_type
).blank?
render json: nil, status: :bad_request
return false
@ -441,6 +475,10 @@ class PseudonymsController < ApplicationController
@pseudonym.authentication_provider = params[:pseudonym][:authentication_provider]
end or return false
has_right_if_requests_change(:declared_user_type, :update) do
@pseudonym.declared_user_type = params[:pseudonym][:declared_user_type]
end or return false
has_right_if_requests_change(:sis_user_id, :manage_sis) do
# convert "" -> nil for sis_user_id
@pseudonym.sis_user_id = params[:pseudonym][:sis_user_id].presence

View File

@ -49,6 +49,9 @@ class Pseudonym < ActiveRecord::Base
end
before_validation :validate_unique_id
before_destroy :retire_channels
validates :declared_user_type,
allow_nil: true,
inclusion: { in: %w[administrative observer staff student student_other teacher] }
before_save :set_password_changed
before_validation :infer_defaults, :verify_unique_sis_user_id, :verify_unique_integration_id

View File

@ -0,0 +1,26 @@
# 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/>.
class AddDeclaredUserTypeToPseudonyms < ActiveRecord::Migration[6.0]
tag :predeploy
def change
add_column :pseudonyms, :declared_user_type, :string, limit: 255
end
end

View File

@ -20,19 +20,22 @@
module Api::V1::Pseudonym
include Api::V1::Json
API_PSEUDONYM_JSON_OPTS = [:id,
:user_id,
:account_id,
:unique_id,
:sis_user_id,
:integration_id,
:authentication_provider_id,
:created_at,
:workflow_state].freeze
API_PSEUDONYM_JSON_OPTS = %i[id
user_id
account_id
unique_id
sis_user_id
integration_id
authentication_provider_id
created_at
workflow_state
declared_user_type].freeze
def pseudonym_json(pseudonym, current_user, session)
opts = API_PSEUDONYM_JSON_OPTS
opts = opts.reject { |opt| [:sis_user_id, :integration_id].include?(opt) } unless pseudonym.account.grants_any_right?(current_user, :read_sis, :manage_sis)
opts = API_PSEUDONYM_JSON_OPTS.dup
opts -= %i[sis_user_id integration_id] unless pseudonym.account.grants_any_right?(
current_user, :read_sis, :manage_sis
)
api_json(pseudonym, current_user, session, :only => opts).tap do |result|
if pseudonym.authentication_provider
result[:authentication_provider_type] = pseudonym.authentication_provider.auth_type

View File

@ -167,6 +167,7 @@ describe "AuthenticationAudit API", type: :request do
"integration_id" => nil,
"authentication_provider_id" => nil,
"workflow_state" => "active",
"declared_user_type" => nil
}]
end
end

View File

@ -49,6 +49,7 @@ describe PseudonymsController, type: :request do
'user_id' => p.user_id,
'created_at' => p.created_at,
'workflow_state' => 'active',
'declared_user_type' => nil
}
end)
end
@ -145,7 +146,8 @@ describe PseudonymsController, type: :request do
:login => {
:password => 'abcd1234',
:sis_user_id => '12345',
:unique_id => 'test@example.com'
:unique_id => 'test@example.com',
:declared_user_type => 'teacher',
}
})
expect(json).to eq({
@ -158,6 +160,7 @@ describe PseudonymsController, type: :request do
'user_id' => @student.id,
'created_at' => json['created_at'],
'workflow_state' => 'active',
'declared_user_type' => 'teacher'
})
end
@ -259,7 +262,8 @@ describe PseudonymsController, type: :request do
:login => {
:unique_id => 'student+new@example.com',
:password => 'password123',
:sis_user_id => 'new-12345'
:sis_user_id => 'new-12345',
:declared_user_type => 'teacher',
}
})
expect(json).to eq({
@ -272,6 +276,7 @@ describe PseudonymsController, type: :request do
'user_id' => @student.id,
'created_at' => @student.pseudonym.created_at.iso8601,
'workflow_state' => 'active',
'declared_user_type' => 'teacher'
})
expect(@student.pseudonym.reload.valid_password?('password123')).to be_truthy
end
@ -292,6 +297,11 @@ describe PseudonymsController, type: :request do
expect(response.code).to eql '400'
end
it 'ignores invalid declared_user_types' do
raw_api_call(:put, @path, @path_options, { login: { declared_user_type: 'ta' } })
expect(response.code).to eql '400'
end
it "returns 400 if the unique_id already exists" do
raw_api_call(:put, @path, @path_options, {
:login => {
@ -439,6 +449,7 @@ describe PseudonymsController, type: :request do
'user_id' => @student.id,
'created_at' => pseudonym.created_at.iso8601,
'workflow_state' => 'deleted',
'declared_user_type' => nil
})
end