Add redmine community auth plugin
authorMagnus Hagander <magnus@hagander.net>
Fri, 25 May 2012 11:10:58 +0000 (13:10 +0200)
committerMagnus Hagander <magnus@hagander.net>
Fri, 25 May 2012 11:10:58 +0000 (13:10 +0200)
Written by Alex Shulgin (ash at commandprompt.com)

tools/communityauth/sample/ruby/README.rdoc [new file with mode: 0644]
tools/communityauth/sample/ruby/app/views/account/pgcommunityauth.html.erb [new file with mode: 0644]
tools/communityauth/sample/ruby/app/views/settings/_redmine_pgcommunityauth_settings.html.erb [new file with mode: 0644]
tools/communityauth/sample/ruby/config/locales/en.yml [new file with mode: 0644]
tools/communityauth/sample/ruby/config/routes.rb [new file with mode: 0644]
tools/communityauth/sample/ruby/init.rb [new file with mode: 0644]
tools/communityauth/sample/ruby/lang/en.yml [new file with mode: 0644]
tools/communityauth/sample/ruby/lib/redmine_pgcommunityauth/account_controller_patch.rb [new file with mode: 0644]
tools/communityauth/sample/ruby/test/test_helper.rb [new file with mode: 0644]

diff --git a/tools/communityauth/sample/ruby/README.rdoc b/tools/communityauth/sample/ruby/README.rdoc
new file mode 100644 (file)
index 0000000..1481aaa
--- /dev/null
@@ -0,0 +1,3 @@
+= pgcommunityauth
+
+Description goes here
diff --git a/tools/communityauth/sample/ruby/app/views/account/pgcommunityauth.html.erb b/tools/communityauth/sample/ruby/app/views/account/pgcommunityauth.html.erb
new file mode 100644 (file)
index 0000000..a205e49
--- /dev/null
@@ -0,0 +1,3 @@
+<p>
+PostgreSQL community auth login page.
+</p>
diff --git a/tools/communityauth/sample/ruby/app/views/settings/_redmine_pgcommunityauth_settings.html.erb b/tools/communityauth/sample/ruby/app/views/settings/_redmine_pgcommunityauth_settings.html.erb
new file mode 100644 (file)
index 0000000..0155351
--- /dev/null
@@ -0,0 +1,12 @@
+<p>
+  <%= label_tag :settings_authsite_id, "Auth site ID" %>
+  <%= text_field_tag 'settings[authsite_id]', settings['authsite_id'], :size => 5 %>
+</p>
+<p>
+  <%= label_tag :settings_cipher_key, "Cipher key (Base64)" %>
+  <%= text_field_tag 'settings[cipher_key]', settings['cipher_key'] %>
+</p>
+<p>
+  <%= label_tag :settings_default_url, "Default URL (/)" %>
+  <%= text_field_tag 'settings[default_url]', settings['default_url'] %>
+</p>
diff --git a/tools/communityauth/sample/ruby/config/locales/en.yml b/tools/communityauth/sample/ruby/config/locales/en.yml
new file mode 100644 (file)
index 0000000..c55090a
--- /dev/null
@@ -0,0 +1,3 @@
+# English strings go here for Rails i18n
+en:
+  my_label: "My label"
diff --git a/tools/communityauth/sample/ruby/config/routes.rb b/tools/communityauth/sample/ruby/config/routes.rb
new file mode 100644 (file)
index 0000000..8fc4613
--- /dev/null
@@ -0,0 +1,3 @@
+ActionController::Routing::Routes.draw do |map|
+  map.pgcommunityauth '/pgcommunityauth', :controller => 'account', :action => 'pgcommunityauth'
+end
diff --git a/tools/communityauth/sample/ruby/init.rb b/tools/communityauth/sample/ruby/init.rb
new file mode 100644 (file)
index 0000000..91030f6
--- /dev/null
@@ -0,0 +1,17 @@
+require 'redmine'
+require 'dispatcher'
+
+Dispatcher.to_prepare do
+  require_dependency 'account_controller'
+
+  AccountController.send(:include, RedminePgcommunityauth::AccountControllerPatch)
+end
+
+Redmine::Plugin.register :redmine_pgcommunityauth do
+  name 'Redmine Pgcommunityauth plugin'
+  author 'Alex Shulgin <ash@commandprompt.com>'
+  description ''
+  version '0.0.1'
+
+  settings :default => {}, :partial => 'settings/redmine_pgcommunityauth_settings'
+end
diff --git a/tools/communityauth/sample/ruby/lang/en.yml b/tools/communityauth/sample/ruby/lang/en.yml
new file mode 100644 (file)
index 0000000..e338591
--- /dev/null
@@ -0,0 +1,2 @@
+# English strings go here
+my_label: "My label"
diff --git a/tools/communityauth/sample/ruby/lib/redmine_pgcommunityauth/account_controller_patch.rb b/tools/communityauth/sample/ruby/lib/redmine_pgcommunityauth/account_controller_patch.rb
new file mode 100644 (file)
index 0000000..48c7036
--- /dev/null
@@ -0,0 +1,104 @@
+require 'base64'
+require 'openssl' # aes gem doesn't let us disable PKCS#5 padding
+
+module RedminePgcommunityauth
+  module AccountControllerPatch
+    unloadable
+
+    class AuthTokenExpiredError < RuntimeError; end
+    class InvalidAuthTokenError < RuntimeError; end
+
+    def self.included(base)
+      base.class_eval do
+        alias_method_chain :login,  :pgcommunityauth
+        alias_method_chain :logout, :pgcommunityauth
+      end
+    end
+
+    def login_with_pgcommunityauth
+      redirect_to pgcommunityauth_login_url
+    end
+
+    def logout_with_pgcommunityauth
+      logout_user
+      redirect_to pgcommunityauth_logout_url
+    end
+
+    # GET /pgcommunityauth
+    def pgcommunityauth
+      if params[:s] == 'logout'
+        flash[:notice] = "Successfully logged out from PG community sites."
+        return
+      end
+
+      data = (params[:d] || "").tr('-_', '+/')
+      iv   = (params[:i] || "").tr('-_', '+/')
+
+      qs = aes_decrypt(data, iv).rstrip
+      auth = Rack::Utils.parse_query(qs)
+
+      # check auth hash for mandatory keys
+      raise InvalidAuthTokenError.new unless %w(t u f l e).all?{ |x| auth.keys.include?(x) }
+
+      # check auth token timestamp: issued 10 seconds ago or less
+      raise AuthTokenExpiredError.new unless Time.now.to_i <= auth['t'].to_i + 10
+
+      # prepare attrs for create or update
+      attrs = {
+        :firstname => auth['f'],
+        :lastname => auth['l'],
+        :mail => auth['e']
+      }
+      if user = User.find_by_login(auth['u'])
+        user.update_attributes! attrs
+      else
+        user = User.new(attrs)
+        # can't pass protected attr in new/create
+        user.login = auth['u']
+        user.save!
+      end
+
+      params[:back_url] = auth['su'] || pgcommunityauth_settings[:default_url]
+      successful_authentication(user)
+    rescue OpenSSL::Cipher::CipherError
+      flash[:error] = "Invalid PG communityauth message received."
+    rescue InvalidAuthTokenError
+      flash[:error] = "Invalid PG communityauth token received."
+    rescue AuthTokenExpiredError
+      flash[:error] = "PG community auth token expired."
+    end
+
+    private
+
+    def pgcommunityauth_settings
+      Setting['plugin_redmine_pgcommunityauth']
+    end
+
+    def pgcommunityauth_base_url
+      "https://www.postgresql.org/account/auth/#{pgcommunityauth_settings[:authsite_id]}"
+    end
+
+    def pgcommunityauth_login_url
+      "#{pgcommunityauth_base_url}/"
+    end
+
+    def pgcommunityauth_logout_url
+      "#{pgcommunityauth_base_url}/logout/"
+    end
+
+    def aes_decrypt(data, iv)
+      key = Base64.decode64(pgcommunityauth_settings[:cipher_key])
+
+      cipher = OpenSSL::Cipher.new("AES-#{key.size*8}-CBC")
+      cipher.decrypt
+
+      # this is the key point here, otherwise we could use
+      # AES.decrypt()
+      cipher.padding = 0
+
+      cipher.key = key
+      cipher.iv  = Base64.decode64(iv)
+      cipher.update(Base64.decode64(data)) + cipher.final
+    end
+  end
+end
diff --git a/tools/communityauth/sample/ruby/test/test_helper.rb b/tools/communityauth/sample/ruby/test/test_helper.rb
new file mode 100644 (file)
index 0000000..bd1ed0c
--- /dev/null
@@ -0,0 +1,5 @@
+# Load the normal Rails helper
+require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper')
+
+# Ensure that we are using the temporary fixture path
+Engines::Testing.set_fixture_path