Skip to content

Instantly share code, notes, and snippets.

@b-murphy
Last active December 11, 2015 07:08
Show Gist options
  • Save b-murphy/4564124 to your computer and use it in GitHub Desktop.
Save b-murphy/4564124 to your computer and use it in GitHub Desktop.
require './lib/enthuse/social_network_mappings'
require 'virtus'
class Authenticator
# Required for Errors & Messages
##########################################
include Virtus
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
def persisted?
false
end
attr_reader :url
def self.authenticator_for_provider(provider)
provider_name = Enthuse::SocialNetworkMappings.downcased_provider_to_camelcased(provider)
"#{provider_name}Authenticator".constantize.new
end
##### Authentication Finder Methods
######################################################
def add_authentication_to_user(user, auth)
authentication = create_authentication_for_user(user, auth)
if authentication.persisted?
@url = Rails.application.routes.url_helpers.profile_path(slug: user.slug)
else
Enthuse.log_persistence_error(authentication)
end
rescue StandardError => e
errors.add(:base, "Sorry, something went wrong and your details were not saved. We're looking into it.")
Enthuse.log_error(e)
end
# this method can and is overidden by subclasses.
def create_authentication_for_user(user, auth)
auth_info = HashWithIndifferentAccess.new(auth)
provider_name = Enthuse::SocialNetworkMappings.downcased_provider_to_camelcased(auth_info['provider'])
if network = user.find_social_network(provider_name)
ActiveRecord::Base.transaction { network.destroy }
end
network_user = "#{provider_name}DataParser".constantize.new(auth_info)
attributes = {
network_name: provider_name,
handle: network_user.handle,
meta_data: network_user.stats
}
ActiveRecord::Base.transaction do
uid = auth_info['uid'].to_s
credentials = auth_info['credentials']
social_network = SocialNetworkServices.create(user, attributes)
authentication = user.find_or_create_by_user_provider_uid_and_credentials(provider_name, uid, credentials['token'], credentials['secret'])
end
user
end
def find_user_with_auth(auth)
auth_info = HashWithIndifferentAccess.new(auth)
provider_name = self.class.to_s.gsub('Authenticator', '')
uid = auth_info[:uid].to_s
if user = User.find_by_provider_and_uid(provider_name, uid)
user.update_provider_token_and_secret(provider_name, auth_info[:credentials][:token], auth_info[:credentials][:secret])
user
end
end
######################################################
## Authentication setting method: Included in all subclasses.
#####################################################
def apply_omniauth_info_to_invite(token, auth)
invite = Invite.find_incomplete_by_token!(token)
if invite
auth_info = HashWithIndifferentAccess.new(auth)
provider_name = Enthuse::SocialNetworkMappings.downcased_provider_to_camelcased(auth_info['provider'])
data = "#{provider_name}DataParser".constantize.new(auth_info)
invite.set_authentication_provider_info data.as_json
invite
end
rescue ActiveRecord::RecordNotFound
binding.pry
Stats.increment("signup.#{@provider}.fail")
errors.add(:base, "Sorry, we couldn't find your invite. Please follow the link from your invite email.")
# When calling valid? after this it is returning true... even with errors.
return
end
end
require './lib/enthuse/error_logger'
require './app/authenticators/authenticator'
require './app/authenticators/facebook_authenticator'
require './app/authenticators/tumblr_authenticator'
require './app/authenticators/twitter_authenticator'
require './app/authenticators/you_tube_authenticator'
require './app/authenticators/linked_in_authenticator'
require './app/authenticators/klout_authenticator'
require './app/authenticators/sound_cloud_authenticator'
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def authenticate
@auth = request.env['omniauth.auth']
@provider = @auth.fetch('provider').downcase
@authenticator = Authenticator.authenticator_for_provider(@provider)
if user_signed_in?
@authenticator.add_authentication_to_user(current_user, @auth)
@authenticator.valid? ? authentication_added : authentication_failed_to_save
else
@user = @authenticator.find_user_with_auth(@auth)
if @user
sign_in_user_and_redirect
else
@invite = @authenticator.apply_omniauth_info_to_invite(session[:token], @auth)
@invite.valid? ? edit_signup_details : back_to_homepage
# called at this point.
end
end
end
# TODO: find a way to get omniauth callbacks working with a single action
alias_method :facebook, :authenticate
alias_method :linkedin, :authenticate
alias_method :twitter, :authenticate
alias_method :youtube, :authenticate
alias_method :klout, :authenticate
alias_method :tumblr, :authenticate
alias_method :soundcloud, :authenticate
########## Devise related path helpers
def after_omniauth_failure_path_for(scope)
new_user_session_path
end
def after_sign_in_path_for(scope)
profile_path(slug: scope.slug)
end
######################################
private
def edit_signup_details
redirect_to edit_signup_path(@invite.token)
end
def back_to_homepage
redirect_to root_url
end
def sign_in_user_and_redirect
Stats.increment("login.#{@provider}")
url = session[:referer] || profile_path(@user.slug)
sign_in @user
redirect_to url, :event => :authentication
end
def authentication_added
flash[:alert] = "Your #{@provider} details have been updated"
redirect_to(@authenticator.url)
end
def authentication_failed_to_save
@authenticator.errors.each { |error| flash[:error] = error }
if current_user
redirect_to profile_path(slug: current_user.slug)
else
redirect_to root_url
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment