From be5261037eb6fe68988e33269c74655a120206ec Mon Sep 17 00:00:00 2001 From: Bilal Date: Tue, 18 Aug 2020 20:43:33 +0300 Subject: [PATCH] Complete teams meeting creation and token refresh --- app/controllers/broadcasts_controller.rb | 22 ++++- .../microsoft_teams_meetings_controller.rb | 17 ---- app/models/broadcast.rb | 1 + app/views/broadcasts/show.html.erb | 2 +- config/routes.rb | 1 - ...ft_teams_meeting_join_url_to_broadcasts.rb | 5 ++ db/structure.sql | 6 +- lib/microsoft_graph.rb | 82 +++++++++++-------- 8 files changed, 80 insertions(+), 56 deletions(-) delete mode 100644 app/controllers/microsoft_teams_meetings_controller.rb create mode 100644 db/migrate/20200817233053_add_microsoft_teams_meeting_join_url_to_broadcasts.rb diff --git a/app/controllers/broadcasts_controller.rb b/app/controllers/broadcasts_controller.rb index 3a6a1ba..fa93af8 100644 --- a/app/controllers/broadcasts_controller.rb +++ b/app/controllers/broadcasts_controller.rb @@ -1,4 +1,5 @@ class BroadcastsController < ApplicationController + require 'microsoft_graph' layout "project" before_action :set_project @@ -17,6 +18,25 @@ class BroadcastsController < ApplicationController def create @broadcast.attributes = broadcast_params + begin + graph_api = MicrosoftGraph.new( + current_user, + ENV['AZURE_CLIENT_ID'], + ENV['AZURE_CLIENT_SECRET'], + ENV['AZURE_TENANT_ID'], + ENV['AZURE_SCOPES'] + ) + + subject = "#{@broadcast.name} Online Meeting" + teams_meeting = graph_api.create_teams_meeting(subject) + join_url = teams_meeting['joinUrl'] + @broadcast.microsoft_teams_meeting_url = join_url if join_url.present? + rescue StandardError => e + @broadcast.errors[:base] << e.message + render :new + return + end + if @broadcast.save log_create_analytics redirect_to [@project, :broadcasts], notice: t(".notice") @@ -27,7 +47,7 @@ class BroadcastsController < ApplicationController def show # @conference_url = url_for [@broadcast.project, @broadcast, :zoom_meeting] - @conference_url = url_for [@broadcast.project, @broadcast, :microsoft_teams_meeting] + @conference_url = @broadcast.microsoft_teams_meeting_url @recordings = @broadcast.broadcast_recordings.order_by_recent.paginate(page: params[:page]) @files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page]) render layout: 'application' diff --git a/app/controllers/microsoft_teams_meetings_controller.rb b/app/controllers/microsoft_teams_meetings_controller.rb deleted file mode 100644 index c86bfc2..0000000 --- a/app/controllers/microsoft_teams_meetings_controller.rb +++ /dev/null @@ -1,17 +0,0 @@ -class MicrosoftTeamsMeetingsController < ApplicationController - require 'microsoft_graph' - def show - authorize broadcast = Broadcast.find(params[:broadcast_id]) - - graph_api = MicrosoftGraph.new(current_user, ENV['AZURE_CLIENT_ID'], ENV['AZURE_CLIENT_SECRET'], ENV['AZURE_SCOPES']) - - meeting_start = DateTime.now - meeting_end = DateTime.now + 1.hour - subject = "Broadcast Meeting" - - - r = graph_api.create_teams_meeting(meeting_start, meeting_end, subject) - - render plain: r - end -end \ No newline at end of file diff --git a/app/models/broadcast.rb b/app/models/broadcast.rb index d1f6943..42df29b 100644 --- a/app/models/broadcast.rb +++ b/app/models/broadcast.rb @@ -1,4 +1,5 @@ class Broadcast < ApplicationRecord + require 'microsoft_graph' include PgSearch belongs_to :project diff --git a/app/views/broadcasts/show.html.erb b/app/views/broadcasts/show.html.erb index d36fccc..ccd5fc2 100644 --- a/app/views/broadcasts/show.html.erb +++ b/app/views/broadcasts/show.html.erb @@ -119,7 +119,7 @@
<% end %>

If you want to join the ZOOM meeting dedicated to this broadcast, follow the link below.

