Skip to content

Commit

Permalink
Merge pull request #10 from centosadmin/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
vladislav-yashin authored Aug 8, 2018
2 parents 4077faa + a0aaeea commit b8499ab
Show file tree
Hide file tree
Showing 36 changed files with 437 additions and 135 deletions.
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ gem 'sidekiq-rate-limiter', git: 'https://github.com/centosadmin/sidekiq-rate-li
gem 'telegram-bot-ruby', '~> 0.8.6'
gem 'slack-ruby-bot'
gem 'celluloid-io'
gem 'tdlib-ruby', '~> 0.9'
gem 'tdlib-ruby', '~> 1.0'
gem 'jwt'
gem 'filelock'
gem 'lazy_object', '~> 0.0.2'

group :test do
gem 'timecop'
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/redmine_telegram_setup_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def authorize
authenticate.(params)
save_phone_settings(phone_number: params['phone_number'])
redirect_to plugin_settings_path('redmine_bots'), notice: t('telegram_common.client.authorize.success')
rescue RedmineBots::Telegram::TdlibAuthenticate::AuthenticationError => e
rescue RedmineBots::Telegram::Tdlib::Authenticate::AuthenticationError => e
redirect_to plugin_settings_path('redmine_bots'), alert: e.message
end
end
Expand Down
29 changes: 24 additions & 5 deletions app/controllers/telegram_login_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,33 @@ def index

def check_auth
user = User.find_by_id(session[:otp_user_id]) || User.current
auth = RedmineBots::Telegram::Bot::Authenticate.(user, login_params, context: context)

auth = RedmineBots::Telegram::Bot::Authenticate.(user, login_params)
handle_auth_result(auth, user)
end

def send_sign_in_link
user = session[:otp_user_id] ? User.find(session[:otp_user_id]) : User.current
RedmineBots::Telegram::Bot::SendSignInLink.(user, context: context, params: params.slice(:autologin, :back_url))
end

def check_jwt
user = User.find_by_id(session[:otp_user_id]) || User.current
auth = RedmineBots::Telegram::Bot::AuthenticateByToken.(user, params[:token], context: context)

handle_auth_result(auth, user)
end

private

def context
session[:otp_user_id] ? '2fa_connection' : 'account_connection'
end

def handle_auth_result(auth, user)
if auth.success?
if session[:otp_user_id]
user.update_column(:two_fa_id, AuthSource.find_by_name('Telegram').id)
if user != User.current
user.update!(two_fa_id: AuthSource.find_by_name('Telegram').id)
successful_authentication(user)
else
redirect_to my_page_path, notice: t('redmine_bots.telegram.bot.login.success')
Expand All @@ -19,8 +40,6 @@ def check_auth
end
end

private

def login_params
params.permit(:id, :first_name, :last_name, :username, :photo_url, :auth_date, :hash)
end
Expand Down
2 changes: 1 addition & 1 deletion app/views/settings/redmine_bots/_telegram.erb
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,6 @@
</div>

<%= hidden_field_tag 'settings[telegram_bot_name]', @settings['telegram_bot_name'] %>
<%= hidden_field_tag 'settings[telegram_bot_id]', @settings['bot_id'] %>
<%= hidden_field_tag 'settings[telegram_bot_id]', @settings['telegram_bot_id'] %>
<%= hidden_field_tag 'settings[telegram_robot_id]', @settings['telegram_robot_id'] %>
<%= hidden_field_tag 'settings[telegram_phone_number]', @settings['telegram_phone_number'] %>
20 changes: 19 additions & 1 deletion app/views/telegram_login/_widget.erb
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
<script async src="https://telegram.org/js/telegram-widget.js?4" data-telegram-login="<%= Setting.plugin_redmine_bots['telegram_bot_name'] %>" data-size="large" data-auth-url="<%= Setting.protocol %>://<%= Setting.host_name %>/telegram/check_auth?<%= params.slice(:autologin, :back_url).to_query %>" data-request-access="write"></script>
<script async src="https://telegram.org/js/telegram-widget.js?4" data-telegram-login="<%= Setting.plugin_redmine_bots['telegram_bot_name'] %>" data-size="large" data-auth-url="<%= Setting.protocol %>://<%= Setting.host_name %>/telegram/check_auth?<%= params.slice(:autologin, :back_url).merge(context: context).to_query %>" data-request-access="write"></script>
<% current_user = @user || User.current %>
<% telegram_account =
case context
when 'account_connection'
current_user.telegram_account
when '2fa_connection'
current_user.telegram_connection
end
%>
<br>

