diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 0000000..7f73a63 --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,51 @@ +name: Test +on: + push: + branches-ignore: [debian] + +jobs: + test: + name: ${{ matrix.redmine }} on ${{ matrix.ruby }} + runs-on: ubuntu-latest + + strategy: + matrix: + include: + - ruby: "2.7" + redmine: "4.2.1" + - ruby: "2.6" + redmine: "4.1.3" + fail-fast: false + + env: + BUNDLE_JOBS: 4 + BUNDLE_PATH: ${{ github.workspace }}/vendor/bundle + BUNDLE_RETRY: 3 + BUNDLE_WITHOUT: development + REDMINE_VERSION: ${{ matrix.redmine }} + + steps: + - uses: actions/checkout@master + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + + - name: setup cache + uses: actions/cache@v1 + with: + path: vendor + # Updating v to v clear the cache + key: v4-ruby-${{ matrix.ruby }}-redmine-${{ matrix.redmine }} + + - name: install ruby dependencies + run: bundle install + + - name: setup redmine ${{ matrix.redmine }} + run: ./redmine update + + - name: run prepare tests + run: ./redmine exec bundle exec rake db:test:prepare + + - name: run plugins tests + run: ./redmine exec bundle exec rake redmine:plugins:test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6829c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.bundle +Gemfile.lock +vendor diff --git a/Gemfile b/Gemfile index 3b6d3e0..46f7131 100644 --- a/Gemfile +++ b/Gemfile @@ -1 +1,4 @@ -gem 'netaddr' \ No newline at end of file +source 'https://rubygems.org' +# as version 2.0. is A complete rewrite and totally incompatible with 1.x. My main motivation now is to reduce bug reports resulting from the poor code quality of 1.x. +# gem 'netaddr' +gem 'netaddr', '< 2.0.0' diff --git a/README.md b/README.md new file mode 100644 index 0000000..9bbbf60 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Redmine Access filters plugin + +This plugin allows you to deny web or REST API access from specific IP addresses for specific users and groups + +## Requirements + +* Redmine 2.3+ +* Ruby 1.8.7, 1.9.3, 2.0.0 + +## Installation and Setup + +* Follow the Redmine plugin installation steps at http://www.redmine.org/wiki/redmine/Plugins +* Restart your Redmine web servers (e.g. mongrel, thin, passenger) + +## License + +This plugin is licensed under the GNU GPL v2. See COPYRIGHT.txt and GPL.txt for details. + +## Sponsorship + +Pluing development was sponsored by MEDEVIT (http://www.medevit.at) + +## Testing + +Checkout a redmine version, clone this plugin under plugins/redmine_acess_filters +and run + + export RAILS_ENV=test + bundle install + bundle exec rake redmine:plugins NAME=redmine_access_filters + bundle exec rake db:test:prepare + bundle exec rake redmine:plugins:test + # run a single test + bundle exec ruby plugins/redmine_access_filters/test/functional/access_filter_test.rb -n test_works_if_no_rules_exist + +## github Actions + +For unknown reason Niklaus was unable to make the github actions pass (August 21). +Running the tests locally worked fine, but on github I got always the error +messages containing `redirect to - <%= reorder_links('access_filter', {:action => 'update', :id => access_filter }, :put) %> + <%= reorder_handle(access_filter, :param => :put) %> <%= link_to l(:label_access_filters_edit), edit_access_filter_path(access_filter), :class => 'icon icon-edit' %> <%= link_to l(:label_access_filters_delete), access_filter_path(access_filter), :method => :delete, :confirm => l(:label_access_filters_delete_confirmation), :class => 'icon icon-del' %> - \ No newline at end of file + diff --git a/db/migrate/001_create_access_filters.rb b/db/migrate/001_create_access_filters.rb index 9e23dfd..da0fb29 100644 --- a/db/migrate/001_create_access_filters.rb +++ b/db/migrate/001_create_access_filters.rb @@ -1,4 +1,4 @@ -class CreateAccessFilters < ActiveRecord::Migration +class CreateAccessFilters < ActiveRecord::Migration[5.2] def change create_table :access_filters do |t| t.references :user @@ -7,4 +7,4 @@ def change t.text :cidrs end end -end \ No newline at end of file +end diff --git a/db/migrate/002_add_active_flag_to_access_filters.rb b/db/migrate/002_add_active_flag_to_access_filters.rb index 59f7794..456f2ac 100644 --- a/db/migrate/002_add_active_flag_to_access_filters.rb +++ b/db/migrate/002_add_active_flag_to_access_filters.rb @@ -1,5 +1,5 @@ -class AddActiveFlagToAccessFilters < ActiveRecord::Migration +class AddActiveFlagToAccessFilters < ActiveRecord::Migration[5.2] def change add_column :access_filters, :active, :boolean, :default => true end -end \ No newline at end of file +end diff --git a/db/migrate/003_add_position_to_access_filters.rb b/db/migrate/003_add_position_to_access_filters.rb index 94b2932..65a06d0 100644 --- a/db/migrate/003_add_position_to_access_filters.rb +++ b/db/migrate/003_add_position_to_access_filters.rb @@ -1,5 +1,5 @@ -class AddPositionToAccessFilters < ActiveRecord::Migration +class AddPositionToAccessFilters < ActiveRecord::Migration[5.2] def change add_column :access_filters, :position, :integer, :default => 0 end -end \ No newline at end of file +end diff --git a/db/migrate/004_add_polymorphism_to_access_filters.rb b/db/migrate/004_add_polymorphism_to_access_filters.rb index 11f06ab..2d7e2b6 100644 --- a/db/migrate/004_add_polymorphism_to_access_filters.rb +++ b/db/migrate/004_add_polymorphism_to_access_filters.rb @@ -1,7 +1,7 @@ -class AddPolymorphismToAccessFilters < ActiveRecord::Migration +class AddPolymorphismToAccessFilters < ActiveRecord::Migration[5.2] def change add_column :access_filters, :owner_type, :string add_column :access_filters, :owner_id, :integer remove_column :access_filters, :user_id end -end \ No newline at end of file +end diff --git a/init.rb b/init.rb index 857ea15..a83f265 100644 --- a/init.rb +++ b/init.rb @@ -2,13 +2,13 @@ name 'Redmine Access Filters plugin' author 'Redmine CRM' description 'Allows setting access filters for API and regular browser access per user' - version '0.0.1' - url 'http://redminecrm.com' - author_url 'mailto:support@redminecrm.com' + version '1.0.1' + url 'https://github.com/ngiger/redmine_access_filters' + author_url 'mailto:niklaus.giger@member.fsf.org' menu :admin_menu, :access_filters, {:controller => 'access_filters', :action => 'index'}, :caption => :label_access_filters_plural end -require 'redmine_access_filters' \ No newline at end of file +require 'redmine_access_filters' diff --git a/lib/redmine_access_filters.rb b/lib/redmine_access_filters.rb index 11e5385..15c4368 100644 --- a/lib/redmine_access_filters.rb +++ b/lib/redmine_access_filters.rb @@ -1,2 +1,2 @@ require 'redmine_access_filters/patches/application_controller_patch' -require 'redmine_access_filters/hooks/view_layouts_hook' \ No newline at end of file +require 'redmine_access_filters/hooks/view_layouts_hook' diff --git a/lib/redmine_access_filters/patches/application_controller_patch.rb b/lib/redmine_access_filters/patches/application_controller_patch.rb index 103e26d..9891102 100644 --- a/lib/redmine_access_filters/patches/application_controller_patch.rb +++ b/lib/redmine_access_filters/patches/application_controller_patch.rb @@ -5,7 +5,7 @@ def self.included(base) base.send(:include, InstanceMethods) base.class_eval do - before_filter :apply_access_filters + before_action :apply_access_filters end require "#{Rails.root}/plugins/redmine_access_filters/app/models/access_filter" end @@ -55,4 +55,4 @@ def access_filters unless ApplicationController.included_modules.include?(RedmineAccessFilters::Patches::ApplicationControllerPatch) ApplicationController.send(:include, RedmineAccessFilters::Patches::ApplicationControllerPatch) -end \ No newline at end of file +end diff --git a/redmine b/redmine new file mode 100755 index 0000000..144aff9 --- /dev/null +++ b/redmine @@ -0,0 +1,53 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +REDMINE_VERSION = ENV['REDMINE_VERSION'] = ENV['REDMINE_VERSION'] || ENV['VERSION'] || '4.1.3' +REDMINE_PATH = ENV['REDMINE_PATH'] = File.expand_path("../vendor/redmine/#{REDMINE_VERSION}/", __FILE__) +REDMINE_EXEC = ENV['REDMINE_EXEC'] = File.expand_path(__FILE__) +PLUGIN_PATH = ENV['PLUGIN_PATH'] = File.expand_path(__dir__) + +require 'pathname' + +SCRIPT_PATH = Pathname.new(__FILE__).expand_path.parent.join('scripts') + +def help + <<-HELP.gsub(/^\s{4}/, '') + Available commands: + #{available_commands.join("\n ")} + HELP +end + +def available_commands + SCRIPT_PATH.children + .select(&:file?) + .select(&:executable?) + .map(&:basename) + .map(&:to_s) +end + +def handle_command(cmd, _argv) + unless ARGV[0] =~ /\A(\w+)\z/ + warn "Invalid command: #{ARGV[0]}" + warn help + Kernel.exit 1 + end + + command = SCRIPT_PATH.join(cmd.to_s) + + if command.file? && command.executable? + Kernel.exec command.to_s, *ARGV[1..-1] + else + warn "Unknown command: #{ARGV[0]}" + warn help + Kernel.exit 1 + end +end + +case ARGV[0] + when '-h', '--help', nil + $stdout.puts help + when 'exec' + Dir.chdir(REDMINE_PATH) { Kernel.exec(*ARGV[1..-1]) } + else + handle_command ARGV[0], ARGV[1..-1] +end diff --git a/scripts/install b/scripts/install new file mode 100755 index 0000000..537619b --- /dev/null +++ b/scripts/install @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +set -e + +[ -z "$PLUGIN_PATH" ] && exit 1 +[ -z "$REDMINE_EXEC" ] && exit 1 +[ -z "$REDMINE_PATH" ] && exit 1 +[ -z "$REDMINE_VERSION" ] && exit 1 + +mkdir -p "${REDMINE_PATH}" +mkdir -p tmp log + +case ${REDMINE_VERSION} in + master) + REDMINE_SOURCE="http://svn.redmine.org/redmine/trunk" + ;; + + *) + REDMINE_SOURCE="http://svn.redmine.org/redmine/tags/${REDMINE_VERSION}" + ;; +esac + +(set -x; svn export --quiet --force "${REDMINE_SOURCE}" "${REDMINE_PATH}") + +echo "Symlink plugin, assets and specs..." + +mkdir -p ${REDMINE_PATH}/public/plugin_assets + +if [ ! -L "${REDMINE_PATH}/plugins/redmine_access_filters" ]; then + ln -s "${PLUGIN_PATH}" "${REDMINE_PATH}/plugins/redmine_access_filters" +fi + +if [ ! -L "${REDMINE_PATH}/spec" ]; then + ln -s "${PLUGIN_PATH}/spec" "${REDMINE_PATH}/spec" +fi + +if [ ! -L "${REDMINE_PATH}/test/fixtures/rdb" ]; then + ln -s "${PLUGIN_PATH}/spec/fixtures" "${REDMINE_PATH}/test/fixtures/rdb" +fi + +if [ ! -L "${REDMINE_PATH}/public/plugin_assets/redmine_access_filters_linked" ]; then + ln -s "${PLUGIN_PATH}/assets" "${REDMINE_PATH}/public/plugin_assets/redmine_access_filters_linked" +fi + +echo "Copy database configuration..." + +(set -x; cp -v $PLUGIN_PATH/test/database.yml "$REDMINE_PATH"/config/ ) + +pushd "${REDMINE_PATH}" + +(set -x; bundle config set --local without 'rmagick' ${BUNDLE_OPTS}) +(set -x; bundle install --without rmagick --jobs=3 --retry=3 ${BUNDLE_OPTS}) +(set -x; bundle exec rake generate_secret_token db:create:all db:migrate redmine:plugins:migrate db:test:prepare) + +popd diff --git a/scripts/update b/scripts/update new file mode 100755 index 0000000..ba4b78c --- /dev/null +++ b/scripts/update @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -e + +[ -z "$PLUGIN_PATH" ] && exit 1 +[ -z "$REDMINE_EXEC" ] && exit 1 +[ -z "$REDMINE_PATH" ] && exit 1 +[ -z "$REDMINE_VERSION" ] && exit 1 + +if [ ! -d "${REDMINE_PATH}" ]; then + exec $REDMINE_EXEC install +fi + +pushd "${REDMINE_PATH}" + +if [ "${REDMINE_VERSION}" == "master" ]; then + (set -x; svn export --quiet --force "${REDMINE_SOURCE}" .) +fi + +(set -x; bundle update --jobs=3 --retry=3) +pwd +(set -x; bundle exec rake db:create:all db:migrate redmine:plugins:migrate db:test:prepare) diff --git a/test/database.yml b/test/database.yml new file mode 100644 index 0000000..df32083 --- /dev/null +++ b/test/database.yml @@ -0,0 +1,13 @@ +# Line indentation must be 2 spaces (no tabs). +production: + adapter: sqlite3 + database: db/redmine_production.sqlite3 + +development: + adapter: sqlite3 + database: db/redmine_development.sqlite3 + +test: + adapter: sqlite3 + database: db/redmine_test.sqlite3 + diff --git a/test/functional/access_filter_test.rb b/test/functional/access_filter_test.rb index e8d8992..f777fb8 100644 --- a/test/functional/access_filter_test.rb +++ b/test/functional/access_filter_test.rb @@ -17,13 +17,10 @@ def setup AccessFilter.destroy_all @controller = IssuesController.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new @request.session[:user_id] = 1 @request.env['REMOTE_ADDR'] = '192.168.0.1' @group = Group.create(:name => 'Test group') @group.users << User.find(1) - end def test_works_if_no_rules_exist @@ -45,7 +42,7 @@ def test_prevents_http_access_if_specified def test_prevents_api_access_if_specified AccessFilter.create(:owner_id => "User|1", :web => false, :api => true, :cidrs => 'any') with_settings :rest_api_enabled => '1' do - get :index, :format => :json, :key => User.find(1).api_key + get :index, :params => { :format => :json, :key => User.find(1).api_key} end assert_response 403 end @@ -77,7 +74,7 @@ def test_does_not_allow_http_access_if_ip_mismatched_for_subnet def test_allows_api_access_if_ip_matched AccessFilter.create(:owner_id => "User|1", :web => false, :api => false, :cidrs => '192.168.0.1') with_settings :rest_api_enabled => '1' do - get :index, :format => :json, :key => User.find(1).api_key + get :index, :params => {:format => :json, :key => User.find(1).api_key} end assert_response :success @@ -86,7 +83,7 @@ def test_allows_api_access_if_ip_matched def test_allows_api_access_if_ip_matched_for_subnet AccessFilter.create(:owner_id => "User|1", :web => false, :api => false, :cidrs => '192.168.0.0/24') with_settings :rest_api_enabled => '1' do - get :index, :format => :json, :key => User.find(1).api_key + get :index, :params => {:format => :json, :key => User.find(1).api_key} end assert_response :success @@ -95,7 +92,7 @@ def test_allows_api_access_if_ip_matched_for_subnet def test_does_not_allow_api_access_if_ip_mismatched_for_subnet AccessFilter.create(:owner_id => "User|1", :web => false, :api => false, :cidrs => '192.168.1.0/24') with_settings :rest_api_enabled => '1' do - get :index, :format => :json, :key => User.find(1).api_key + get :index, :params => {:format => :json, :key => User.find(1).api_key} end assert_response 403 end @@ -132,4 +129,4 @@ def test_access_filter_with_empty_cidrs_field_treated_as_any assert_response 200 end -end \ No newline at end of file +end