Commit ec7549a3 authored by Gabriel Mazetto's avatar Gabriel Mazetto

More refactor to Geo Authentication with OAuth and added error handling

parent 8c6e9eb0
class Oauth::GeoAuthController < ActionController::Base
rescue_from Gitlab::Geo::RemoteNode::InvalidCredentialsError, with: :invalid_credentials
rescue_from OAuth2::Error, with: :auth
def auth
oauth = Geo::OauthSession.new(state: params[:state])
oauth = Gitlab::Geo::OauthSession.new(state: params[:state])
unless oauth.is_oauth_state_valid?
redirect_to root_url
return
......@@ -11,14 +13,14 @@ class Oauth::GeoAuthController < ActionController::Base
end
def callback
oauth = Geo::OauthSession.new(state: params[:state])
oauth = Gitlab::Geo::OauthSession.new(state: params[:state])
unless oauth.is_oauth_state_valid?
redirect_to new_user_sessions_path
return
end
token = oauth.get_token(params[:code], redirect_uri: oauth_geo_callback_url)
remote_user = Geo::RemoteNode.new.authenticate(token)
remote_user = Gitlab::Geo::RemoteNode.new.authenticate(token)
user = User.find(remote_user['id'])
......@@ -26,9 +28,15 @@ class Oauth::GeoAuthController < ActionController::Base
return_to = oauth.get_oauth_state_return_to
redirect_to(return_to || root_path)
else
@error = 'Invalid credentials'
render :error
invalid_credentials
end
end
private
def invalid_credentials
@error = 'Cannot authenticate to Primary Geo node with your credentials.'
render :error, layout: 'errors'
end
end
......@@ -109,7 +109,7 @@ class SessionsController < Devise::SessionsController
def gitlab_geo_login
if !signed_in? && Gitlab::Geo.enabled? && Gitlab::Geo.secondary?
oauth = Geo::OauthSession.new
oauth = Gitlab::Geo::OauthSession.new
# share full url with primary node by shared session
user_return_to = URI.join(root_url, session[:user_return_to].to_s).to_s
......
class Geo::OauthSession
include ActiveModel::Model
attr_accessor :state
attr_accessor :return_to
def is_oauth_state_valid?
return true unless state
salt, hmac, return_to = state.split(':', 3)
return false unless return_to
hmac == generate_oauth_hmac(salt, return_to)
end
def generate_oauth_state
return unless return_to
hmac = generate_oauth_hmac(oauth_salt, return_to)
"#{oauth_salt}:#{hmac}:#{return_to}"
end
def get_oauth_state_return_to
state.split(':', 3)[2] if state
end
def authorize_url(params = {})
oauth_client.auth_code.authorize_url(params)
end
def get_token(code, params = {}, opts = {})
oauth_client.auth_code.get_token(code, params, opts).token
end
private
def generate_oauth_hmac(salt, return_to)
return false unless return_to
digest = OpenSSL::Digest.new('sha256')
key = Gitlab::Application.secrets.secret_key_base + salt
OpenSSL::HMAC.hexdigest(digest, key, return_to)
end
def oauth_salt
@salt ||= SecureRandom.hex(16)
end
def oauth_client
@client ||= begin
::OAuth2::Client.new(
oauth_app.uid,
oauth_app.secret,
{
site: primary_node_url,
authorize_url: 'oauth/authorize',
token_url: 'oauth/token'
}
)
end
end
def oauth_app
Gitlab::Geo.oauth_authentication
end
def primary_node_url
Gitlab::Geo.primary_node.url
end
end
class Geo::RemoteNode
class UnauthorizedError < StandardError
end
include HTTParty
API_PREFIX = '/api/v3/'
def authenticate(access_token)
opts = {
query: { access_token: access_token }
}
response = self.class.get(authenticate_endpoint, default_opts.merge(opts))
build_response(response)
end
private
def authenticate_endpoint
File.join(primary_node_url, API_PREFIX, 'user')
end
def primary_node_url
Gitlab::Geo.primary_node.url
end
def default_opts
{
headers: { 'Content-Type' => 'application/json' },
}
end
def build_response(response)
case response.code
when 200
response.parsed_response
when 401
raise UnauthorizedError
else
nil
end
end
end
module Gitlab
module Geo
class OauthSession
include ActiveModel::Model
attr_accessor :state
attr_accessor :return_to
def is_oauth_state_valid?
return true unless state
salt, hmac, return_to = state.split(':', 3)
return false unless return_to
hmac == generate_oauth_hmac(salt, return_to)
end
def generate_oauth_state
return unless return_to
hmac = generate_oauth_hmac(oauth_salt, return_to)
"#{oauth_salt}:#{hmac}:#{return_to}"
end
def get_oauth_state_return_to
state.split(':', 3)[2] if state
end
def authorize_url(params = {})
oauth_client.auth_code.authorize_url(params)
end
def get_token(code, params = {}, opts = {})
oauth_client.auth_code.get_token(code, params, opts).token
end
private
def generate_oauth_hmac(salt, return_to)
return false unless return_to
digest = OpenSSL::Digest.new('sha256')
key = Gitlab::Application.secrets.secret_key_base + salt
OpenSSL::HMAC.hexdigest(digest, key, return_to)
end
def oauth_salt
@salt ||= SecureRandom.hex(16)
end
def oauth_client
@client ||= begin
::OAuth2::Client.new(
oauth_app.uid,
oauth_app.secret,
{
site: primary_node_url,
authorize_url: 'oauth/authorize',
token_url: 'oauth/token'
}
)
end
end
def oauth_app
Gitlab::Geo.oauth_authentication
end
def primary_node_url
Gitlab::Geo.primary_node.url
end
end
end
end
module Gitlab
module Geo
class RemoteNode
class InvalidCredentialsError < StandardError; end
include HTTParty
API_PREFIX = '/api/v3/'
def authenticate(access_token)
opts = {
query: { access_token: access_token }
}
response = self.class.get(authenticate_endpoint, default_opts.merge(opts))
build_response(response)
end
private
def authenticate_endpoint
File.join(primary_node_url, API_PREFIX, 'user')
end
def primary_node_url
Gitlab::Geo.primary_node.url
end
def default_opts
{
headers: { 'Content-Type' => 'application/json' },
}
end
def build_response(response)
case response.code
when 200
response.parsed_response
when 401
raise InvalidCredentialsError
else
nil
end
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment