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:
parent
3449a7aabd
commit
1de37eb112
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue