commit 5ffef911fe5f47d5b5c58f90d07387f71c423792 Author: Serghei MIHAI Date: Wed Jun 4 10:48:36 2014 +0200 Initial commit diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..7723190 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'http://rubygems.org' + +gem 'oauth2' +gem 'json' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..a495cd5 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,22 @@ +GEM + remote: http://rubygems.org/ + specs: + addressable (2.3.2) + faraday (0.7.6) + addressable (~> 2.2) + multipart-post (~> 1.1) + rack (~> 1.1) + json (1.7.5) + multi_json (1.3.6) + multipart-post (1.1.5) + oauth2 (0.5.2) + faraday (~> 0.7) + multi_json (~> 1.0) + rack (1.4.1) + +PLATFORMS + ruby + +DEPENDENCIES + json + oauth2 diff --git a/app/controllers/redmine_oauth_controller.rb b/app/controllers/redmine_oauth_controller.rb new file mode 100644 index 0000000..757225f --- /dev/null +++ b/app/controllers/redmine_oauth_controller.rb @@ -0,0 +1,95 @@ +require 'account_controller' +require 'json' +require 'oauth2' + +class RedmineOauthController < AccountController + include Helpers::MailHelper + include Helpers::Checker + def oauth_a2 + if Setting.plugin_redmine_oauth2_a2[:oauth_authentification] + session[:back_url] = params[:back_url] + redirect_to oauth_client.auth_code.authorize_url(:redirect_uri => oauth_a2_callback_url, :scope => 'read') + else + password_authentication + end + end + + def oauth_a2_callback + if params[:error] + flash[:error] = l(:notice_access_denied) + redirect_to signin_path + else + token = oauth_client.auth_code.get_token(params[:code], :redirect_uri => oauth_a2_callback_url) + result = token.get(Setting.plugin_redmine_oauth_a2[:a2_server_url] + '/idp/oauth2/user-info/') + info = JSON.parse(result.body) + if info && info["verified_email"] + if allowed_domain_for?(info["email"]) + try_to_login info + else + flash[:error] = l(:notice_domain_not_allowed, :domain => parse_email(info["email"])[:domain]) + redirect_to signin_path + end + else + flash[:error] = l(:notice_unable_to_obtain_a2_credentials) + redirect_to signin_path + end + end + end + + def try_to_login info + params[:back_url] = session[:back_url] + session.delete(:back_url) + user = User.find_or_initialize_by_mail(info["email"]) + if user.new_record? + # Self-registration off + redirect_to(home_url) && return unless Setting.self_registration? + # Create on the fly + user.firstname, user.lastname = info["name"].split(' ') unless info['name'].nil? + user.firstname ||= info[:given_name] + user.lastname ||= info[:family_name] + user.mail = info["email"] + user.login = parse_email(info["email"])[:login] + user.login ||= [user.firstname, user.lastname]*"." + user.random_password + user.register + + case Setting.self_registration + when '1' + register_by_email_activation(user) do + onthefly_creation_failed(user) + end + when '3' + register_automatically(user) do + onthefly_creation_failed(user) + end + else + register_manually_by_administrator(user) do + onthefly_creation_failed(user) + end + end + else + # Existing record + if user.active? + successful_authentication(user) + else + account_pending + end + end + end + + def oauth_client + @client ||= OAuth2::Client.new(settings[:client_id], settings[:client_secret], + :site => settings[:a2_server_url], + :authorize_url => '/idp/oauth2/authorize/', + :token_url => '/idp/oauth2/access_token/' + ) + end + + def settings + @settings ||= Setting.plugin_redmine_oauth2_a2 + end + + def scopes + Setting.plugin_redmine_oauth2_a2[:a2_scopes_urls] + end +end diff --git a/app/helpers/redmine_oauth_helper.rb b/app/helpers/redmine_oauth_helper.rb new file mode 100644 index 0000000..c8b1c1e --- /dev/null +++ b/app/helpers/redmine_oauth_helper.rb @@ -0,0 +1,2 @@ +module RedmineOauthHelper +end diff --git a/app/views/hooks/_view_account_login_bottom.html.erb b/app/views/hooks/_view_account_login_bottom.html.erb new file mode 100644 index 0000000..3dd0e19 --- /dev/null +++ b/app/views/hooks/_view_account_login_bottom.html.erb @@ -0,0 +1,7 @@ +<% if Setting.plugin_redmine_oauth2_a2[:oauth_authentification] %> + <%= link_to oauth_a2_path(:back_url => back_url) do %> + <%= button_tag :class => 'button-login' do %> + <%= content_tag :div, l(:login_via_oauth2_a2), :class => 'button-login-text' %> + <% end %> + <% end %> +<% end %> diff --git a/app/views/settings/_oauth_settings.html.erb b/app/views/settings/_oauth_settings.html.erb new file mode 100644 index 0000000..d44ed54 --- /dev/null +++ b/app/views/settings/_oauth_settings.html.erb @@ -0,0 +1,21 @@ +

