From 4334c28a0f4b0b4a83b0c4f20603a3ca267d6d85 Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Wed, 6 Dec 2017 13:54:11 -0800 Subject: [PATCH 01/11] Add OAuth gems for LinkedIn --- Gemfile | 5 ++++- Gemfile.lock | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index d81c745..bc2dabe 100644 --- a/Gemfile +++ b/Gemfile @@ -27,11 +27,14 @@ gem 'jbuilder', '~> 2.5' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 3.0' # Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' +gem 'bcrypt', '~> 3.1.7' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +gem 'omniauth' +gem 'omniauth-linkedin-oauth2' + group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] diff --git a/Gemfile.lock b/Gemfile.lock index 2f66ec0..3e77128 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -47,6 +47,7 @@ GEM minitest (~> 5.1) tzinfo (~> 1.1) arel (8.0.0) + bcrypt (3.1.11) bindex (0.5.0) builder (3.2.3) byebug (9.1.0) @@ -64,14 +65,18 @@ GEM factory_bot_rails (4.8.2) factory_bot (~> 4.8.2) railties (>= 3.0.0) + faraday (0.12.2) + multipart-post (>= 1.2, < 3) ffi (1.9.18) globalid (0.4.1) activesupport (>= 4.2.0) + hashie (3.5.6) i18n (0.9.1) concurrent-ruby (~> 1.0) jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) + jwt (1.5.6) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -86,9 +91,26 @@ GEM mini_portile2 (2.3.0) minitest (5.10.3) multi_json (1.12.2) + multi_xml (0.6.0) + multipart-post (2.0.0) nio4r (2.1.0) nokogiri (1.8.1) mini_portile2 (~> 2.3.0) + oauth2 (1.4.0) + faraday (>= 0.8, < 0.13) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + omniauth (1.7.1) + hashie (>= 3.4.6, < 3.6.0) + rack (>= 1.6.2, < 3) + omniauth-linkedin-oauth2 (0.2.5) + omniauth (~> 1.0) + omniauth-oauth2 + omniauth-oauth2 (1.4.0) + oauth2 (~> 1.0) + omniauth (~> 1.2) pg (0.21.0) puma (3.11.0) rack (2.0.3) @@ -188,12 +210,15 @@ PLATFORMS ruby DEPENDENCIES + bcrypt (~> 3.1.7) byebug dotenv-rails factory_bot_rails (~> 4.0) faker! jbuilder (~> 2.5) listen (>= 3.0.5, < 3.2) + omniauth + omniauth-linkedin-oauth2 pg (~> 0.18) puma (~> 3.7) rails (~> 5.1.4) From 60574fba8c69b164738efbce7482ab69dd0af9f6 Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Wed, 6 Dec 2017 13:55:07 -0800 Subject: [PATCH 02/11] Add linked in user fields to model --- app/models/user.rb | 15 +++++++++++++++ db/migrate/20171205182332_add_uid_to_user.rb | 5 +++++ 2 files changed, 20 insertions(+) create mode 100644 db/migrate/20171205182332_add_uid_to_user.rb diff --git a/app/models/user.rb b/app/models/user.rb index 9a6ac28..c8370d2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -14,4 +14,19 @@ def json_with_associations def serializable_hash(options = nil) super({except: [:created_at, :updated_at]}.merge(options || {})) end + + def self.find_or_create_from_auth_hash(auth_hash) + self.find_or_create_by(uid: auth_hash[:uid]) do |user| + user.name = auth_hash[:info][:first_name] + ' ' + auth_hash[:info][:last_name] + user.email = auth_hash[:info][:email] + # user.description = auth_hash[:info][:description] + user.location = auth_hash[:info][:location] + user.photo_url = auth_hash.extra.raw_info.pictureUrls.values[1][0] + user.linkedin_url = auth_hash[:info][:urls][:public_profile] + # user.linkedin_token = auth_hash[:credentials][:token] + # user.linkedin_token_expiration = auth_hash[:credentials][:expires_at] + user.current_company = auth_hash[:extra][:raw_info][:positions][:values][0][:company][:name] + user.current_position = auth_hash[:extra][:raw_info][:positions][:values][0][:title] + end + end end diff --git a/db/migrate/20171205182332_add_uid_to_user.rb b/db/migrate/20171205182332_add_uid_to_user.rb new file mode 100644 index 0000000..f6476ba --- /dev/null +++ b/db/migrate/20171205182332_add_uid_to_user.rb @@ -0,0 +1,5 @@ +class AddUidToUser < ActiveRecord::Migration[5.1] + def change + add_column :users, :uid, :string + end +end From da1f21e825d6fddb3e4b0b982f16e4fb7929534e Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Wed, 6 Dec 2017 13:56:42 -0800 Subject: [PATCH 03/11] Create Token module for encoding and decoding jwts --- app/lib/tokenize.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 app/lib/tokenize.rb diff --git a/app/lib/tokenize.rb b/app/lib/tokenize.rb new file mode 100644 index 0000000..17d0cbc --- /dev/null +++ b/app/lib/tokenize.rb @@ -0,0 +1,23 @@ +module Tokenize + def self.encode(sub) + payload = { + iss: 'dbc-bootup', + sub: sub, + exp: 4.hours.from_now.to_i, + iat: Time.now.to_i + } + JWT.encode payload, ENV['JWT_SECRET'], 'HS256' + end + + def self.decode(token) + options = { + iss: 'dbc-bootup', + verify_iss: true, + verify_iat: true, + leeway: 30, + algorithm: 'HS256' + } + JWT.decode token, ENV['JWT_SECRET'], true, options + end +end + From 8c530905ee42f206daab0fc7d93b61306f085a3d Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Wed, 6 Dec 2017 13:58:08 -0800 Subject: [PATCH 04/11] Add support for sesssions and cookies so that omniauth will work --- app/controllers/application_controller.rb | 2 +- config/application.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3f1a602..257a571 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,3 +1,3 @@ class ApplicationController < ActionController::API - + include ActionController::Cookies end diff --git a/config/application.rb b/config/application.rb index 2aebca5..1ac3d8b 100644 --- a/config/application.rb +++ b/config/application.rb @@ -28,5 +28,9 @@ class Application < Rails::Application # Don't generate system test files. config.generators.system_tests = nil + + config.session_store :cookie_store, key: '_interslice_session' + config.middleware.use ActionDispatch::Cookies # Required for all session management + config.middleware.use ActionDispatch::Session::CookieStore, config.session_options end end From 23044da9d427f0cf083d80807f23681b762fff2c Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Wed, 6 Dec 2017 13:58:29 -0800 Subject: [PATCH 05/11] Add routes and controller for linkedin login --- app/controllers/sessions_controller.rb | 39 ++++++++++++++++++++++++++ config/initializers/omniauth.rb | 5 ++++ config/routes.rb | 2 ++ db/schema.rb | 21 +++++++------- 4 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 app/controllers/sessions_controller.rb create mode 100644 config/initializers/omniauth.rb diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..8c47da9 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,39 @@ +class SessionsController < ApplicationController + def login + user = User.find_or_create_from_auth_hash(auth_hash) + if user.valid? + token = Tokenize.encode({uid: user.uid}) + cookies[:jwt] = {value: token, httponly: true} + redirect_to '/' + else + flash[:error] = "Login Failed" + redirect_to '/user/info' + end + end + + def logout + cookies.delete :jwt + redirect_to '/test' + end + + protected + + def auth_hash + request.env['omniauth.auth'] + end + + private + + def linkedin_params + params.require(:user).permit( + :first_name, + :last_name, + :linkedin_image_url, + :current_title, + :current_company, + :usership_company, + :email + ) + end +end + diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb new file mode 100644 index 0000000..25d2ab4 --- /dev/null +++ b/config/initializers/omniauth.rb @@ -0,0 +1,5 @@ +Rails.application.config.middleware.use OmniAuth::Builder do + # provider :developer unless Rails.env.production? + provider :linkedin, ENV['LINKEDIN_CLIENT_ID'], ENV['LINKEDIN_SECRET_KEY'], fields: ['id', 'email-address', 'first-name', 'last-name', 'headline', 'location', 'industry', 'picture-url', 'public-profile-url', 'positions', "picture-urls::(original)"], callback_path: '/api/auth/linkedin/callback', path_prefix: '/api/auth' +end + diff --git a/config/routes.rb b/config/routes.rb index ab9cdaf..25dd668 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,5 +4,7 @@ resources :interviews resources :companies resources :skills, only: [:create, :index] + get '/auth/linkedin/callback', to: 'sessions#login' + get '/auth/linkedin/logout', to: 'sessions#logout' end end diff --git a/db/schema.rb b/db/schema.rb index 34c8bed..b28b6c4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171204013541) do +ActiveRecord::Schema.define(version: 20171205182332) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -20,7 +20,7 @@ t.string "location" t.string "website" t.string "tech_field" - t.boolean "has_apprenticeship" + t.boolean "has_apprenticeship?" t.datetime "created_at", null: false t.datetime "updated_at", null: false end @@ -34,21 +34,21 @@ create_table "interviews", force: :cascade do |t| t.string "job_title" - t.boolean "referred" - t.boolean "received_offer" + t.boolean "referred?" + t.boolean "received_offer?" t.text "notes" t.integer "difficulty_rating" t.integer "experience_rating" - t.boolean "accepted_offer" - t.boolean "phone_screen" + t.boolean "accepted_offer?" + t.boolean "phone_screen?" t.text "phone_screen_details" - t.boolean "tech_screen" + t.boolean "tech_screen?" t.text "tech_screen_details" - t.boolean "take_home_challenge" + t.boolean "take_home_challenge?" t.text "take_home_challenge_details" - t.boolean "onsite" + t.boolean "onsite?" t.text "onsite_details" - t.boolean "whiteboarding" + t.boolean "whiteboarding?" t.text "whiteboarding_details" t.text "negotiation_details" t.bigint "user_id" @@ -93,6 +93,7 @@ t.string "photo_url" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "uid" end end From 4994f60876c1f227c500f3b682ad2b90979f8356 Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Wed, 6 Dec 2017 17:53:25 -0800 Subject: [PATCH 06/11] Update list of params put in the jwt token --- app/controllers/application_controller.rb | 15 ++++++++++----- app/controllers/sessions_controller.rb | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3f52b22..add6f26 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,9 +2,14 @@ class ApplicationController < ActionController::API include ActionController::Cookies after_action :cors_set_access_control_headers - def cors_set_access_control_headers - headers['Access-Control-Allow-Origin'] = '*' - headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS' - headers['Access-Control-Allow-Headers'] = '*' - end + def current_user + token = Tokenize.decode(cookies[:jwt]) + User.try(:find_by, {id: token.first['sub']['id']}) + end + + def cors_set_access_control_headers + headers['Access-Control-Allow-Origin'] = '*' + headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS' + headers['Access-Control-Allow-Headers'] = '*' + end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 8c47da9..b903a33 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -2,7 +2,7 @@ class SessionsController < ApplicationController def login user = User.find_or_create_from_auth_hash(auth_hash) if user.valid? - token = Tokenize.encode({uid: user.uid}) + token = Tokenize.encode({uid: user.uid, id: user.id}) cookies[:jwt] = {value: token, httponly: true} redirect_to '/' else From e944838ce2c3216bd39245d0fc2b91458b7a0c0a Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Wed, 6 Dec 2017 21:41:37 -0800 Subject: [PATCH 07/11] Add completed registration flag to User --- .../20171207054024_add_completed_registration_to_users.rb | 5 +++++ db/schema.rb | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20171207054024_add_completed_registration_to_users.rb diff --git a/db/migrate/20171207054024_add_completed_registration_to_users.rb b/db/migrate/20171207054024_add_completed_registration_to_users.rb new file mode 100644 index 0000000..2c36e09 --- /dev/null +++ b/db/migrate/20171207054024_add_completed_registration_to_users.rb @@ -0,0 +1,5 @@ +class AddCompletedRegistrationToUsers < ActiveRecord::Migration[5.1] + def change + add_column :users, :completed_registration, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 13bf716..0353ef9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171205193026) do +ActiveRecord::Schema.define(version: 20171207054024) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -103,6 +103,7 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "uid" + t.boolean "completed_registration" end end From 7744cdb90d6fa45f0b642a3bad2fbbdea6d00c2b Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Wed, 6 Dec 2017 21:44:15 -0800 Subject: [PATCH 08/11] Make User#completed_registration false by default --- db/migrate/20171207054255_change_completed_registration.rb | 5 +++++ db/schema.rb | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20171207054255_change_completed_registration.rb diff --git a/db/migrate/20171207054255_change_completed_registration.rb b/db/migrate/20171207054255_change_completed_registration.rb new file mode 100644 index 0000000..cd4137e --- /dev/null +++ b/db/migrate/20171207054255_change_completed_registration.rb @@ -0,0 +1,5 @@ +class ChangeCompletedRegistration < ActiveRecord::Migration[5.1] + def change + change_column :users, :completed_registration, :boolean, :default => false + end +end diff --git a/db/schema.rb b/db/schema.rb index 0353ef9..893df44 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171207054024) do +ActiveRecord::Schema.define(version: 20171207054255) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -103,7 +103,7 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "uid" - t.boolean "completed_registration" + t.boolean "completed_registration", default: false end end From 0be5ca54e18b7f44975794fa4754d6fcbbc0b181 Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Thu, 7 Dec 2017 00:22:03 -0800 Subject: [PATCH 09/11] Add logged_in? and current_user application controller methods --- app/controllers/application_controller.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index add6f26..2e50eb0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -3,10 +3,15 @@ class ApplicationController < ActionController::API after_action :cors_set_access_control_headers def current_user + return nil if cookies[:jwt].nil? || cookies[:jwt].empty? token = Tokenize.decode(cookies[:jwt]) User.try(:find_by, {id: token.first['sub']['id']}) end + def logged_in? + !!current_user + end + def cors_set_access_control_headers headers['Access-Control-Allow-Origin'] = '*' headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS' From 5ed1b332279e1d1ebc5567d4ad339d09403ff8ee Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Thu, 7 Dec 2017 00:24:20 -0800 Subject: [PATCH 10/11] More login and add logout functionality --- app/controllers/sessions_controller.rb | 2 +- app/controllers/users_controller.rb | 9 +++++++++ app/models/user.rb | 2 +- config/routes.rb | 3 ++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index b903a33..28abd27 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -13,7 +13,7 @@ def login def logout cookies.delete :jwt - redirect_to '/test' + redirect_to '/' end protected diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index bec3bf1..348c6b0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -37,6 +37,15 @@ def destroy render json: {message: "User destroyed"} end + def profile + user = current_user + if user + render json: user.json_with_associations + else + render json: {} + end + end + private def user_params params.require(:user).permit(:name, :year, :cohort, :location, :email, :linkedin_url, :github_url, :facebook_url, :current_company, :current_position, :photo_url) diff --git a/app/models/user.rb b/app/models/user.rb index 85fe176..46a44e2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -20,7 +20,7 @@ def self.find_or_create_from_auth_hash(auth_hash) user.name = auth_hash[:info][:first_name] + ' ' + auth_hash[:info][:last_name] user.email = auth_hash[:info][:email] # user.description = auth_hash[:info][:description] - user.location = auth_hash[:info][:location] + user.location = auth_hash[:info][:location][:name] user.photo_url = auth_hash.extra.raw_info.pictureUrls.values[1][0] user.linkedin_url = auth_hash[:info][:urls][:public_profile] # user.linkedin_token = auth_hash[:credentials][:token] diff --git a/config/routes.rb b/config/routes.rb index 17531bf..cda59bb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,7 +5,8 @@ resources :companies resources :skills, only: [:create, :index] get '/auth/linkedin/callback', to: 'sessions#login' - get '/auth/linkedin/logout', to: 'sessions#logout' + get '/logout', to: 'sessions#logout' resources :searches + get '/profile', to: 'users#profile' end end From 33576f438d2465f53818116f1f1726593299d23d Mon Sep 17 00:00:00 2001 From: Josh Leichtung Date: Thu, 7 Dec 2017 00:25:46 -0800 Subject: [PATCH 11/11] Allow Interview#create to utilize and require current_user to make new record --- app/controllers/interviews_controller.rb | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/controllers/interviews_controller.rb b/app/controllers/interviews_controller.rb index 07747f9..9e9684e 100644 --- a/app/controllers/interviews_controller.rb +++ b/app/controllers/interviews_controller.rb @@ -14,13 +14,19 @@ def show end def create - @interview = Interview.new(interview_params) - if @interview.save - render json: @interview.json_with_associations + if !logged_in? + render json: {error: "Not logged in", message: "Must be logged in to report on an interview"}, status: 403 else - p @interview.errors.full_messages - render json: {errors: @interview.errors.full_messages}, status: 422 + interview = Interview.new(interview_params) + interview.interviewee = current_user + if interview.save + render json: interview.json_with_associations + else + p interview.errors.full_messages + render json: {errors: interview.errors.full_messages}, status: 422 + end end + end def update @@ -40,6 +46,6 @@ def destroy private def interview_params - params.require(:interview).permit(:job_title, :referred, :received_offer, :notes, :difficulty_rating, :experience_rating, :accepted_offer, :phone_screen, :phone_screen_details, :tech_screen, :tech_screen_details, :take_home_challenge, :take_home_challenge_details, :onsite, :onsite_details, :whiteboarding, :whiteboarding_details, :negotiation_details, :user_id, :company_id) + params.require(:interview).permit(:job_title, :referred, :received_offer, :notes, :difficulty_rating, :experience_rating, :accepted_offer, :phone_screen, :phone_screen_details, :tech_screen, :tech_screen_details, :take_home_challenge, :take_home_challenge_details, :onsite, :onsite_details, :whiteboarding, :whiteboarding_details, :negotiation_details, :company_id) end end