diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index 9d570a9a..514e9923 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -20,9 +20,9 @@ class AccountController < ApplicationController # Login request and validation def login - if request.get? - logout_user - else + if User.current.logged? + redirect_to home_url + elsif request.post? authenticate_user end end diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index c83e972d..c74c0041 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -173,6 +173,7 @@ class WikiController < ApplicationController def annotate @annotate = @page.annotate(params[:version]) render_404 unless @annotate + @editable = editable? end verify :method => :delete, :only => [:destroy], :redirect_to => { :action => :show } diff --git a/app/models/auth_source_ldap.rb b/app/models/auth_source_ldap.rb index d009ae33..3536b4f6 100644 --- a/app/models/auth_source_ldap.rb +++ b/app/models/auth_source_ldap.rb @@ -21,6 +21,7 @@ class AuthSourceLdap < AuthSource validates_length_of :account, :account_password, :base_dn, :maximum => 255, :allow_nil => true validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true validates_numericality_of :port, :only_integer => true + validate :custom_filter_should_be_valid_ldap_filter_syntax before_validation :strip_ldap_attributes @@ -101,10 +102,17 @@ class AuthSourceLdap < AuthSource ldap_con = initialize_ldap_con(self.account, self.account_password) login_filter = Net::LDAP::Filter.eq( self.attr_login, login ) object_filter = Net::LDAP::Filter.eq( "objectClass", "*" ) - attrs = {} + custom_ldap_filter = custom_filter_to_ldap - ldap_con.search( :base => self.base_dn, - :filter => object_filter & login_filter, + if custom_ldap_filter.present? + search_filters = object_filter & login_filter & custom_ldap_filter + else + search_filters = object_filter & login_filter + end + attrs = {} + + ldap_con.search( :base => self.base_dn, + :filter => search_filters, :attributes=> search_attributes) do |entry| if onthefly_register? @@ -119,6 +127,27 @@ class AuthSourceLdap < AuthSource attrs end + def custom_filter_to_ldap + return nil unless custom_filter.present? + + begin + return Net::LDAP::Filter.construct(custom_filter) + rescue Net::LDAP::LdapError # Filter syntax error + logger.debug "LDAP custom filter syntax error for: #{custom_filter}" if logger && logger.debug? + return nil + end + end + + def custom_filter_should_be_valid_ldap_filter_syntax + return true unless custom_filter.present? + + begin + return Net::LDAP::Filter.construct(custom_filter) + rescue Net::LDAP::LdapError # Filter syntax error + errors.add(:custom_filter, :invalid) + end + end + def self.get_attr(entry, attr_name) if !attr_name.blank? entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name] diff --git a/app/views/ldap_auth_sources/_form.rhtml b/app/views/ldap_auth_sources/_form.rhtml index 9ffffafc..8699a2cd 100644 --- a/app/views/ldap_auth_sources/_form.rhtml +++ b/app/views/ldap_auth_sources/_form.rhtml @@ -25,6 +25,9 @@

<%= check_box 'auth_source', 'onthefly_register' %>

+ +

+<%= text_field 'auth_source', 'custom_filter', :size => 60 %>

