diff --git a/Gemfile b/Gemfile
index 9f3d3046cd..ee53b9187b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -30,6 +30,7 @@ gem 'cloudinary', '~> 1.11.1'
gem 'figaro', '1.1.1'
gem 'google-api-client', '~> 0.30.2'
gem 'kaminari', '1.1.1'
+gem 'omniauth-github'
gem 'omniauth-google-oauth2', '~> 0.7.0'
gem 'omniauth-rails_csrf_protection', '~> 0.1.2'
gem 'premailer-rails'
@@ -84,6 +85,7 @@ group :development, :test do
gem 'bullet'
+ gem 'awesome_print'
gem 'webdrivers'
end
diff --git a/Gemfile.lock b/Gemfile.lock
old mode 100644
new mode 100755
index db9f4846c1..9b94801e5a
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -51,6 +51,7 @@ GEM
rake (>= 10.4, < 13.0)
arel (9.0.0)
ast (2.4.0)
+ awesome_print (1.8.0)
aws_cf_signer (0.1.3)
bcrypt (3.1.13)
better_errors (2.6.0)
@@ -225,6 +226,9 @@ GEM
omniauth (1.9.1)
hashie (>= 3.4.6)
rack (>= 1.6.2, < 3)
+ omniauth-github (1.3.0)
+ omniauth (~> 1.5)
+ omniauth-oauth2 (>= 1.4.0, < 2.0)
omniauth-google-oauth2 (0.7.0)
jwt (>= 2.0)
omniauth (>= 1.1.1)
@@ -495,6 +499,7 @@ PLATFORMS
DEPENDENCIES
activerecord-import
annotate (~> 2.7)
+ awesome_print
bcrypt (= 3.1.13)
better_errors (~> 2.5)
bullet
@@ -522,6 +527,7 @@ DEPENDENCIES
jquery-rails (= 4.3.5)
kaminari (= 1.1.1)
letter_opener
+ omniauth-github
omniauth-google-oauth2 (~> 0.7.0)
omniauth-rails_csrf_protection (~> 0.1.2)
pg (= 1.1.4)
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 7805994619..7e45c876bb 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -10,7 +10,20 @@ def google_oauth2
kind: t('omniauth.google'))
sign_in_and_redirect @user, event: :authentication
else
- redirect_to new_user_session_path, notice: t('omniauth.access_denied')
+ redirect_to new_user_session_path, alert: t('omniauth.access_denied')
+ end
+ end
+
+ def github
+ @user = User.from_omniauth request.env['omniauth.auth']
+
+ if @user.valid?
+ flash[:notice] = I18n.t('devise.omniauth_callbacks.success',
+ kind: t('omniauth.github'))
+
+ sign_in_and_redirect @user, event: :authentication
+ else
+ redirect_to new_user_session_path, alert: t('omniauth.access_denied')
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 01cd481739..f75bcf57e1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -55,7 +55,7 @@ class User < ApplicationRecord
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :database_authenticatable, :registerable, :uid,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable,
- omniauth_providers: [:google_oauth2]
+ omniauth_providers: %i[google_oauth2 github]
mount_uploader :avatar, AvatarUploader
@@ -81,24 +81,38 @@ class User < ApplicationRecord
validates :locale, inclusion: {
in: Rails.application.config.i18n.available_locales.map(&:to_s).push(nil)
}
- validate :password_complexity
+
+ # add unless statement here instead of the concern for readability
+ # TODO: refactor the Password validator concern
+ validate :password_complexity, unless: :oauth_provided?
def active_for_authentication?
super && !banned
end
- def self.find_for_google_oauth2(access_token)
- user = find_or_initialize_by(email: access_token.info.email)
- user.name ||= access_token.info.name
+ # TODO: to refactor and to move to builder pattern
+ # UserBuilder::GoogleOauth2
+ def self.find_for_google_oauth2(auth)
+ user = find_or_initialize_by(email: auth.info.email)
+ user.name ||= auth.info.name
user.password ||= Devise.friendly_token[0, 20]
- update_access_token_fields(user: user, access_token: access_token)
+ update_access_token_fields(user: user, access_token: auth)
user
end
+ # to refactor, could be single oauth method to begin with
+ def self.from_omniauth(auth)
+ where(provider: auth.provider,
+ uid: auth.provider + auth.uid).first_or_create do |user|
+ UserBuilder::Builder.build(user: user, auth: auth)
+ end
+ end
+
def google_access_token
google_access_token_expired? ? update_access_token : token
end
+ # TODO: refactor this and use one checker
def google_oauth2_enabled?
token.present?
end
@@ -140,6 +154,11 @@ def update_access_token
private
+ # checks if user is created from oauth
+ def oauth_provided?
+ provider.present? || token.present?
+ end
+
def google_access_token_expired?
!access_expires_at || Time.zone.now > access_expires_at
end
diff --git a/app/services/user_builder/base.rb b/app/services/user_builder/base.rb
new file mode 100644
index 0000000000..d0e03de1b0
--- /dev/null
+++ b/app/services/user_builder/base.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+module UserBuilder
+ class Base
+ attr_reader :user
+
+ def self.call(user:, auth:)
+ new(user: user, auth: auth)
+ end
+
+ def initialize(user:, auth:)
+ @user = user
+ @user.tap do
+ user.provider = auth.provider
+ user.name = auth.info.name
+ user.uid = provider_uid(auth)
+ user.email = auth.info.email
+ user.password = default_password
+ end
+ end
+
+ private
+
+ def default_password
+ Devise.friendly_token[0, 20]
+ end
+
+ def provider_uid(auth)
+ auth.provider + auth.uid
+ end
+ end
+end
diff --git a/app/services/user_builder/builder.rb b/app/services/user_builder/builder.rb
new file mode 100644
index 0000000000..c153302abb
--- /dev/null
+++ b/app/services/user_builder/builder.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+module UserBuilder
+ class Builder
+ # Adds new omni auth providers here
+ SERVICE = {
+ # 'google_oauth' => GoogleOauth2
+ }.freeze
+
+ # UserBuilder::Builder.build(user: user, provider: provider, auth: auth)
+ def self.build(user:, auth:)
+ service = SERVICE.fetch(auth.provider, UserBuilder::Base)
+ service.call(user: user, auth: auth)
+ end
+ end
+end
diff --git a/app/views/devise/shared/_links.erb b/app/views/devise/shared/_links.erb
index 6a446c8d79..8d0c78661e 100644
--- a/app/views/devise/shared/_links.erb
+++ b/app/views/devise/shared/_links.erb
@@ -1,11 +1,16 @@
<%- if devise_mapping.omniauthable? and request.base_url != 'http://0.0.0.0:3000' %>
- <%- resource_class.omniauth_providers.each do |provider| %>
- <% if provider.to_s == "google_oauth2" && (current_page?(new_user_session_path) || current_page?(new_user_password_path)) %>
- <%= button_to t('devise.shared.sign_in_google'), omniauth_authorize_path(resource_name, provider), method: :post, class: 'buttonGhostM' %>
- <% elsif provider.to_s == "google_oauth2" %>
- <%= button_to t('devise.shared.sign_up_google'), omniauth_authorize_path(resource_name, provider), method: :post, class: 'buttonGhostM' %>
- <% end -%>
+ <%- resource_class.omniauth_providers.each do |provider| %>
+
+ <% if provider.to_s == "google_oauth2" && (current_page?(new_user_session_path) || current_page?(new_user_password_path)) %>
+ <%= button_to t('devise.shared.sign_in_google'), omniauth_authorize_path(resource_name, provider), method: :post, class: 'buttonGhostM smallMarginBottom' %>
+ <% elsif provider.to_s == "github" && (current_page?(new_user_session_path) || current_page?(new_user_password_path)) %>
+ <%= button_to t('devise.shared.sign_in_github'), omniauth_authorize_path(resource_name, provider), method: :post, class: 'buttonGhostM smallMarginBottom' %>
+ <% elsif provider.to_s == "google_oauth2" %>
+ <%= button_to t('devise.shared.sign_up_google'), omniauth_authorize_path(resource_name, provider), method: :post, class: 'buttonGhostM smallMarginBottom' %>
+ <% elsif provider.to_s == 'github' %>
+ <%= button_to t('devise.shared.sign_up_github'), omniauth_authorize_path(resource_name, provider), method: :post, class: 'buttonGhostM smallMarginBottom' %>
+ <% end -%>
<% end -%>
<% end -%>
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index a29cf9033c..96958113e9 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -317,4 +317,6 @@
approval_prompt: 'select_account consent force',
scope: 'userinfo.email,userinfo.profile,calendar'
)
+
+ config.omniauth :github, ENV['GITHUB_CLIENT_ID'], ENV['GITHUB_CLIENT_SECRET'], scope: 'user:email'
end
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 6ad91b5d80..6e73cf7b8e 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -511,6 +511,7 @@ de:
mehr blockiert.
omniauth:
google: Google
+ github: Github
access_denied: 'Zutritt verweigert'
pages:
about:
@@ -1183,6 +1184,8 @@ de:
shared:
sign_in_google: 'Mit Google einloggen'
sign_up_google: 'Mit Google anmelden'
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: 'Keine Anweisungen zur Bestätigung erhalten?'
unlock: 'Keine Anweisungen zur Freischaltung erhalten?'
unlock:
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 9c7d2e3ffe..7e4ed96c59 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -483,6 +483,7 @@ en:
%{code_of_conduct_link}. You are no longer banned.
omniauth:
google: Google
+ github: Github
access_denied: 'Access Denied'
pages:
about:
@@ -1107,6 +1108,8 @@ en:
shared:
sign_in_google: 'Sign in with Google'
sign_up_google: 'Sign up with Google'
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: 'Didn''t receive confirmation instructions?'
unlock: 'Didn''t receive unlock instructions?'
unlock:
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 068cceb515..71952f1f46 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -434,6 +434,7 @@ es:
%{code_of_conduct_link}. Ya no estás baneado/a.'
omniauth:
google: Google
+ github: Github
access_denied: 'Acceso Denegado'
pages:
about:
@@ -972,6 +973,8 @@ es:
shared:
sign_in_google: 'Registrarse con Google'
sign_up_google: 'Ingresar con Google'
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: '¿No recibiste instrucciones de confirmación?'
unlock: '¿No recibiste instrucciones para desbloquear?'
unlock:
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index b1269b46e4..e3ce1a0555 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -491,6 +491,7 @@ fr:
%{code_of_conduct_link}.
omniauth:
google: Google
+ github: Github
access_denied: 'Accès refusé'
pages:
about:
@@ -1160,6 +1161,8 @@ fr:
shared:
sign_in_google: "S'identifier avec Google"
sign_up_google: "S'enregistrer avec Google"
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: "Vous n'avez pas reçu vous informations de confirmation ?"
unlock: >-
Vous n'avez pas reçu vos informations pour débloquer votre compte ?
diff --git a/config/locales/hi.yml b/config/locales/hi.yml
index fb0bd6ae7a..0e52c37d10 100644
--- a/config/locales/hi.yml
+++ b/config/locales/hi.yml
@@ -414,6 +414,7 @@ hi:
किया गया था। अब आप प्रतिबंधित नहीं हैं'
omniauth:
google: 'गूगल'
+ github: 'Github'
access_denied: 'पहुंच अस्वीकृत'
pages:
about:
@@ -888,6 +889,8 @@ hi:
shared:
sign_in_google: 'Google के साथ साइन इन करें'
sign_up_google: 'Google के साथ साइन अप करें'
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: 'पुष्टि निर्देश प्राप्त नहीं हुआ?'
unlock: 'क्या अनलॉक निर्देश प्राप्त नहीं होंगे?'
unlock:
diff --git a/config/locales/it.yml b/config/locales/it.yml
index 0581085acc..1336501cd2 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -440,6 +440,7 @@ it:
nostra %{code_of_conduct_link}. Non sei più bannato.'
omniauth:
google: Google
+ github: Github
access_denied: 'Accesso Negato'
pages:
about:
@@ -965,6 +966,8 @@ it:
shared:
sign_in_google: 'Effettua il login con Google'
sign_up_google: 'Registrati con Google'
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: 'Non hai ricevuto le istruzioni per confermare la tua EMail?'
unlock: 'Non hai ricevuto le istruzioni per sbloccare il tuo account?'
unlock:
diff --git a/config/locales/nb.yml b/config/locales/nb.yml
index c950c522a8..e8c3b64061 100644
--- a/config/locales/nb.yml
+++ b/config/locales/nb.yml
@@ -436,6 +436,7 @@ nb:
%{code_of_conduct_link}. Du er ikke lenger utestengt.'
omniauth:
google: Google
+ github: Github
access_denied: 'Adgang avvist'
pages:
about:
@@ -957,6 +958,8 @@ nb:
shared:
sign_in_google: 'Logg inn med Google'
sign_up_google: 'Registrer deg med Google'
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: 'Mottok ikke bekreftelsesinstruksjoner?'
unlock: 'Mottok ikke opplåsningsinstruksjoner?'
unlock:
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 274b5ce3d6..9b3d324dd1 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -447,6 +447,7 @@ nl:
%{code_of_conduct_link} schond. Je bent niet langer verbannen.'
omniauth:
google: Google
+ github: Github
access_denied: 'Toegang geweigerd'
pages:
about:
@@ -976,6 +977,8 @@ nl:
shared:
sign_in_google: 'Log in met Google'
sign_up_google: 'Registreren met Google'
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: 'Bevestiginginstructies niet ontvangen?'
unlock: 'Ontgrendelingsinstructies niet ontvangen?'
unlock:
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index a3c1247551..433bff4264 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -446,6 +446,7 @@ pt-BR:
%{code_of_conduct_link}. Você não está mais banido.'
omniauth:
google: Google
+ github: Github
access_denied: 'Acesso negado'
pages:
about:
@@ -1000,6 +1001,8 @@ pt-BR:
shared:
sign_in_google: 'Entrar com sua conta Google'
sign_up_google: 'Cadastre-se com sua conta Google'
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: 'Não recebeu as instruções de confirmação?'
unlock: 'Não recebeu as instruções de desbloqueio?'
unlock:
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index c91b9b43ab..e2c0d5dba2 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -433,6 +433,7 @@ sv:
%{code_of_conduct_link}. Du är inte längre förbjuden.'
omniauth:
google: Google
+ github: Github
access_denied: 'Åtkomst nekad'
pages:
about:
@@ -952,6 +953,8 @@ sv:
shared:
sign_in_google: 'Logga in med Google'
sign_up_google: 'Registrera dig hos Google'
+ sign_in_github: 'Logga in med Github'
+ sign_up_github: 'Registrera dig hos Github'
confirmation: 'Fick du inte bekräftelsesinstruktioner?'
unlock: 'Fick du inte upplåsningsinstruktioner?'
unlock:
diff --git a/config/locales/vi.yml b/config/locales/vi.yml
index 54d8bab904..b50e4428d3 100644
--- a/config/locales/vi.yml
+++ b/config/locales/vi.yml
@@ -435,6 +435,7 @@ vi:
%{code_of_conduct_link} của chúng tôi. Bạn không còn bị cấm nữa.'
omniauth:
google: Google
+ github: Github
access_denied: 'Truy nhập từ chối'
pages:
about:
@@ -965,6 +966,8 @@ vi:
shared:
sign_in_google: 'Đăng nhập với Google'
sign_up_google: 'Đăng ký với Google'
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: 'Không nhận được hướng dẫn xác nhận?'
unlock: 'Không nhận được hứng dẫn mở khóa?'
unlock:
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
index 80f38de8c5..5f076f7bc4 100644
--- a/config/locales/zh-CN.yml
+++ b/config/locales/zh-CN.yml
@@ -397,6 +397,7 @@ zh-CN:
您不再被禁止。'
omniauth:
google: 谷歌
+ github: Github
access_denied: '拒绝访问'
pages:
about:
@@ -828,6 +829,8 @@ zh-CN:
shared:
sign_in_google: '使用Google登录'
sign_up_google: '使用Google注册'
+ sign_in_github: 'Sign in with Github'
+ sign_up_github: 'Sign up with Github'
confirmation: '没有收到确认指示吗?'
unlock: '没有收到解锁指令吗?'
unlock:
diff --git a/spec/services/user_builder/base.rb b/spec/services/user_builder/base.rb
new file mode 100644
index 0000000000..67f413a4cd
--- /dev/null
+++ b/spec/services/user_builder/base.rb
@@ -0,0 +1,31 @@
+describe UserBuilder::Base do
+ describe '.call' do
+ subject { described_class }
+
+ let(:user_one) { FactoryBot.build(:user1) }
+
+ let(:auth) { OmniAuth::AuthHash.new({
+ :provider => 'github',
+ :uid => '123545',
+ :info => info })
+ }
+
+ let(:info) do
+ { name: 'joe', email: 'test@email.com' }
+ end
+
+ let(:builder) { UserBuilder::Base.call(user: user_one, auth: auth) }
+
+ it 'builds the user object' do
+ expect(builder.user).to eql(user_one)
+ end
+
+ it 'creates correct user uid' do
+ expect(builder.user.uid).to eql('github123545')
+ end
+
+ it 'creates user under provider' do
+ expect(builder.user.provider).to eql('github')
+ end
+ end
+end