Skip to content

Commit d6f7f7e

Browse files
committed
WIP: Flesh out PersonPlatformIntegration
1 parent 390dcfc commit d6f7f7e

21 files changed

+439
-266
lines changed

app/controllers/better_together/omniauth_callbacks_controller.rb

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,56 @@
1-
21
class BetterTogether::OmniauthCallbacksController < Devise::OmniauthCallbacksController
32
# See https://github.com/omniauth/omniauth/wiki/FAQ#rails-session-is-clobbered-after-callback-on-developer-strategy
43
skip_before_action :verify_authenticity_token, only: %i[github]
54

5+
before_action :set_person_platform_integration, except: [:failure]
6+
before_action :set_user, except: [:failure]
7+
8+
attr_reader :person_platform_integration, :user
9+
10+
# def facebook
11+
# handle_auth "Facebook"
12+
# end
13+
614
def github
7-
@user = ::BetterTogether.user_class.from_omniauth(request.env['omniauth.auth'])
8-
if @user.persisted?
9-
sign_in_and_redirect @user
10-
set_flash_message(:notice, :success, kind: 'Github') if is_navigational_format?
11-
else
12-
flash[:error] = 'There was a problem signing you in through Github. Please register or try signing in later.'
13-
redirect_to new_user_registration_url
14-
end
15+
handle_auth 'Github'
16+
end
17+
18+
private
19+
20+
def handle_auth(kind)
21+
return unless user_signed_in?
22+
23+
flash[:success] = t 'devise_omniauth_callbacks.success', kind: if is_navigational_format?
24+
redirect_to edit_user_registration_path
25+
else
26+
flash[:alert] = t 'devise_omniauth_callbacks.failure', kind:, reason: "#{auth.info.email} is not authorized"
27+
redirect_to new_user_registration_path
28+
end
1529
end
1630

31+
def auth
32+
request.env['omniauth.auth']
33+
end
34+
35+
def set_person_platform_integration
36+
@person_platform_integration = PersonPlatformIntegration.find_by(provider: auth.provider, uid: auth.uid)
37+
end
38+
39+
def set_user
40+
@user = ::BetterTogether.user_class.from_omniauth(person_platform_integration:, auth:, current_user:)
41+
end
42+
43+
# def github
44+
# @user = ::BetterTogether.user_class.from_omniauth(request.env['omniauth.auth'])
45+
# if @user.persisted?
46+
# sign_in_and_redirect @user
47+
# set_flash_message(:notice, :success, kind: 'Github') if is_navigational_format?
48+
# else
49+
# flash[:error] = 'There was a problem signing you in through Github. Please register or try signing in later.'
50+
# redirect_to new_user_registration_url
51+
# end
52+
# end
53+
1754
def failure
1855
flash[:error] = 'There was a problem signing you in. Please register or try signing in later.'
1956
redirect_to helpers.base_url
Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,55 @@
1-
class BetterTogether::PersonPlatformIntegrationsController < ApplicationController
2-
before_action :set_person_platform_integration, only: %i[ show edit update destroy ]
1+
# frozen_string_literal: true
32

4-
# GET /better_together/person_platform_integrations
5-
def index
6-
@person_platform_integrations = BetterTogether::PersonPlatformIntegration.all
7-
end
3+
module BetterTogether
4+
class PersonPlatformIntegrationsController < ApplicationController
5+
before_action :set_person_platform_integration, only: %i[show edit update destroy]
86

9-
# GET /better_together/person_platform_integrations/1
10-
def show
11-
end
7+
# GET /better_together/person_platform_integrations
8+
def index
9+
@person_platform_integrations = BetterTogether::PersonPlatformIntegration.all
10+
end
1211

13-
# GET /better_together/person_platform_integrations/new
14-
def new
15-
@person_platform_integration = BetterTogether::PersonPlatformIntegration.new
16-
end
12+
# GET /better_together/person_platform_integrations/1
13+
def show; end
1714

18-
# GET /better_together/person_platform_integrations/1/edit
19-
def edit
20-
end
15+
# GET /better_together/person_platform_integrations/new
16+
def new
17+
@person_platform_integration = BetterTogether::PersonPlatformIntegration.new
18+
end
19+
20+
# GET /better_together/person_platform_integrations/1/edit
21+
def edit; end
2122

22-
# POST /better_together/person_platform_integrations
23-
def create
24-
@better_together_person_platform_integration = BetterTogether::PersonPlatformIntegration.new(person_platform_integration_params)
23+
# POST /better_together/person_platform_integrations
24+
def create
25+
@better_together_person_platform_integration = BetterTogether::PersonPlatformIntegration.new(person_platform_integration_params)
2526

