Skip to content

Commit

Permalink
Merge branch 'release-0.15.x' into master-elos
Browse files Browse the repository at this point in the history
  • Loading branch information
wpramio committed Mar 26, 2024
2 parents fb0a331 + 10cbb4f commit baa7832
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 4 deletions.
108 changes: 107 additions & 1 deletion app/controllers/rooms_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require 'net/http'
require 'user'
require 'bbb_api'
require './lib/mconf/eduplay'
Expand Down Expand Up @@ -242,6 +243,10 @@ def set_launch_room
return
end

# check if we need to fetch the context/handler from an external URL
proceed, handler = fetch_external_context(launch_params)
return unless proceed

bbbltibroker_url = omniauth_bbbltibroker_url("/api/v1/sessions/#{launch_nonce}/invalidate")
Rails.logger.info "Making a session request to #{bbbltibroker_url}"
session_params = JSON.parse(
Expand All @@ -253,13 +258,15 @@ def set_launch_room

# Store the data from this launch for easier access
expires_at = Rails.configuration.launch_duration_mins.from_now
app_launch = AppLaunch.find_or_create_by(nonce: launch_nonce) do |launch|
app_launch = AppLaunch.create_with(room_handler: handler)
.find_or_create_by(nonce: launch_nonce) do |launch|
launch.update(
params: launch_params,
omniauth_auth: session['omniauth_auth']['bbbltibroker'],
expires_at: expires_at
)
end
Rails.logger.info "Saved the AppLaunch nonce=#{app_launch.nonce} room_handler=#{app_launch.room_handler}"

# Use this data only during the launch
# From now on, take it from the AppLaunch
Expand All @@ -284,4 +291,103 @@ def set_room_title
@subtitle = @room.description
end
end

def fetch_external_context(launch_params)
launch_nonce = params['launch_nonce']

# this is a temporary user in case we are responding the request here and we need it (at least
# the locale we need to set, even for error pages)
user_params = AppLaunch.new(params: launch_params).user_params
@user = BbbAppRooms::User.new(user_params)
set_current_locale

# will only try to get an external context/handler if the ConsumerConfig is configured to do so
if launch_params.key?('custom_params') && launch_params['custom_params'].key?('oauth_consumer_key')
consumer_key = launch_params['custom_params']['oauth_consumer_key']
if consumer_key.present?
ext_context_url = ConsumerConfig.find_by(key: consumer_key)&.external_context_url
end
end
return true, nil if ext_context_url.blank? # proceed without a handler

Rails.logger.info "The consumer is configured to use an API to fetch the context/handler consumer_key=#{consumer_key} url=#{ext_context_url}"

Rails.logger.info "Making a request to an external API to define the context/handler url=#{ext_context_url}"
begin
response = send_request(ext_context_url, launch_params)
# example response:
# [
# {
# "handler": "82af745030b9e1394815e61184d50fd25dfe884a",
# "name": "STRW2S/Q16.06",
# "uuid": "a9a2689a-1e27-4ce8-aa91-ca488620bb89"
# }
# ]
handlers = JSON.parse(response.body)
Rails.logger.warn "Got the following contexts from the API: #{handlers.inspect}"
rescue JSON::ParserError => error
Rails.logger.warn "Error parsing the external context API's response"
set_error('room', 'external_context_parse_error', 500)
respond_with_error(@error)
return false, nil
end
# in case the response is anything other than an array, consider it empty
handlers = [] unless handlers.is_a?(Array)

# if the handler was already set, try to use it
# this will happen in the 2nd step, after the user selects a handler/room to access
selected_handler = params['handler']
unless selected_handler.blank?
Rails.logger.info "Found a handler in the params, will try to use it handler=#{selected_handler}"

if handlers.find{ |h| h['handler'] == selected_handler }.nil?
Rails.logger.info "The handler found is NOT allowed, will not use it handler=#{selected_handler}"
set_error('room', 'external_context_invalid_handler', :forbidden)
respond_with_error(@error)
return false, nil
else
Rails.logger.info "The handler found is allowed, will use it handler=#{selected_handler}"
return true, selected_handler # proceed with a handler
end
end

if handlers.size == 0
Rails.logger.warn "Couldn't define a handler using the external request"
set_error('room', 'external_context_no_handler', :forbidden)
respond_with_error(@error)
return false, nil
elsif handlers.size > 1
@handlers = handlers
@launch_nonce = launch_nonce
respond_to do |format|
format.html { render 'rooms/external_context_selector' }
end
return false, nil
else
handler = handlers.first['handler']
Rails.logger.info "Defined a handler using the external request handler=#{handler}"
return true, handler
end
end

def send_request(url, data=nil)
url_parsed = URI.parse(url)
http = Net::HTTP.new(url_parsed.host, url_parsed.port)
http.open_timeout = 30
http.read_timeout = 30
http.use_ssl = true if url_parsed.scheme.downcase == 'https'

if data.nil?
Rails.logger.info "Sending a GET request to '#{url}'"
response = http.get(url_parsed.request_uri, @request_headers)
else
data = data.to_json
Rails.logger.info "Sending a POST request to '#{url}' with data='#{data.inspect}' (size=#{data.size})"
opts = { 'Content-Type' => 'application/json' }
response = http.post(url_parsed.request_uri, data, opts)
end
Rails.logger.info "Response: request=#{url} response_status=#{response.class.name} response_code=#{response.code} message_key=#{response.message} body=#{response.body}"

response
end
end
2 changes: 1 addition & 1 deletion app/models/app_launch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def room_params
name = self.params['context_title']

p = {
handler: resource_handler,
handler: self.room_handler,
name: name,
welcome: '',
consumer_key: self.consumer_key
Expand Down
2 changes: 1 addition & 1 deletion config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

module BbbAppRooms
class Application < Rails::Application
VERSION = "0.14.1"
VERSION = "0.15.0"

config.eager_load_paths << Rails.root.join('lib')

Expand Down
18 changes: 18 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,24 @@ en:
suggestion: "The token could not be validated"
explanation: "This problem can be caused by using an invalid key when validating the request through the LTI Provider."
status_code: "403"
external_context_parse_error:
code: "ParseError"
message: "Internal error fetching data"
suggestion: "There might be a problem in the server"
explanation: "This problem is caused when the external context API returns an invalid JSON response."
status_code: "500"
external_context_no_handler:
code: "NoHandler"
message: "Unauthorized"
suggestion: "User not found in the external context database"
explanation: "The user was not found in the external context database so they won't be able to access a class."
status_code: "401"
external_context_invalid_handler:
code: "InvalidHandler"
message: "Forbidden"
suggestion: "Unallowed context"
explanation: "You are trying to access a context you are not allowed to access."
status_code: "403"
scheduled_meeting:
not_found:
code: "NotFound"
Expand Down
18 changes: 18 additions & 0 deletions config/locales/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,24 @@ es:
suggestion: "No se puede verificar el token"
explanation: "El problema puede deberse al uso de una clave no válida para validar la solicitud realizada por el proveedor de LTI."
status_code: "403"
external_context_parse_error:
code: "ParseError"
message: "Error interno al buscar datos"
suggestion: "Se produjo un error en el servidor"
explanation: "Este problema ocurre cuando la API de contexto externo devuelve una respuesta JSON no válida."
status_code: "500"
external_context_no_handler:
code: "NoHandler"
message: "No autorizado"
suggestion: "Usuario no encontrado en la base de datos de contexto externo"
explanation: "El usuario no se encontró en la base de datos de contexto externo, por lo tanto, no tiene acceso a ninguna clase."
status_code: "401"
external_context_invalid_handler:
code: "InvalidHandler"
message: "Prohibido"
suggestion: "Contexto no permitido"
explanation: "Estás intentando acceder a un contexto al que no tienes permiso de acceso."
status_code: "403"
scheduled_meeting:
not_found:
code: "NotFound"
Expand Down
18 changes: 18 additions & 0 deletions config/locales/pt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,24 @@ pt:
suggestion: "O token não pode ser validado"
explanation: "O problema pode ser causado pela utilização de uma chave inválida na validação da requisição feita pelo provedor LTI."
status_code: "403"
external_context_parse_error:
code: “ParseError”
message: "Erro interno ao buscar dados"
suggestion: "Ocorreu um erro no servidor"
explanation: "Esse problema é causado quando a API de contexto externo retorna uma resposta JSON inválida."
status_code: "500"
external_context_no_handler:
code: "NoHandler"
message: "Não autorizado"
suggestion: "Usuário não encontrado no banco de dados de contexto externo"
explanation: "O usuário não foi encontrado no banco de dados de contexto externo, portanto não possui acesso a nenhuma classe."
status_code: "401"
external_context_invalid_handler:
code: "InvalidHandler"
message: "Proibido"
suggestion: "Contexto não permitido"
explanation: "Você está tentando acessar um contexto ao qual não tem permissão de acesso."
status_code: "403"
scheduled_meeting:
not_found:
code: "NotFound"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddExternalContextUrlToConsumerConfigs < ActiveRecord::Migration[6.1]
def change
add_column(:consumer_configs, :external_context_url, :string)
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_02_28_094200) do
ActiveRecord::Schema.define(version: 2024_03_14_172824) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -70,6 +70,7 @@
t.boolean "message_reference_terms_use", default: true
t.string "external_widget", default: ""
t.boolean "force_disable_external_link", default: false
t.string "external_context_url"
t.index ["key"], name: "index_consumer_configs_on_key", unique: true
end

Expand Down
16 changes: 16 additions & 0 deletions themes/elos/assets/stylesheets/theme-application-elos.scss
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,22 @@ body.theme-elos {
width: 80%;
}

.btn-external-handler-access {
background-color: var(--color-sec);
color: var(--color-white);
font-size: var(--font-size);
padding: 8px 48px;
}

.user-data {
color: var(--color-gray);

.user-name {
font-size: 16px;
margin-left: 4px;
}
}

.form-actions {
text-align: right;
margin-top: blocks(6);
Expand Down
4 changes: 4 additions & 0 deletions themes/elos/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ en:
close:
message: "Thank you for participating and until the next time."
title: "Your session has ended"
external_handler_selector:
enter: "Access"
entering: "Acessing..."
title: "Classes"
coc:
schools: "Schools"
classes_header:
Expand Down
4 changes: 4 additions & 0 deletions themes/elos/config/locales/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ es:
close:
message: "Gracias por su participación y hasta luego."
title: "Tu sesión ha terminado"
external_handler_selector:
enter: "Acceso"
entering: "Accediendo..."
title: "Clases"
scheduled_meetings:
destroy: "Eliminar"
edit:
Expand Down
4 changes: 4 additions & 0 deletions themes/elos/config/locales/pt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ pt:
close:
message: "Obrigado pela sua participação e até a próxima."
title: "Sua sessão acabou"
external_handler_selector:
enter: "Acessar"
entering: "Acessando..."
title: "Turmas"
coc:
schools: "Escolas"
classes_header:
Expand Down
34 changes: 34 additions & 0 deletions themes/elos/views/rooms/external_context_selector.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<div class="col-12 col-md-12 col-lg-12">

<div class="d-flex justify-content-end align-items-center user-data mb-5">
<i class="icon material-icons mr-2">person</i>
<div class="user-name"><%= @user.full_name %> (<%= @user.email %>) </div>
</div>

<h2 class="mb-4">
<%= t('rooms.external_handler_selector.title') %>
</h2>

<table class="table">
<tbody>
<% @handlers.each_with_index do |handler, index| %>
<% # TODO: remove the gsub %>
<% path = Rails.application.routes.url_helpers.room_launch_path(launch_nonce: @launch_nonce, handler: handler['handler']).gsub(Regexp.new('^/rooms/rooms'), '/rooms') %>
<tr class="d-flex row tr-row">
<td class="d-flex col-12 col-md-6 align-items-sm-center align-items-md-start">
<%= handler['name'] %>
</td>
<td class="col-12 col-md-6 align-items-md-end">
<%= button_to t('rooms.external_handler_selector.enter'), path, method: :post, class: "btn btn-primary btn-external-handler-access", data: { disable_with: t('rooms.external_handler_selector.entering') } %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div id="elos-logo" class="d-flex align-items-center">
<%= image_tag "elos-logo.svg", title: "Elos", class: "mx-auto" %>
</div>

0 comments on commit baa7832

Please sign in to comment.