📜 ⬆️ ⬇️

Authorization in Redmine from another site

On the website centos-admin.ru, the designer came up with a very healthy effect for the login form. The idea of ​​the form is that the user enters his login and password into Redmine and gets logged in to his page.

Everything would be great, but in Ruby on Rails (on which Redmine is made) direct POST requests from external sites are not accepted - for a successful request an authorization token is needed.

This token is generated by rails-applications automatically, stored in cookies. In this connection, at first I thought in iframe to load the site with Redmine and from cookies to take the necessary key. But somehow this is not a rails-way.
')
The simplest solution is to patch Redmine slightly - add the ability to process requests from external resources. Fortunately, Redmine has everything for it - you can write a small plugin that will solve this problem.

What will the plugin do?


The question would seem simple, but you need to remember about maintaining the security of user data.

The first Stackoverflow solutions suggested turning off the token check for a particular action. But these are not solutions at all, since open a security hole in the site.

Accordingly, there remains the option to use a self-generated token, check it on the Redmine side and, if successful, authorize.

How to generate a token?


The simplest option is to use any character string, but it seemed to me that this is not enough for secure authorization. Since a simple token can be intercepted and used to send data from unauthorized resources.

Therefore, I decided to add the domain in the authorization token from which the request is sent and information about the current date.

On the site side in helpers we create a method
def authenticity_token token = Settings.redmine_remote_login_token Base64.encode64 "#{token}-#{request.host}-#{Date.today}" end 

and then use it in the form

 <%= form_tag 'http://factory.southbridge.ru/remote_login', authenticity_token: authenticity_token do %> 

What happens on the Redmine side?


On the Redmine side, you need to add a route to handle POST requests for the path / remote_login
 post 'remote_login', to: 'account#remote_login' 

And slightly patch the AccountController by adding the remote_login action to it:
 module RemoteLogin module AccountControllerPatch def self.included(base) # :nodoc: base.class_eval do unloadable skip_before_filter :verify_authenticity_token, only: :remote_login before_filter :verify_remote_authenticity_token, only: :remote_login def remote_login authenticate_user rescue AuthSourceException => e logger.error "An error occured when authenticating #{params[:username]}: #{e.message}" render_error :message => e.message end private def verify_remote_authenticity_token uri = URI.parse(request.env['HTTP_REFERER']) token = Setting.plugin_redmine_remote_login['token'] unless Base64.decode64(params['authenticity_token']) == "#{token}-#{uri.host}-#{Date.today}" if logger && log_warning_on_csrf_failure logger.warn "Can't verify CSRF token authenticity" end handle_unverified_request end end end end end end AccountController.send(:include, RemoteLogin::AccountControllerPatch) 

It uses the abolition of standard token verification
 skip_before_filter :verify_authenticity_token, only: :remote_login 

and instead it is written by us
 before_filter :verify_remote_authenticity_token, only: :remote_login 

That's the whole plugin. A small, but solves an important task - it is convenient for site visitors to log into your personal account, while not allowing authorization from the left resources.

The plugin code can be found here .

Tips, questions and comments are accepted in the comments.

Source: https://habr.com/ru/post/267921/


All Articles