Admin: API Token Reporting

As an admin, I would like to be able to easily see who in my Canvas
account has API tokens and for what tools. This will allow me to audit who is
accessing data and manage tools.

columns:

user_name, user_id, token_name, token_expiration, token_creation_date,
token_last_used, developer_key_name, developer_key_id

Fixes PLAT-1261

Test Plan:
Generate the report
make sure it has all of the right columns

Change-Id: Ifb5b950586fd344ede8eb95b9d3c5e6783bbd70d
Reviewed-on: https://gerrit.instructure.com/69734
Tested-by: Jenkins
Reviewed-by: Nathan Mills <nathanm@instructure.com>
QA-Review: August Thornton <august@instructure.com>
Product-Review: Brad Horrocks <bhorrocks@instructure.com>
This commit is contained in:
Brad Horrocks 2016-01-05 13:36:02 -07:00
parent 3449a7aabd
commit 1de37eb112
5 changed files with 162 additions and 0 deletions

View File

@ -0,0 +1,38 @@
<p><%= t('account_reports.default.user_access_token.text',
%{This report shows users with access tokens.}) %></p>
<h3><%= t('#account_reports.default.description.header', %{Example}) %></h3>
<table class="report_example">
<thead>
<tr>
<th><%= t('#account_reports.report_header_user_id', %{user id}) %></th>
<th><%= t('#account_reports.report_header_user_name', %{user name}) %></th>
<th><%= t('#account_reports.report_header_token_hint', %{token hint}) %></th>
<th><%= t('#account_reports.report_header_expiration', %{expiration}) %></th>
<th><%= t('#account_reports.report_header_token_last_used', %{last used}) %></th>
<th><%= t('#account_reports.report_header_token_dev_key_id', %{dev key id}) %></th>
<th><%= t('#account_reports.report_header_token_dev_key_name', %{dev key name}) %></th>
</tr>
</thead>
<tbody>
<tr>
<td>u96872</td>
<td>smith, heather</td>
<td>yXj1V</td>
<td>2019-12-12 12:12:12</td>
<td>2015-12-24 04:13:12</td>
<td>10000000000002</td>
<td>User Generated</td>
</tr>
<tr>
<td>u48956</td>
<td>Montgomery, Montgomery</td>
<td>FeFQi</td>
<td>2019-12-12 12:12:12</td>
<td>2015-12-24 04:13:12</td>
<td>123</td>
<td>August's Tool of Unfortunate Events</td>
</tr>
</tbody>
</table>

View File

@ -70,5 +70,9 @@ module AccountReports
def self.last_enrollment_activity_csv(account_report)
StudentReports.new(account_report).last_enrollment_activity
end
def self.user_access_tokens_csv(account_report)
StudentReports.new(account_report).user_access_tokens
end
end
end

View File

@ -286,6 +286,12 @@ module AccountReports
:description => 'The course to report on'
}
}
},
'user_access_tokens_csv' => {
:title => proc { I18n.t(:user_access_tokens_title, 'User Access Tokens') },
:description_partial => true,
:parameters => {
}
}
}
end

View File

@ -346,5 +346,60 @@ module AccountReports
end
send_report(file)
end
def user_access_tokens
file = AccountReports.generate_file(@account_report)
CSV.open(file, "w") do |csv|
headers = []
headers << I18n.t('#account_reports.report_header_user_id', 'user id')
headers << I18n.t('#account_reports.report_header_user_name', 'user name')
headers << I18n.t('#account_reports.report_header_token_hint', 'token hint')
headers << I18n.t('#account_reports.report_header_expiration', 'expiration')
headers << I18n.t('#account_reports.report_header_token_last_used', 'last used')
headers << I18n.t('#account_reports.report_header_token_dev_key_id', 'dev key id')
headers << I18n.t('#account_reports.report_header_token_dev_key_name', 'dev key name')
csv << headers
columns = []
columns << 'access_tokens.user_id'
columns << 'users.sortable_name'
columns << 'access_tokens.token_hint'
columns << 'access_tokens.expires_at'
columns << 'access_tokens.last_used_at'
columns << 'access_tokens.developer_key_id'
user_tokens = AccessToken
.select(columns)
.joins(user: {pseudonym: :account})
.where("accounts.id = ? OR accounts.root_account_id = ?", root_account, root_account)
.order("users.id, sortable_name, last_used_at DESC")
user_tokens = add_user_sub_account_scope(user_tokens)
Shackles.activate(:slave) do
user_tokens.each do |token|
dev_key = developer_key(token[:developer_key_id])
row = []
row << token[:user_id]
row << token[:sortable_name]
row << token[:token_hint]
row << (token[:expires_at] ? default_timezone_format(token[:expires_at]) : 'never')
row << (token[:last_used_at] ? default_timezone_format(token[:last_used_at]) : 'never')
row << token[:developer_key_id]
row << dev_key.name
csv << row
end
end
end
send_report(file)
end
def developer_key(dev_key_id)
@dev_keys ||= {}
@dev_keys[dev_key_id] ||= DeveloperKey.find(dev_key_id)
end
end
end

View File

@ -583,4 +583,63 @@ describe 'Student reports' do
expect(parsed.length).to eq 3
end
end
describe 'user access token report' do
before(:each) do
@type = 'user_access_tokens_csv'
@at1 = AccessToken.create!(
:user => @user1,
:developer_key => DeveloperKey.default,
:expires_at => 2.hours.ago
)
@at2 = AccessToken.create!(
:user => @user2,
:developer_key => DeveloperKey.default,
:expires_at => 2.hours.from_now
)
@at2.update_attribute(:last_used_at, 2.hours.ago)
@at3 = AccessToken.create!(
:user => @user3,
:developer_key => DeveloperKey.default,
:expires_at => nil
)
end
it 'should run the user access tokens report' do
parsed = read_report(@type, {order: 1})
expect(parsed.length).to eq 3
expect(parsed[0]).to eq([
@user3.id.to_s,
"Astley, Rick",
@at3.token_hint.gsub(/.+~/, ''),
'never',
'never',
DeveloperKey.default.id.to_s,
"User-Generated"
])
expect(parsed[1]).to eq([
@user2.id.to_s,
"Bolton, Michael",
@at2.token_hint.gsub(/.+~/, ''),
@at2.expires_at.iso8601,
@at2.last_used_at.iso8601,
DeveloperKey.default.id.to_s,
"User-Generated"
])
expect(parsed[2]).to eq([
@user1.id.to_s,
"Clair, John St.",
@at1.token_hint.gsub(/.+~/, ''),
@at1.expires_at.iso8601,
'never',
DeveloperKey.default.id.to_s,
"User-Generated"
])
end
end
end