Add git support to repository authentication script

Refactors the original PR from #1629
* dispersed authorization methods in SysController
* refactored and updated SysController specs
pull/3375/head
Philipp Tessenow 11 years ago committed by Oliver Günther
parent a5e625cb4a
commit 373473c563
  1. 38
      app/controllers/sys_controller.rb
  2. 140
      doc/subversion_and_git_integration.md
  3. 34
      extra/svn/OpenProjectAuthentication.pm
  4. 686
      spec/controllers/sys_controller_spec.rb

@ -91,19 +91,41 @@ class SysController < ActionController::Base
end
def repo_auth
@project = Project.find_by(identifier: params[:repository])
if (%w(GET PROPFIND REPORT OPTIONS).include?(params[:method]) &&
@authenticated_user.allowed_to?(:browse_repository, @project)) ||
@authenticated_user.allowed_to?(:commit_access, @project)
project = Project.find_by(identifier: params[:repository])
if authorized?(project, @authenticated_user)
render text: 'Access granted'
return
else
render text: 'Not allowed', status: 403 # default to deny
end
end
private
def authorized?(project, user)
if git_auth?
authorized_with_git?(project, user, params[:uri], params[:location])
else
authorized_with_subversion?(project, user, params[:method])
end
end
render text: 'Not allowed', status: 403 # default to deny
def git_auth?
params[:git_smart_http] == '1'
end
protected
def authorized_with_git?(project, user, uri, location)
read_only = !%r{^#{location}/*[^/]+/+(info/refs\?service=)?git\-receive\-pack$}o.match(uri)
(read_only && user.allowed_to?(:browse_repository, project)) ||
user.allowed_to?(:commit_access, project)
end
def authorized_with_subversion?(project, user, method)
read_only = %w(GET PROPFIND REPORT OPTIONS).include?(method)
(read_only && user.allowed_to?(:browse_repository, project)) ||
user.allowed_to?(:commit_access, project)
end
def check_enabled
User.current = nil

@ -1,8 +1,6 @@
# Subversion and Git Integration
OpenProject can (by default) browse subversion and git repositories.
But it does not serves them to git/svn clients.
OpenProject can (by default) browse subversion and git repositories, but it does not serve them to git/svn clients.
However, with the help of the apache webserver it is possible to serve repositories.
## Set-up
@ -20,9 +18,11 @@ It requires some apache modules to be enabled and installed:
<pre>
aptitude install libapache2-mod-perl2 libapache2-svn
a2enmod proxy proxy_http
a2enmod proxy proxy_http cgi
</pre>
Note that mod_cgi is required by git. If you use SVN only, you do not need that. The same thing applies vice versa with libapache2-svn - you do not need that if you use git only.
Also, the extra/svn/OpenProjectAuthentication.pm script needs to be in your apache perl path
(for example it might be sym-linked into /etc/apache2/Apache).
@ -33,66 +33,86 @@ On that page, enable the "Enable WS for repository management" setting and gener
for get to save the settings). We need that API key later in our apache config.
Find a place to store the repositories. For this guide we assume that you put your svn repositories in
/srv/openproject/svn . All things in that repository should be accessible by the apache system user and
by the user running your openproject server.
/srv/openproject/svn and your git repositories in /srv/openproject/git .
All things in that repository should be accessible by the apache system user and by the user running your openproject server.
## An example apache configuration
We provide an example apache configuration. Some details are explained inline as comments.
<pre>
# Load OpenProject per module used to authenticate requests against the user database.
# Be sure that the OpenProjectAuthentication.pm script in located in your perl path.
PerlSwitches -I/srv/www/perl-lib -T
PerlLoadModule Apache::OpenProjectAuthentication
&lt;VirtualHost *:80&gt;
ErrorLog /var/log/apache2/error
# The /sys endpoint is an internal API used to authenticate repository
# access requests. It shall not be reachable from remote.
&lt;LocationMatch "/sys"&gt;
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
&lt;/LocationMatch&gt;
# This fixes COPY for webdav over https
RequestHeader edit Destination ^https: http: early
# Serves svn repositories locates in /srv/openproject/svn via WebDAV
# It is secure with basic auth against the OpenProject user database.
&lt;Location /svn&gt;
DAV svn
SVNParentPath "/srv/openproject/svn"
DirectorySlash Off
AuthType Basic
AuthName "Secured Area"
Require valid-user
PerlAccessHandler Apache::Authn::OpenProject::access_handler
PerlAuthenHandler Apache::Authn::OpenProject::authen_handler
OpenProjectUrl 'http://127.0.0.1:3000'
OpenProjectApiKey 'REPLACE WITH REPOSITORY API KEY'
&lt;Limit OPTIONS PROPFIND GET REPORT MKACTIVITY PROPPATCH PUT CHECKOUT MKCOL MOVE COPY DELETE LOCK UNLOCK MERGE&gt;
Allow from all
&lt;/Limit&gt;
&lt;/Location&gt;
# Requires the apache module mod_proxy. Enable it with
# a2enmod proxy proxy_http
# See: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#ProxyPass
# Note that the ProxyPass with the longest path should be listed first, otherwise
# a shorter path may match and will do an early redirect (without looking for other
# more specific matching paths).
ProxyPass /svn !
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
&lt;/VirtualHost&gt;
</pre>
# Load OpenProject per module used to authenticate requests against the user database.
# Be sure that the OpenProjectAuthentication.pm script in located in your perl path.
PerlSwitches -I/srv/www/perl-lib -T
PerlLoadModule Apache::OpenProjectAuthentication
<VirtualHost *:80>
ErrorLog /var/log/apache2/error
# The /sys endpoint is an internal API used to authenticate repository
# access requests. It shall not be reachable from remote.
<LocationMatch "/sys">
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
</LocationMatch>
# This fixes COPY for webdav over https
RequestHeader edit Destination ^https: http: early
# Serves svn repositories locates in /srv/openproject/svn via WebDAV
# It is secure with basic auth against the OpenProject user database.
<Location /svn>
DAV svn
SVNParentPath "/srv/openproject/svn"
DirectorySlash Off
AuthType Basic
AuthName "Secured Area"
Require valid-user
PerlAccessHandler Apache::Authn::OpenProject::access_handler
PerlAuthenHandler Apache::Authn::OpenProject::authen_handler
OpenProjectUrl 'http://127.0.0.1:3000'
OpenProjectApiKey 'REPLACE WITH REPOSITORY API KEY'
<Limit OPTIONS PROPFIND GET REPORT MKACTIVITY PROPPATCH PUT CHECKOUT MKCOL MOVE COPY DELETE LOCK UNLOCK MERGE>
Allow from all
</Limit>
</Location>
# see https://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html for details
# needs mod_cgi to work -> a2enmod cgi
SetEnv GIT_PROJECT_ROOT /srv/openproject/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/lib/git-core/git-http-backend/
<Location /git>
Order allow,deny
Allow from all
AuthType Basic
AuthName "OpenProject GIT"
Require valid-user
PerlAccessHandler Apache::Authn::OpenProject::access_handler
PerlAuthenHandler Apache::Authn::OpenProject::authen_handler
OpenProjectGitSmartHttp yes
OpenProjectUrl 'http://127.0.0.1:3000'
OpenProjectApiKey 'REPLACE WITH REPOSITORY API KEY'
</Location>
# Requires the apache module mod_proxy. Enable it with
# a2enmod proxy proxy_http
# See: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#ProxyPass
# Note that the ProxyPass with the longest path should be listed first, otherwise
# a shorter path may match and will do an early redirect (without looking for other
# more specific matching paths).
ProxyPass /svn !
ProxyPass /git !
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
</VirtualHost>
## Automatically create repositories with reposman.rb
@ -114,3 +134,5 @@ ruby extra/svn/reposman.rb \
--verbose
</pre>
the downside (if you want to call it a downside) is that you have to choose which kind (svn or git) of repository you want to create.