<% if current_user.logged? && telegram_account.present? %>
<%= link_to send_telegram_sign_in_link_path(params.slice(:autologin, :back_url)), method: :post, remote: true do %>
<%= t('redmine_bots.telegram.bot.login.send_to_telegram') %>
<% end %> (<%= t('redmine_bots.telegram.bot.login.widget_not_visible') %>)
<% else %>
<%= t('redmine_bots.telegram.bot.login.write_to_bot', bot: Setting.plugin_redmine_bots['telegram_bot_name']) %>
<% end %>
2 changes: 1 addition & 1 deletion app/views/telegram_login/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div align="center" style="margin-top: 20%">
<%= render partial: 'telegram_login/widget' %>
<%= render partial: 'telegram_login/widget', locals: { context: 'account_connection' } %>
</div>
6 changes: 6 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ en:
start: "Start work with bot"
connect: "Connect Redmine and Telegram account"
help: "Help about commands"
token: Get auth link
login:
success: Authentication succeed
errors:
Expand All @@ -66,6 +67,11 @@ en:
hash_outdated: Request is outdated
wrong_account: Wrong Telegram account
not_persisted: Failed to persist Telegram account data
invalid_token: Token is invalid
follow_link: Please, follow link
send_to_telegram: Send auth link to Telegram
widget_not_visible: if widget isn not visible
write_to_bot: "Please, write command /token to bot @%{bot} if widget is not visible"
slack:
commands:
connect: connect Slack account with Redmine
Expand Down
6 changes: 6 additions & 0 deletions config/locales/ru.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ ru:
start: "Начало работы с ботом"
connect: "Связывание аккаунтов Redmine и Telegram"
help: "Справка по командам"
token: Получить ссылку для аутентификации
login:
success: Аутентификация прошла успешно
errors:
Expand All @@ -66,6 +67,11 @@ ru:
hash_outdated: Истекло время ожидания
wrong_account: Неверный аккаунт Telegram
not_persisted: Не удалось сохранить данные о Telegram-аккаунте
invalid_token: Неверный token
follow_link: Пожалуйста, проследуйте по ссылке
send_to_telegram: Отправить ссылку в Telegram
widget_not_visible: если виджет недоступен
write_to_bot: "Пожалуйста, напишите команду /token боту @%{bot}, если не видно виджет"
slack:
commands:
connect: связывание аккаунтов Slack и Redmine
Expand Down
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@

get 'login' => 'telegram_login#index', as: 'telegram_login'
get 'check_auth' => 'telegram_login#check_auth'
get 'check_jwt' => 'telegram_login#check_jwt'
post 'send_sign_in_link' => 'telegram_login#send_sign_in_link', as: 'send_telegram_sign_in_link'
end
17 changes: 17 additions & 0 deletions lib/redmine_bots/result.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module RedmineBots
class Result
attr_reader :value

def initialize(success, value)
@success, @value = success, value
end

def success?
@success
end

def failure?
!success?
end
end
end
4 changes: 0 additions & 4 deletions lib/redmine_bots/telegram.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ module RedmineBots::Telegram
extend Tdlib::DependencyProviders::GetMe
extend Tdlib::DependencyProviders::AddBot

def self.table_name_prefix
'telegram_common_'
end

def self.set_locale
I18n.locale = Setting['default_language']
end
Expand Down
1 change: 1 addition & 0 deletions lib/redmine_bots/telegram/bot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class Bot
include BotCommand::Start
include BotCommand::Connect
include BotCommand::Help
include BotCommand::Token

attr_reader :bot_token, :logger, :command

Expand Down
117 changes: 60 additions & 57 deletions lib/redmine_bots/telegram/bot/authenticate.rb
Original file line number Diff line number Diff line change
@@ -1,77 +1,80 @@
module RedmineBots::Telegram
class Bot::Authenticate
AUTH_TIMEOUT = 60.minutes
module RedmineBots
module Telegram
class Bot::Authenticate
AUTH_TIMEOUT = 60.minutes

def self.call(user, auth_data)
new(user, auth_data).call
end

def initialize(user, auth_data)
@user, @auth_data = user, Hash[auth_data.sort_by { |k, _| k }]
end
def self.call(user, auth_data, context:)
new(user, auth_data, context: context).call
end

def call
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.not_logged')) unless @user.logged?
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.hash_invalid')) unless hash_valid?
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.hash_outdated')) unless up_to_date?
def initialize(user, auth_data, context:)
@user, @auth_data, @context = user, Hash[auth_data.sort_by { |k, _| k }], context
end

telegram_account = TelegramAccount.find_by(user_id: @user.id)
def call
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.not_logged')) unless @user.logged?
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.hash_invalid')) unless hash_valid?
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.hash_outdated')) unless up_to_date?

