@@ -1216,5 +1246,28 @@ $('.extend-form').click(function(e) {
$(tgt).append(form);
});
+{% if config.get('ENABLE_GIVE_PROJECTS', True)
+ and repo.user.user == g.fas_user.username
+ and not repo.is_fork %}
+$('#user').selectize({
+ valueField: 'user',
+ labelField: 'user',
+ searchField: 'user',
+ maxItems: 1,
+ create: false,
+ load: function(query, callback) {
+ if (!query.length) return callback();
+ $.getJSON(
+ "{{ url_for('api_ns.api_users') }}", {
+ pattern: query.term
+ },
+ function( data ) {
+ callback( data.users.map(function(x) { return { user: x }; }) );
+ }
+ );
+ }
+});
+{% endif %}
+
{% endblock %}
diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py
index edd6237..b304a52 100644
--- a/pagure/ui/repo.py
+++ b/pagure/ui/repo.py
@@ -2560,3 +2560,60 @@ def delete_report(repo, username=None, namespace=None):
return flask.redirect(flask.url_for(
'view_settings', username=username, repo=repo.name,
namespace=namespace))
+
+
+@APP.route('/
/give', methods=['POST'])
+@APP.route('///give', methods=['POST'])
+@APP.route('/fork///give', methods=['POST'])
+@APP.route(
+ '/fork////give',
+ methods=['POST'])
+@login_required
+def give_project(repo, username=None, namespace=None):
+ """ Give a project to someone else.
+ """
+ if not APP.config.get('ENABLE_GIVE_PROJECTS', True):
+ flask.abort(404)
+
+ if admin_session_timedout():
+ flask.flash('Action canceled, try it again', 'error')
+ url = flask.url_for(
+ 'view_settings', username=username, repo=repo,
+ namespace=namespace)
+ return flask.redirect(
+ flask.url_for('auth_login', next=url))
+
+ repo = flask.g.repo
+
+ if not flask.g.repo_admin:
+ flask.abort(
+ 403,
+ 'You are not allowed to change the settings for this project')
+
+ if flask.g.fas_user.username != repo.user.user and not pagure.is_admin():
+ flask.abort(
+ 403,
+ 'You are not allowed to give this project')
+
+ form = pagure.forms.ConfirmationForm()
+
+ if form.validate_on_submit():
+ new_username = flask.request.form.get('user', '').strip()
+ new_owner = pagure.lib.search_user(
+ SESSION, username=new_username)
+ if not new_owner:
+ flask.abort(
+ 500,
+ 'No such user %s found' % new_username)
+ try:
+ repo.user = new_owner
+ SESSION.add(repo)
+ SESSION.commit()
+ flask.flash('Project updated')
+ except SQLAlchemyError as err: # pragma: no cover
+ SESSION.rollback()
+ flask.flash(str(err), 'error')
+
+ return flask.redirect(flask.url_for(
+ 'view_repo', username=username, repo=repo.name,
+ namespace=namespace))
diff --git a/tests/test_pagure_flask_ui_app_give_project.py b/tests/test_pagure_flask_ui_app_give_project.py
new file mode 100644
index 0000000..66399be
--- /dev/null
+++ b/tests/test_pagure_flask_ui_app_give_project.py
@@ -0,0 +1,262 @@
+# -*- coding: utf-8 -*-
+
+"""
+ (c) 2017 - Copyright Red Hat Inc
+
+ Authors:
+ Pierre-Yves Chibon
+
+"""
+
+__requires__ = ['SQLAlchemy >= 0.8']
+import pkg_resources
+
+import unittest
+import shutil
+import sys
+import tempfile
+import os
+
+from mock import patch, MagicMock
+
+sys.path.insert(0, os.path.join(os.path.dirname(
+ os.path.abspath(__file__)), '..'))
+
+import pagure
+import pagure.lib
+import tests
+
+
+class PagureFlaskGiveRepotests(tests.Modeltests):
+ """ Tests for give a project on pagure """
+
+ def setUp(self):
+ """ Set up the environnment, ran before every tests. """
+ super(PagureFlaskGiveRepotests, self).setUp()
+
+ pagure.APP.config['TESTING'] = True
+ pagure.SESSION = self.session
+ pagure.ui.SESSION = self.session
+ pagure.ui.app.SESSION = self.session
+ pagure.ui.filters.SESSION = self.session
+ pagure.ui.repo.SESSION = self.session
+
+ pagure.APP.config['VIRUS_SCAN_ATTACHMENTS'] = False
+ pagure.APP.config['GIT_FOLDER'] = self.path
+ pagure.APP.config['REQUESTS_FOLDER'] = os.path.join(
+ self.path, 'requests')
+ pagure.APP.config['TICKETS_FOLDER'] = os.path.join(
+ self.path, 'tickets')
+ pagure.APP.config['DOCS_FOLDER'] = os.path.join(
+ self.path, 'docs')
+ pagure.APP.config['UPLOAD_FOLDER_URL'] = '/releases/'
+ pagure.APP.config['UPLOAD_FOLDER_PATH'] = os.path.join(
+ self.path, 'releases')
+ self.app = pagure.APP.test_client()
+
+ tests.create_projects(self.session)
+ tests.create_projects_git(self.path, bare=True)
+
+ def _check_user(self, user='pingou'):
+ project = pagure.get_authorized_project(
+ self.session, project_name='test')
+ self.assertEqual(project.user.user, user)
+
+ def test_give_project_no_project(self):
+ """ Test the give_project endpoint. """
+
+ # No such project
+ output = self.app.post('/test42/give')
+ self.assertEqual(output.status_code, 404)
+
+ def test_give_project_no_csrf(self):
+ """ Test the give_project endpoint. """
+
+ user = tests.FakeUser()
+ user.username = 'pingou'
+ with tests.user_set(pagure.APP, user):
+
+ self._check_user()
+
+ # Missing CSRF
+ data = {
+ 'user': 'foo',
+ }
+
+ output = self.app.post(
+ '/test/give', data=data, follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertIn(
+ 'Overview - test - Pagure',
+ output.data)
+
+ self._check_user()
+
+ def test_give_project_invalid_user(self):
+ """ Test the give_project endpoint. """
+
+ user = tests.FakeUser()
+ user.username = 'pingou'
+ with tests.user_set(pagure.APP, user):
+ csrf_token = self.get_csrf()
+
+ self._check_user()
+
+ # Invalid user
+ data = {
+ 'user': 'foobar',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post(
+ '/test/give', data=data, follow_redirects=True)
+ self.assertEqual(output.status_code, 500)
+ self.assertIn(
+ 'No such user foobar found
',
+ output.data)
+
+ self._check_user()
+
+ def test_give_project_not_owner(self):
+ """ Test the give_project endpoint. """
+
+ user = tests.FakeUser()
+ user.username = 'foo'
+ with tests.user_set(pagure.APP, user):
+ csrf_token = self.get_csrf()
+
+ self._check_user()
+
+ # User isn't the admin
+ data = {
+ 'user': 'foo',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post(
+ '/test/give', data=data, follow_redirects=True)
+ self.assertEqual(output.status_code, 403)
+ self.assertIn(
+ 'You are not allowed to change the settings for this '
+ 'project
', output.data)
+
+ self._check_user()
+
+ def test_give_project_not_admin(self):
+ """ Test the give_project endpoint. """
+
+ user = tests.FakeUser()
+ user.username = 'foo'
+ with tests.user_set(pagure.APP, user):
+ csrf_token = self.get_csrf()
+
+ self._check_user()
+
+ # User isn't the admin
+ data = {
+ 'user': 'foo',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post(
+ '/test/give', data=data, follow_redirects=True)
+ self.assertEqual(output.status_code, 403)
+ self.assertIn(
+ 'You are not allowed to change the settings for this '
+ 'project
', output.data)
+
+ self._check_user()
+
+ def test_give_project_not_owner(self):
+ """ Test the give_project endpoint. """
+ project = pagure.get_authorized_project(
+ self.session, project_name='test')
+
+ msg = pagure.lib.add_user_to_project(
+ self.session,
+ project=project,
+ new_user='foo',
+ user='pingou',
+ access='admin')
+ self.session.commit()
+ self.assertEqual(msg, 'User added')
+
+ user = tests.FakeUser()
+ user.username = 'foo'
+ with tests.user_set(pagure.APP, user):
+ csrf_token = self.get_csrf()
+
+ self._check_user()
+
+ # User isn't the owner
+ data = {
+ 'user': 'foo',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post(
+ '/test/give', data=data, follow_redirects=True)
+ self.assertEqual(output.status_code, 403)
+ self.assertIn(
+ 'You are not allowed to give this project
',
+ output.data)
+
+ self._check_user()
+
+ @patch.dict('pagure.APP.config', {'PAGURE_ADMIN_USERS': 'foo'})
+ def test_give_project_not_owner_but_admin(self):
+ """ Test the give_project endpoint. """
+
+ user = tests.FakeUser()
+ user.username = 'foo'
+ user.cla_done = True
+ user.groups = ['foo']
+ with tests.user_set(pagure.APP, user):
+ csrf_token = self.get_csrf()
+
+ self._check_user()
+
+ # User isn't the owner but is an instance admin
+ data = {
+ 'user': 'foo',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post(
+ '/test/give', data=data, follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertIn(
+ '\n Project updated\n',
+ output.data)
+
+ self._check_user('foo')
+
+ @patch.dict('pagure.APP.config', {'PAGURE_ADMIN_USERS': 'foo'})
+ def test_give_project(self):
+ """ Test the give_project endpoint. """
+
+ user = tests.FakeUser()
+ user.username = 'pingou'
+ with tests.user_set(pagure.APP, user):
+ csrf_token = self.get_csrf()
+
+ self._check_user()
+
+ # All good
+ data = {
+ 'user': 'foo',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post(
+ '/test/give', data=data, follow_redirects=True)
+ self.assertEqual(output.status_code, 200)
+ self.assertIn(
+ '\n Project updated\n',
+ output.data)
+
+ self._check_user('foo')
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)