<%=l(:label_attribute_plural)%> diff --git a/app/views/wiki/annotate.rhtml b/app/views/wiki/annotate.rhtml index 097ed149..032ff057 100644 --- a/app/views/wiki/annotate.rhtml +++ b/app/views/wiki/annotate.rhtml @@ -1,6 +1,13 @@
-<%= link_to(l(:button_edit), {:action => 'edit', :id => @page.title}, :class => 'icon icon-edit') %> -<%= link_to(l(:label_history), {:action => 'history', :id => @page.title}, :class => 'icon icon-history') %> +<% if @editable %> +<%= link_to_if_authorized(l(:button_edit), {:action => 'edit', :id => @page.title}, :class => 'icon icon-edit', :accesskey => accesskey(:edit)) if @annotate.content.version == @page.content.version %> +<%= watcher_link(@page, User.current) %> +<%= link_to_if_authorized(l(:button_lock), {:action => 'protect', :id => @page.title, :protected => 1}, :method => :post, :class => 'icon icon-lock') if !@page.protected? %> +<%= link_to_if_authorized(l(:button_unlock), {:action => 'protect', :id => @page.title, :protected => 0}, :method => :post, :class => 'icon icon-unlock') if @page.protected? %> +<%= link_to_if_authorized(l(:button_rename), {:action => 'rename', :id => @page.title}, :class => 'icon icon-move') if @annotate.content.version == @page.content.version %> +<%= link_to_if_authorized(l(:button_delete), {:action => 'destroy', :id => @page.title}, :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %> +<% end %> +<%= link_to_if_authorized(l(:label_history), {:action => 'history', :id => @page.title}, :class => 'icon icon-history') %>

<%= h(@page.pretty_title) %>

diff --git a/config/locales/en.yml b/config/locales/en.yml index 625503e2..83b8be4a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -307,6 +307,7 @@ en: field_text: Text field field_visible: Visible field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text" + field_custom_filter: Custom LDAP filter setting_app_title: Application title setting_app_subtitle: Application subtitle diff --git a/db/migrate/20100217010520_add_custom_filter_to_auth_sources.rb b/db/migrate/20100217010520_add_custom_filter_to_auth_sources.rb new file mode 100644 index 00000000..8543c297 --- /dev/null +++ b/db/migrate/20100217010520_add_custom_filter_to_auth_sources.rb @@ -0,0 +1,9 @@ +class AddCustomFilterToAuthSources < ActiveRecord::Migration + def self.up + add_column :auth_sources, :custom_filter, :string + end + + def self.down + remove_column :auth_sources, :custom_filter + end +end diff --git a/public/htaccess.fcgi.example b/public/htaccess.fcgi.example index 3d3fb88b..6d34071d 100644 --- a/public/htaccess.fcgi.example +++ b/public/htaccess.fcgi.example @@ -1,55 +1,55 @@ -# General Apache options - - AddHandler fastcgi-script .fcgi - - - AddHandler fcgid-script .fcgi - - - AddHandler cgi-script .cgi - -Options +FollowSymLinks +ExecCGI - -# If you don't want Rails to look in certain directories, -# use the following rewrite rules so that Apache won't rewrite certain requests -# -# Example: -# RewriteCond %{REQUEST_URI} ^/notrails.* -# RewriteRule .* - [L] - -# Redirect all requests not available on the filesystem to Rails -# By default the cgi dispatcher is used which is very slow -# -# For better performance replace the dispatcher with the fastcgi one -# -# Example: -# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] -RewriteEngine On - -# If your Rails application is accessed via an Alias directive, -# then you MUST also set the RewriteBase in this htaccess file. -# -# Example: -# Alias /myrailsapp /path/to/myrailsapp/public -# RewriteBase /myrailsapp - -RewriteRule ^$ index.html [QSA] -RewriteRule ^([^.]+)$ $1.html [QSA] -RewriteCond %{REQUEST_FILENAME} !-f - - RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] - - - RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] - - - RewriteRule ^(.*)$ dispatch.cgi [QSA,L] - - -# In case Rails experiences terminal errors -# Instead of displaying this message you can supply a file here which will be rendered instead -# -# Example: -# ErrorDocument 500 /500.html - -ErrorDocument 500 "

Application error

Rails application failed to start properly" \ No newline at end of file +# General Apache options + + AddHandler fastcgi-script .fcgi + + + AddHandler fcgid-script .fcgi + + + AddHandler cgi-script .cgi + +Options +FollowSymLinks +ExecCGI + +# If you don't want Rails to look in certain directories, +# use the following rewrite rules so that Apache won't rewrite certain requests +# +# Example: +# RewriteCond %{REQUEST_URI} ^/notrails.* +# RewriteRule .* - [L] + +# Redirect all requests not available on the filesystem to Rails +# By default the cgi dispatcher is used which is very slow +# +# For better performance replace the dispatcher with the fastcgi one +# +# Example: +# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] +RewriteEngine On + +# If your Rails application is accessed via an Alias directive, +# then you MUST also set the RewriteBase in this htaccess file. +# +# Example: +# Alias /myrailsapp /path/to/myrailsapp/public +# RewriteBase /myrailsapp + +RewriteRule ^$ index.html [QSA] +RewriteRule ^([^.]+)$ $1.html [QSA] +RewriteCond %{REQUEST_FILENAME} !-f + + RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:{HTTP:Authorization},QSA,L] + + + RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:{HTTP:Authorization},QSA,L] + + + RewriteRule ^(.*)$ dispatch.cgi [E=X-HTTP_AUTHORIZATION:{HTTP:Authorization},QSA,L] + + +# In case Rails experiences terminal errors +# Instead of displaying this message you can supply a file here which will be rendered instead +# +# Example: +# ErrorDocument 500 /500.html + +ErrorDocument 500 "

Application error

Rails application failed to start properly" diff --git a/test/fixtures/ldap/slapd_config.ldif b/test/fixtures/ldap/slapd_config.ldif new file mode 100644 index 00000000..b08dddbc --- /dev/null +++ b/test/fixtures/ldap/slapd_config.ldif @@ -0,0 +1,27 @@ +# This is a configuration for a new database to test the ldap plugin. +# It is assumed that you have a ldap server up and running. Do not +# use this file on a new openldap installation. + +# Database settings +dn: olcDatabase=hdb,cn=config +objectClass: olcDatabaseConfig +objectClass: olcHdbConfig +olcDatabase: hdb +olcSuffix: dc=redmine,dc=org +# WARNING: Do not use a directory that already contains a ldap database. +# Each database has to be stored in a seperate directory. The directory +# /var/lib/ldap is the default directory on Debian. +olcDbDirectory: /var/lib/ldap/redmine +olcRootDN: cn=Manager,dc=redmine,dc=org +olcRootPW: secret +olcDbConfig: set_cachesize 0 2097152 0 +olcDbConfig: set_lk_max_objects 1500 +olcDbConfig: set_lk_max_locks 1500 +olcDbConfig: set_lk_max_lockers 1500 +olcDbIndex: objectClass eq +olcLastMod: TRUE +olcDbCheckpoint: 512 30 +olcAccess: to attrs=userPassword by dn="cn=Manager,dc=redmine,dc=org" write by anonymous auth by self write by * none +olcAccess: to attrs=shadowLastChange by self write by * read +olcAccess: to dn.base="" by * read +olcAccess: to * by dn="cn=Manager,dc=redmine,dc=org" write by * read diff --git a/test/fixtures/ldap/test-ldap.ldif b/test/fixtures/ldap/test-ldap.ldif index 7d9e109c..8a4ce5aa 100644 --- a/test/fixtures/ldap/test-ldap.ldif +++ b/test/fixtures/ldap/test-ldap.ldif @@ -4,39 +4,11 @@ objectClass: dcObject objectClass: organization o: redmine.org dc: redmine -structuralObjectClass: organization -entryUUID: 886f5fca-0a87-102e-8d06-67c361d9bd2d -creatorsName: -createTimestamp: 20090721211642Z -entryCSN: 20090721211642.955188Z#000000#000#000000 -modifiersName: -modifyTimestamp: 20090721211642Z - -dn: cn=admin,dc=redmine,dc=org -objectClass: simpleSecurityObject -objectClass: organizationalRole -cn: admin -description: LDAP administrator -userPassword:: e2NyeXB0fWlWTU9DcUt6WWxXRDI= -structuralObjectClass: organizationalRole -entryUUID: 88704e44-0a87-102e-8d07-67c361d9bd2d -creatorsName: -createTimestamp: 20090721211642Z -entryCSN: 20090721211642.961418Z#000000#000#000000 -modifiersName: -modifyTimestamp: 20090721211642Z dn: ou=Person,dc=redmine,dc=org ou: Person objectClass: top objectClass: organizationalUnit -structuralObjectClass: organizationalUnit -entryUUID: d39dd388-0c84-102e-82fa-dff86c63a7d6 -creatorsName: cn=admin,dc=redmine,dc=org -createTimestamp: 20090724100222Z -entryCSN: 20090724100222.924226Z#000000#000#000000 -modifiersName: cn=admin,dc=redmine,dc=org -modifyTimestamp: 20090724100222Z dn: uid=example1,ou=Person,dc=redmine,dc=org objectClass: posixAccount @@ -48,16 +20,9 @@ sn: One uid: example1 homeDirectory: /home/example1 cn: Example One -structuralObjectClass: inetOrgPerson -entryUUID: 285d304e-0c8a-102e-82fc-dff86c63a7d6 -creatorsName: cn=admin,dc=redmine,dc=org -createTimestamp: 20090724104032Z uidNumber: 0 mail: example1@redmine.org userPassword:: e1NIQX1mRXFOQ2NvM1lxOWg1WlVnbEQzQ1pKVDRsQnM9 -entryCSN: 20090724105945.375801Z#000000#000#000000 -modifiersName: cn=admin,dc=redmine,dc=org -modifyTimestamp: 20090724105945Z dn: uid=edavis,ou=Person,dc=redmine,dc=org objectClass: posixAccount @@ -68,15 +33,8 @@ givenName: Eric sn: Davis uid: edavis mail: edavis@littlestreamsoftware.com -structuralObjectClass: inetOrgPerson -entryUUID: 9c5f0502-0c8b-102e-82fe-dff86c63a7d6 -creatorsName: cn=admin,dc=redmine,dc=org -createTimestamp: 20090724105056Z homeDirectory: /home/edavis cn: Eric Davis uidNumber: 0 userPassword:: e1NIQX1mRXFOQ2NvM1lxOWg1WlVnbEQzQ1pKVDRsQnM9 -entryCSN: 20090724105937.734480Z#000000#000#000000 -modifiersName: cn=admin,dc=redmine,dc=org -modifyTimestamp: 20090724105937Z diff --git a/test/functional/account_controller_test.rb b/test/functional/account_controller_test.rb index 6c3ab70d..4ee25b14 100644 --- a/test/functional/account_controller_test.rb +++ b/test/functional/account_controller_test.rb @@ -47,6 +47,17 @@ class AccountControllerTest < ActionController::TestCase :content => /Invalid user or password/ end + def test_login + get :login + assert_template 'login' + end + + def test_login_with_logged_account + @request.session[:user_id] = 2 + get :login + assert_redirected_to home_url + end + if Object.const_defined?(:OpenID) def test_login_with_openid_for_existing_user diff --git a/test/unit/auth_source_ldap_test.rb b/test/unit/auth_source_ldap_test.rb index b383b906..0effa103 100644 --- a/test/unit/auth_source_ldap_test.rb +++ b/test/unit/auth_source_ldap_test.rb @@ -31,6 +31,20 @@ class AuthSourceLdapTest < ActiveSupport::TestCase assert_equal 'givenName', a.reload.attr_firstname end + context "validations" do + should "validate that custom_filter is a valid LDAP filter" do + @auth = AuthSourceLdap.new(:name => 'Validation', :host => 'localhost', :port => 389, :attr_login => 'login') + @auth.custom_filter = "(& (homeDirectory=*) (sn=O*" # Missing (( + assert @auth.invalid? + assert_equal "is invalid", @auth.errors.on(:custom_filter) + + @auth.custom_filter = "(& (homeDirectory=*) (sn=O*))" + assert @auth.valid? + assert_equal nil, @auth.errors.on(:custom_filter) + + end + end + if ldap_configured? context '#authenticate' do setup do @@ -69,6 +83,32 @@ class AuthSourceLdapTest < ActiveSupport::TestCase end end + context "using a valid custom filter" do + setup do + @auth.update_attributes(:custom_filter => "(& (homeDirectory=*) (sn=O*))") + end + + should "find a user who authenticates and matches the custom filter" do + assert_not_nil @auth.authenticate('example1', '123456') + end + + should "be nil for users who don't match the custom filter" do + assert_nil @auth.authenticate('edavis', '123456') + end + end + + context "using an invalid custom filter" do + setup do + # missing )) at the end + @auth.update_attributes(:custom_filter => "(& (homeDirectory=*) (sn=O*") + end + + should "skip the custom filter" do + assert_not_nil @auth.authenticate('example1', '123456') + assert_not_nil @auth.authenticate('edavis', '123456') + end + end + end else puts '(Test LDAP server not configured)'