if telegram_account.present?
if telegram_account.telegram_id
unless @auth_data['id'].to_i == telegram_account.telegram_id
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.wrong_account'))
end
case @context
when '2fa_connection'
telegram_account = prepare_telegram_account(model_class: Redmine2FA::TelegramConnection)
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.wrong_account')) unless telegram_account
when 'account_connection'
telegram_account = prepare_telegram_account(model_class: TelegramAccount)
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.wrong_account')) unless telegram_account
telegram_account.assign_attributes(@auth_data.slice('first_name', 'last_name', 'username'))
else
telegram_account.telegram_id = @auth_data['id']
return failure('Invalid context')
end
else
telegram_account = TelegramAccount.find_or_initialize_by(telegram_id: @auth_data['id'])
if telegram_account.user_id
unless telegram_account.user_id == @user.id
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.wrong_account'))
end

if telegram_account.save
success(telegram_account)
else
telegram_account.user_id = @user.id
failure(I18n.t('redmine_bots.telegram.bot.login.errors.not_persisted'))
end
end

telegram_account.assign_attributes(@auth_data.slice(*%w[first_name last_name username]))

if telegram_account.save
success(telegram_account)
else
failure(I18n.t('redmine_bots.telegram.bot.login.errors.not_persisted'))
end
end

private
private

def hash_valid?
Utils.auth_hash(@auth_data) == @auth_data['hash']
end

def up_to_date?
Time.at(@auth_data['auth_date'].to_i) > Time.now - AUTH_TIMEOUT
end
def prepare_telegram_account(model_class:)
telegram_account = model_class.find_by(user_id: @user.id)

def success(value)
Result.new(true, value)
end
if telegram_account.present?
if telegram_account.telegram_id
unless @auth_data['id'].to_i == telegram_account.telegram_id
return nil
end
else
telegram_account.telegram_id = @auth_data['id']
end
else
telegram_account = model_class.find_or_initialize_by(telegram_id: @auth_data['id'])
if telegram_account.user_id
unless telegram_account.user_id == @user.id
return nil
end
else
telegram_account.user_id = @user.id
end
end
telegram_account
end

def failure(value)
Result.new(false, value)
end
def hash_valid?
Utils.auth_hash(@auth_data) == @auth_data['hash']
end

class Result
attr_reader :value
def up_to_date?
Time.at(@auth_data['auth_date'].to_i) > Time.now - AUTH_TIMEOUT
end

def initialize(success, value)
@success, @value = success, value
def success(value)
Result.new(true, value)
end

def success?
@success
def failure(value)
Result.new(false, value)
end
end
end
Expand Down
70 changes: 70 additions & 0 deletions lib/redmine_bots/telegram/bot/authenticate_by_token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
module RedmineBots
module Telegram
class Bot::AuthenticateByToken
def self.call(*args)
new(*args).call
end

def initialize(user, token, context:)
@user, @token, @context = user, token, context
end

def call
return failure(I18n.t('redmine_bots.telegram.bot.login.errors.not_logged')) if @user.anonymous?

case @context
when '2fa_connection'
telegram_account = prepare_telegram_account(model_class: Redmine2FA::TelegramConnection)
when 'account_connection'
telegram_account = prepare_telegram_account(model_class: TelegramAccount)
else
return failure('Invalid context')
end

return failure(I18n.t('redmine_bots.telegram.bot.login.errors.wrong_account')) unless telegram_account

if telegram_account.save
success(telegram_account)
else
failure(I18n.t('redmine_bots.telegram.bot.login.errors.not_persisted'))
end
rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::InvalidIssuerError, JWT::InvalidIatError
failure(I18n.t('redmine_bots.telegram.bot.login.errors.invalid_token'))
end

def prepare_telegram_account(model_class:)
telegram_data = Jwt.decode_token(@token).first
telegram_id = telegram_data['telegram_id'].to_i
telegram_account = model_class.find_by(user_id: @user.id)

if telegram_account.present?
if telegram_account.telegram_id
unless telegram_id == telegram_account.telegram_id
return nil
end
else
telegram_account.telegram_id = telegram_id
end
else
telegram_account = model_class.find_or_initialize_by(telegram_id: telegram_id)
if telegram_account.user_id
unless telegram_account.user_id == @user.id
return nil
end
else
telegram_account.user_id = @user.id
end
end
telegram_account
end

def success(value)
Result.new(true, value)
end

def failure(value)
Result.new(false, value)
end
end
end
end
Loading

0 comments on commit b8499ab

Please sign in to comment.