Skip to content

Commit

Permalink
[#256] Ability to connect another integration
Browse files Browse the repository at this point in the history
  • Loading branch information
evheny0 committed Jan 6, 2020
1 parent 7fd79f2 commit 5eff77b
Show file tree
Hide file tree
Showing 13 changed files with 141 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ ROLLBAR_POST_CLIENT_ITEM_TOKEN=""
GITLAB_APP_ID=
GITLAB_APP_SECRET=
GITLAB_REDIRECT_URI=
GITLAB_API_ENDPOINT=
GITLAB_API_ENDPOINT="https://gitlab.com/api/v4"
GITLAB_WEBHOOK_SECRET=
GITLAB_DEPLOYQA_BOT_ID=
GITLAB_DEPLOYQA_BOT_TOKEN=
14 changes: 10 additions & 4 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ class SessionsController < ApplicationController
def show; end

def create
result = user_from_omniauth
result = current_user ? connect_user : user_from_omniauth

if result.error?
flash.notice = result.errors.join("/n")
return redirect_to sessions_path
end

setup_session(request.session, result.object)
setup_session(result.object)
redirect_to projects_path
end

Expand All @@ -25,8 +25,14 @@ def destroy

protected

def setup_session(session, user)
::Auth::SessionHandler.new(session).set!(user_id: user.id, provider: auth_info_presenter.provider)
def setup_session(user)
return if current_user

::Auth::SessionHandler.new(request.session).set!(user_id: user.id, provider: auth_info_presenter.provider)
end

def connect_user
Auth::AnotherServiceConnection.new(auth_info_presenter, current_user).call
end

def user_from_omniauth
Expand Down
15 changes: 15 additions & 0 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

class UsersController < ApplicationController
def show
@user = find_user
@github_auth = @user.user_references.find { |reference| reference.auth_provider == OmniauthConstants::GITHUB }
@gitlab_auth = @user.user_references.find { |reference| reference.auth_provider == OmniauthConstants::GITLAB }
end

private

def find_user
authorize User.includes(user_references: :auth_info).find(params[:id]), :show?, policy_class: UserPolicy
end
end
2 changes: 1 addition & 1 deletion app/models/user_reference.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class UserReference < ApplicationRecord
belongs_to :user, dependent: :destroy, required: false
belongs_to :user, required: false
has_one :auth_info, dependent: :destroy

validates :full_name, :auth_uid, :auth_provider, presence: true
Expand Down
9 changes: 9 additions & 0 deletions app/policies/user_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class UserPolicy < ApplicationPolicy
def show?
return true if user.system_role == UserConstants::SystemRoles::ADMIN

user.actual_user == record
end
end
21 changes: 19 additions & 2 deletions app/services/auth/accessibility_check.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,33 @@ def initialize(user_reference, email)
@email = email
end

def call
def ensure_user_doesnt_exists!
raise ::Auth::NotPermittedError, "User already exists" if @user_reference.blank? && auth_info_email_occupied?
end

def ensure_reference_is_not_secondary!
return if @user_reference.blank?
return if @user_reference.auth_info.blank?
return if @user_reference.auth_info.primary?

raise ::Auth::NotPermittedError, "Please login with another provider"
raise ::Auth::NotPermittedError, "You've already registered in another provider with the same email"
end

def ensure_reference_is_not_connected!
return if @user_reference.blank?
return if @user_reference.auth_info.blank? && @user_reference.user_id.blank?

raise ::Auth::NotPermittedError, "New account is already registered in system"
end

def ensure_email_is_the_same!(current_user)
return if current_user.auth_info.email == @email

raise ::Auth::NotPermittedError, "Your email '#{@email}' doesn't match with your account's email '#{current_user.auth_info.email}'"
end

private

def auth_info_email_occupied?
AuthInfo.find_by(email: @email).present?
end
Expand Down
51 changes: 51 additions & 0 deletions app/services/auth/another_service_connection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

module Auth
class AnotherServiceConnection
def initialize(auth_info_presenter, current_user)
@auth_info_presenter = auth_info_presenter
@current_user = current_user
end

def call
user_reference = UserReference.find_by(auth_uid: @auth_info_presenter.uid, auth_provider: @auth_info_presenter.provider)
ensure_user_is_ready!(user_reference)

ActiveRecord::Base.transaction do
user_reference = user_reference.present? ? update_reference(user_reference) : create_reference
create_auth_info(user_reference)
end

ReturnValue.ok(@current_user.actual_user)
rescue ::Auth::NotPermittedError => error
ReturnValue.error(errors: error.message)
end

private

def ensure_user_is_ready!(user_reference)
checker = Auth::AccessibilityCheck.new(user_reference, @auth_info_presenter.email)
checker.ensure_reference_is_not_connected!
checker.ensure_email_is_the_same!(@current_user)
end

def update_reference(user_reference)
user_reference.update!(user: @current_user.actual_user)
user_reference
end

def create_reference
UserReference.create!(
user: @current_user.actual_user,
auth_uid: @auth_info_presenter.uid,
auth_provider: @auth_info_presenter.provider,
full_name: @auth_info_presenter.full_name
)
end

def create_auth_info(user_reference)
auth_info_params = Auth::AuthInfoParamsBuilder.new(@auth_info_presenter, user_reference).call
AuthInfo.create!(auth_info_params)
end
end
end
3 changes: 2 additions & 1 deletion app/services/auth/auth_info_params_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ def call
def load_email(params)
return if params[:email].present?

# TODO: why do we need USER_API_CLIENT? GitLab always have an email and there is no :email method in ProviderAPI::Gitlab::UserClient
api_client = USER_API_CLIENT.fetch(@omniauth_info_presenter.provider).new(@omniauth_info_presenter.token)
email = api_client.emails.find { |email_info| email_info[:primary] }.fetch(:email)
email = api_client.emails.find { |email_info| email_info[:primary] }[:email]
params[:email] = email
end
end
Expand Down
4 changes: 3 additions & 1 deletion app/services/auth/user_authenticator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ module Auth
class UserAuthenticator
def initialize(auth_info_presenter)
@auth_info_presenter = auth_info_presenter
@checker = Auth::AccessibilityCheck.new(user_reference, @auth_info_presenter.email)
end

def call
Auth::AccessibilityCheck.new(user_reference, @auth_info_presenter.email).call
@checker.ensure_user_doesnt_exists!
@checker.ensure_reference_is_not_secondary!

if user_reference.blank?
::Auth::UserCreator.new(@auth_info_presenter).call
Expand Down
4 changes: 4 additions & 0 deletions app/services/auth/user_wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,9 @@ def email
def token
auth_info.token
end

def actual_user
@user
end
end
end
1 change: 1 addition & 0 deletions app/views/layouts/_header.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ nav.navbar.navbar-expand-lg.navbar-light.bg-light
a.nav-link.dropdown-toggle href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
= current_user.full_name
.dropdown-menu aria-labelledby="navbarDropdown"
= link_to "User preferences", user_path(current_user.actual_user), class: "dropdown-item"
= link_to "Log out", sessions_path, method: :delete, class: "dropdown-item"
23 changes: 23 additions & 0 deletions app/views/users/show.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.container
= render partial: "shared/breadcrumb", locals: {\
text_link_hash: {\
"User" => nil\
},
class: "mt-4" }


.d-flex.justify-content-between.mt-4
h1 = current_user.full_name
.mt-4
p
= image_pack_tag("media/images/logos/github-logo.svg", size: "32x32", class: "mr-2")
- if @github_auth.present?
span.text-success Github integration connected!
- else
= link_to "Click to connect Github", omniauth_path("github"), method: :post
p
= image_pack_tag("media/images/logos/gitlab-logo.svg", size: "32x32", class: "mr-2")
- if @gitlab_auth.present?
span.text-success Gitlab integration connected!
- else
= link_to "Click to connect Gitlab", omniauth_path("gitlab", some_data: "123"), method: :post
3 changes: 2 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
get "/auth/slack/callback", to: "slack/authentications#create"
get "/auth/failure", to: "slack/authentications#show"
get "/auth/:provider/callback", to: "sessions#create"
get "/auth/:provider", to: "sessions#show", as: "omniauth"
post "/auth/:provider", to: "sessions#show", as: "omniauth"

constraints Routes::LoggedUserConstraint.new(SidekiqPolicy) do
mount Sidekiq::Web => "/sidekiq"
end

resource :sessions, only: %i[show create destroy]
resources :users, only: %i[show]

namespace :webhooks do
resources :github, only: %i[create]
Expand Down

0 comments on commit 5eff77b

Please sign in to comment.