Initial commit

This commit is contained in:
Yaser
2026-08-13 19:56:46 +03:30
commit de1d57a67b
474 changed files with 43185 additions and 0 deletions

View File

@@ -0,0 +1,405 @@
# frozen_string_literal: true
require_dependency 'barong/jwt'
module API::V2
module Identity
class Users < Grape::API
helpers do
def parse_refid!
error!({ errors: ['identity.user.invalid_referral_format'] }, 422) unless params[:refid].start_with?(Barong::App.config.uid_prefix.upcase)
user = User.find_by_uid(params[:refid])
error!({ errors: ['identity.user.referral_doesnt_exist'] }, 422) if user.nil?
user.id
end
end
desc 'User related routes'
resource :users do
desc 'Creates new whitelist restriction',
failure: [
{ code: 400, message: 'Required params are missing' },
{ code: 422, message: 'Validation errors' }
],
success: { code: 200, message: 'Whitelist restriction was created' }
params do
requires :whitelink_token,
type: String,
allow_blank: false
end
post '/access' do
if Rails.cache.read(params[:whitelink_token]) == 'active'
restriction = Restriction.new(
category: 'whitelist',
scope: 'ip',
value: remote_ip,
state: 'enabled'
)
code_error!(restriction.errors.details, 422) unless restriction.save
Rails.cache.delete('restrictions')
else
error!({ errors: ['identity.user.access.invalid_token'] }, 422)
end
end
desc 'Creates new user (sign up)',
success: API::V2::Entities::UserWithFullInfo,
failure: [
{ code: 400, message: 'Required params are missing' },
{ code: 422, message: 'Validation errors' }
]
params do
requires :email,
type: String,
allow_blank: false,
desc: 'User Email'
requires :password,
type: String,
allow_blank: false,
desc: 'User Password'
optional :refid,
type: String,
desc: 'Referral uid'
optional :captcha_response,
types: [String, Hash],
desc: 'Response from captcha widget'
optional :data,
type: String,
desc: 'Any additional key: value pairs in json string format'
end
post do
verify_captcha!(response: params['captcha_response'], endpoint: 'user_create')
declared_params = declared(params, include_missing: false)
user_params = declared_params.slice('email', 'password', 'data')
user_params[:referral_id] = parse_refid! unless params[:refid].nil?
user = User.find_by(email: user_params[:email])
error!({ errors: ['identity.user.active_or_banned'] }, 422) if user.present? && %w[active ban].include?(user.state)
if user.present?
code_error!(user.errors.details, 422) unless user.update(user_params)
else
user = User.new(user_params)
code_error!(user.errors.details, 422) unless user.save
end
activity_record(user: user.id, action: 'signup', result: 'succeed', topic: 'account')
# Creates superadmin user in first platform registration
if Barong::App.config.first_registration_superadmin && User.count == 1
user.update(role: 'superadmin', state: 'active')
user.labels.create(key: 'email', value: 'verified', scope: 'private')
else
publish_confirmation_code(user, Barong::App.config.domain, 'sign-up')
user.write_cache('register_email', 'true', 3600)
end
csrf_token = open_session(user)
present user, with: API::V2::Entities::UserWithFullInfo, csrf_token: csrf_token
end
desc 'Register Geetest captcha'
get '/register_geetest' do
CaptchaService::GeetestVerifier.new.register
end
namespace :email do
desc 'Send confirmations instructions (code in email)',
success: { code: 201, message: 'Generated verification code' },
failure: [
{ code: 400, message: 'Required params are missing' },
{ code: 422, message: 'Validation errors' }
]
params do
requires :email,
type: String,
allow_blank: false,
desc: 'Account email'
optional :captcha_response,
types: [String, Hash],
desc: 'Response from captcha widget'
end
post '/generate_email_code' do
verify_captcha!(response: params['captcha_response'], endpoint: 'email_confirmation')
current_user = User.find_by_email(params[:email])
return status 201 if current_user.nil? || current_user.active?
publish_confirmation_code(current_user, Barong::App.config.domain, 'sign-up')
status 201
end
desc 'Confirms an account by token (one-time link in email)',
success: API::V2::Entities::UserWithFullInfo,
failure: [
{ code: 400, message: 'Required params are missing' },
{ code: 422, message: 'Validation errors' }
]
params do
requires :token,
type: String,
allow_blank: false,
desc: 'Token from email'
end
post '/confirm_code' do
payload = codec.decode_and_verify(
params[:token],
pub_key: Barong::App.config.keystore.public_key,
sub: 'confirmation'
)
current_user = User.find_by_email(payload[:email])
if current_user.nil? || current_user.active?
error!({ errors: ['identity.user.active_or_doesnt_exist'] }, 422)
end
token_uniq?(payload[:jti])
current_user.labels.create!(key: 'email', value: 'verified', scope: 'private')
csrf_token = open_session(current_user)
EventAPI.notify('system.user.email.confirmed',
record: {
user: current_user.as_json_for_event_api,
domain: Barong::App.config.domain
})
present current_user, with: API::V2::Entities::UserWithFullInfo, csrf_token: csrf_token
end
desc 'Confirms an account by Authorization code (verify user email and achieve first label)',
success: API::V2::Entities::UserWithFullInfo,
failure: [
{ code: 400, message: 'Required code are missing' },
{ code: 422, message: 'Validation errors' }
]
params do
requires :code,
type: String,
allow_blank: false,
desc: 'Code from email'
requires :email,
type: String,
allow_blank: false,
desc: 'user email'
end
post '/confirm_email' do
current_user = User.find_by_email(params[:email])
if current_user.nil? || current_user.active?
error!({ errors: ['identity.user.active_or_doesnt_exist'] }, 422)
end
unless TOTPServiceAction.new('sign-up').validate?(current_user.uid, declared(params)[:code])
error!({ errors: ['identity.user.code_invalid'] }, 422)
end
error!({ errors: ['identity.user.code_invalid'] }, 422) unless current_user.read_cache('register_email')
current_user.labels.create!(key: 'email', value: 'verified', scope: 'private')
csrf_token = open_session(current_user)
EventAPI.notify('system.user.email.confirmed',
record: {
user: current_user.as_json_for_event_api,
domain: Barong::App.config.domain
})
present current_user, with: API::V2::Entities::UserWithFullInfo, csrf_token: csrf_token
end
end
# forgot reset password
namespace :password do
desc 'Send password reset instructions(forget password)',
success: { code: 201, message: 'Generated password reset code' },
failure: [
{ code: 400, message: 'Required params are missing' },
{ code: 422, message: 'Validation errors' },
{ code: 404, message: 'User doesn\'t exist'}
]
params do
requires :email,
type: String,
message: 'identity.user.missing_email',
allow_blank: false,
desc: 'Account email'
optional :captcha_response,
types: [String, Hash],
desc: 'Response from captcha widget'
end
post '/generate_code' do
verify_captcha!(response: params['captcha_response'], endpoint: 'password_reset')
current_user = User.find_by_email(params[:email])
return status 404 if current_user.nil?
activity_record(user: current_user.id, action: 'request password reset', result: 'succeed', topic: 'password')
publish_confirmation_code(current_user, Barong::App.config.domain, 'reset-password')
status 201
end
desc 'Validate reset code or set a new password via one-time email link',
success: { code: 201, message: 'reset code is ok' },
failure: [
{ code: 400, message: 'Required params are missing' },
{ code: 422, message: 'Validation errors' },
{ code: 404, message: 'User doesn\'t exist'}
]
params do
optional :email,
type: String,
allow_blank: false,
desc: 'Account email'
optional :code,
type: String,
allow_blank: false,
desc: 'Confirm code'
optional :reset_password_token,
type: String,
allow_blank: false,
desc: 'Token from email'
optional :password,
type: String,
allow_blank: false,
desc: 'User new password'
optional :confirm_password,
type: String,
allow_blank: false,
desc: 'User new password'
optional :captcha_response,
types: [String, Hash],
desc: 'Response from captcha widget'
end
post '/confirm_code' do
token_reset_request = params[:reset_password_token].present? ||
params[:password].present? ||
params[:confirm_password].present?
if token_reset_request
error!({ errors: ['identity.user.missing_pass_token', 'identity.user.empty_reset_password_token'] }, 422) if params[:reset_password_token].blank?
error!({ errors: ['identity.user.missing_password', 'identity.user.empty_password'] }, 422) if params[:password].blank?
error!({ errors: ['identity.user.missing_confirm_password', 'identity.user.empty_confirm_password'] }, 422) if params[:confirm_password].blank?
error!({ errors: ['identity.user.passwords_doesnt_match'] }, 422) unless params[:password] == params[:confirm_password]
payload = codec.decode_and_verify(
params[:reset_password_token],
pub_key: Barong::App.config.keystore.public_key,
sub: 'reset'
)
if Rails.cache.read("reset_password_#{payload[:email]}") != payload[:reset_token] ||
Rails.cache.read(payload[:jti]) == 'utilized'
error!({ errors: ['identity.user.utilized_token'] }, 422)
end
current_user = User.find_by_email(payload[:email])
return status 404 if current_user.nil?
unless PasswordStrengthChecker.validate!(params[:password]) == 'strong'
error!({ errors: ["resource.password.#{PasswordStrengthChecker.validate!(params[:password])}"] }, 422)
end
unless current_user.update(password: params[:password])
error_note = { reason: current_user.errors.full_messages.to_sentence }.to_json
activity_record(user: current_user.id, action: 'password reset',
result: 'failed', topic: 'password', data: error_note)
code_error!(current_user.errors.details, 422)
end
Rails.cache.delete("reset_password_#{payload[:email]}")
Rails.cache.write(payload[:jti], 'utilized', expires_in: Barong::App.config.jwt_expire_time.seconds)
activity_record(user: current_user.id, action: 'password reset', result: 'succeed', topic: 'password')
EventAPI.notify('system.user.password.reset',
record: {
user: current_user.as_json_for_event_api,
domain: Barong::App.config.domain
})
status 201
else
error!({ errors: ['identity.user.missing_email', 'identity.user.empty_email'] }, 422) if params[:email].blank?
error!({ errors: ['identity.user.missing_code', 'identity.user.empty_code'] }, 422) if params[:code].blank?
verify_captcha!(response: params['captcha_response'], endpoint: 'password_reset')
current_user = User.find_by_email(params[:email])
return status 404 if current_user.nil?
totp = TOTPServiceAction.new('reset-password')
error!({ errors: ['resource.totp.code'] }, 422) unless totp.safe_validate?(current_user.uid, declared(params)[:code])
status 200
end
end
# reset forgot password
desc 'Sets new account password(for forgot password)',
success: { code: 201, message: 'Resets password' },
failure: [
{ code: 400, message: 'Required params are empty' },
{ code: 404, message: 'Record is not found' },
{ code: 422, message: 'Validation errors' }
]
params do
requires :email,
type: String,
message: 'identity.user.email',
allow_blank: false,
desc: 'user email'
requires :code,
type: String,
message: 'identity.user.missing_pass_token',
allow_blank: false,
desc: 'code from email again'
requires :password,
type: String,
message: 'identity.user.missing_password',
allow_blank: false,
desc: 'User new password'
requires :confirm_password,
type: String,
message: 'identity.user.missing_confirm_password',
allow_blank: false,
desc: 'User new password'
end
post '/reset' do
current_user = User.find_by_email(params[:email])
return status 404 if current_user.nil?
totp = TOTPServiceAction.new('reset-password')
error!({ errors: ['resource.totp.code'] }, 422) unless totp.safe_validate?(current_user.uid, declared(params)[:code])
unless params[:password] == params[:confirm_password]
error!({ errors: ['identity.user.passwords_doesnt_match'] }, 422)
end
unless PasswordStrengthChecker.validate!(params[:password]) == 'strong'
error!({ errors: ["resource.password.#{PasswordStrengthChecker.validate!(temp_password)}"] }, 422)
end
unless current_user.update(password: params[:password])
error_note = { reason: current_user.errors.full_messages.to_sentence }.to_json
activity_record(user: current_user.id, action: 'password reset',
result: 'failed', topic: 'password', data: error_note)
code_error!(current_user.errors.details, 422)
end
totp.validate?(current_user.uid, declared(params)[:code])
activity_record(user: current_user.id, action: 'password reset', result: 'succeed', topic: 'password')
EventAPI.notify('system.user.password.reset',
record: {
user: current_user.as_json_for_event_api,
domain: Barong::App.config.domain
})
status 201
end
end
end
end
end
end