From 3986c71a0b7d135c9e22c633cd272477f013d192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikko=20Pyykk=C3=B6?= Date: Fri, 3 Sep 2021 15:34:17 +0300 Subject: [PATCH 1/5] accept moocfi tokens --- app/controllers/api/v8/base_controller.rb | 80 +++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/app/controllers/api/v8/base_controller.rb b/app/controllers/api/v8/base_controller.rb index f8e633da1..724994728 100644 --- a/app/controllers/api/v8/base_controller.rb +++ b/app/controllers/api/v8/base_controller.rb @@ -35,10 +35,23 @@ def present(hash) private def authenticate_user! + puts "current_user", @current_user + puts "token", bearer_token + return @current_user if @current_user + if doorkeeper_token @current_user ||= User.find_by(id: doorkeeper_token.resource_owner_id) raise 'Invalid token' unless @current_user + else + moocfi_user = validate_moocfi_user + puts "moocfi_user", moocfi_user + raise 'Invalid token' unless moocfi_user + + @current_user ||= User.find_by(id: moocfi_user['upstream_id']) if moocfi_user['upstream_id'] > 0 + @current_user ||= create_user_from_moocfi(moocfi_user) if not moocfi_user['upstream_id'] or moocfi_user['upstream_id'] < 0 + puts "current_user after find or create", @current_user + raise 'Invalid token' unless @current_user end @current_user ||= user_from_session || Guest.new end @@ -125,6 +138,73 @@ def check_client_minimum_version(client_name, client_version) end end end + + def bearer_token + pattern = /^Bearer / + authorization = request.authorization + authorization.gsub(pattern, '') if authorization && authorization.match(pattern) + end + + def validate_moocfi_user + uri = URI('http://localhost:4000/auth/validate') + req = Net::HTTP::Get.new(uri) + req["Authorization"] = request.authorization + + mooc_response = Net::HTTP.start(uri.hostname, uri.port) {|http| + res = http.request(req) + JSON.parse(res.body) + } + user = mooc_response["user"] + puts user + user + # @current_user ||= user + # @current_user ||= User.new({ + # id: user.id, + # login: user.username, + # email: user.email + # }) + + # .find_by(id: mooc_response["user"]["upstream_id"]) + end + + def create_user_from_moocfi(moocfi_user) + puts "creating new user from", moocfi_user + user = User.new + user.login = SecureRandom.uuid + user.email = moocfi_user['email'] + user.password = SecureRandom.base64(12) + user.administrator = moocfi_user['administrator'] || false + + user.save! + + first_name = UserFieldValue.new(field_name: 'first_name', user_id: user.id, value: moocfi_user['first_name']) + last_name = UserFieldValue.new(field_name: 'last_name', user_id: user.id, value: moocfi_user['last_name']) + # first_name = user.user_field_values.new(field_name: 'first_name', value: moocfi_user['first_name']) + # last_name = user.user_field_values.new(field_name: 'last_name', value: moocfi_user['last_name']) + + first_name.save! + last_name.save! + + puts "new user?", user.to_json + + update_moocfi_user(user) + + user + end + + def update_moocfi_user(user) + uri = URI('http://localhost:4000/api/user/update-from-tmc') + req = Net::HTTP::Post.new(uri, initheader = { 'Content-Type' => 'application/json' }) + req["Authorization"] = request.authorization + req.body = { upstream_id: user['id'] }.to_json + + mooc_response = Net::HTTP.start(uri.hostname, uri.port) {|http| + http.request(req) + } + puts mooc_response.code + raise "Error updating MOOC.fi user" if mooc_response.code.to_i >= 400 + + end end end end From b4ace5c8d869db4326fb29c8a5911bb36b5cfd97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikko=20Pyykk=C3=B6?= Date: Mon, 6 Sep 2021 17:36:10 +0300 Subject: [PATCH 2/5] secret for MOOC.fi request to update user; error refactoring --- Gemfile | 2 +- Gemfile.lock | 27 +++++ app/controllers/api/v8/base_controller.rb | 121 +++++++++++----------- app/models/site_setting.rb | 1 + config/site.dev.yml | 4 +- 5 files changed, 92 insertions(+), 63 deletions(-) diff --git a/Gemfile b/Gemfile index b289639f0..72f8f4597 100644 --- a/Gemfile +++ b/Gemfile @@ -61,7 +61,7 @@ group :development, :test do gem 'pry' gem 'pry-byebug' gem 'pry-rails' - + gem 'solargraph' # vscode gem 'database_cleaner', '~> 2.0' gem 'launchy' # for capybara's save_and_open_page gem 'mimic', '~> 0.4' diff --git a/Gemfile.lock b/Gemfile.lock index 16c14e824..5ca06311b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -76,6 +76,8 @@ GEM ast (2.4.2) autoprefixer-rails (10.2.5.0) execjs (< 2.8.0) + backport (1.2.0) + benchmark (0.1.1) bootstrap (4.6.0) autoprefixer-rails (>= 9.1.0) popper_js (>= 1.14.3, < 2) @@ -114,6 +116,7 @@ GEM unf (>= 0.0.5, < 1.0.0) doorkeeper (5.5.1) railties (>= 5) + e2mmap (0.1.0) erubi (1.10.0) eventmachine (1.2.7) execjs (2.7.0) @@ -182,6 +185,7 @@ GEM io-console (0.5.9) irb (1.3.5) reline (>= 0.1.5) + jaro_winkler (1.5.4) jquery-rails (4.4.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) @@ -190,6 +194,10 @@ GEM json-schema (2.8.1) addressable (>= 2.4) jwt (2.2.3) + kramdown (2.3.1) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) launchy (2.5.0) addressable (~> 2.7) letter_opener (1.7.0) @@ -321,6 +329,8 @@ GEM mime-types (>= 1.16, < 4.0) netrc (~> 0.8) retriable (3.1.2) + reverse_markdown (2.0.0) + nokogiri rexml (3.2.5) rspec (3.10.0) rspec-core (~> 3.10.0) @@ -403,6 +413,21 @@ GEM rack (~> 2.2) rack-protection (= 2.1.0) tilt (~> 2.0) + solargraph (0.43.0) + backport (~> 1.2) + benchmark + bundler (>= 1.17.2) + diff-lcs (~> 1.4) + e2mmap + jaro_winkler (~> 1.5) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.1) + parser (~> 3.0) + reverse_markdown (>= 1.0.5, < 3) + rubocop (>= 0.52) + thor (~> 1.0) + tilt (~> 2.0) + yard (~> 0.9, >= 0.9.24) sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -434,6 +459,7 @@ GEM xml-simple (1.1.8) xpath (3.2.0) nokogiri (~> 1.8) + yard (0.9.26) zeitwerk (2.4.2) PLATFORMS @@ -494,6 +520,7 @@ DEPENDENCIES ruby-prof (~> 1.4) sassc-rails (~> 2.1) simplecov + solargraph sprockets-rails swagger-blocks (~> 3.0) uglifier (~> 4.2) diff --git a/app/controllers/api/v8/base_controller.rb b/app/controllers/api/v8/base_controller.rb index 724994728..f936a0091 100644 --- a/app/controllers/api/v8/base_controller.rb +++ b/app/controllers/api/v8/base_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'json' +require 'rest-client' module Api module V8 @@ -35,9 +36,6 @@ def present(hash) private def authenticate_user! - puts "current_user", @current_user - puts "token", bearer_token - return @current_user if @current_user if doorkeeper_token @@ -45,13 +43,10 @@ def authenticate_user! raise 'Invalid token' unless @current_user else moocfi_user = validate_moocfi_user - puts "moocfi_user", moocfi_user - raise 'Invalid token' unless moocfi_user + # raise 'Invalid token' unless moocfi_user - @current_user ||= User.find_by(id: moocfi_user['upstream_id']) if moocfi_user['upstream_id'] > 0 - @current_user ||= create_user_from_moocfi(moocfi_user) if not moocfi_user['upstream_id'] or moocfi_user['upstream_id'] < 0 - puts "current_user after find or create", @current_user - raise 'Invalid token' unless @current_user + @current_user ||= User.find_by(id: moocfi_user['upstream_id']) || create_user_from_moocfi(moocfi_user) + # raise 'Invalid token' unless @current_user end @current_user ||= user_from_session || Guest.new end @@ -146,64 +141,68 @@ def bearer_token end def validate_moocfi_user - uri = URI('http://localhost:4000/auth/validate') - req = Net::HTTP::Get.new(uri) - req["Authorization"] = request.authorization - - mooc_response = Net::HTTP.start(uri.hostname, uri.port) {|http| - res = http.request(req) - JSON.parse(res.body) - } - user = mooc_response["user"] - puts user - user - # @current_user ||= user - # @current_user ||= User.new({ - # id: user.id, - # login: user.username, - # email: user.email - # }) - - # .find_by(id: mooc_response["user"]["upstream_id"]) + base_url_for_moocfi = SiteSetting.value('base_url_for_moocfi') + + puts 'I am validating' + begin + res = RestClient::Request.execute(method: :get, url: "#{base_url_for_moocfi}/auth/validate", headers: { 'Authorization': request.authorization }) + moocfi_response = JSON.parse(res.body) + + moocfi_response['user'] + rescue RestClient::ExceptionWithResponse => e + nil + # raise 'Invalid token' if (400..499).include? e.http_code.to_i + # raise 'Internal error' if e.http_code.to_i >= 500 + end end def create_user_from_moocfi(moocfi_user) - puts "creating new user from", moocfi_user - user = User.new - user.login = SecureRandom.uuid - user.email = moocfi_user['email'] - user.password = SecureRandom.base64(12) - user.administrator = moocfi_user['administrator'] || false - - user.save! - - first_name = UserFieldValue.new(field_name: 'first_name', user_id: user.id, value: moocfi_user['first_name']) - last_name = UserFieldValue.new(field_name: 'last_name', user_id: user.id, value: moocfi_user['last_name']) - # first_name = user.user_field_values.new(field_name: 'first_name', value: moocfi_user['first_name']) - # last_name = user.user_field_values.new(field_name: 'last_name', value: moocfi_user['last_name']) - - first_name.save! - last_name.save! - - puts "new user?", user.to_json - - update_moocfi_user(user) + puts 'creating' + # TODO: find user by email + ActiveRecord::Base.transaction do + user = User.create!( + login: SecureRandom.uuid, + email: moocfi_user['email'], + password: SecureRandom.base64(12), + administrator: moocfi_user['administrator'] || false, + ) + UserFieldValue.create!(field_name: 'first_name', user_id: user.id, value: moocfi_user['first_name']) + UserFieldValue.create!(field_name: 'last_name', user_id: user.id, value: moocfi_user['last_name']) + UserFieldValue.create!(field_name: 'organizational_id', user_id: user.id, value: moocfi_user['real_student_number'] || '') + + update_moocfi_user(user) + + user + rescue StandardError, ScriptError => e + ActiveRecord::Rollback + + raise e + end + end - user - end - def update_moocfi_user(user) - uri = URI('http://localhost:4000/api/user/update-from-tmc') - req = Net::HTTP::Post.new(uri, initheader = { 'Content-Type' => 'application/json' }) - req["Authorization"] = request.authorization - req.body = { upstream_id: user['id'] }.to_json - - mooc_response = Net::HTTP.start(uri.hostname, uri.port) {|http| - http.request(req) - } - puts mooc_response.code - raise "Error updating MOOC.fi user" if mooc_response.code.to_i >= 400 + puts 'updating' + base_url_for_moocfi = SiteSetting.value('base_url_for_moocfi') + moocfi_update_secret = 'faux' # SiteSetting.value('moocfi_update_secret') + begin + res = RestClient::Request.execute( + method: :post, + url: "#{base_url_for_moocfi}/api/user/update-from-tmc", + payload: { 'upstream_id': user['id'], 'secret': moocfi_update_secret }.to_json, + headers: { + 'Authorization': request.authorization, + 'Content-Type': 'application/json' + } + ) + moocfi_response = JSON.parse(res.body) + + raise "Error updating MOOC.fi user: #{moocfi_response.message}" unless moocfi_response.success + rescue RestClient::ExceptionWithResponse => e + puts 'res', res + raise 'Error updating MOOC.fi user' if (400..499).include? e.http_code.to_i + raise 'Internal error' if e.http_code.to_i >= 500 + end end end end diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index ad9d4f868..4d22c4802 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -26,6 +26,7 @@ def self.settings_from_files(files) raise "Invalid configuration file #{path}" unless data.is_a? Hash result = result.deep_merge(data) end + puts "settings", result result end diff --git a/config/site.dev.yml b/config/site.dev.yml index 75e82c79d..02c99abd7 100644 --- a/config/site.dev.yml +++ b/config/site.dev.yml @@ -19,4 +19,6 @@ valid_clients: min_version: 1.4.0 kafka_bridge_url: "test" kafka_bridge_secret: "test" -moocfi_service_id: "test" \ No newline at end of file +moocfi_service_id: "test" +moocfi_update_secret: "test" +base_url_for_moocfi: http://localhost:4000 From c8d67fe570e77c1c02254290fece878dfe379382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikko=20Pyykk=C3=B6?= Date: Wed, 8 Sep 2021 17:03:22 +0300 Subject: [PATCH 3/5] check existing user by email if no upstream_id --- app/controllers/api/v8/base_controller.rb | 55 ++++++++++++----------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/app/controllers/api/v8/base_controller.rb b/app/controllers/api/v8/base_controller.rb index f936a0091..7cdfc414d 100644 --- a/app/controllers/api/v8/base_controller.rb +++ b/app/controllers/api/v8/base_controller.rb @@ -45,7 +45,8 @@ def authenticate_user! moocfi_user = validate_moocfi_user # raise 'Invalid token' unless moocfi_user - @current_user ||= User.find_by(id: moocfi_user['upstream_id']) || create_user_from_moocfi(moocfi_user) + puts 'validated ok', moocfi_user + @current_user ||= User.find_by(id: moocfi_user['upstream_id']) || create_or_update_user_from_moocfi(moocfi_user) # raise 'Invalid token' unless @current_user end @current_user ||= user_from_session || Guest.new @@ -150,40 +151,45 @@ def validate_moocfi_user moocfi_response['user'] rescue RestClient::ExceptionWithResponse => e - nil - # raise 'Invalid token' if (400..499).include? e.http_code.to_i - # raise 'Internal error' if e.http_code.to_i >= 500 + raise 'Invalid MOOC.fi token' if (400..499).include? e.http_code.to_i + raise 'Internal error' if e.http_code.to_i >= 500 end end - def create_user_from_moocfi(moocfi_user) - puts 'creating' - # TODO: find user by email - ActiveRecord::Base.transaction do - user = User.create!( - login: SecureRandom.uuid, - email: moocfi_user['email'], - password: SecureRandom.base64(12), - administrator: moocfi_user['administrator'] || false, - ) - UserFieldValue.create!(field_name: 'first_name', user_id: user.id, value: moocfi_user['first_name']) - UserFieldValue.create!(field_name: 'last_name', user_id: user.id, value: moocfi_user['last_name']) - UserFieldValue.create!(field_name: 'organizational_id', user_id: user.id, value: moocfi_user['real_student_number'] || '') + def create_or_update_user_from_moocfi(moocfi_user) + # in case we have a disrepancy, ie. MOOC.fi user and TMC user both exist, but MOOC.fi user doesn't have TMC id + user = User.find_by(email: moocfi_user['email']) + if user update_moocfi_user(user) - user - rescue StandardError, ScriptError => e - ActiveRecord::Rollback - - raise e + else + ActiveRecord::Base.transaction do + user = User.create!( + login: SecureRandom.uuid, + email: moocfi_user['email'], + password: SecureRandom.base64(12), + administrator: moocfi_user['administrator'] || false, + ) + UserFieldValue.create!(field_name: 'first_name', user_id: user.id, value: moocfi_user['first_name']) + UserFieldValue.create!(field_name: 'last_name', user_id: user.id, value: moocfi_user['last_name']) + UserFieldValue.create!(field_name: 'organizational_id', user_id: user.id, value: moocfi_user['real_student_number'] || '') + + update_moocfi_user(user) + + UserMailer.email_confirmation(user, nil, nil).deliver_now + + user + rescue StandardError, ScriptError + raise ActiveRecord::Rollback + end end end def update_moocfi_user(user) puts 'updating' base_url_for_moocfi = SiteSetting.value('base_url_for_moocfi') - moocfi_update_secret = 'faux' # SiteSetting.value('moocfi_update_secret') + moocfi_update_secret = SiteSetting.value('moocfi_update_secret') begin res = RestClient::Request.execute( @@ -197,9 +203,8 @@ def update_moocfi_user(user) ) moocfi_response = JSON.parse(res.body) - raise "Error updating MOOC.fi user: #{moocfi_response.message}" unless moocfi_response.success + raise "Error updating MOOC.fi user: #{moocfi_response.message}" unless moocfi_response['success'] rescue RestClient::ExceptionWithResponse => e - puts 'res', res raise 'Error updating MOOC.fi user' if (400..499).include? e.http_code.to_i raise 'Internal error' if e.http_code.to_i >= 500 end From 2eb3b37ad64e99d16f29c5cb59ed3cfa27f5914d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikko=20Pyykk=C3=B6?= Date: Wed, 29 Sep 2021 16:27:37 +0300 Subject: [PATCH 4/5] changed endpoint in mooc.fi user update --- app/controllers/api/v8/base_controller.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/v8/base_controller.rb b/app/controllers/api/v8/base_controller.rb index 7cdfc414d..bf651c36b 100644 --- a/app/controllers/api/v8/base_controller.rb +++ b/app/controllers/api/v8/base_controller.rb @@ -45,7 +45,6 @@ def authenticate_user! moocfi_user = validate_moocfi_user # raise 'Invalid token' unless moocfi_user - puts 'validated ok', moocfi_user @current_user ||= User.find_by(id: moocfi_user['upstream_id']) || create_or_update_user_from_moocfi(moocfi_user) # raise 'Invalid token' unless @current_user end @@ -144,7 +143,6 @@ def bearer_token def validate_moocfi_user base_url_for_moocfi = SiteSetting.value('base_url_for_moocfi') - puts 'I am validating' begin res = RestClient::Request.execute(method: :get, url: "#{base_url_for_moocfi}/auth/validate", headers: { 'Authorization': request.authorization }) moocfi_response = JSON.parse(res.body) @@ -157,7 +155,7 @@ def validate_moocfi_user end def create_or_update_user_from_moocfi(moocfi_user) - # in case we have a disrepancy, ie. MOOC.fi user and TMC user both exist, but MOOC.fi user doesn't have TMC id + # in case we have a discrepancy, ie. MOOC.fi user and TMC user both exist, but MOOC.fi user doesn't have TMC id user = User.find_by(email: moocfi_user['email']) if user @@ -187,14 +185,13 @@ def create_or_update_user_from_moocfi(moocfi_user) end def update_moocfi_user(user) - puts 'updating' base_url_for_moocfi = SiteSetting.value('base_url_for_moocfi') moocfi_update_secret = SiteSetting.value('moocfi_update_secret') begin res = RestClient::Request.execute( - method: :post, - url: "#{base_url_for_moocfi}/api/user/update-from-tmc", + method: :patch, + url: "#{base_url_for_moocfi}/api/user", payload: { 'upstream_id': user['id'], 'secret': moocfi_update_secret }.to_json, headers: { 'Authorization': request.authorization, From 187a12c2f5e9bf8ad97a24e2efa18272ffe6b6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikko=20Pyykk=C3=B6?= Date: Thu, 30 Sep 2021 11:01:57 +0300 Subject: [PATCH 5/5] remove debug log --- app/models/site_setting.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index 4d22c4802..ad9d4f868 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -26,7 +26,6 @@ def self.settings_from_files(files) raise "Invalid configuration file #{path}" unless data.is_a? Hash result = result.deep_merge(data) end - puts "settings", result result end