- <%= link_to 'Video Conference', @conference_url, class: 'btn btn-primary btn-block', target: '_blank' %> + <%= link_to 'Video Conference', @conference_url, class: "btn btn-primary btn-block #{@conference_url.present? ? '' : 'disabled'}", target: '_blank' %>
params[:active_tab] == 'recordings') %>" id="recordings">
diff --git a/config/routes.rb b/config/routes.rb index 327a924..5bdeac1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -102,7 +102,6 @@ Rails.application.routes.draw do delete :destroy_file end # resource :zoom_meeting, only: [:show] - resource :microsoft_teams_meeting, only: [:show] end resources :directories, except: [:index] do member do diff --git a/db/migrate/20200817233053_add_microsoft_teams_meeting_join_url_to_broadcasts.rb b/db/migrate/20200817233053_add_microsoft_teams_meeting_join_url_to_broadcasts.rb new file mode 100644 index 0000000..ddff8e9 --- /dev/null +++ b/db/migrate/20200817233053_add_microsoft_teams_meeting_join_url_to_broadcasts.rb @@ -0,0 +1,5 @@ +class AddMicrosoftTeamsMeetingJoinUrlToBroadcasts < ActiveRecord::Migration[6.0] + def change + add_column :broadcasts, :microsoft_teams_meeting_url, :string + end +end \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 34371f4..1707992 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -562,7 +562,8 @@ CREATE TABLE public.broadcasts ( token character varying, streamer_status integer DEFAULT 0, shoot_location_time_zone character varying DEFAULT 'UTC'::character varying, - full_live_stream_playback_uid character varying + full_live_stream_playback_uid character varying, + microsoft_teams_meeting_url character varying ); @@ -3972,6 +3973,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200721140821'), ('20200725231419'), ('20200810140331'), -('20200812161119'); +('20200812161119'), +('20200817233053'); diff --git a/lib/microsoft_graph.rb b/lib/microsoft_graph.rb index 040d22c..586eea6 100644 --- a/lib/microsoft_graph.rb +++ b/lib/microsoft_graph.rb @@ -2,49 +2,29 @@ require 'httparty' class MicrosoftGraph BASE_URL = 'https://graph.microsoft.com/v1.0'.freeze - BETA_BASE_URL = 'https://graph.microsoft.com/beta'.freeze - # Documentation says that "common" can be used (instead of tenantID) to refresh access token - REFRESH_TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'.freeze - def initialize(current_user, client_id, client_secret, scopes) + def initialize(current_user, client_id, client_secret, tenant_id, scopes) @current_user = current_user @uid = current_user.microsoft_user_id @token = current_user.microsoft_access_token @refresh_token = current_user.microsoft_refresh_token - @expires_at = current_user.microsoft_token_expires_at + @token_expires_at = current_user.microsoft_token_expires_at @client_id = client_id @client_secret = client_secret + @tenant_id = tenant_id @scopes = scopes end - def request(resource) - return false unless @token - - response = HTTParty.get( - "#{BASE_URL}/#{resource}", - headers: { - Authorization: "Bearer #{@token}" - } - ) - - if response.code != 200 - p '[Microsoft Graph API Error]' - p response.inspect - false - else - JSON.parse(response.body) + def create_teams_meeting(subject) + # Obtain new token if token is expired or will expire in less than 5 minutes + if 5.minutes.from_now.to_i > @token_expires_at.seconds + refresh_access_token end - end - - def create_teams_meeting(start_date_time, end_date_time, subject) - # check if token expired and obtain new access_token using refresh token response = HTTParty.post( - "#{BETA_BASE_URL}/me/onlineMeetings", + "#{BASE_URL}/me/onlineMeetings", body: { - startDateTime: start_date_time, - endDateTime: end_date_time, subject: subject, participants: { organizer: { @@ -55,22 +35,35 @@ class MicrosoftGraph } } } - }, + }.to_json, headers: { - Authorization: "Bearer #{@token}" + Authorization: "Bearer #{@token}", + 'Content-Type': 'application/json' } ) + raise StandardError, 'Authenticated user does not have a permission to create Teams Online Meeting' if response.code == 403 + if response.code != 201 - p '[Microsoft Graph API Error] Failed to create online meeting' - p response.inspect + Rails.logger.error('[Microsoft Graph Error]') + Rails.logger.error(response.inspect) + raise StandardError, "Failed to create teams meeting [#{response.code}]" else JSON.parse(response.body) end end - def refresh_token - response = HTTParty.post(REFRESH_TOKEN_URL, + private + + def refresh_token_url + "https://login.microsoftonline.com/#{@tenant_id}/oauth2/v2.0/token" + end + + def refresh_access_token + response = HTTParty.post(refresh_token_url, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, body: { client_id: @client_id, client_secret: @client_secret, @@ -79,5 +72,26 @@ class MicrosoftGraph scope: @scopes }) + if response.code != 200 + Rails.logger.error '[Microsoft Graph Error] Failed to obtain new access token using refresh token' + Rails.logger.error(response.inspect) + raise StandardError, 'Failed to obtain new access token' + end + + parsed_response = JSON.parse(response.body) + + new_access_token = parsed_response['access_token'] + new_refresh_token = parsed_response['refresh_token'] + token_expires_in = parsed_response['expires_in'] # For how long access token is valid (in seconds) + token_new_expiration_time = Time.now.to_i + token_expires_in + + @current_user.microsoft_access_token = new_access_token + @current_user.microsoft_refresh_token = new_refresh_token + @current_user.microsoft_token_expires_at = token_new_expiration_time + @current_user.save! + + @token = new_access_token + @refresh_token = new_refresh_token + @token_expires_at = token_new_expiration_time end end \ No newline at end of file