Complete teams meeting creation and token refresh

This commit is contained in:
Bilal
2020-08-18 20:43:33 +03:00
parent f25a72004e
commit be5261037e
8 changed files with 80 additions and 56 deletions

View File

@@ -1,4 +1,5 @@
class BroadcastsController < ApplicationController class BroadcastsController < ApplicationController
require 'microsoft_graph'
layout "project" layout "project"
before_action :set_project before_action :set_project
@@ -17,6 +18,25 @@ class BroadcastsController < ApplicationController
def create def create
@broadcast.attributes = broadcast_params @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 if @broadcast.save
log_create_analytics log_create_analytics
redirect_to [@project, :broadcasts], notice: t(".notice") redirect_to [@project, :broadcasts], notice: t(".notice")
@@ -27,7 +47,7 @@ class BroadcastsController < ApplicationController
def show def show
# @conference_url = url_for [@broadcast.project, @broadcast, :zoom_meeting] # @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]) @recordings = @broadcast.broadcast_recordings.order_by_recent.paginate(page: params[:page])
@files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page]) @files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page])
render layout: 'application' render layout: 'application'

View File

@@ -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

View File

@@ -1,4 +1,5 @@
class Broadcast < ApplicationRecord class Broadcast < ApplicationRecord
require 'microsoft_graph'
include PgSearch include PgSearch
belongs_to :project belongs_to :project

View File

@@ -119,7 +119,7 @@
<hr> <hr>
<% end %> <% end %>
<p class="card-text">If you want to join the ZOOM meeting dedicated to this broadcast, follow the link below.</p> <p class="card-text">If you want to join the ZOOM meeting dedicated to this broadcast, follow the link below.</p>
<%= 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' %>
</div> </div>
<div class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == 'recordings') %>" id="recordings"> <div class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == 'recordings') %>" id="recordings">
<div id="broadcast_recordings"> <div id="broadcast_recordings">

View File

@@ -102,7 +102,6 @@ Rails.application.routes.draw do
delete :destroy_file delete :destroy_file
end end
# resource :zoom_meeting, only: [:show] # resource :zoom_meeting, only: [:show]
resource :microsoft_teams_meeting, only: [:show]
end end
resources :directories, except: [:index] do resources :directories, except: [:index] do
member do member do

View File

@@ -0,0 +1,5 @@
class AddMicrosoftTeamsMeetingJoinUrlToBroadcasts < ActiveRecord::Migration[6.0]
def change
add_column :broadcasts, :microsoft_teams_meeting_url, :string
end
end

View File

@@ -562,7 +562,8 @@ CREATE TABLE public.broadcasts (
token character varying, token character varying,
streamer_status integer DEFAULT 0, streamer_status integer DEFAULT 0,
shoot_location_time_zone character varying DEFAULT 'UTC'::character varying, 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'), ('20200721140821'),
('20200725231419'), ('20200725231419'),
('20200810140331'), ('20200810140331'),
('20200812161119'); ('20200812161119'),
('20200817233053');

View File

@@ -2,49 +2,29 @@ require 'httparty'
class MicrosoftGraph class MicrosoftGraph
BASE_URL = 'https://graph.microsoft.com/v1.0'.freeze 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 @current_user = current_user
@uid = current_user.microsoft_user_id @uid = current_user.microsoft_user_id
@token = current_user.microsoft_access_token @token = current_user.microsoft_access_token
@refresh_token = current_user.microsoft_refresh_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_id = client_id
@client_secret = client_secret @client_secret = client_secret
@tenant_id = tenant_id
@scopes = scopes @scopes = scopes
end end
def request(resource) def create_teams_meeting(subject)
return false unless @token # 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
response = HTTParty.get( refresh_access_token
"#{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)
end 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( response = HTTParty.post(
"#{BETA_BASE_URL}/me/onlineMeetings", "#{BASE_URL}/me/onlineMeetings",
body: { body: {
startDateTime: start_date_time,
endDateTime: end_date_time,
subject: subject, subject: subject,
participants: { participants: {
organizer: { organizer: {
@@ -55,22 +35,35 @@ class MicrosoftGraph
} }
} }
} }
}, }.to_json,
headers: { 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 if response.code != 201
p '[Microsoft Graph API Error] Failed to create online meeting' Rails.logger.error('[Microsoft Graph Error]')
p response.inspect Rails.logger.error(response.inspect)
raise StandardError, "Failed to create teams meeting [#{response.code}]"
else else
JSON.parse(response.body) JSON.parse(response.body)
end end
end end
def refresh_token private
response = HTTParty.post(REFRESH_TOKEN_URL,
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: { body: {
client_id: @client_id, client_id: @client_id,
client_secret: @client_secret, client_secret: @client_secret,
@@ -79,5 +72,26 @@ class MicrosoftGraph
scope: @scopes 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
end end