@ -31,11 +31,27 @@ my @directives = (
req_override => OR_AUTHCFG,
args_how => TAKE1,
},
{
name => 'OpenProjectGitSmartHttp',
req_override => OR_AUTHCFG,
args_how => TAKE1,
},
);
sub OpenProjectUrl { set_val('OpenProjectUrl', @_); }
sub OpenProjectApiKey { set_val('OpenProjectApiKey', @_); }
sub OpenProjectGitSmartHttp {
my ($self, $params, $arg) = @_;
$arg = lc $arg;
if ($arg eq "yes" || $arg eq "true") {
$self->{OpenProjectGitSmartHttp} = 1;
} else {
$self->{OpenProjectGitSmartHttp} = 0;
}
}
sub trim {
my $string = shift;
$string =~ s/\s{2,}/ /g;
@ -93,8 +109,20 @@ sub is_access_allowed {
my $key = $cfg->{OpenProjectApiKey};
my $openproject_url = $cfg->{OpenProjectUrl} . '/sys/repo_auth';
my $openproject_unparsed_uri = $r->unparsed_uri;
my $openproject_location = $r->location;
my $openproject_git_smart_http = 0;
if (defined $cfg->{OpenProjectGitSmartHttp} and $cfg->{OpenProjectGitSmartHttp}) {
$openproject_git_smart_http = 1;
}
my $openproject_req = POST $openproject_url , [ repository => $identifier, key => $key, method => $method ];
my $openproject_req = POST $openproject_url , [
repository => $identifier,
key => $key,
method => $method,
location => $openproject_location,
uri => $openproject_unparsed_uri,
git_smart_http => $openproject_git_smart_http ];
$openproject_req->authorization_basic( $login, $password );
my $ua = LWP::UserAgent->new;
@ -106,8 +134,10 @@ sub is_access_allowed {
sub get_project_identifier {
my $r = shift;
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
my $location = $r->location;
my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
$location =~ s/\.git$// if (defined $cfg->{OpenProjectGitSmartHttp} and $cfg->{OpenProjectGitSmartHttp});
my ($identifier) = $r->uri =~ m{$location/*([^/.]+)};
$identifier;
}

@ -28,208 +28,568 @@
require 'spec_helper'
module OpenProjectRepositoryAuthenticationSpecs
describe SysController, type: :controller do
let(:commit_role) {
FactoryGirl.create(:role, permissions: [:commit_access,
:browse_repository])
}
let(:browse_role) { FactoryGirl.create(:role, permissions: [:browse_repository]) }
let(:guest_role) { FactoryGirl.create(:role, permissions: []) }
let(:valid_user_password) { 'Top Secret Password' }
let(:valid_user) {
FactoryGirl.create(:user,
login: 'johndoe',
password: valid_user_password,
password_confirmation: valid_user_password)
}
describe SysController, type: :controller do
let(:commit_role) {
FactoryGirl.create(:role, permissions: [:commit_access,
:browse_repository])
}
let(:browse_role) { FactoryGirl.create(:role, permissions: [:browse_repository]) }
let(:guest_role) { FactoryGirl.create(:role, permissions: []) }
let(:valid_user_password) { 'Top Secret Password' }
let(:valid_user) {
FactoryGirl.create(:user,
login: 'johndoe',
password: valid_user_password,
password_confirmation: valid_user_password)
}
before(:each) do
FactoryGirl.create(:non_member, permissions: [:browse_repository])
DeletedUser.first # creating it first in order to avoid problems with should_receive
random_project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [browse_role],
project: random_project)
allow(Setting).to receive(:sys_api_key).and_return('12345678')
allow(Setting).to receive(:sys_api_enabled?).and_return(true)
allow(Setting).to receive(:repository_authentication_caching_enabled?).and_return(true)
end
before(:each) do
FactoryGirl.create(:non_member, permissions: [:browse_repository])
DeletedUser.first # creating it first in order to avoid problems with should_receive
random_project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [browse_role],
project: random_project)
allow(Setting).to receive(:sys_api_key).and_return('12345678')
allow(Setting).to receive(:sys_api_enabled?).and_return(true)
allow(Setting).to receive(:repository_authentication_caching_enabled?).and_return(true)
end
describe 'svn' do
describe 'repo_auth' do
context 'for valid login, but no access to repo_auth' do
before(:each) do
@key = Setting.sys_api_key
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
post 'repo_auth',
key: @key,
repository: 'without-access',
method: 'GET'
end
describe '#repo_auth', 'for valid login, but no access to repo_auth' do
before(:each) do
@key = Setting.sys_api_key
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(valid_user.login, valid_user_password)
post 'repo_auth', key: @key, repository: 'without-access', method: 'GET'
it 'should respond 403 not allowed' do
expect(response.code).to eq('403')
expect(response.body).to eq('Not allowed')
end
end
it 'should respond 403 not allowed' do
expect(response.code).to eq('403')
expect(response.body).to eq('Not allowed')
end
end
context 'for valid login and user has read permission (role reporter) for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [browse_role],
project: @project)
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
end
describe '#repo_auth', 'for valid login and user has browse repository permission (role reporter) for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [browse_role],
project: @project)
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(valid_user.login, valid_user_password)
end
it 'should respond 200 okay dokay for GET' do
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'GET'
expect(response.code).to eq('200')
end
it 'should respond 403 not allowed for POST' do
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'POST'
it 'should respond 200 okay dokay for GET' do
post 'repo_auth', key: @key, repository: @project.identifier, method: 'GET'
expect(response.code).to eq('200')
expect(response.code).to eq('403')
end
end
it 'should respond 403 not allowed for POST' do
post 'repo_auth', key: @key, repository: @project.identifier, method: 'POST'
expect(response.code).to eq('403')
context 'for valid login and user has rw permission (role developer) for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [commit_role],
project: @project)
valid_user.save
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
end
it 'should respond 200 okay dokay for GET' do
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'GET'
expect(response.code).to eq('200')
end
it 'should respond 200 okay dokay for POST' do
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'POST'
expect(response.code).to eq('200')
end
end
end
describe '#repo_auth', 'for valid login and user has commit access permission (role developer) for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [commit_role],
project: @project)
valid_user.save
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(valid_user.login, valid_user_password)
context 'for invalid login and user has role manager for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [commit_role],
project: @project)
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password + 'made invalid'
)
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'GET'
end
it 'should respond 401 auth required' do
expect(response.code).to eq('401')
end
end
it 'should respond 200 okay dokay for GET' do
post 'repo_auth', key: @key, repository: @project.identifier, method: 'GET'
expect(response.code).to eq('200')
context 'for valid login and user is not member for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'GET'
end
it 'should respond 403 not allowed' do
expect(response.code).to eq('403')
end
end
it 'should respond 200 okay dokay for POST' do
post 'repo_auth', key: @key, repository: @project.identifier, method: 'POST'
expect(response.code).to eq('200')
context 'for valid login and project is public' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: true)
random_project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [browse_role],
project: random_project)
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'GET'
end
it 'should respond 200 OK' do
expect(response.code).to eq('200')
end
end
end
describe '#repo_auth', 'for invalid login and user has role manager for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [commit_role],
project: @project)
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(valid_user.login, valid_user_password + 'made invalid')
post 'repo_auth', key: @key, repository: @project.identifier, method: 'GET'
context 'for invalid credentials' do
before(:each) do
@key = Setting.sys_api_key
post 'repo_auth',
key: @key,
repository: 'any-repo',
method: 'GET'
end
it 'should respond 401 auth required' do
expect(response.code).to eq('401')
expect(response.body).to eq('Authorization required')
end
end
it 'should respond 401 auth required' do
expect(response.code).to eq('401')
context 'for invalid api key' do
before(:each) do
@key = 'invalid'
end
it 'should respond 403 for valid username/password' do
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
post 'repo_auth',
key: @key,
repository: 'any-repo',
method: 'GET'
expect(response.code).to eq('403')
expect(response.body)
.to eq('Access denied. Repository management WS is disabled or key is invalid.')
end
it 'should respond 403 for invalid username/password' do
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
'invalid',
'invalid'
)
post 'repo_auth',
key: @key,
repository: 'any-repo',
method: 'GET'
expect(response.code).to eq('403')
expect(response.body)
.to eq('Access denied. Repository management WS is disabled or key is invalid.')
end
end
end
end
describe '#repo_auth', 'for valid login and user is not member for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(valid_user.login, valid_user_password)
post 'repo_auth', key: @key, repository: @project.identifier, method: 'GET'
end
describe 'git' do
describe 'repo_auth' do
context 'for valid login, but no access to repo_auth' do
before(:each) do
@key = Setting.sys_api_key
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
post 'repo_auth',
key: @key,
repository: 'without-access',
method: 'GET',
git_smart_http: '1',
uri: '/git',
location: '/git'
end
it 'should respond 403 not allowed' do
expect(response.code).to eq('403')
it 'should respond 403 not allowed' do
expect(response.code).to eq('403')
expect(response.body).to eq('Not allowed')
end
end
end
describe '#repo_auth', 'for valid login and project is public' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: true)
context 'for valid login and user has read permission (role reporter) for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [browse_role],
project: @project)
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
end
it 'should respond 200 okay dokay for read-only access' do
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'GET',
git_smart_http: '1',
uri: '/git',
location: '/git'
expect(response.code).to eq('200')
end
random_project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [browse_role],
project: random_project)
it 'should respond 403 not allowed for write (push)' do
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'POST',
git_smart_http: '1',
uri: "/git/#{@project.identifier}/git-receive-pack",
location: '/git'
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(valid_user.login, valid_user_password)
post 'repo_auth', key: @key, repository: @project.identifier, method: 'GET'
expect(response.code).to eq('403')
end
end
it 'should respond 200 OK' do
expect(response.code).to eq('200')
context 'for valid login and user has rw permission (role developer) for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [commit_role],
project: @project)
valid_user.save
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
end
it 'should respond 200 okay dokay for GET' do
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'GET',
git_smart_http: '1',
uri: '/git',
location: '/git'
expect(response.code).to eq('200')
end
it 'should respond 200 okay dokay for POST' do
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'POST',
git_smart_http: '1',
uri: "/git/#{@project.identifier}/git-receive-pack",
location: '/git'
expect(response.code).to eq('200')
end
end
end
describe '#repo_auth', 'for invalid credentials' do
before(:each) do
@key = Setting.sys_api_key
post 'repo_auth', key: @key, repository: 'any-repo', method: 'GET'
context 'for invalid login and user has role manager for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [commit_role],
project: @project)
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password + 'made invalid'
)
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'GET',
git_smart_http: '1',
uri: '/git',
location: '/git'
end
it 'should respond 401 auth required' do
expect(response.code).to eq('401')
end
end
it 'should respond 401 auth required' do
expect(response.code).to eq('401')
expect(response.body).to eq('Authorization required')
context 'for valid login and user is not member for project' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: false)
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'GET',
git_smart_http: '1',
uri: '/git',
location: '/git'
end
it 'should respond 403 not allowed' do
expect(response.code).to eq('403')
end
end
end
describe '#repo_auth', 'for invalid api key' do
before(:each) do
@key = 'invalid'
context 'for valid login and project is public' do
before(:each) do
@key = Setting.sys_api_key
@project = FactoryGirl.create(:project, is_public: true)
random_project = FactoryGirl.create(:project, is_public: false)
@member = FactoryGirl.create(:member,
user: valid_user,
roles: [browse_role],
project: random_project)
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
post 'repo_auth',
key: @key,
repository: @project.identifier,
method: 'GET',
git_smart_http: '1',
uri: '/git',
location: '/git'
end
it 'should respond 200 OK' do
expect(response.code).to eq('200')
end
end
it 'should respond 403 for valid username/password' do
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(valid_user.login, valid_user_password)
post 'repo_auth', key: @key, repository: 'any-repo', method: 'GET'
expect(response.code).to eq('403')
expect(response.body).to eq('Access denied. Repository management WS is disabled or key is invalid.')
context 'for invalid credentials' do
before(:each) do
@key = Setting.sys_api_key
post 'repo_auth',
key: @key,
repository: 'any-repo',
method: 'GET',
git_smart_http: '1',
uri: '/git',
location: '/git'
end
it 'should respond 401 auth required' do
expect(response.code).to eq('401')
expect(response.body).to eq('Authorization required')
end
end
it 'should respond 403 for invalid username/password' do
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('invalid', 'invalid')
post 'repo_auth', key: @key, repository: 'any-repo', method: 'GET'
expect(response.code).to eq('403')
expect(response.body).to eq('Access denied. Repository management WS is disabled or key is invalid.')
context 'for invalid api key' do
before(:each) do
@key = 'invalid'
end
it 'should respond 403 for valid username/password' do
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
valid_user.login,
valid_user_password
)
post 'repo_auth',
key: @key,
repository: 'any-repo',
method: 'GET',
git_smart_http: '1',
uri: '/git',
location: '/git'
expect(response.code).to eq('403')
expect(response.body)
.to eq('Access denied. Repository management WS is disabled or key is invalid.')
end
it 'should respond 403 for invalid username/password' do
request.env['HTTP_AUTHORIZATION'] =
ActionController::HttpAuthentication::Basic.encode_credentials(
'invalid',
'invalid'
)
post 'repo_auth',
key: @key,
repository: 'any-repo',
method: 'GET',
git_smart_http: '1',
uri: '/git',
location: '/git'
expect(response.code).to eq('403')
expect(response.body)
.to eq('Access denied. Repository management WS is disabled or key is invalid.')
end
end
end
end
before(:each) do
Rails.cache.clear
allow(Rails.cache).to receive(:kind_of?).with(anything).and_return(false)
end
describe '#cached_user_login' do
let(:cache_key) {
OpenProject::RepositoryAuthentication::CACHE_PREFIX +
Digest::SHA1.hexdigest("#{valid_user.login}#{valid_user_password}")
}
let(:cache_expiry) { OpenProject::RepositoryAuthentication::CACHE_EXPIRES_AFTER }
it 'should call user_login only once when called twice' do
expect(controller).to receive(:user_login).once.and_return(valid_user)
2.times { controller.send(:cached_user_login, valid_user.login, valid_user_password) }
end
before(:each) do
Rails.cache.clear
allow(Rails.cache).to receive(:kind_of?).with(anything).and_return(false)
it 'should return the same as user_login for valid creds' do
expect(controller.send(:cached_user_login, valid_user.login, valid_user_password))
.to eq(controller.send(:user_login, valid_user.login, valid_user_password))
end
describe '#cached_user_login' do
let(:cache_key) {
OpenProject::RepositoryAuthentication::CACHE_PREFIX +
Digest::SHA1.hexdigest("#{valid_user.login}#{valid_user_password}")
}
let(:cache_expiry) { OpenProject::RepositoryAuthentication::CACHE_EXPIRES_AFTER }
it 'should return the same as user_login for invalid creds' do
expect(controller.send(:cached_user_login, 'invalid', 'invalid'))
.to eq(controller.send(:user_login, 'invalid', 'invalid'))
end
it 'should call user_login only once when called twice' do
expect(controller).to receive(:user_login).once.and_return(valid_user)
2.times { controller.send(:cached_user_login, valid_user.login, valid_user_password) }
end
it 'should use cache' do
# allow the cache to return something reasonable for
# other requests, while ensuring that it is not queried
# with the cache key in question
# unfortunately, and_call_original currently fails
allow(Rails.cache).to receive(:fetch) do |*args|
expect(args.first).not_to eq(cache_key)
it 'should return the same as user_login for valid creds' do
expect(controller.send(:cached_user_login, valid_user.login, valid_user_password))
.to eq(controller.send(:user_login, valid_user.login, valid_user_password))
name = args.first.split('/').last
Marshal.dump(Setting.send(:find_or_default, name).value)
end
# Rails.cache.should_receive(:fetch).with(anything).and_call_original
expect(Rails.cache).to receive(:fetch).with(cache_key, expires_in: cache_expiry) \
.and_return(Marshal.dump(valid_user.id.to_s))
controller.send(:cached_user_login, valid_user.login, valid_user_password)
end
it 'should return the same as user_login for invalid creds' do
expect(controller.send(:cached_user_login, 'invalid', 'invalid'))
.to eq(controller.send(:user_login, 'invalid', 'invalid'))
describe 'with caching disabled' do
before do
allow(Setting).to receive(:repository_authentication_caching_enabled?).and_return(false)
end
it 'should use cache' do
it 'should not use a cache' do
# allow the cache to return something reasonable for
# other requests, while ensuring that it is not queried
# with the cache key in question
#
# unfortunately, and_call_original currently fails
allow(Rails.cache).to receive(:fetch) do |*args|
expect(args.first).not_to eq(cache_key)
@ -237,32 +597,8 @@ module OpenProjectRepositoryAuthenticationSpecs
name = args.first.split('/').last
Marshal.dump(Setting.send(:find_or_default, name).value)
end
# Rails.cache.should_receive(:fetch).with(anything).and_call_original
expect(Rails.cache).to receive(:fetch).with(cache_key, expires_in: cache_expiry) \
.and_return(Marshal.dump(valid_user.id.to_s))
controller.send(:cached_user_login, valid_user.login, valid_user_password)
end
describe 'with caching disabled' do
before do
allow(Setting).to receive(:repository_authentication_caching_enabled?).and_return(false)
end
it 'should not use a cache' do
# allow the cache to return something reasonable for
# other requests, while ensuring that it is not queried
# with the cache key in question
#
# unfortunately, and_call_original currently fails
allow(Rails.cache).to receive(:fetch) do |*args|
expect(args.first).not_to eq(cache_key)
name = args.first.split('/').last
Marshal.dump(Setting.send(:find_or_default, name).value)
end
controller.send(:cached_user_login, valid_user.login, valid_user_password)
end
controller.send(:cached_user_login, valid_user.login, valid_user_password)
end
end

Loading…
Cancel
Save