162 lines
5.3 KiB
Ruby
162 lines
5.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module API::V2
|
|
module Identity
|
|
module Utils
|
|
def session
|
|
request.session
|
|
end
|
|
|
|
def codec
|
|
@_codec ||= Barong::JWT.new(key: Barong::App.config.keystore.private_key)
|
|
end
|
|
|
|
def open_session(user)
|
|
csrf_token = SecureRandom.hex(10)
|
|
session.merge!(
|
|
"uid": user.uid,
|
|
"user_ip": remote_ip,
|
|
"user_agent": request.env['HTTP_USER_AGENT'],
|
|
"expire_time": Time.now.to_i + Barong::App.config.session_expire_time,
|
|
"csrf_token": csrf_token
|
|
)
|
|
|
|
csrf_token
|
|
end
|
|
|
|
def verify_captcha!(response:, endpoint:, error_statuses: [400, 422])
|
|
# by default we protect user_create session_create password_reset email_confirmation endpoints
|
|
return unless BarongConfig.list['captcha_protected_endpoints']&.include?(endpoint)
|
|
|
|
case Barong::App.config.captcha
|
|
when 'recaptcha'
|
|
recaptcha(response: response)
|
|
when 'geetest'
|
|
geetest(response: response)
|
|
end
|
|
end
|
|
|
|
def recaptcha(response:, error_statuses: [400, 422])
|
|
error!({ errors: ['identity.captcha.required'] }, error_statuses.first) if response.blank?
|
|
|
|
captcha_error_message = 'identity.captcha.verification_failed'
|
|
|
|
if CaptchaService::RecaptchaVerifier.new(request: request).response_valid?(skip_remote_ip: true, response: response)
|
|
return
|
|
end
|
|
|
|
error!({ errors: [captcha_error_message] }, error_statuses.last)
|
|
rescue StandardError
|
|
error!({ errors: [captcha_error_message] }, error_statuses.last)
|
|
end
|
|
|
|
def geetest(response:, error_statuses: [400, 422])
|
|
error!({ errors: ['identity.captcha.required'] }, error_statuses.first) if response.blank?
|
|
|
|
geetest_error_message = 'identity.captcha.verification_failed'
|
|
validate_geetest_response(response: response)
|
|
|
|
return if CaptchaService::GeetestVerifier.new.validate(response)
|
|
|
|
error!({ errors: [geetest_error_message] }, error_statuses.last)
|
|
rescue StandardError
|
|
error!({ errors: [geetest_error_message] }, error_statuses.last)
|
|
end
|
|
|
|
def validate_geetest_response(response:)
|
|
unless (response['geetest_challenge'].is_a? String) &&
|
|
(response['geetest_validate'].is_a? String) &&
|
|
(response['geetest_seccode'].is_a? String)
|
|
error!({ errors: ['identity.captcha.mandatory_fields'] }, 400)
|
|
end
|
|
end
|
|
|
|
def login_error!(options = {})
|
|
options[:data] = { reason: options[:reason] }.to_json
|
|
options[:topic] = 'session'
|
|
activity_record(options.except(:reason, :error_code, :error_text))
|
|
error!({ errors: ['identity.session.' + options[:error_text]] }, options[:error_code])
|
|
end
|
|
|
|
def activity_record(options = {})
|
|
params = {
|
|
category: 'user',
|
|
user_id: options[:user],
|
|
user_ip: remote_ip,
|
|
user_agent: request.env['HTTP_USER_AGENT'],
|
|
topic: options[:topic],
|
|
action: options[:action],
|
|
result: options[:result],
|
|
data: options[:data]
|
|
}
|
|
Activity.create(params)
|
|
end
|
|
|
|
def token_uniq?(jti)
|
|
error!({ errors: ['identity.user.utilized_token'] }, 422) if Rails.cache.read(jti) == 'utilized'
|
|
Rails.cache.write(jti, 'utilized', expires_in: Barong::App.config.jwt_expire_time.seconds)
|
|
end
|
|
|
|
def publish_confirmation(user, domain)
|
|
token = codec.encode(sub: 'confirmation', email: user.email, uid: user.uid)
|
|
EventAPI.notify(
|
|
'system.user.email.confirmation.token',
|
|
record: {
|
|
user: user.as_json_for_event_api,
|
|
domain: domain,
|
|
token: token
|
|
}
|
|
)
|
|
end
|
|
|
|
def publish_confirmation_code(user, domain, action)
|
|
totp = TOTPServiceAction.new(action)
|
|
totp.create(user.uid, user.email)
|
|
record = {
|
|
user: user.as_json_for_event_api,
|
|
domain: domain,
|
|
code: totp.read_code(user.uid)
|
|
}
|
|
|
|
case action
|
|
when 'sign-up'
|
|
record[:token] = codec.encode(sub: 'confirmation', email: user.email, uid: user.uid)
|
|
when 'reset-password'
|
|
reset_token = SecureRandom.hex(10)
|
|
Rails.cache.write(
|
|
"reset_password_#{user.email}",
|
|
reset_token,
|
|
expires_in: Barong::App.config.jwt_expire_time.seconds
|
|
)
|
|
record[:token] = codec.encode(
|
|
sub: 'reset',
|
|
email: user.email,
|
|
uid: user.uid,
|
|
reset_token: reset_token
|
|
)
|
|
end
|
|
|
|
EventAPI.notify(action, record: record)
|
|
end
|
|
|
|
def publish_session_create(user)
|
|
EventAPI.notify('system.session.create',
|
|
record: {
|
|
user: user.as_json_for_event_api,
|
|
user_ip: remote_ip,
|
|
user_agent: request.env['HTTP_USER_AGENT']
|
|
})
|
|
end
|
|
|
|
def publish_session_failed(user)
|
|
EventAPI.notify('system.session.failed',
|
|
record: {
|
|
user: user.as_json_for_event_api,
|
|
user_ip: remote_ip,
|
|
user_agent: request.env['HTTP_USER_AGENT']
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|