26-
if @person_platform_integration.save
27-
redirect_to @person_platform_integration, notice: "PersonPlatformIntegration was successfully created."
28-
else
29-
render :new, status: :unprocessable_entity
27+
if @person_platform_integration.save
28+
redirect_to @person_platform_integration, notice: 'PersonPlatformIntegration was successfully created.'
29+
else
30+
render :new, status: :unprocessable_entity
31+
end
3032
end
31-
end
3233

33-
# PATCH/PUT /better_together/person_platform_integrations/1
34-
def update
35-
if @person_platform_integration.update(person_platform_integration_params)
36-
redirect_to @person_platform_integration, notice: "PersonPlatformIntegration was successfully updated.", status: :see_other
37-
else
38-
render :edit, status: :unprocessable_entity
34+
# PATCH/PUT /better_together/person_platform_integrations/1
35+
def update
36+
if @person_platform_integration.update(person_platform_integration_params)
37+
redirect_to @person_platform_integration, notice: 'PersonPlatformIntegration was successfully updated.',
38+
status: :see_other
39+
else
40+
render :edit, status: :unprocessable_entity
41+
end
3942
end
40-
end
4143

42-
# DELETE /better_together/person_platform_integrations/1
43-
def destroy
44-
@person_platform_integration.destroy!
45-
redirect_to person_platform_integrations_url, notice: "PersonPlatformIntegration was successfully destroyed.", status: :see_other
46-
end
44+
# DELETE /better_together/person_platform_integrations/1
45+
def destroy
46+
@person_platform_integration.destroy!
47+
redirect_to person_platform_integrations_url, notice: 'PersonPlatformIntegration was successfully destroyed.',
48+
status: :see_other
49+
end
50+
51+
private
4752

48-
private
4953
# Use callbacks to share common setup or constraints between actions.
5054
def set_person_platform_integration
5155
@person_platform_integration = BetterTogether::PersonPlatformIntegration.find(params[:id])
@@ -55,4 +59,5 @@ def set_person_platform_integration
5559
def person_platform_integration_params
5660
params.require(:person_platform_integration).permit(:provider, :uid, :token, :secret, :profile_url, :user_id)
5761
end
62+
end
5863
end
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
1-
module BetterTogether::PersonPlatformIntegrationsHelper
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
module PersonPlatformIntegrationsHelper
5+
end
26
end

app/integrations/better_together/github.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,27 @@
44

55
module BetterTogether
66
class Github
7+
def access_token
8+
Octokit::Client.new(bearer_token: jwt).create_app_installation_access_token(Rails.application.credentials.dig(
9+
:github, :installation_id
10+
))[:token]
11+
end
12+
13+
def jwt
14+
payload = {
15+
iat: Time.now.to_i - 60, # issued at time
16+
exp: Time.now.to_i + (10 * 60),
17+
iss: Rails.application.credentials.dig(:github, :app_id)
18+
}
19+
JWT.encode(payload, private_key, 'RS256')
20+
end
21+
22+
def private_key
23+
@private_key ||= OpenSSL::PKey::RSA.new(private_pem)
24+
end
25+
26+
def private_pem
27+
@private_pem ||= Rails.application.credentials.dig(:github, :private_pem)
28+
end
729
end
830
end

app/models/better_together/person.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ def self.primary_community_delegation_attrs
4545
has_many :agreement_participants, class_name: 'BetterTogether::AgreementParticipant', dependent: :destroy
4646
has_many :agreements, through: :agreement_participants
4747

