Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webservice: check OAuth scopes #525

Merged
merged 1 commit into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Optional functionality can be enabled by granting additional scopes:
- Reports for meetings / webinars
- dashboard_meetings:read:admin (Business accounts and higher)
- dashboard_webinars:read:admin (Business accounts and higher)
- report:read:admin (Pro user and up)
- report:read:admin (Pro accounts and higher)
- Allow recordings to be viewed (zoom | viewrecordings)
- recording:read:admin
- Tracking fields (zoom | defaulttrackingfields)
Expand Down
34 changes: 22 additions & 12 deletions classes/task/get_meeting_reports.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,16 @@ public function execute($paramstart = null, $paramend = null, $hostuuids = null)
mtrace(sprintf('Finding meetings between %s to %s', $start, $end));

$recordedallmeetings = true;
try {
if (!empty($hostuuids)) {
// Can only query on $hostuuids using Report API. So throw
// exception to skip Dashboard API.
throw new \Exception('Querying $hostuuids; need to use Report API');
}

// Can only query on $hostuuids using Report API.
if (empty($hostuuids) && $this->service->has_scope('dashboard_meetings:read:admin')) {
$allmeetings = $this->get_meetings_via_dashboard($start, $end);
} catch (\Exception $e) {
mtrace($e->getMessage());
// If ran into exception, then Dashboard API must have failed. Try
// using Report API.
} else if ($this->service->has_scope('report:read:admin')) {
$allmeetings = $this->get_meetings_via_reports($start, $end, $hostuuids);
} else {
$requiredscope = !empty($hostuuids) ? 'report:read:admin' : 'dashboard_meetings:read:admin or report:read:admin';
mtrace('Skipping task - missing required OAuth scope: ' . $requiredscope);
return;
}

// Sort all meetings based on end_time so that we know where to pick
Expand Down Expand Up @@ -205,6 +202,11 @@ public function format_participant($participant, $detailsid, $names, $emails) {
$moodleuserid = null;
$name = null;

// Consolidate fields.
$participant->name = $participant->name ?? $participant->user_name ?? '';
$participant->id = $participant->id ?? $participant->participant_user_id ?? '';
$participant->user_email = $participant->user_email ?? $participant->email ?? '';

// Cleanup the name. For some reason # gets into the name instead of a comma.
$participant->name = str_replace('#', ',', $participant->name);

Expand Down Expand Up @@ -373,8 +375,16 @@ public function get_meetings_via_reports($start, $end, $hostuuids) {
public function get_meetings_via_dashboard($start, $end) {
mtrace('Using Dashboard API');

$meetings = $this->service->get_meetings($start, $end);
$webinars = $this->service->get_webinars($start, $end);
$meetings = [];
if ($this->service->has_scope('dashboard_meetings:read:admin')) {
$meetings = $this->service->get_meetings($start, $end);
}

$webinars = [];
if ($this->service->has_scope('dashboard_webinars:read:admin')) {
$webinars = $this->service->get_webinars($start, $end);
}

$allmeetings = array_merge($meetings, $webinars);

return $allmeetings;
Expand Down
41 changes: 39 additions & 2 deletions classes/webservice.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ class mod_zoom_webservice {
*/
protected $makecallretries = 0;

/**
* Granted OAuth scopes
* @var array
*/
protected $scopes;

/**
* The constructor for the webservice class.
* @throws moodle_exception Moodle exception is thrown for missing config settings.
Expand Down Expand Up @@ -844,8 +850,20 @@ public function list_meetings($userid, $webinar) {
*/
public function get_meeting_participants($meetinguuid, $webinar) {
$meetinguuid = $this->encode_uuid($meetinguuid);
return $this->make_paginated_call('report/' . ($webinar ? 'webinars' : 'meetings') . '/'
. $meetinguuid . '/participants', null, 'participants');

$meetingtype = ($webinar ? 'webinars' : 'meetings');

if ($this->has_scope('report:read:admin')) {
$apitype = 'report';
} else if ($this->has_scope('dashboard_' . $meetingtype . ':read:admin')) {
$apitype = 'metrics';
} else {
mtrace('Missing required OAuth scope: report:read:admin or dashboard_' . $meetingtype . ':read:admin');
return [];
}

$url = $apitype . '/' . $meetingtype . '/' . $meetinguuid . '/participants';
return $this->make_paginated_call($url, [], 'participants');
}

/**
Expand Down Expand Up @@ -1012,6 +1030,22 @@ protected function get_access_token() {
return $token;
}

/**
* Has the request OAuth scope been granted?
*
* @param string $scope OAuth scope.
* @throws moodle_exception
* @return bool
*/
public function has_scope($scope) {
if (!isset($this->scopes)) {
$this->get_access_token();
}

mtrace('checking has_scope(' . $scope . ')');
return \in_array($scope, $this->scopes, true);
}

/**
* Stores token and expiration in cache, returns token from OAuth call.
*
Expand Down Expand Up @@ -1054,6 +1088,9 @@ private function oauth($cache) {
$scopes = explode(' ', $response->scope);
$missingscopes = array_diff($requiredscopes, $scopes);

// Keep the scope information in memory.
$this->scopes = $scopes;

if (!empty($missingscopes)) {
$missingscopes = implode(', ', $missingscopes);
throw new moodle_exception('errorwebservice', 'mod_zoom', '', get_string('zoomerr_scopes', 'mod_zoom', $missingscopes));
Expand Down