+ + <%= text_field_tag 'settings[client_id]', @settings[:client_id] %> +

+

+ + <%= text_field_tag 'settings[client_secret]', @settings[:client_secret] %> +

+

+ + <%= text_field_tag 'settings[a2_server_url]', @settings[:a2_server_url] %> +

+ +

+ + <%= text_area_tag "settings[allowed_domains]", @settings[:allowed_domains], :rows => 5 %> +

+

+ + <%= check_box_tag "settings[oauth_authentification]", true, @settings[:oauth_authentification] %> +

diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..b101fd6 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,5 @@ +en: + notice_unable_to_obtain_a2_credentials: "Unable to obtain credentials from Authentic." + notice_domain_not_allowed: "You can not login using %{domain} domain." + notice_access_denied: "You must allow to use you Google credentials to enter this site." + login_via_oauth2_a2: "Login via Authentic using OAuth2" diff --git a/config/locales/ru.yml b/config/locales/ru.yml new file mode 100644 index 0000000..44d1235 --- /dev/null +++ b/config/locales/ru.yml @@ -0,0 +1,5 @@ +ru: + notice_unable_to_obtain_a2_credentials: "Не удалось получить данные от Authentic." + notice_domain_not_allowed: "Вы не можете войти в систему при помощи домена %{domain}." + notice_access_denied: "Для корректного входа необходимо разрешить приложению доступ к аккаунту." + login_via_a2: "Войти с Authentic" diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..ca736e0 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,2 @@ +get 'oauth_a2', :to => 'redmine_oauth#oauth_a2' +get 'oauth2callback', :to => 'redmine_oauth#oauth_a2_callback', :as => 'oauth_a2_callback' diff --git a/init.rb b/init.rb new file mode 100644 index 0000000..3c8015e --- /dev/null +++ b/init.rb @@ -0,0 +1,20 @@ +require 'redmine' +require_dependency 'redmine_oauth2_a2/hooks' + +Redmine::Plugin.register :redmine_oauth2_a2 do + name 'Redmine Oauth Authentic plugin' + author 'Serghei Mihai' + description 'Plugin for authentication through Authentic' + version '0.0.1' + url '' + author_url '' + + settings :default => { + :client_id => "", + :client_secret => "", + :oauth_autentification => false, + :allowed_domains => "", + :a2_server_url => "", + :a2_scopes_urls => "", + }, :partial => 'settings/oauth_settings' +end diff --git a/lib/helpers/checker.rb b/lib/helpers/checker.rb new file mode 100644 index 0000000..64b9114 --- /dev/null +++ b/lib/helpers/checker.rb @@ -0,0 +1,11 @@ +module Helpers + module Checker + def allowed_domain_for? email + allowed_domains = Setting.plugin_redmine_oauth_a2[:allowed_domains] + return unless allowed_domains + allowed_domains = allowed_domains.split + return true if allowed_domains.empty? + allowed_domains.index(parse_email(email)[:domain]) + end + end +end diff --git a/lib/helpers/mail_helper.rb b/lib/helpers/mail_helper.rb new file mode 100644 index 0000000..eec5b41 --- /dev/null +++ b/lib/helpers/mail_helper.rb @@ -0,0 +1,8 @@ +module Helpers + module MailHelper + def parse_email email + email_data = email && email.is_a?(String) ? email.match(/(.*?)@(.*)/) : nil + {:login => email_data[1], :domain => email_data[2]} if email_data + end + end +end diff --git a/lib/redmine_oauth2_a2/hooks.rb b/lib/redmine_oauth2_a2/hooks.rb new file mode 100644 index 0000000..0f78818 --- /dev/null +++ b/lib/redmine_oauth2_a2/hooks.rb @@ -0,0 +1,9 @@ +module RedmineOauth2A2 + class Hooks < Redmine::Hook::ViewListener + def view_account_login_bottom(context = {}) + context[:controller].send(:render_to_string, { + :partial => "hooks/view_account_login_bottom", + :locals => context}) + end + end +end