48+
has_many :person_platform_integrations, dependent: :destroy
49+
4850
has_one :user_identification,
4951
lambda {
5052
where(
@@ -78,6 +80,8 @@ def self.primary_community_delegation_attrs
7880
show_conversation_details Boolean, default: false
7981
end
8082

83+
# has_one_attached :profile_image
84+
8185
validates :name,
8286
presence: true
8387

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,97 @@
1-
class BetterTogether::PersonPlatformIntegration < ApplicationRecord
2-
belongs_to :person
3-
belongs_to :platform
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
class PersonPlatformIntegration < ApplicationRecord
5+
PROVIDERS = {
6+
facebook: 'Facebook',
7+
github: 'Github',
8+
google_oauth2: 'Google',
9+
linkedin: 'Linkedin'
10+
}.freeze
11+
12+
belongs_to :person
13+
belongs_to :platform
14+
belongs_to :user
15+
16+
Devise.omniauth_configs.each_key do |provider|
17+
scope provider, -> { where(provider:) }
18+
end
19+
20+
def expired?
21+
expires_at? && expires_at <= Time.zone.now
22+
end
23+
24+
def token
25+
renew_token! if expired?
26+
access_token
27+
end
28+
29+
def renew_token!
30+
new_token = current_token.refresh!
31+
update(
32+
access_token: new_token.token,
33+
refresh_token: new_token.refresh_token,
34+
expires_at: Time.at(new_token.expires_at)
35+
)
36+
end
37+
38+
def refresh_auth_hash
39+
renew_token! if expired?
40+
41+
omniauth = strategy
42+
omniauth.access_token = current_token
43+
44+
update(self.class.attributes_from_omniauth(omniauth.auth_hash))
45+
end
46+
47+
def current_token
48+
OAuth2::AccessToken.new(
49+
strategy.client,
50+
access_token,
51+
refresh_token:
52+
)
53+
end
54+
55+
# return an OmniAuth::Strategies instance for the provider
56+
def strategy
57+
OmniAuth::Strategies.const_get(OmniAuth::Utils.camelize(provider)).new(
58+
nil,
59+
ENV.fetch("#{provider.downcase}_client_id", nil),
60+
ENV.fetch("#{provider.downcase}_client_secret", nil)
61+
)
62+
end
63+
64+
def self.attributes_from_omniauth(auth)
65+
expires_at = auth.credentials.expires_at.present? ? Time.at(auth.credentials.expires_at) : nil
66+
67+
attributes = {
68+
provider: auth.provider,
69+
uid: auth.uid,
70+
expires_at:,
71+
access_token: auth.credentials.token,
72+
access_token_secret: auth.credentials.secret,
73+
auth: auth.to_hash
74+
}
75+
76+
attributes[:handle] = auth.info.nickname if auth.info.nickname.present?
77+
attributes[:name] = auth.info.name if auth.info.name.present?
78+
attributes[:image_url] = URI.parse(auth.info.image) if auth.info.image.present?
79+
80+
attributes[:profile_url] = auth.info.urls.first.last unless person_platform_integration.persisted?
81+
82+
attributes
83+
end
84+
85+
def self.update_or_initialize(person_platform_integration, auth)
86+
if person_platform_integration.present?
87+
person_platform_integration.update(attributes_from_omniauth(auth))
88+
else
89+
person_platform_integration = new(
90+
attributes_from_omniauth(auth)
91+
)
92+
end
93+
94+
person_platform_integration
95+
end
96+
end
497
end

app/models/concerns/better_together/devise_user.rb

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,44 @@ module DeviseUser
1010

1111
slugged :email
1212

13+
has_many :person_platform_integrations, dependent: :destroy
14+
1315
validates :email, presence: true, uniqueness: { case_sensitive: false }
1416

15-
def send_devise_notification(notification, *)
16-
devise_mailer.send(notification, self, *).deliver_later
17-
end
17+
def self.from_omniauth(person_platform_integration:, auth:, current_user:)
18+
person_platform_integration = PersonPlatformIntegration.update_or_initialize(person_platform_integration, auth)
1819

19-
def self.from_omniauth(auth)
20-
find_or_create_by(provider: auth.provider, uid: auth.uid) do |user|
21-
user.email = auth.info.email
22-
user.password = Devise.friendly_token[0, 20]
23-
# user.name = auth.info.name # assuming the user model has a name
24-
# user.image = auth.info.image # assuming the user model has an image
25-
# If you are using confirmable and the provider(s) you use validate emails,
26-
# uncomment the line below to skip the confirmation emails.
27-
# user.skip_confirmation!
28-
end
29-
end
20+
return person_platform_integration.user if person_platform_integration.user.present?
21+
22+
unless person_platform_integration.persisted?
23+
user = current_user.present? ? current_user : find_by(email: auth['info']['email'])
3024

31-
def self.new_with_session(params, session)
32-
super.tap do |user|
33-
if (data = session['devise.github_data'] && session['devise.github_data']['extra']['raw_info']) && user.email.blank?
34-
user.email = data['email']
25+
if user.blank?
26+
user = new
27+
user.skip_confirmation!
28+
user.password = ::Devise.friendly_token[0, 20]
29+
user.set_attributes_from_auth(auth)
30+
31+
person_attributes = {
32+
name: person_platform_integration.name || user.email.split('@').first || 'Unidentified Person',
33+
handle: person_platform_integration.handle || user.email.split('@').first
34+
}
35+
user.build_person(person_attributes)
36+
37+
user.save
3538
end
39+
40+
person_platform_integration.user = user
41+
person_platform_integration.person = user.person
42+
43+
person_platform_integration.save
3644
end
45+
46+
person_platform_integration.user
47+
end
48+
49+
def set_attributes_from_auth(auth)
50+
self.email = auth.info.email
3751
end
3852

3953
# TODO: address the confirmation and password reset email modifications for api users when the API is under

spec/dummy/db/migrate/20231125163430_create_active_storage_tables.active_storage.rb renamed to db/migrate/20231125163430_create_active_storage_tables.active_storage.rb

File renamed without changes.

spec/dummy/db/migrate/20231125164028_create_action_text_tables.action_text.rb renamed to db/migrate/20231125164028_create_action_text_tables.action_text.rb

File renamed without changes.

0 commit comments

Comments
 (0)