##// END OF EJS Templates
bugfix: fixed #3965 and updated a bunch of api tests which...
dan -
r68:36f83786 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,144 +1,161 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 from rhodecode.model.repo import RepoModel
24 from rhodecode.model.repo import RepoModel
25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN
25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN
26 from rhodecode.api.tests.utils import (
26 from rhodecode.api.tests.utils import (
27 build_data, api_call, assert_error, assert_ok, crash)
27 build_data, api_call, assert_error, assert_ok, crash, jsonify)
28 from rhodecode.tests.fixture import Fixture
28 from rhodecode.tests.fixture import Fixture
29
29
30
30
31 fixture = Fixture()
31 fixture = Fixture()
32
32
33 UPDATE_REPO_NAME = 'api_update_me'
34
35 class SAME_AS_UPDATES(object): """ Constant used for tests below """
33
36
34 @pytest.mark.usefixtures("testuser_api", "app")
37 @pytest.mark.usefixtures("testuser_api", "app")
35 class TestApiUpdateRepo(object):
38 class TestApiUpdateRepo(object):
36 @pytest.mark.parametrize("changing_attr, updates", [
39
37 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
40 @pytest.mark.parametrize("changing_attr, updates, expected", [
38 ('description', {'description': 'new description'}),
41 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}, SAME_AS_UPDATES),
39 ('active', {'active': True}),
42 ('description', {'description': 'new description'}, SAME_AS_UPDATES),
40 ('active', {'active': False}),
43 ('clone_uri', {'clone_uri': 'http://foo.com/repo'}, SAME_AS_UPDATES),
41 ('clone_uri', {'clone_uri': 'http://foo.com/repo'}),
44 ('clone_uri', {'clone_uri': None}, {'clone_uri': ''}),
42 ('clone_uri', {'clone_uri': None}),
45 ('clone_uri', {'clone_uri': ''}, {'clone_uri': ''}),
43 ('landing_rev', {'landing_rev': 'branch:master'}),
46 ('landing_rev', {'landing_rev': 'branch:master'},
44 ('enable_statistics', {'enable_statistics': True}),
47 {'landing_rev': ['branch', 'master']}),
45 ('enable_locking', {'enable_locking': True}),
48 ('enable_statistics', {'enable_statistics': True}, SAME_AS_UPDATES),
46 ('enable_downloads', {'enable_downloads': True}),
49 ('enable_locking', {'enable_locking': True}, SAME_AS_UPDATES),
47 ('name', {'name': 'new_repo_name'}),
50 ('enable_downloads', {'enable_downloads': True}, SAME_AS_UPDATES),
48 ('repo_group', {'group': 'test_group_for_update'}),
51 ('name', {'name': 'new_repo_name'},
52 {'repo_name': 'new_repo_name'}),
53 ('repo_group',
54 {'group': 'test_group_for_update'},
55 {'repo_name': 'test_group_for_update/%s' % UPDATE_REPO_NAME}),
49 ])
56 ])
50 def test_api_update_repo(self, changing_attr, updates, backend):
57 def test_api_update_repo(self, changing_attr, updates, expected, backend):
51 repo_name = 'api_update_me'
58 repo_name = UPDATE_REPO_NAME
52 repo = fixture.create_repo(repo_name, repo_type=backend.alias)
59 repo = fixture.create_repo(repo_name, repo_type=backend.alias)
53 if changing_attr == 'repo_group':
60 if changing_attr == 'repo_group':
54 fixture.create_repo_group(updates['group'])
61 fixture.create_repo_group(updates['group'])
55
62
63 expected_api_data = repo.get_api_data(include_secrets=True)
64 if expected is SAME_AS_UPDATES:
65 expected_api_data.update(updates)
66 else:
67 expected_api_data.update(expected)
68
69
56 id_, params = build_data(
70 id_, params = build_data(
57 self.apikey, 'update_repo', repoid=repo_name, **updates)
71 self.apikey, 'update_repo', repoid=repo_name, **updates)
58 response = api_call(self.app, params)
72 response = api_call(self.app, params)
73
59 if changing_attr == 'name':
74 if changing_attr == 'name':
60 repo_name = updates['name']
75 repo_name = updates['name']
61 if changing_attr == 'repo_group':
76 if changing_attr == 'repo_group':
62 repo_name = '/'.join([updates['group'], repo_name])
77 repo_name = '/'.join([updates['group'], repo_name])
78
63 try:
79 try:
64 expected = {
80 expected = {
65 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
81 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
66 'repository': repo.get_api_data(include_secrets=True)
82 'repository': jsonify(expected_api_data)
67 }
83 }
68 assert_ok(id_, expected, given=response.body)
84 assert_ok(id_, expected, given=response.body)
69 finally:
85 finally:
70 fixture.destroy_repo(repo_name)
86 fixture.destroy_repo(repo_name)
71 if changing_attr == 'repo_group':
87 if changing_attr == 'repo_group':
72
73 fixture.destroy_repo_group(updates['group'])
88 fixture.destroy_repo_group(updates['group'])
74
89
75 def test_api_update_repo_fork_of_field(self, backend):
90 def test_api_update_repo_fork_of_field(self, backend):
76 master_repo = backend.create_repo()
91 master_repo = backend.create_repo()
77 repo = backend.create_repo()
92 repo = backend.create_repo()
78
79 updates = {
93 updates = {
80 'fork_of': master_repo.repo_name
94 'fork_of': master_repo.repo_name
81 }
95 }
96 expected_api_data = repo.get_api_data(include_secrets=True)
97 expected_api_data.update(updates)
98
82 id_, params = build_data(
99 id_, params = build_data(
83 self.apikey, 'update_repo', repoid=repo.repo_name, **updates)
100 self.apikey, 'update_repo', repoid=repo.repo_name, **updates)
84 response = api_call(self.app, params)
101 response = api_call(self.app, params)
85 expected = {
102 expected = {
86 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
103 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
87 'repository': repo.get_api_data(include_secrets=True)
104 'repository': jsonify(expected_api_data)
88 }
105 }
89 assert_ok(id_, expected, given=response.body)
106 assert_ok(id_, expected, given=response.body)
90 result = response.json['result']['repository']
107 result = response.json['result']['repository']
91 assert result['fork_of'] == master_repo.repo_name
108 assert result['fork_of'] == master_repo.repo_name
92
109
93 def test_api_update_repo_fork_of_not_found(self, backend):
110 def test_api_update_repo_fork_of_not_found(self, backend):
94 master_repo_name = 'fake-parent-repo'
111 master_repo_name = 'fake-parent-repo'
95 repo = backend.create_repo()
112 repo = backend.create_repo()
96 updates = {
113 updates = {
97 'fork_of': master_repo_name
114 'fork_of': master_repo_name
98 }
115 }
99 id_, params = build_data(
116 id_, params = build_data(
100 self.apikey, 'update_repo', repoid=repo.repo_name, **updates)
117 self.apikey, 'update_repo', repoid=repo.repo_name, **updates)
101 response = api_call(self.app, params)
118 response = api_call(self.app, params)
102 expected = 'repository `{}` does not exist'.format(master_repo_name)
119 expected = 'repository `{}` does not exist'.format(master_repo_name)
103 assert_error(id_, expected, given=response.body)
120 assert_error(id_, expected, given=response.body)
104
121
105 def test_api_update_repo_with_repo_group_not_existing(self):
122 def test_api_update_repo_with_repo_group_not_existing(self):
106 repo_name = 'admin_owned'
123 repo_name = 'admin_owned'
107 fixture.create_repo(repo_name)
124 fixture.create_repo(repo_name)
108 updates = {'group': 'test_group_for_update'}
125 updates = {'group': 'test_group_for_update'}
109 id_, params = build_data(
126 id_, params = build_data(
110 self.apikey, 'update_repo', repoid=repo_name, **updates)
127 self.apikey, 'update_repo', repoid=repo_name, **updates)
111 response = api_call(self.app, params)
128 response = api_call(self.app, params)
112 try:
129 try:
113 expected = 'repository group `%s` does not exist' % (
130 expected = 'repository group `%s` does not exist' % (
114 updates['group'],)
131 updates['group'],)
115 assert_error(id_, expected, given=response.body)
132 assert_error(id_, expected, given=response.body)
116 finally:
133 finally:
117 fixture.destroy_repo(repo_name)
134 fixture.destroy_repo(repo_name)
118
135
119 def test_api_update_repo_regular_user_not_allowed(self):
136 def test_api_update_repo_regular_user_not_allowed(self):
120 repo_name = 'admin_owned'
137 repo_name = 'admin_owned'
121 fixture.create_repo(repo_name)
138 fixture.create_repo(repo_name)
122 updates = {'active': False}
139 updates = {'active': False}
123 id_, params = build_data(
140 id_, params = build_data(
124 self.apikey_regular, 'update_repo', repoid=repo_name, **updates)
141 self.apikey_regular, 'update_repo', repoid=repo_name, **updates)
125 response = api_call(self.app, params)
142 response = api_call(self.app, params)
126 try:
143 try:
127 expected = 'repository `%s` does not exist' % (repo_name,)
144 expected = 'repository `%s` does not exist' % (repo_name,)
128 assert_error(id_, expected, given=response.body)
145 assert_error(id_, expected, given=response.body)
129 finally:
146 finally:
130 fixture.destroy_repo(repo_name)
147 fixture.destroy_repo(repo_name)
131
148
132 @mock.patch.object(RepoModel, 'update', crash)
149 @mock.patch.object(RepoModel, 'update', crash)
133 def test_api_update_repo_exception_occurred(self, backend):
150 def test_api_update_repo_exception_occurred(self, backend):
134 repo_name = 'api_update_me'
151 repo_name = UPDATE_REPO_NAME
135 fixture.create_repo(repo_name, repo_type=backend.alias)
152 fixture.create_repo(repo_name, repo_type=backend.alias)
136 id_, params = build_data(
153 id_, params = build_data(
137 self.apikey, 'update_repo', repoid=repo_name,
154 self.apikey, 'update_repo', repoid=repo_name,
138 owner=TEST_USER_ADMIN_LOGIN,)
155 owner=TEST_USER_ADMIN_LOGIN,)
139 response = api_call(self.app, params)
156 response = api_call(self.app, params)
140 try:
157 try:
141 expected = 'failed to update repo `%s`' % (repo_name,)
158 expected = 'failed to update repo `%s`' % (repo_name,)
142 assert_error(id_, expected, given=response.body)
159 assert_error(id_, expected, given=response.body)
143 finally:
160 finally:
144 fixture.destroy_repo(repo_name)
161 fixture.destroy_repo(repo_name)
@@ -1,100 +1,108 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 from rhodecode.model.user import UserModel
24 from rhodecode.model.user import UserModel
25 from rhodecode.model.user_group import UserGroupModel
25 from rhodecode.model.user_group import UserGroupModel
26 from rhodecode.tests import TEST_USER_REGULAR_LOGIN
26 from rhodecode.tests import TEST_USER_REGULAR_LOGIN
27 from rhodecode.api.tests.utils import (
27 from rhodecode.api.tests.utils import (
28 build_data, api_call, assert_error, assert_ok, crash)
28 build_data, api_call, assert_error, assert_ok, crash, jsonify)
29
29
30
30
31 @pytest.mark.usefixtures("testuser_api", "app")
31 @pytest.mark.usefixtures("testuser_api", "app")
32 class TestUpdateUserGroup(object):
32 class TestUpdateUserGroup(object):
33 @pytest.mark.parametrize("changing_attr, updates", [
33 @pytest.mark.parametrize("changing_attr, updates", [
34 ('group_name', {'group_name': 'new_group_name'}),
34 ('group_name', {'group_name': 'new_group_name'}),
35 ('group_name', {'group_name': 'test_group_for_update'}),
35 ('group_name', {'group_name': 'test_group_for_update'}),
36 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
36 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
37 ('active', {'active': False}),
37 ('active', {'active': False}),
38 ('active', {'active': True})
38 ('active', {'active': True})
39 ])
39 ])
40 def test_api_update_user_group(self, changing_attr, updates, user_util):
40 def test_api_update_user_group(self, changing_attr, updates, user_util):
41 user_group = user_util.create_user_group()
41 user_group = user_util.create_user_group()
42 group_name = user_group.users_group_name
42 group_name = user_group.users_group_name
43 expected_api_data = user_group.get_api_data()
44 expected_api_data.update(updates)
45
43 id_, params = build_data(
46 id_, params = build_data(
44 self.apikey, 'update_user_group', usergroupid=group_name,
47 self.apikey, 'update_user_group', usergroupid=group_name,
45 **updates)
48 **updates)
46 response = api_call(self.app, params)
49 response = api_call(self.app, params)
50
47 expected = {
51 expected = {
48 'msg': 'updated user group ID:%s %s' % (
52 'msg': 'updated user group ID:%s %s' % (
49 user_group.users_group_id, user_group.users_group_name),
53 user_group.users_group_id, user_group.users_group_name),
50 'user_group': user_group.get_api_data()
54 'user_group': jsonify(expected_api_data)
51 }
55 }
52 assert_ok(id_, expected, given=response.body)
56 assert_ok(id_, expected, given=response.body)
53
57
54 @pytest.mark.parametrize("changing_attr, updates", [
58 @pytest.mark.parametrize("changing_attr, updates", [
55 # TODO: mikhail: decide if we need to test against the commented params
59 # TODO: mikhail: decide if we need to test against the commented params
56 # ('group_name', {'group_name': 'new_group_name'}),
60 # ('group_name', {'group_name': 'new_group_name'}),
57 # ('group_name', {'group_name': 'test_group_for_update'}),
61 # ('group_name', {'group_name': 'test_group_for_update'}),
58 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
62 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
59 ('active', {'active': False}),
63 ('active', {'active': False}),
60 ('active', {'active': True})
64 ('active', {'active': True})
61 ])
65 ])
62 def test_api_update_user_group_regular_user(
66 def test_api_update_user_group_regular_user(
63 self, changing_attr, updates, user_util):
67 self, changing_attr, updates, user_util):
64 user_group = user_util.create_user_group()
68 user_group = user_util.create_user_group()
65 group_name = user_group.users_group_name
69 group_name = user_group.users_group_name
70 expected_api_data = user_group.get_api_data()
71 expected_api_data.update(updates)
72
73
66 # grant permission to this user
74 # grant permission to this user
67 user = UserModel().get_by_username(self.TEST_USER_LOGIN)
75 user = UserModel().get_by_username(self.TEST_USER_LOGIN)
68
76
69 user_util.grant_user_permission_to_user_group(
77 user_util.grant_user_permission_to_user_group(
70 user_group, user, 'usergroup.admin')
78 user_group, user, 'usergroup.admin')
71 id_, params = build_data(
79 id_, params = build_data(
72 self.apikey_regular, 'update_user_group',
80 self.apikey_regular, 'update_user_group',
73 usergroupid=group_name, **updates)
81 usergroupid=group_name, **updates)
74 response = api_call(self.app, params)
82 response = api_call(self.app, params)
75 expected = {
83 expected = {
76 'msg': 'updated user group ID:%s %s' % (
84 'msg': 'updated user group ID:%s %s' % (
77 user_group.users_group_id, user_group.users_group_name),
85 user_group.users_group_id, user_group.users_group_name),
78 'user_group': user_group.get_api_data()
86 'user_group': jsonify(expected_api_data)
79 }
87 }
80 assert_ok(id_, expected, given=response.body)
88 assert_ok(id_, expected, given=response.body)
81
89
82 def test_api_update_user_group_regular_user_no_permission(self, user_util):
90 def test_api_update_user_group_regular_user_no_permission(self, user_util):
83 user_group = user_util.create_user_group()
91 user_group = user_util.create_user_group()
84 group_name = user_group.users_group_name
92 group_name = user_group.users_group_name
85 id_, params = build_data(
93 id_, params = build_data(
86 self.apikey_regular, 'update_user_group', usergroupid=group_name)
94 self.apikey_regular, 'update_user_group', usergroupid=group_name)
87 response = api_call(self.app, params)
95 response = api_call(self.app, params)
88
96
89 expected = 'user group `%s` does not exist' % (group_name)
97 expected = 'user group `%s` does not exist' % (group_name)
90 assert_error(id_, expected, given=response.body)
98 assert_error(id_, expected, given=response.body)
91
99
92 @mock.patch.object(UserGroupModel, 'update', crash)
100 @mock.patch.object(UserGroupModel, 'update', crash)
93 def test_api_update_user_group_exception_occurred(self, user_util):
101 def test_api_update_user_group_exception_occurred(self, user_util):
94 user_group = user_util.create_user_group()
102 user_group = user_util.create_user_group()
95 group_name = user_group.users_group_name
103 group_name = user_group.users_group_name
96 id_, params = build_data(
104 id_, params = build_data(
97 self.apikey, 'update_user_group', usergroupid=group_name)
105 self.apikey, 'update_user_group', usergroupid=group_name)
98 response = api_call(self.app, params)
106 response = api_call(self.app, params)
99 expected = 'failed to update user group `%s`' % (group_name,)
107 expected = 'failed to update user group `%s`' % (group_name,)
100 assert_error(id_, expected, given=response.body)
108 assert_error(id_, expected, given=response.body)
@@ -1,1777 +1,1779 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2016 RhodeCode GmbH
3 # Copyright (C) 2011-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import time
22 import time
23
23
24 import colander
24 import colander
25
25
26 from rhodecode import BACKENDS
26 from rhodecode import BACKENDS
27 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden, json
27 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden, json
28 from rhodecode.api.utils import (
28 from rhodecode.api.utils import (
29 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
29 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
30 get_user_group_or_error, get_user_or_error, has_repo_permissions,
30 get_user_group_or_error, get_user_or_error, has_repo_permissions,
31 get_perm_or_error, store_update, get_repo_group_or_error, parse_args,
31 get_perm_or_error, store_update, get_repo_group_or_error, parse_args,
32 get_origin, build_commit_data)
32 get_origin, build_commit_data)
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 HasPermissionAnyApi, HasRepoGroupPermissionAnyApi,
34 HasPermissionAnyApi, HasRepoGroupPermissionAnyApi,
35 HasUserGroupPermissionAnyApi)
35 HasUserGroupPermissionAnyApi)
36 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
36 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
37 from rhodecode.lib.utils import map_groups
37 from rhodecode.lib.utils import map_groups
38 from rhodecode.lib.utils2 import str2bool, time_to_datetime
38 from rhodecode.lib.utils2 import str2bool, time_to_datetime
39 from rhodecode.model.changeset_status import ChangesetStatusModel
39 from rhodecode.model.changeset_status import ChangesetStatusModel
40 from rhodecode.model.comment import ChangesetCommentsModel
40 from rhodecode.model.comment import ChangesetCommentsModel
41 from rhodecode.model.db import (
41 from rhodecode.model.db import (
42 Session, ChangesetStatus, RepositoryField, Repository)
42 Session, ChangesetStatus, RepositoryField, Repository)
43 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.repo_group import RepoGroupModel
44 from rhodecode.model.repo_group import RepoGroupModel
45 from rhodecode.model.scm import ScmModel, RepoList
45 from rhodecode.model.scm import ScmModel, RepoList
46 from rhodecode.model.settings import SettingsModel
46 from rhodecode.model.settings import SettingsModel
47 from rhodecode.model.validation_schema import RepoSchema
47 from rhodecode.model.validation_schema import RepoSchema
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 @jsonrpc_method()
52 @jsonrpc_method()
53 def get_repo(request, apiuser, repoid, cache=Optional(True)):
53 def get_repo(request, apiuser, repoid, cache=Optional(True)):
54 """
54 """
55 Gets an existing repository by its name or repository_id.
55 Gets an existing repository by its name or repository_id.
56
56
57 The members section so the output returns users groups or users
57 The members section so the output returns users groups or users
58 associated with that repository.
58 associated with that repository.
59
59
60 This command can only be run using an |authtoken| with admin rights,
60 This command can only be run using an |authtoken| with admin rights,
61 or users with at least read rights to the |repo|.
61 or users with at least read rights to the |repo|.
62
62
63 :param apiuser: This is filled automatically from the |authtoken|.
63 :param apiuser: This is filled automatically from the |authtoken|.
64 :type apiuser: AuthUser
64 :type apiuser: AuthUser
65 :param repoid: The repository name or repository id.
65 :param repoid: The repository name or repository id.
66 :type repoid: str or int
66 :type repoid: str or int
67 :param cache: use the cached value for last changeset
67 :param cache: use the cached value for last changeset
68 :type: cache: Optional(bool)
68 :type: cache: Optional(bool)
69
69
70 Example output:
70 Example output:
71
71
72 .. code-block:: bash
72 .. code-block:: bash
73
73
74 {
74 {
75 "error": null,
75 "error": null,
76 "id": <repo_id>,
76 "id": <repo_id>,
77 "result": {
77 "result": {
78 "clone_uri": null,
78 "clone_uri": null,
79 "created_on": "timestamp",
79 "created_on": "timestamp",
80 "description": "repo description",
80 "description": "repo description",
81 "enable_downloads": false,
81 "enable_downloads": false,
82 "enable_locking": false,
82 "enable_locking": false,
83 "enable_statistics": false,
83 "enable_statistics": false,
84 "followers": [
84 "followers": [
85 {
85 {
86 "active": true,
86 "active": true,
87 "admin": false,
87 "admin": false,
88 "api_key": "****************************************",
88 "api_key": "****************************************",
89 "api_keys": [
89 "api_keys": [
90 "****************************************"
90 "****************************************"
91 ],
91 ],
92 "email": "user@example.com",
92 "email": "user@example.com",
93 "emails": [
93 "emails": [
94 "user@example.com"
94 "user@example.com"
95 ],
95 ],
96 "extern_name": "rhodecode",
96 "extern_name": "rhodecode",
97 "extern_type": "rhodecode",
97 "extern_type": "rhodecode",
98 "firstname": "username",
98 "firstname": "username",
99 "ip_addresses": [],
99 "ip_addresses": [],
100 "language": null,
100 "language": null,
101 "last_login": "2015-09-16T17:16:35.854",
101 "last_login": "2015-09-16T17:16:35.854",
102 "lastname": "surname",
102 "lastname": "surname",
103 "user_id": <user_id>,
103 "user_id": <user_id>,
104 "username": "name"
104 "username": "name"
105 }
105 }
106 ],
106 ],
107 "fork_of": "parent-repo",
107 "fork_of": "parent-repo",
108 "landing_rev": [
108 "landing_rev": [
109 "rev",
109 "rev",
110 "tip"
110 "tip"
111 ],
111 ],
112 "last_changeset": {
112 "last_changeset": {
113 "author": "User <user@example.com>",
113 "author": "User <user@example.com>",
114 "branch": "default",
114 "branch": "default",
115 "date": "timestamp",
115 "date": "timestamp",
116 "message": "last commit message",
116 "message": "last commit message",
117 "parents": [
117 "parents": [
118 {
118 {
119 "raw_id": "commit-id"
119 "raw_id": "commit-id"
120 }
120 }
121 ],
121 ],
122 "raw_id": "commit-id",
122 "raw_id": "commit-id",
123 "revision": <revision number>,
123 "revision": <revision number>,
124 "short_id": "short id"
124 "short_id": "short id"
125 },
125 },
126 "lock_reason": null,
126 "lock_reason": null,
127 "locked_by": null,
127 "locked_by": null,
128 "locked_date": null,
128 "locked_date": null,
129 "members": [
129 "members": [
130 {
130 {
131 "name": "super-admin-name",
131 "name": "super-admin-name",
132 "origin": "super-admin",
132 "origin": "super-admin",
133 "permission": "repository.admin",
133 "permission": "repository.admin",
134 "type": "user"
134 "type": "user"
135 },
135 },
136 {
136 {
137 "name": "owner-name",
137 "name": "owner-name",
138 "origin": "owner",
138 "origin": "owner",
139 "permission": "repository.admin",
139 "permission": "repository.admin",
140 "type": "user"
140 "type": "user"
141 },
141 },
142 {
142 {
143 "name": "user-group-name",
143 "name": "user-group-name",
144 "origin": "permission",
144 "origin": "permission",
145 "permission": "repository.write",
145 "permission": "repository.write",
146 "type": "user_group"
146 "type": "user_group"
147 }
147 }
148 ],
148 ],
149 "owner": "owner-name",
149 "owner": "owner-name",
150 "permissions": [
150 "permissions": [
151 {
151 {
152 "name": "super-admin-name",
152 "name": "super-admin-name",
153 "origin": "super-admin",
153 "origin": "super-admin",
154 "permission": "repository.admin",
154 "permission": "repository.admin",
155 "type": "user"
155 "type": "user"
156 },
156 },
157 {
157 {
158 "name": "owner-name",
158 "name": "owner-name",
159 "origin": "owner",
159 "origin": "owner",
160 "permission": "repository.admin",
160 "permission": "repository.admin",
161 "type": "user"
161 "type": "user"
162 },
162 },
163 {
163 {
164 "name": "user-group-name",
164 "name": "user-group-name",
165 "origin": "permission",
165 "origin": "permission",
166 "permission": "repository.write",
166 "permission": "repository.write",
167 "type": "user_group"
167 "type": "user_group"
168 }
168 }
169 ],
169 ],
170 "private": true,
170 "private": true,
171 "repo_id": 676,
171 "repo_id": 676,
172 "repo_name": "user-group/repo-name",
172 "repo_name": "user-group/repo-name",
173 "repo_type": "hg"
173 "repo_type": "hg"
174 }
174 }
175 }
175 }
176 """
176 """
177
177
178 repo = get_repo_or_error(repoid)
178 repo = get_repo_or_error(repoid)
179 cache = Optional.extract(cache)
179 cache = Optional.extract(cache)
180 include_secrets = False
180 include_secrets = False
181 if has_superadmin_permission(apiuser):
181 if has_superadmin_permission(apiuser):
182 include_secrets = True
182 include_secrets = True
183 else:
183 else:
184 # check if we have at least read permission for this repo !
184 # check if we have at least read permission for this repo !
185 _perms = (
185 _perms = (
186 'repository.admin', 'repository.write', 'repository.read',)
186 'repository.admin', 'repository.write', 'repository.read',)
187 has_repo_permissions(apiuser, repoid, repo, _perms)
187 has_repo_permissions(apiuser, repoid, repo, _perms)
188
188
189 permissions = []
189 permissions = []
190 for _user in repo.permissions():
190 for _user in repo.permissions():
191 user_data = {
191 user_data = {
192 'name': _user.username,
192 'name': _user.username,
193 'permission': _user.permission,
193 'permission': _user.permission,
194 'origin': get_origin(_user),
194 'origin': get_origin(_user),
195 'type': "user",
195 'type': "user",
196 }
196 }
197 permissions.append(user_data)
197 permissions.append(user_data)
198
198
199 for _user_group in repo.permission_user_groups():
199 for _user_group in repo.permission_user_groups():
200 user_group_data = {
200 user_group_data = {
201 'name': _user_group.users_group_name,
201 'name': _user_group.users_group_name,
202 'permission': _user_group.permission,
202 'permission': _user_group.permission,
203 'origin': get_origin(_user_group),
203 'origin': get_origin(_user_group),
204 'type': "user_group",
204 'type': "user_group",
205 }
205 }
206 permissions.append(user_group_data)
206 permissions.append(user_group_data)
207
207
208 following_users = [
208 following_users = [
209 user.user.get_api_data(include_secrets=include_secrets)
209 user.user.get_api_data(include_secrets=include_secrets)
210 for user in repo.followers]
210 for user in repo.followers]
211
211
212 if not cache:
212 if not cache:
213 repo.update_commit_cache()
213 repo.update_commit_cache()
214 data = repo.get_api_data(include_secrets=include_secrets)
214 data = repo.get_api_data(include_secrets=include_secrets)
215 data['members'] = permissions # TODO: this should be deprecated soon
215 data['members'] = permissions # TODO: this should be deprecated soon
216 data['permissions'] = permissions
216 data['permissions'] = permissions
217 data['followers'] = following_users
217 data['followers'] = following_users
218 return data
218 return data
219
219
220
220
221 @jsonrpc_method()
221 @jsonrpc_method()
222 def get_repos(request, apiuser):
222 def get_repos(request, apiuser):
223 """
223 """
224 Lists all existing repositories.
224 Lists all existing repositories.
225
225
226 This command can only be run using an |authtoken| with admin rights,
226 This command can only be run using an |authtoken| with admin rights,
227 or users with at least read rights to |repos|.
227 or users with at least read rights to |repos|.
228
228
229 :param apiuser: This is filled automatically from the |authtoken|.
229 :param apiuser: This is filled automatically from the |authtoken|.
230 :type apiuser: AuthUser
230 :type apiuser: AuthUser
231
231
232 Example output:
232 Example output:
233
233
234 .. code-block:: bash
234 .. code-block:: bash
235
235
236 id : <id_given_in_input>
236 id : <id_given_in_input>
237 result: [
237 result: [
238 {
238 {
239 "repo_id" : "<repo_id>",
239 "repo_id" : "<repo_id>",
240 "repo_name" : "<reponame>"
240 "repo_name" : "<reponame>"
241 "repo_type" : "<repo_type>",
241 "repo_type" : "<repo_type>",
242 "clone_uri" : "<clone_uri>",
242 "clone_uri" : "<clone_uri>",
243 "private": : "<bool>",
243 "private": : "<bool>",
244 "created_on" : "<datetimecreated>",
244 "created_on" : "<datetimecreated>",
245 "description" : "<description>",
245 "description" : "<description>",
246 "landing_rev": "<landing_rev>",
246 "landing_rev": "<landing_rev>",
247 "owner": "<repo_owner>",
247 "owner": "<repo_owner>",
248 "fork_of": "<name_of_fork_parent>",
248 "fork_of": "<name_of_fork_parent>",
249 "enable_downloads": "<bool>",
249 "enable_downloads": "<bool>",
250 "enable_locking": "<bool>",
250 "enable_locking": "<bool>",
251 "enable_statistics": "<bool>",
251 "enable_statistics": "<bool>",
252 },
252 },
253 ...
253 ...
254 ]
254 ]
255 error: null
255 error: null
256 """
256 """
257
257
258 include_secrets = has_superadmin_permission(apiuser)
258 include_secrets = has_superadmin_permission(apiuser)
259 _perms = ('repository.read', 'repository.write', 'repository.admin',)
259 _perms = ('repository.read', 'repository.write', 'repository.admin',)
260 extras = {'user': apiuser}
260 extras = {'user': apiuser}
261
261
262 repo_list = RepoList(
262 repo_list = RepoList(
263 RepoModel().get_all(), perm_set=_perms, extra_kwargs=extras)
263 RepoModel().get_all(), perm_set=_perms, extra_kwargs=extras)
264 return [repo.get_api_data(include_secrets=include_secrets)
264 return [repo.get_api_data(include_secrets=include_secrets)
265 for repo in repo_list]
265 for repo in repo_list]
266
266
267
267
268 @jsonrpc_method()
268 @jsonrpc_method()
269 def get_repo_changeset(request, apiuser, repoid, revision,
269 def get_repo_changeset(request, apiuser, repoid, revision,
270 details=Optional('basic')):
270 details=Optional('basic')):
271 """
271 """
272 Returns information about a changeset.
272 Returns information about a changeset.
273
273
274 Additionally parameters define the amount of details returned by
274 Additionally parameters define the amount of details returned by
275 this function.
275 this function.
276
276
277 This command can only be run using an |authtoken| with admin rights,
277 This command can only be run using an |authtoken| with admin rights,
278 or users with at least read rights to the |repo|.
278 or users with at least read rights to the |repo|.
279
279
280 :param apiuser: This is filled automatically from the |authtoken|.
280 :param apiuser: This is filled automatically from the |authtoken|.
281 :type apiuser: AuthUser
281 :type apiuser: AuthUser
282 :param repoid: The repository name or repository id
282 :param repoid: The repository name or repository id
283 :type repoid: str or int
283 :type repoid: str or int
284 :param revision: revision for which listing should be done
284 :param revision: revision for which listing should be done
285 :type revision: str
285 :type revision: str
286 :param details: details can be 'basic|extended|full' full gives diff
286 :param details: details can be 'basic|extended|full' full gives diff
287 info details like the diff itself, and number of changed files etc.
287 info details like the diff itself, and number of changed files etc.
288 :type details: Optional(str)
288 :type details: Optional(str)
289
289
290 """
290 """
291 repo = get_repo_or_error(repoid)
291 repo = get_repo_or_error(repoid)
292 if not has_superadmin_permission(apiuser):
292 if not has_superadmin_permission(apiuser):
293 _perms = (
293 _perms = (
294 'repository.admin', 'repository.write', 'repository.read',)
294 'repository.admin', 'repository.write', 'repository.read',)
295 has_repo_permissions(apiuser, repoid, repo, _perms)
295 has_repo_permissions(apiuser, repoid, repo, _perms)
296
296
297 changes_details = Optional.extract(details)
297 changes_details = Optional.extract(details)
298 _changes_details_types = ['basic', 'extended', 'full']
298 _changes_details_types = ['basic', 'extended', 'full']
299 if changes_details not in _changes_details_types:
299 if changes_details not in _changes_details_types:
300 raise JSONRPCError(
300 raise JSONRPCError(
301 'ret_type must be one of %s' % (
301 'ret_type must be one of %s' % (
302 ','.join(_changes_details_types)))
302 ','.join(_changes_details_types)))
303
303
304 pre_load = ['author', 'branch', 'date', 'message', 'parents',
304 pre_load = ['author', 'branch', 'date', 'message', 'parents',
305 'status', '_commit', '_file_paths']
305 'status', '_commit', '_file_paths']
306
306
307 try:
307 try:
308 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
308 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
309 except TypeError as e:
309 except TypeError as e:
310 raise JSONRPCError(e.message)
310 raise JSONRPCError(e.message)
311 _cs_json = cs.__json__()
311 _cs_json = cs.__json__()
312 _cs_json['diff'] = build_commit_data(cs, changes_details)
312 _cs_json['diff'] = build_commit_data(cs, changes_details)
313 if changes_details == 'full':
313 if changes_details == 'full':
314 _cs_json['refs'] = {
314 _cs_json['refs'] = {
315 'branches': [cs.branch],
315 'branches': [cs.branch],
316 'bookmarks': getattr(cs, 'bookmarks', []),
316 'bookmarks': getattr(cs, 'bookmarks', []),
317 'tags': cs.tags
317 'tags': cs.tags
318 }
318 }
319 return _cs_json
319 return _cs_json
320
320
321
321
322 @jsonrpc_method()
322 @jsonrpc_method()
323 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
323 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
324 details=Optional('basic')):
324 details=Optional('basic')):
325 """
325 """
326 Returns a set of commits limited by the number starting
326 Returns a set of commits limited by the number starting
327 from the `start_rev` option.
327 from the `start_rev` option.
328
328
329 Additional parameters define the amount of details returned by this
329 Additional parameters define the amount of details returned by this
330 function.
330 function.
331
331
332 This command can only be run using an |authtoken| with admin rights,
332 This command can only be run using an |authtoken| with admin rights,
333 or users with at least read rights to |repos|.
333 or users with at least read rights to |repos|.
334
334
335 :param apiuser: This is filled automatically from the |authtoken|.
335 :param apiuser: This is filled automatically from the |authtoken|.
336 :type apiuser: AuthUser
336 :type apiuser: AuthUser
337 :param repoid: The repository name or repository ID.
337 :param repoid: The repository name or repository ID.
338 :type repoid: str or int
338 :type repoid: str or int
339 :param start_rev: The starting revision from where to get changesets.
339 :param start_rev: The starting revision from where to get changesets.
340 :type start_rev: str
340 :type start_rev: str
341 :param limit: Limit the number of commits to this amount
341 :param limit: Limit the number of commits to this amount
342 :type limit: str or int
342 :type limit: str or int
343 :param details: Set the level of detail returned. Valid option are:
343 :param details: Set the level of detail returned. Valid option are:
344 ``basic``, ``extended`` and ``full``.
344 ``basic``, ``extended`` and ``full``.
345 :type details: Optional(str)
345 :type details: Optional(str)
346
346
347 .. note::
347 .. note::
348
348
349 Setting the parameter `details` to the value ``full`` is extensive
349 Setting the parameter `details` to the value ``full`` is extensive
350 and returns details like the diff itself, and the number
350 and returns details like the diff itself, and the number
351 of changed files.
351 of changed files.
352
352
353 """
353 """
354 repo = get_repo_or_error(repoid)
354 repo = get_repo_or_error(repoid)
355 if not has_superadmin_permission(apiuser):
355 if not has_superadmin_permission(apiuser):
356 _perms = (
356 _perms = (
357 'repository.admin', 'repository.write', 'repository.read',)
357 'repository.admin', 'repository.write', 'repository.read',)
358 has_repo_permissions(apiuser, repoid, repo, _perms)
358 has_repo_permissions(apiuser, repoid, repo, _perms)
359
359
360 changes_details = Optional.extract(details)
360 changes_details = Optional.extract(details)
361 _changes_details_types = ['basic', 'extended', 'full']
361 _changes_details_types = ['basic', 'extended', 'full']
362 if changes_details not in _changes_details_types:
362 if changes_details not in _changes_details_types:
363 raise JSONRPCError(
363 raise JSONRPCError(
364 'ret_type must be one of %s' % (
364 'ret_type must be one of %s' % (
365 ','.join(_changes_details_types)))
365 ','.join(_changes_details_types)))
366
366
367 limit = int(limit)
367 limit = int(limit)
368 pre_load = ['author', 'branch', 'date', 'message', 'parents',
368 pre_load = ['author', 'branch', 'date', 'message', 'parents',
369 'status', '_commit', '_file_paths']
369 'status', '_commit', '_file_paths']
370
370
371 vcs_repo = repo.scm_instance()
371 vcs_repo = repo.scm_instance()
372 # SVN needs a special case to distinguish its index and commit id
372 # SVN needs a special case to distinguish its index and commit id
373 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
373 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
374 start_rev = vcs_repo.commit_ids[0]
374 start_rev = vcs_repo.commit_ids[0]
375
375
376 try:
376 try:
377 commits = vcs_repo.get_commits(
377 commits = vcs_repo.get_commits(
378 start_id=start_rev, pre_load=pre_load)
378 start_id=start_rev, pre_load=pre_load)
379 except TypeError as e:
379 except TypeError as e:
380 raise JSONRPCError(e.message)
380 raise JSONRPCError(e.message)
381 except Exception:
381 except Exception:
382 log.exception('Fetching of commits failed')
382 log.exception('Fetching of commits failed')
383 raise JSONRPCError('Error occurred during commit fetching')
383 raise JSONRPCError('Error occurred during commit fetching')
384
384
385 ret = []
385 ret = []
386 for cnt, commit in enumerate(commits):
386 for cnt, commit in enumerate(commits):
387 if cnt >= limit != -1:
387 if cnt >= limit != -1:
388 break
388 break
389 _cs_json = commit.__json__()
389 _cs_json = commit.__json__()
390 _cs_json['diff'] = build_commit_data(commit, changes_details)
390 _cs_json['diff'] = build_commit_data(commit, changes_details)
391 if changes_details == 'full':
391 if changes_details == 'full':
392 _cs_json['refs'] = {
392 _cs_json['refs'] = {
393 'branches': [commit.branch],
393 'branches': [commit.branch],
394 'bookmarks': getattr(commit, 'bookmarks', []),
394 'bookmarks': getattr(commit, 'bookmarks', []),
395 'tags': commit.tags
395 'tags': commit.tags
396 }
396 }
397 ret.append(_cs_json)
397 ret.append(_cs_json)
398 return ret
398 return ret
399
399
400
400
401 @jsonrpc_method()
401 @jsonrpc_method()
402 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
402 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
403 ret_type=Optional('all'), details=Optional('basic')):
403 ret_type=Optional('all'), details=Optional('basic')):
404 """
404 """
405 Returns a list of nodes and children in a flat list for a given
405 Returns a list of nodes and children in a flat list for a given
406 path at given revision.
406 path at given revision.
407
407
408 It's possible to specify ret_type to show only `files` or `dirs`.
408 It's possible to specify ret_type to show only `files` or `dirs`.
409
409
410 This command can only be run using an |authtoken| with admin rights,
410 This command can only be run using an |authtoken| with admin rights,
411 or users with at least read rights to |repos|.
411 or users with at least read rights to |repos|.
412
412
413 :param apiuser: This is filled automatically from the |authtoken|.
413 :param apiuser: This is filled automatically from the |authtoken|.
414 :type apiuser: AuthUser
414 :type apiuser: AuthUser
415 :param repoid: The repository name or repository ID.
415 :param repoid: The repository name or repository ID.
416 :type repoid: str or int
416 :type repoid: str or int
417 :param revision: The revision for which listing should be done.
417 :param revision: The revision for which listing should be done.
418 :type revision: str
418 :type revision: str
419 :param root_path: The path from which to start displaying.
419 :param root_path: The path from which to start displaying.
420 :type root_path: str
420 :type root_path: str
421 :param ret_type: Set the return type. Valid options are
421 :param ret_type: Set the return type. Valid options are
422 ``all`` (default), ``files`` and ``dirs``.
422 ``all`` (default), ``files`` and ``dirs``.
423 :type ret_type: Optional(str)
423 :type ret_type: Optional(str)
424 :param details: Returns extended information about nodes, such as
424 :param details: Returns extended information about nodes, such as
425 md5, binary, and or content. The valid options are ``basic`` and
425 md5, binary, and or content. The valid options are ``basic`` and
426 ``full``.
426 ``full``.
427 :type details: Optional(str)
427 :type details: Optional(str)
428
428
429 Example output:
429 Example output:
430
430
431 .. code-block:: bash
431 .. code-block:: bash
432
432
433 id : <id_given_in_input>
433 id : <id_given_in_input>
434 result: [
434 result: [
435 {
435 {
436 "name" : "<name>"
436 "name" : "<name>"
437 "type" : "<type>",
437 "type" : "<type>",
438 "binary": "<true|false>" (only in extended mode)
438 "binary": "<true|false>" (only in extended mode)
439 "md5" : "<md5 of file content>" (only in extended mode)
439 "md5" : "<md5 of file content>" (only in extended mode)
440 },
440 },
441 ...
441 ...
442 ]
442 ]
443 error: null
443 error: null
444 """
444 """
445
445
446 repo = get_repo_or_error(repoid)
446 repo = get_repo_or_error(repoid)
447 if not has_superadmin_permission(apiuser):
447 if not has_superadmin_permission(apiuser):
448 _perms = (
448 _perms = (
449 'repository.admin', 'repository.write', 'repository.read',)
449 'repository.admin', 'repository.write', 'repository.read',)
450 has_repo_permissions(apiuser, repoid, repo, _perms)
450 has_repo_permissions(apiuser, repoid, repo, _perms)
451
451
452 ret_type = Optional.extract(ret_type)
452 ret_type = Optional.extract(ret_type)
453 details = Optional.extract(details)
453 details = Optional.extract(details)
454 _extended_types = ['basic', 'full']
454 _extended_types = ['basic', 'full']
455 if details not in _extended_types:
455 if details not in _extended_types:
456 raise JSONRPCError(
456 raise JSONRPCError(
457 'ret_type must be one of %s' % (','.join(_extended_types)))
457 'ret_type must be one of %s' % (','.join(_extended_types)))
458 extended_info = False
458 extended_info = False
459 content = False
459 content = False
460 if details == 'basic':
460 if details == 'basic':
461 extended_info = True
461 extended_info = True
462
462
463 if details == 'full':
463 if details == 'full':
464 extended_info = content = True
464 extended_info = content = True
465
465
466 _map = {}
466 _map = {}
467 try:
467 try:
468 # check if repo is not empty by any chance, skip quicker if it is.
468 # check if repo is not empty by any chance, skip quicker if it is.
469 _scm = repo.scm_instance()
469 _scm = repo.scm_instance()
470 if _scm.is_empty():
470 if _scm.is_empty():
471 return []
471 return []
472
472
473 _d, _f = ScmModel().get_nodes(
473 _d, _f = ScmModel().get_nodes(
474 repo, revision, root_path, flat=False,
474 repo, revision, root_path, flat=False,
475 extended_info=extended_info, content=content)
475 extended_info=extended_info, content=content)
476 _map = {
476 _map = {
477 'all': _d + _f,
477 'all': _d + _f,
478 'files': _f,
478 'files': _f,
479 'dirs': _d,
479 'dirs': _d,
480 }
480 }
481 return _map[ret_type]
481 return _map[ret_type]
482 except KeyError:
482 except KeyError:
483 raise JSONRPCError(
483 raise JSONRPCError(
484 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
484 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
485 except Exception:
485 except Exception:
486 log.exception("Exception occurred while trying to get repo nodes")
486 log.exception("Exception occurred while trying to get repo nodes")
487 raise JSONRPCError(
487 raise JSONRPCError(
488 'failed to get repo: `%s` nodes' % repo.repo_name
488 'failed to get repo: `%s` nodes' % repo.repo_name
489 )
489 )
490
490
491
491
492 @jsonrpc_method()
492 @jsonrpc_method()
493 def get_repo_refs(request, apiuser, repoid):
493 def get_repo_refs(request, apiuser, repoid):
494 """
494 """
495 Returns a dictionary of current references. It returns
495 Returns a dictionary of current references. It returns
496 bookmarks, branches, closed_branches, and tags for given repository
496 bookmarks, branches, closed_branches, and tags for given repository
497
497
498 It's possible to specify ret_type to show only `files` or `dirs`.
498 It's possible to specify ret_type to show only `files` or `dirs`.
499
499
500 This command can only be run using an |authtoken| with admin rights,
500 This command can only be run using an |authtoken| with admin rights,
501 or users with at least read rights to |repos|.
501 or users with at least read rights to |repos|.
502
502
503 :param apiuser: This is filled automatically from the |authtoken|.
503 :param apiuser: This is filled automatically from the |authtoken|.
504 :type apiuser: AuthUser
504 :type apiuser: AuthUser
505 :param repoid: The repository name or repository ID.
505 :param repoid: The repository name or repository ID.
506 :type repoid: str or int
506 :type repoid: str or int
507
507
508 Example output:
508 Example output:
509
509
510 .. code-block:: bash
510 .. code-block:: bash
511
511
512 id : <id_given_in_input>
512 id : <id_given_in_input>
513 result: [
513 result: [
514 TODO...
514 TODO...
515 ]
515 ]
516 error: null
516 error: null
517 """
517 """
518
518
519 repo = get_repo_or_error(repoid)
519 repo = get_repo_or_error(repoid)
520 if not has_superadmin_permission(apiuser):
520 if not has_superadmin_permission(apiuser):
521 _perms = ('repository.admin', 'repository.write', 'repository.read',)
521 _perms = ('repository.admin', 'repository.write', 'repository.read',)
522 has_repo_permissions(apiuser, repoid, repo, _perms)
522 has_repo_permissions(apiuser, repoid, repo, _perms)
523
523
524 try:
524 try:
525 # check if repo is not empty by any chance, skip quicker if it is.
525 # check if repo is not empty by any chance, skip quicker if it is.
526 vcs_instance = repo.scm_instance()
526 vcs_instance = repo.scm_instance()
527 refs = vcs_instance.refs()
527 refs = vcs_instance.refs()
528 return refs
528 return refs
529 except Exception:
529 except Exception:
530 log.exception("Exception occurred while trying to get repo refs")
530 log.exception("Exception occurred while trying to get repo refs")
531 raise JSONRPCError(
531 raise JSONRPCError(
532 'failed to get repo: `%s` references' % repo.repo_name
532 'failed to get repo: `%s` references' % repo.repo_name
533 )
533 )
534
534
535
535
536 @jsonrpc_method()
536 @jsonrpc_method()
537 def create_repo(request, apiuser, repo_name, repo_type,
537 def create_repo(request, apiuser, repo_name, repo_type,
538 owner=Optional(OAttr('apiuser')), description=Optional(''),
538 owner=Optional(OAttr('apiuser')), description=Optional(''),
539 private=Optional(False), clone_uri=Optional(None),
539 private=Optional(False), clone_uri=Optional(None),
540 landing_rev=Optional('rev:tip'),
540 landing_rev=Optional('rev:tip'),
541 enable_statistics=Optional(False),
541 enable_statistics=Optional(False),
542 enable_locking=Optional(False),
542 enable_locking=Optional(False),
543 enable_downloads=Optional(False),
543 enable_downloads=Optional(False),
544 copy_permissions=Optional(False)):
544 copy_permissions=Optional(False)):
545 """
545 """
546 Creates a repository.
546 Creates a repository.
547
547
548 * If the repository name contains "/", all the required repository
548 * If the repository name contains "/", all the required repository
549 groups will be created.
549 groups will be created.
550
550
551 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
551 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
552 (with "foo" as parent). It will also create the "baz" repository
552 (with "foo" as parent). It will also create the "baz" repository
553 with "bar" as |repo| group.
553 with "bar" as |repo| group.
554
554
555 This command can only be run using an |authtoken| with at least
555 This command can only be run using an |authtoken| with at least
556 write permissions to the |repo|.
556 write permissions to the |repo|.
557
557
558 :param apiuser: This is filled automatically from the |authtoken|.
558 :param apiuser: This is filled automatically from the |authtoken|.
559 :type apiuser: AuthUser
559 :type apiuser: AuthUser
560 :param repo_name: Set the repository name.
560 :param repo_name: Set the repository name.
561 :type repo_name: str
561 :type repo_name: str
562 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
562 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
563 :type repo_type: str
563 :type repo_type: str
564 :param owner: user_id or username
564 :param owner: user_id or username
565 :type owner: Optional(str)
565 :type owner: Optional(str)
566 :param description: Set the repository description.
566 :param description: Set the repository description.
567 :type description: Optional(str)
567 :type description: Optional(str)
568 :param private:
568 :param private:
569 :type private: bool
569 :type private: bool
570 :param clone_uri:
570 :param clone_uri:
571 :type clone_uri: str
571 :type clone_uri: str
572 :param landing_rev: <rev_type>:<rev>
572 :param landing_rev: <rev_type>:<rev>
573 :type landing_rev: str
573 :type landing_rev: str
574 :param enable_locking:
574 :param enable_locking:
575 :type enable_locking: bool
575 :type enable_locking: bool
576 :param enable_downloads:
576 :param enable_downloads:
577 :type enable_downloads: bool
577 :type enable_downloads: bool
578 :param enable_statistics:
578 :param enable_statistics:
579 :type enable_statistics: bool
579 :type enable_statistics: bool
580 :param copy_permissions: Copy permission from group in which the
580 :param copy_permissions: Copy permission from group in which the
581 repository is being created.
581 repository is being created.
582 :type copy_permissions: bool
582 :type copy_permissions: bool
583
583
584
584
585 Example output:
585 Example output:
586
586
587 .. code-block:: bash
587 .. code-block:: bash
588
588
589 id : <id_given_in_input>
589 id : <id_given_in_input>
590 result: {
590 result: {
591 "msg": "Created new repository `<reponame>`",
591 "msg": "Created new repository `<reponame>`",
592 "success": true,
592 "success": true,
593 "task": "<celery task id or None if done sync>"
593 "task": "<celery task id or None if done sync>"
594 }
594 }
595 error: null
595 error: null
596
596
597
597
598 Example error output:
598 Example error output:
599
599
600 .. code-block:: bash
600 .. code-block:: bash
601
601
602 id : <id_given_in_input>
602 id : <id_given_in_input>
603 result : null
603 result : null
604 error : {
604 error : {
605 'failed to create repository `<repo_name>`
605 'failed to create repository `<repo_name>`
606 }
606 }
607
607
608 """
608 """
609 schema = RepoSchema()
609 schema = RepoSchema()
610 try:
610 try:
611 data = schema.deserialize({
611 data = schema.deserialize({
612 'repo_name': repo_name
612 'repo_name': repo_name
613 })
613 })
614 except colander.Invalid as e:
614 except colander.Invalid as e:
615 raise JSONRPCError("Validation failed: %s" % (e.asdict(),))
615 raise JSONRPCError("Validation failed: %s" % (e.asdict(),))
616 repo_name = data['repo_name']
616 repo_name = data['repo_name']
617
617
618 (repo_name_cleaned,
618 (repo_name_cleaned,
619 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
619 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
620 repo_name)
620 repo_name)
621
621
622 if not HasPermissionAnyApi(
622 if not HasPermissionAnyApi(
623 'hg.admin', 'hg.create.repository')(user=apiuser):
623 'hg.admin', 'hg.create.repository')(user=apiuser):
624 # check if we have admin permission for this repo group if given !
624 # check if we have admin permission for this repo group if given !
625
625
626 if parent_group_name:
626 if parent_group_name:
627 repogroupid = parent_group_name
627 repogroupid = parent_group_name
628 repo_group = get_repo_group_or_error(parent_group_name)
628 repo_group = get_repo_group_or_error(parent_group_name)
629
629
630 _perms = ('group.admin',)
630 _perms = ('group.admin',)
631 if not HasRepoGroupPermissionAnyApi(*_perms)(
631 if not HasRepoGroupPermissionAnyApi(*_perms)(
632 user=apiuser, group_name=repo_group.group_name):
632 user=apiuser, group_name=repo_group.group_name):
633 raise JSONRPCError(
633 raise JSONRPCError(
634 'repository group `%s` does not exist' % (
634 'repository group `%s` does not exist' % (
635 repogroupid,))
635 repogroupid,))
636 else:
636 else:
637 raise JSONRPCForbidden()
637 raise JSONRPCForbidden()
638
638
639 if not has_superadmin_permission(apiuser):
639 if not has_superadmin_permission(apiuser):
640 if not isinstance(owner, Optional):
640 if not isinstance(owner, Optional):
641 # forbid setting owner for non-admins
641 # forbid setting owner for non-admins
642 raise JSONRPCError(
642 raise JSONRPCError(
643 'Only RhodeCode admin can specify `owner` param')
643 'Only RhodeCode admin can specify `owner` param')
644
644
645 if isinstance(owner, Optional):
645 if isinstance(owner, Optional):
646 owner = apiuser.user_id
646 owner = apiuser.user_id
647
647
648 owner = get_user_or_error(owner)
648 owner = get_user_or_error(owner)
649
649
650 if RepoModel().get_by_repo_name(repo_name):
650 if RepoModel().get_by_repo_name(repo_name):
651 raise JSONRPCError("repo `%s` already exist" % repo_name)
651 raise JSONRPCError("repo `%s` already exist" % repo_name)
652
652
653 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
653 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
654 if isinstance(private, Optional):
654 if isinstance(private, Optional):
655 private = defs.get('repo_private') or Optional.extract(private)
655 private = defs.get('repo_private') or Optional.extract(private)
656 if isinstance(repo_type, Optional):
656 if isinstance(repo_type, Optional):
657 repo_type = defs.get('repo_type')
657 repo_type = defs.get('repo_type')
658 if isinstance(enable_statistics, Optional):
658 if isinstance(enable_statistics, Optional):
659 enable_statistics = defs.get('repo_enable_statistics')
659 enable_statistics = defs.get('repo_enable_statistics')
660 if isinstance(enable_locking, Optional):
660 if isinstance(enable_locking, Optional):
661 enable_locking = defs.get('repo_enable_locking')
661 enable_locking = defs.get('repo_enable_locking')
662 if isinstance(enable_downloads, Optional):
662 if isinstance(enable_downloads, Optional):
663 enable_downloads = defs.get('repo_enable_downloads')
663 enable_downloads = defs.get('repo_enable_downloads')
664
664
665 clone_uri = Optional.extract(clone_uri)
665 clone_uri = Optional.extract(clone_uri)
666 description = Optional.extract(description)
666 description = Optional.extract(description)
667 landing_rev = Optional.extract(landing_rev)
667 landing_rev = Optional.extract(landing_rev)
668 copy_permissions = Optional.extract(copy_permissions)
668 copy_permissions = Optional.extract(copy_permissions)
669
669
670 try:
670 try:
671 # create structure of groups and return the last group
671 # create structure of groups and return the last group
672 repo_group = map_groups(repo_name)
672 repo_group = map_groups(repo_name)
673 data = {
673 data = {
674 'repo_name': repo_name_cleaned,
674 'repo_name': repo_name_cleaned,
675 'repo_name_full': repo_name,
675 'repo_name_full': repo_name,
676 'repo_type': repo_type,
676 'repo_type': repo_type,
677 'repo_description': description,
677 'repo_description': description,
678 'owner': owner,
678 'owner': owner,
679 'repo_private': private,
679 'repo_private': private,
680 'clone_uri': clone_uri,
680 'clone_uri': clone_uri,
681 'repo_group': repo_group.group_id if repo_group else None,
681 'repo_group': repo_group.group_id if repo_group else None,
682 'repo_landing_rev': landing_rev,
682 'repo_landing_rev': landing_rev,
683 'enable_statistics': enable_statistics,
683 'enable_statistics': enable_statistics,
684 'enable_locking': enable_locking,
684 'enable_locking': enable_locking,
685 'enable_downloads': enable_downloads,
685 'enable_downloads': enable_downloads,
686 'repo_copy_permissions': copy_permissions,
686 'repo_copy_permissions': copy_permissions,
687 }
687 }
688
688
689 if repo_type not in BACKENDS.keys():
689 if repo_type not in BACKENDS.keys():
690 raise Exception("Invalid backend type %s" % repo_type)
690 raise Exception("Invalid backend type %s" % repo_type)
691 task = RepoModel().create(form_data=data, cur_user=owner)
691 task = RepoModel().create(form_data=data, cur_user=owner)
692 from celery.result import BaseAsyncResult
692 from celery.result import BaseAsyncResult
693 task_id = None
693 task_id = None
694 if isinstance(task, BaseAsyncResult):
694 if isinstance(task, BaseAsyncResult):
695 task_id = task.task_id
695 task_id = task.task_id
696 # no commit, it's done in RepoModel, or async via celery
696 # no commit, it's done in RepoModel, or async via celery
697 return {
697 return {
698 'msg': "Created new repository `%s`" % (repo_name,),
698 'msg': "Created new repository `%s`" % (repo_name,),
699 'success': True, # cannot return the repo data here since fork
699 'success': True, # cannot return the repo data here since fork
700 # cann be done async
700 # cann be done async
701 'task': task_id
701 'task': task_id
702 }
702 }
703 except Exception:
703 except Exception:
704 log.exception(
704 log.exception(
705 u"Exception while trying to create the repository %s",
705 u"Exception while trying to create the repository %s",
706 repo_name)
706 repo_name)
707 raise JSONRPCError(
707 raise JSONRPCError(
708 'failed to create repository `%s`' % (repo_name,))
708 'failed to create repository `%s`' % (repo_name,))
709
709
710
710
711 @jsonrpc_method()
711 @jsonrpc_method()
712 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
712 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
713 description=Optional('')):
713 description=Optional('')):
714 """
714 """
715 Adds an extra field to a repository.
715 Adds an extra field to a repository.
716
716
717 This command can only be run using an |authtoken| with at least
717 This command can only be run using an |authtoken| with at least
718 write permissions to the |repo|.
718 write permissions to the |repo|.
719
719
720 :param apiuser: This is filled automatically from the |authtoken|.
720 :param apiuser: This is filled automatically from the |authtoken|.
721 :type apiuser: AuthUser
721 :type apiuser: AuthUser
722 :param repoid: Set the repository name or repository id.
722 :param repoid: Set the repository name or repository id.
723 :type repoid: str or int
723 :type repoid: str or int
724 :param key: Create a unique field key for this repository.
724 :param key: Create a unique field key for this repository.
725 :type key: str
725 :type key: str
726 :param label:
726 :param label:
727 :type label: Optional(str)
727 :type label: Optional(str)
728 :param description:
728 :param description:
729 :type description: Optional(str)
729 :type description: Optional(str)
730 """
730 """
731 repo = get_repo_or_error(repoid)
731 repo = get_repo_or_error(repoid)
732 if not has_superadmin_permission(apiuser):
732 if not has_superadmin_permission(apiuser):
733 _perms = ('repository.admin',)
733 _perms = ('repository.admin',)
734 has_repo_permissions(apiuser, repoid, repo, _perms)
734 has_repo_permissions(apiuser, repoid, repo, _perms)
735
735
736 label = Optional.extract(label) or key
736 label = Optional.extract(label) or key
737 description = Optional.extract(description)
737 description = Optional.extract(description)
738
738
739 field = RepositoryField.get_by_key_name(key, repo)
739 field = RepositoryField.get_by_key_name(key, repo)
740 if field:
740 if field:
741 raise JSONRPCError('Field with key '
741 raise JSONRPCError('Field with key '
742 '`%s` exists for repo `%s`' % (key, repoid))
742 '`%s` exists for repo `%s`' % (key, repoid))
743
743
744 try:
744 try:
745 RepoModel().add_repo_field(repo, key, field_label=label,
745 RepoModel().add_repo_field(repo, key, field_label=label,
746 field_desc=description)
746 field_desc=description)
747 Session().commit()
747 Session().commit()
748 return {
748 return {
749 'msg': "Added new repository field `%s`" % (key,),
749 'msg': "Added new repository field `%s`" % (key,),
750 'success': True,
750 'success': True,
751 }
751 }
752 except Exception:
752 except Exception:
753 log.exception("Exception occurred while trying to add field to repo")
753 log.exception("Exception occurred while trying to add field to repo")
754 raise JSONRPCError(
754 raise JSONRPCError(
755 'failed to create new field for repository `%s`' % (repoid,))
755 'failed to create new field for repository `%s`' % (repoid,))
756
756
757
757
758 @jsonrpc_method()
758 @jsonrpc_method()
759 def remove_field_from_repo(request, apiuser, repoid, key):
759 def remove_field_from_repo(request, apiuser, repoid, key):
760 """
760 """
761 Removes an extra field from a repository.
761 Removes an extra field from a repository.
762
762
763 This command can only be run using an |authtoken| with at least
763 This command can only be run using an |authtoken| with at least
764 write permissions to the |repo|.
764 write permissions to the |repo|.
765
765
766 :param apiuser: This is filled automatically from the |authtoken|.
766 :param apiuser: This is filled automatically from the |authtoken|.
767 :type apiuser: AuthUser
767 :type apiuser: AuthUser
768 :param repoid: Set the repository name or repository ID.
768 :param repoid: Set the repository name or repository ID.
769 :type repoid: str or int
769 :type repoid: str or int
770 :param key: Set the unique field key for this repository.
770 :param key: Set the unique field key for this repository.
771 :type key: str
771 :type key: str
772 """
772 """
773
773
774 repo = get_repo_or_error(repoid)
774 repo = get_repo_or_error(repoid)
775 if not has_superadmin_permission(apiuser):
775 if not has_superadmin_permission(apiuser):
776 _perms = ('repository.admin',)
776 _perms = ('repository.admin',)
777 has_repo_permissions(apiuser, repoid, repo, _perms)
777 has_repo_permissions(apiuser, repoid, repo, _perms)
778
778
779 field = RepositoryField.get_by_key_name(key, repo)
779 field = RepositoryField.get_by_key_name(key, repo)
780 if not field:
780 if not field:
781 raise JSONRPCError('Field with key `%s` does not '
781 raise JSONRPCError('Field with key `%s` does not '
782 'exists for repo `%s`' % (key, repoid))
782 'exists for repo `%s`' % (key, repoid))
783
783
784 try:
784 try:
785 RepoModel().delete_repo_field(repo, field_key=key)
785 RepoModel().delete_repo_field(repo, field_key=key)
786 Session().commit()
786 Session().commit()
787 return {
787 return {
788 'msg': "Deleted repository field `%s`" % (key,),
788 'msg': "Deleted repository field `%s`" % (key,),
789 'success': True,
789 'success': True,
790 }
790 }
791 except Exception:
791 except Exception:
792 log.exception(
792 log.exception(
793 "Exception occurred while trying to delete field from repo")
793 "Exception occurred while trying to delete field from repo")
794 raise JSONRPCError(
794 raise JSONRPCError(
795 'failed to delete field for repository `%s`' % (repoid,))
795 'failed to delete field for repository `%s`' % (repoid,))
796
796
797
797
798 @jsonrpc_method()
798 @jsonrpc_method()
799 def update_repo(request, apiuser, repoid, name=Optional(None),
799 def update_repo(request, apiuser, repoid, name=Optional(None),
800 owner=Optional(OAttr('apiuser')),
800 owner=Optional(OAttr('apiuser')),
801 group=Optional(None),
801 group=Optional(None),
802 active=Optional(True),
802 fork_of=Optional(None),
803 fork_of=Optional(None),
803 description=Optional(''), private=Optional(False),
804 description=Optional(''), private=Optional(False),
804 clone_uri=Optional(None), landing_rev=Optional('rev:tip'),
805 clone_uri=Optional(None), landing_rev=Optional('rev:tip'),
805 enable_statistics=Optional(False),
806 enable_statistics=Optional(False),
806 enable_locking=Optional(False),
807 enable_locking=Optional(False),
807 enable_downloads=Optional(False),
808 enable_downloads=Optional(False),
808 fields=Optional('')):
809 fields=Optional('')):
809 """
810 """
810 Updates a repository with the given information.
811 Updates a repository with the given information.
811
812
812 This command can only be run using an |authtoken| with at least
813 This command can only be run using an |authtoken| with at least
813 write permissions to the |repo|.
814 write permissions to the |repo|.
814
815
815 :param apiuser: This is filled automatically from the |authtoken|.
816 :param apiuser: This is filled automatically from the |authtoken|.
816 :type apiuser: AuthUser
817 :type apiuser: AuthUser
817 :param repoid: repository name or repository ID.
818 :param repoid: repository name or repository ID.
818 :type repoid: str or int
819 :type repoid: str or int
819 :param name: Update the |repo| name.
820 :param name: Update the |repo| name.
820 :type name: str
821 :type name: str
821 :param owner: Set the |repo| owner.
822 :param owner: Set the |repo| owner.
822 :type owner: str
823 :type owner: str
823 :param group: Set the |repo| group the |repo| belongs to.
824 :param group: Set the |repo| group the |repo| belongs to.
824 :type group: str
825 :type group: str
825 :param fork_of: Set the master |repo| name.
826 :param fork_of: Set the master |repo| name.
826 :type fork_of: str
827 :type fork_of: str
827 :param description: Update the |repo| description.
828 :param description: Update the |repo| description.
828 :type description: str
829 :type description: str
829 :param private: Set the |repo| as private. (True | False)
830 :param private: Set the |repo| as private. (True | False)
830 :type private: bool
831 :type private: bool
831 :param clone_uri: Update the |repo| clone URI.
832 :param clone_uri: Update the |repo| clone URI.
832 :type clone_uri: str
833 :type clone_uri: str
833 :param landing_rev: Set the |repo| landing revision. Default is
834 :param landing_rev: Set the |repo| landing revision. Default is
834 ``tip``.
835 ``tip``.
835 :type landing_rev: str
836 :type landing_rev: str
836 :param enable_statistics: Enable statistics on the |repo|,
837 :param enable_statistics: Enable statistics on the |repo|,
837 (True | False).
838 (True | False).
838 :type enable_statistics: bool
839 :type enable_statistics: bool
839 :param enable_locking: Enable |repo| locking.
840 :param enable_locking: Enable |repo| locking.
840 :type enable_locking: bool
841 :type enable_locking: bool
841 :param enable_downloads: Enable downloads from the |repo|,
842 :param enable_downloads: Enable downloads from the |repo|,
842 (True | False).
843 (True | False).
843 :type enable_downloads: bool
844 :type enable_downloads: bool
844 :param fields: Add extra fields to the |repo|. Use the following
845 :param fields: Add extra fields to the |repo|. Use the following
845 example format: ``field_key=field_val,field_key2=fieldval2``.
846 example format: ``field_key=field_val,field_key2=fieldval2``.
846 Escape ', ' with \,
847 Escape ', ' with \,
847 :type fields: str
848 :type fields: str
848 """
849 """
849 repo = get_repo_or_error(repoid)
850 repo = get_repo_or_error(repoid)
850 include_secrets = False
851 include_secrets = False
851 if has_superadmin_permission(apiuser):
852 if has_superadmin_permission(apiuser):
852 include_secrets = True
853 include_secrets = True
853 else:
854 else:
854 _perms = ('repository.admin',)
855 _perms = ('repository.admin',)
855 has_repo_permissions(apiuser, repoid, repo, _perms)
856 has_repo_permissions(apiuser, repoid, repo, _perms)
856
857
857 updates = {
858 updates = {
858 # update function requires this.
859 # update function requires this.
859 'repo_name': repo.just_name
860 'repo_name': repo.just_name
860 }
861 }
861 repo_group = group
862 repo_group = group
862 if not isinstance(repo_group, Optional):
863 if not isinstance(repo_group, Optional):
863 repo_group = get_repo_group_or_error(repo_group)
864 repo_group = get_repo_group_or_error(repo_group)
864 repo_group = repo_group.group_id
865 repo_group = repo_group.group_id
865
866
866 repo_fork_of = fork_of
867 repo_fork_of = fork_of
867 if not isinstance(repo_fork_of, Optional):
868 if not isinstance(repo_fork_of, Optional):
868 repo_fork_of = get_repo_or_error(repo_fork_of)
869 repo_fork_of = get_repo_or_error(repo_fork_of)
869 repo_fork_of = repo_fork_of.repo_id
870 repo_fork_of = repo_fork_of.repo_id
870
871
871 try:
872 try:
872 store_update(updates, name, 'repo_name')
873 store_update(updates, name, 'repo_name')
873 store_update(updates, repo_group, 'repo_group')
874 store_update(updates, repo_group, 'repo_group')
874 store_update(updates, repo_fork_of, 'fork_id')
875 store_update(updates, repo_fork_of, 'fork_id')
875 store_update(updates, owner, 'user')
876 store_update(updates, owner, 'user')
877 store_update(updates, active, 'active')
876 store_update(updates, description, 'repo_description')
878 store_update(updates, description, 'repo_description')
877 store_update(updates, private, 'repo_private')
879 store_update(updates, private, 'repo_private')
878 store_update(updates, clone_uri, 'clone_uri')
880 store_update(updates, clone_uri, 'clone_uri')
879 store_update(updates, landing_rev, 'repo_landing_rev')
881 store_update(updates, landing_rev, 'repo_landing_rev')
880 store_update(updates, enable_statistics, 'repo_enable_statistics')
882 store_update(updates, enable_statistics, 'repo_enable_statistics')
881 store_update(updates, enable_locking, 'repo_enable_locking')
883 store_update(updates, enable_locking, 'repo_enable_locking')
882 store_update(updates, enable_downloads, 'repo_enable_downloads')
884 store_update(updates, enable_downloads, 'repo_enable_downloads')
883
885
884 # extra fields
886 # extra fields
885 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
887 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
886 if fields:
888 if fields:
887 updates.update(fields)
889 updates.update(fields)
888
890
889 RepoModel().update(repo, **updates)
891 RepoModel().update(repo, **updates)
890 Session().commit()
892 Session().commit()
891 return {
893 return {
892 'msg': 'updated repo ID:%s %s' % (
894 'msg': 'updated repo ID:%s %s' % (
893 repo.repo_id, repo.repo_name),
895 repo.repo_id, repo.repo_name),
894 'repository': repo.get_api_data(
896 'repository': repo.get_api_data(
895 include_secrets=include_secrets)
897 include_secrets=include_secrets)
896 }
898 }
897 except Exception:
899 except Exception:
898 log.exception(
900 log.exception(
899 u"Exception while trying to update the repository %s",
901 u"Exception while trying to update the repository %s",
900 repoid)
902 repoid)
901 raise JSONRPCError('failed to update repo `%s`' % repoid)
903 raise JSONRPCError('failed to update repo `%s`' % repoid)
902
904
903
905
904 @jsonrpc_method()
906 @jsonrpc_method()
905 def fork_repo(request, apiuser, repoid, fork_name,
907 def fork_repo(request, apiuser, repoid, fork_name,
906 owner=Optional(OAttr('apiuser')),
908 owner=Optional(OAttr('apiuser')),
907 description=Optional(''), copy_permissions=Optional(False),
909 description=Optional(''), copy_permissions=Optional(False),
908 private=Optional(False), landing_rev=Optional('rev:tip')):
910 private=Optional(False), landing_rev=Optional('rev:tip')):
909 """
911 """
910 Creates a fork of the specified |repo|.
912 Creates a fork of the specified |repo|.
911
913
912 * If using |RCE| with Celery this will immediately return a success
914 * If using |RCE| with Celery this will immediately return a success
913 message, even though the fork will be created asynchronously.
915 message, even though the fork will be created asynchronously.
914
916
915 This command can only be run using an |authtoken| with fork
917 This command can only be run using an |authtoken| with fork
916 permissions on the |repo|.
918 permissions on the |repo|.
917
919
918 :param apiuser: This is filled automatically from the |authtoken|.
920 :param apiuser: This is filled automatically from the |authtoken|.
919 :type apiuser: AuthUser
921 :type apiuser: AuthUser
920 :param repoid: Set repository name or repository ID.
922 :param repoid: Set repository name or repository ID.
921 :type repoid: str or int
923 :type repoid: str or int
922 :param fork_name: Set the fork name.
924 :param fork_name: Set the fork name.
923 :type fork_name: str
925 :type fork_name: str
924 :param owner: Set the fork owner.
926 :param owner: Set the fork owner.
925 :type owner: str
927 :type owner: str
926 :param description: Set the fork descripton.
928 :param description: Set the fork descripton.
927 :type description: str
929 :type description: str
928 :param copy_permissions: Copy permissions from parent |repo|. The
930 :param copy_permissions: Copy permissions from parent |repo|. The
929 default is False.
931 default is False.
930 :type copy_permissions: bool
932 :type copy_permissions: bool
931 :param private: Make the fork private. The default is False.
933 :param private: Make the fork private. The default is False.
932 :type private: bool
934 :type private: bool
933 :param landing_rev: Set the landing revision. The default is tip.
935 :param landing_rev: Set the landing revision. The default is tip.
934
936
935 Example output:
937 Example output:
936
938
937 .. code-block:: bash
939 .. code-block:: bash
938
940
939 id : <id_for_response>
941 id : <id_for_response>
940 api_key : "<api_key>"
942 api_key : "<api_key>"
941 args: {
943 args: {
942 "repoid" : "<reponame or repo_id>",
944 "repoid" : "<reponame or repo_id>",
943 "fork_name": "<forkname>",
945 "fork_name": "<forkname>",
944 "owner": "<username or user_id = Optional(=apiuser)>",
946 "owner": "<username or user_id = Optional(=apiuser)>",
945 "description": "<description>",
947 "description": "<description>",
946 "copy_permissions": "<bool>",
948 "copy_permissions": "<bool>",
947 "private": "<bool>",
949 "private": "<bool>",
948 "landing_rev": "<landing_rev>"
950 "landing_rev": "<landing_rev>"
949 }
951 }
950
952
951 Example error output:
953 Example error output:
952
954
953 .. code-block:: bash
955 .. code-block:: bash
954
956
955 id : <id_given_in_input>
957 id : <id_given_in_input>
956 result: {
958 result: {
957 "msg": "Created fork of `<reponame>` as `<forkname>`",
959 "msg": "Created fork of `<reponame>` as `<forkname>`",
958 "success": true,
960 "success": true,
959 "task": "<celery task id or None if done sync>"
961 "task": "<celery task id or None if done sync>"
960 }
962 }
961 error: null
963 error: null
962
964
963 """
965 """
964 if not has_superadmin_permission(apiuser):
966 if not has_superadmin_permission(apiuser):
965 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
967 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
966 raise JSONRPCForbidden()
968 raise JSONRPCForbidden()
967
969
968 repo = get_repo_or_error(repoid)
970 repo = get_repo_or_error(repoid)
969 repo_name = repo.repo_name
971 repo_name = repo.repo_name
970
972
971 (fork_name_cleaned,
973 (fork_name_cleaned,
972 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
974 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
973 fork_name)
975 fork_name)
974
976
975 if not has_superadmin_permission(apiuser):
977 if not has_superadmin_permission(apiuser):
976 # check if we have at least read permission for
978 # check if we have at least read permission for
977 # this repo that we fork !
979 # this repo that we fork !
978 _perms = (
980 _perms = (
979 'repository.admin', 'repository.write', 'repository.read')
981 'repository.admin', 'repository.write', 'repository.read')
980 has_repo_permissions(apiuser, repoid, repo, _perms)
982 has_repo_permissions(apiuser, repoid, repo, _perms)
981
983
982 if not isinstance(owner, Optional):
984 if not isinstance(owner, Optional):
983 # forbid setting owner for non super admins
985 # forbid setting owner for non super admins
984 raise JSONRPCError(
986 raise JSONRPCError(
985 'Only RhodeCode admin can specify `owner` param'
987 'Only RhodeCode admin can specify `owner` param'
986 )
988 )
987 # check if we have a create.repo permission if not maybe the parent
989 # check if we have a create.repo permission if not maybe the parent
988 # group permission
990 # group permission
989 if not HasPermissionAnyApi('hg.create.repository')(user=apiuser):
991 if not HasPermissionAnyApi('hg.create.repository')(user=apiuser):
990 if parent_group_name:
992 if parent_group_name:
991 repogroupid = parent_group_name
993 repogroupid = parent_group_name
992 repo_group = get_repo_group_or_error(parent_group_name)
994 repo_group = get_repo_group_or_error(parent_group_name)
993
995
994 _perms = ('group.admin',)
996 _perms = ('group.admin',)
995 if not HasRepoGroupPermissionAnyApi(*_perms)(
997 if not HasRepoGroupPermissionAnyApi(*_perms)(
996 user=apiuser, group_name=repo_group.group_name):
998 user=apiuser, group_name=repo_group.group_name):
997 raise JSONRPCError(
999 raise JSONRPCError(
998 'repository group `%s` does not exist' % (
1000 'repository group `%s` does not exist' % (
999 repogroupid,))
1001 repogroupid,))
1000 else:
1002 else:
1001 raise JSONRPCForbidden()
1003 raise JSONRPCForbidden()
1002
1004
1003 _repo = RepoModel().get_by_repo_name(fork_name)
1005 _repo = RepoModel().get_by_repo_name(fork_name)
1004 if _repo:
1006 if _repo:
1005 type_ = 'fork' if _repo.fork else 'repo'
1007 type_ = 'fork' if _repo.fork else 'repo'
1006 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
1008 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
1007
1009
1008 if isinstance(owner, Optional):
1010 if isinstance(owner, Optional):
1009 owner = apiuser.user_id
1011 owner = apiuser.user_id
1010
1012
1011 owner = get_user_or_error(owner)
1013 owner = get_user_or_error(owner)
1012
1014
1013 try:
1015 try:
1014 # create structure of groups and return the last group
1016 # create structure of groups and return the last group
1015 repo_group = map_groups(fork_name)
1017 repo_group = map_groups(fork_name)
1016 form_data = {
1018 form_data = {
1017 'repo_name': fork_name_cleaned,
1019 'repo_name': fork_name_cleaned,
1018 'repo_name_full': fork_name,
1020 'repo_name_full': fork_name,
1019 'repo_group': repo_group.group_id if repo_group else None,
1021 'repo_group': repo_group.group_id if repo_group else None,
1020 'repo_type': repo.repo_type,
1022 'repo_type': repo.repo_type,
1021 'description': Optional.extract(description),
1023 'description': Optional.extract(description),
1022 'private': Optional.extract(private),
1024 'private': Optional.extract(private),
1023 'copy_permissions': Optional.extract(copy_permissions),
1025 'copy_permissions': Optional.extract(copy_permissions),
1024 'landing_rev': Optional.extract(landing_rev),
1026 'landing_rev': Optional.extract(landing_rev),
1025 'fork_parent_id': repo.repo_id,
1027 'fork_parent_id': repo.repo_id,
1026 }
1028 }
1027
1029
1028 task = RepoModel().create_fork(form_data, cur_user=owner)
1030 task = RepoModel().create_fork(form_data, cur_user=owner)
1029 # no commit, it's done in RepoModel, or async via celery
1031 # no commit, it's done in RepoModel, or async via celery
1030 from celery.result import BaseAsyncResult
1032 from celery.result import BaseAsyncResult
1031 task_id = None
1033 task_id = None
1032 if isinstance(task, BaseAsyncResult):
1034 if isinstance(task, BaseAsyncResult):
1033 task_id = task.task_id
1035 task_id = task.task_id
1034 return {
1036 return {
1035 'msg': 'Created fork of `%s` as `%s`' % (
1037 'msg': 'Created fork of `%s` as `%s`' % (
1036 repo.repo_name, fork_name),
1038 repo.repo_name, fork_name),
1037 'success': True, # cannot return the repo data here since fork
1039 'success': True, # cannot return the repo data here since fork
1038 # can be done async
1040 # can be done async
1039 'task': task_id
1041 'task': task_id
1040 }
1042 }
1041 except Exception:
1043 except Exception:
1042 log.exception("Exception occurred while trying to fork a repo")
1044 log.exception("Exception occurred while trying to fork a repo")
1043 raise JSONRPCError(
1045 raise JSONRPCError(
1044 'failed to fork repository `%s` as `%s`' % (
1046 'failed to fork repository `%s` as `%s`' % (
1045 repo_name, fork_name))
1047 repo_name, fork_name))
1046
1048
1047
1049
1048 @jsonrpc_method()
1050 @jsonrpc_method()
1049 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1051 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1050 """
1052 """
1051 Deletes a repository.
1053 Deletes a repository.
1052
1054
1053 * When the `forks` parameter is set it's possible to detach or delete
1055 * When the `forks` parameter is set it's possible to detach or delete
1054 forks of deleted repository.
1056 forks of deleted repository.
1055
1057
1056 This command can only be run using an |authtoken| with admin
1058 This command can only be run using an |authtoken| with admin
1057 permissions on the |repo|.
1059 permissions on the |repo|.
1058
1060
1059 :param apiuser: This is filled automatically from the |authtoken|.
1061 :param apiuser: This is filled automatically from the |authtoken|.
1060 :type apiuser: AuthUser
1062 :type apiuser: AuthUser
1061 :param repoid: Set the repository name or repository ID.
1063 :param repoid: Set the repository name or repository ID.
1062 :type repoid: str or int
1064 :type repoid: str or int
1063 :param forks: Set to `detach` or `delete` forks from the |repo|.
1065 :param forks: Set to `detach` or `delete` forks from the |repo|.
1064 :type forks: Optional(str)
1066 :type forks: Optional(str)
1065
1067
1066 Example error output:
1068 Example error output:
1067
1069
1068 .. code-block:: bash
1070 .. code-block:: bash
1069
1071
1070 id : <id_given_in_input>
1072 id : <id_given_in_input>
1071 result: {
1073 result: {
1072 "msg": "Deleted repository `<reponame>`",
1074 "msg": "Deleted repository `<reponame>`",
1073 "success": true
1075 "success": true
1074 }
1076 }
1075 error: null
1077 error: null
1076 """
1078 """
1077
1079
1078 repo = get_repo_or_error(repoid)
1080 repo = get_repo_or_error(repoid)
1079 if not has_superadmin_permission(apiuser):
1081 if not has_superadmin_permission(apiuser):
1080 _perms = ('repository.admin',)
1082 _perms = ('repository.admin',)
1081 has_repo_permissions(apiuser, repoid, repo, _perms)
1083 has_repo_permissions(apiuser, repoid, repo, _perms)
1082
1084
1083 try:
1085 try:
1084 handle_forks = Optional.extract(forks)
1086 handle_forks = Optional.extract(forks)
1085 _forks_msg = ''
1087 _forks_msg = ''
1086 _forks = [f for f in repo.forks]
1088 _forks = [f for f in repo.forks]
1087 if handle_forks == 'detach':
1089 if handle_forks == 'detach':
1088 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1090 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1089 elif handle_forks == 'delete':
1091 elif handle_forks == 'delete':
1090 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1092 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1091 elif _forks:
1093 elif _forks:
1092 raise JSONRPCError(
1094 raise JSONRPCError(
1093 'Cannot delete `%s` it still contains attached forks' %
1095 'Cannot delete `%s` it still contains attached forks' %
1094 (repo.repo_name,)
1096 (repo.repo_name,)
1095 )
1097 )
1096
1098
1097 RepoModel().delete(repo, forks=forks)
1099 RepoModel().delete(repo, forks=forks)
1098 Session().commit()
1100 Session().commit()
1099 return {
1101 return {
1100 'msg': 'Deleted repository `%s`%s' % (
1102 'msg': 'Deleted repository `%s`%s' % (
1101 repo.repo_name, _forks_msg),
1103 repo.repo_name, _forks_msg),
1102 'success': True
1104 'success': True
1103 }
1105 }
1104 except Exception:
1106 except Exception:
1105 log.exception("Exception occurred while trying to delete repo")
1107 log.exception("Exception occurred while trying to delete repo")
1106 raise JSONRPCError(
1108 raise JSONRPCError(
1107 'failed to delete repository `%s`' % (repo.repo_name,)
1109 'failed to delete repository `%s`' % (repo.repo_name,)
1108 )
1110 )
1109
1111
1110
1112
1111 #TODO: marcink, change name ?
1113 #TODO: marcink, change name ?
1112 @jsonrpc_method()
1114 @jsonrpc_method()
1113 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1115 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1114 """
1116 """
1115 Invalidates the cache for the specified repository.
1117 Invalidates the cache for the specified repository.
1116
1118
1117 This command can only be run using an |authtoken| with admin rights to
1119 This command can only be run using an |authtoken| with admin rights to
1118 the specified repository.
1120 the specified repository.
1119
1121
1120 This command takes the following options:
1122 This command takes the following options:
1121
1123
1122 :param apiuser: This is filled automatically from |authtoken|.
1124 :param apiuser: This is filled automatically from |authtoken|.
1123 :type apiuser: AuthUser
1125 :type apiuser: AuthUser
1124 :param repoid: Sets the repository name or repository ID.
1126 :param repoid: Sets the repository name or repository ID.
1125 :type repoid: str or int
1127 :type repoid: str or int
1126 :param delete_keys: This deletes the invalidated keys instead of
1128 :param delete_keys: This deletes the invalidated keys instead of
1127 just flagging them.
1129 just flagging them.
1128 :type delete_keys: Optional(``True`` | ``False``)
1130 :type delete_keys: Optional(``True`` | ``False``)
1129
1131
1130 Example output:
1132 Example output:
1131
1133
1132 .. code-block:: bash
1134 .. code-block:: bash
1133
1135
1134 id : <id_given_in_input>
1136 id : <id_given_in_input>
1135 result : {
1137 result : {
1136 'msg': Cache for repository `<repository name>` was invalidated,
1138 'msg': Cache for repository `<repository name>` was invalidated,
1137 'repository': <repository name>
1139 'repository': <repository name>
1138 }
1140 }
1139 error : null
1141 error : null
1140
1142
1141 Example error output:
1143 Example error output:
1142
1144
1143 .. code-block:: bash
1145 .. code-block:: bash
1144
1146
1145 id : <id_given_in_input>
1147 id : <id_given_in_input>
1146 result : null
1148 result : null
1147 error : {
1149 error : {
1148 'Error occurred during cache invalidation action'
1150 'Error occurred during cache invalidation action'
1149 }
1151 }
1150
1152
1151 """
1153 """
1152
1154
1153 repo = get_repo_or_error(repoid)
1155 repo = get_repo_or_error(repoid)
1154 if not has_superadmin_permission(apiuser):
1156 if not has_superadmin_permission(apiuser):
1155 _perms = ('repository.admin', 'repository.write',)
1157 _perms = ('repository.admin', 'repository.write',)
1156 has_repo_permissions(apiuser, repoid, repo, _perms)
1158 has_repo_permissions(apiuser, repoid, repo, _perms)
1157
1159
1158 delete = Optional.extract(delete_keys)
1160 delete = Optional.extract(delete_keys)
1159 try:
1161 try:
1160 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1162 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1161 return {
1163 return {
1162 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1164 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1163 'repository': repo.repo_name
1165 'repository': repo.repo_name
1164 }
1166 }
1165 except Exception:
1167 except Exception:
1166 log.exception(
1168 log.exception(
1167 "Exception occurred while trying to invalidate repo cache")
1169 "Exception occurred while trying to invalidate repo cache")
1168 raise JSONRPCError(
1170 raise JSONRPCError(
1169 'Error occurred during cache invalidation action'
1171 'Error occurred during cache invalidation action'
1170 )
1172 )
1171
1173
1172
1174
1173 #TODO: marcink, change name ?
1175 #TODO: marcink, change name ?
1174 @jsonrpc_method()
1176 @jsonrpc_method()
1175 def lock(request, apiuser, repoid, locked=Optional(None),
1177 def lock(request, apiuser, repoid, locked=Optional(None),
1176 userid=Optional(OAttr('apiuser'))):
1178 userid=Optional(OAttr('apiuser'))):
1177 """
1179 """
1178 Sets the lock state of the specified |repo| by the given user.
1180 Sets the lock state of the specified |repo| by the given user.
1179 From more information, see :ref:`repo-locking`.
1181 From more information, see :ref:`repo-locking`.
1180
1182
1181 * If the ``userid`` option is not set, the repository is locked to the
1183 * If the ``userid`` option is not set, the repository is locked to the
1182 user who called the method.
1184 user who called the method.
1183 * If the ``locked`` parameter is not set, the current lock state of the
1185 * If the ``locked`` parameter is not set, the current lock state of the
1184 repository is displayed.
1186 repository is displayed.
1185
1187
1186 This command can only be run using an |authtoken| with admin rights to
1188 This command can only be run using an |authtoken| with admin rights to
1187 the specified repository.
1189 the specified repository.
1188
1190
1189 This command takes the following options:
1191 This command takes the following options:
1190
1192
1191 :param apiuser: This is filled automatically from the |authtoken|.
1193 :param apiuser: This is filled automatically from the |authtoken|.
1192 :type apiuser: AuthUser
1194 :type apiuser: AuthUser
1193 :param repoid: Sets the repository name or repository ID.
1195 :param repoid: Sets the repository name or repository ID.
1194 :type repoid: str or int
1196 :type repoid: str or int
1195 :param locked: Sets the lock state.
1197 :param locked: Sets the lock state.
1196 :type locked: Optional(``True`` | ``False``)
1198 :type locked: Optional(``True`` | ``False``)
1197 :param userid: Set the repository lock to this user.
1199 :param userid: Set the repository lock to this user.
1198 :type userid: Optional(str or int)
1200 :type userid: Optional(str or int)
1199
1201
1200 Example error output:
1202 Example error output:
1201
1203
1202 .. code-block:: bash
1204 .. code-block:: bash
1203
1205
1204 id : <id_given_in_input>
1206 id : <id_given_in_input>
1205 result : {
1207 result : {
1206 'repo': '<reponame>',
1208 'repo': '<reponame>',
1207 'locked': <bool: lock state>,
1209 'locked': <bool: lock state>,
1208 'locked_since': <int: lock timestamp>,
1210 'locked_since': <int: lock timestamp>,
1209 'locked_by': <username of person who made the lock>,
1211 'locked_by': <username of person who made the lock>,
1210 'lock_reason': <str: reason for locking>,
1212 'lock_reason': <str: reason for locking>,
1211 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1213 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1212 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1214 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1213 or
1215 or
1214 'msg': 'Repo `<repository name>` not locked.'
1216 'msg': 'Repo `<repository name>` not locked.'
1215 or
1217 or
1216 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1218 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1217 }
1219 }
1218 error : null
1220 error : null
1219
1221
1220 Example error output:
1222 Example error output:
1221
1223
1222 .. code-block:: bash
1224 .. code-block:: bash
1223
1225
1224 id : <id_given_in_input>
1226 id : <id_given_in_input>
1225 result : null
1227 result : null
1226 error : {
1228 error : {
1227 'Error occurred locking repository `<reponame>`
1229 'Error occurred locking repository `<reponame>`
1228 }
1230 }
1229 """
1231 """
1230
1232
1231 repo = get_repo_or_error(repoid)
1233 repo = get_repo_or_error(repoid)
1232 if not has_superadmin_permission(apiuser):
1234 if not has_superadmin_permission(apiuser):
1233 # check if we have at least write permission for this repo !
1235 # check if we have at least write permission for this repo !
1234 _perms = ('repository.admin', 'repository.write',)
1236 _perms = ('repository.admin', 'repository.write',)
1235 has_repo_permissions(apiuser, repoid, repo, _perms)
1237 has_repo_permissions(apiuser, repoid, repo, _perms)
1236
1238
1237 # make sure normal user does not pass someone else userid,
1239 # make sure normal user does not pass someone else userid,
1238 # he is not allowed to do that
1240 # he is not allowed to do that
1239 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1241 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1240 raise JSONRPCError('userid is not the same as your user')
1242 raise JSONRPCError('userid is not the same as your user')
1241
1243
1242 if isinstance(userid, Optional):
1244 if isinstance(userid, Optional):
1243 userid = apiuser.user_id
1245 userid = apiuser.user_id
1244
1246
1245 user = get_user_or_error(userid)
1247 user = get_user_or_error(userid)
1246
1248
1247 if isinstance(locked, Optional):
1249 if isinstance(locked, Optional):
1248 lockobj = repo.locked
1250 lockobj = repo.locked
1249
1251
1250 if lockobj[0] is None:
1252 if lockobj[0] is None:
1251 _d = {
1253 _d = {
1252 'repo': repo.repo_name,
1254 'repo': repo.repo_name,
1253 'locked': False,
1255 'locked': False,
1254 'locked_since': None,
1256 'locked_since': None,
1255 'locked_by': None,
1257 'locked_by': None,
1256 'lock_reason': None,
1258 'lock_reason': None,
1257 'lock_state_changed': False,
1259 'lock_state_changed': False,
1258 'msg': 'Repo `%s` not locked.' % repo.repo_name
1260 'msg': 'Repo `%s` not locked.' % repo.repo_name
1259 }
1261 }
1260 return _d
1262 return _d
1261 else:
1263 else:
1262 _user_id, _time, _reason = lockobj
1264 _user_id, _time, _reason = lockobj
1263 lock_user = get_user_or_error(userid)
1265 lock_user = get_user_or_error(userid)
1264 _d = {
1266 _d = {
1265 'repo': repo.repo_name,
1267 'repo': repo.repo_name,
1266 'locked': True,
1268 'locked': True,
1267 'locked_since': _time,
1269 'locked_since': _time,
1268 'locked_by': lock_user.username,
1270 'locked_by': lock_user.username,
1269 'lock_reason': _reason,
1271 'lock_reason': _reason,
1270 'lock_state_changed': False,
1272 'lock_state_changed': False,
1271 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1273 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1272 % (repo.repo_name, lock_user.username,
1274 % (repo.repo_name, lock_user.username,
1273 json.dumps(time_to_datetime(_time))))
1275 json.dumps(time_to_datetime(_time))))
1274 }
1276 }
1275 return _d
1277 return _d
1276
1278
1277 # force locked state through a flag
1279 # force locked state through a flag
1278 else:
1280 else:
1279 locked = str2bool(locked)
1281 locked = str2bool(locked)
1280 lock_reason = Repository.LOCK_API
1282 lock_reason = Repository.LOCK_API
1281 try:
1283 try:
1282 if locked:
1284 if locked:
1283 lock_time = time.time()
1285 lock_time = time.time()
1284 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1286 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1285 else:
1287 else:
1286 lock_time = None
1288 lock_time = None
1287 Repository.unlock(repo)
1289 Repository.unlock(repo)
1288 _d = {
1290 _d = {
1289 'repo': repo.repo_name,
1291 'repo': repo.repo_name,
1290 'locked': locked,
1292 'locked': locked,
1291 'locked_since': lock_time,
1293 'locked_since': lock_time,
1292 'locked_by': user.username,
1294 'locked_by': user.username,
1293 'lock_reason': lock_reason,
1295 'lock_reason': lock_reason,
1294 'lock_state_changed': True,
1296 'lock_state_changed': True,
1295 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1297 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1296 % (user.username, repo.repo_name, locked))
1298 % (user.username, repo.repo_name, locked))
1297 }
1299 }
1298 return _d
1300 return _d
1299 except Exception:
1301 except Exception:
1300 log.exception(
1302 log.exception(
1301 "Exception occurred while trying to lock repository")
1303 "Exception occurred while trying to lock repository")
1302 raise JSONRPCError(
1304 raise JSONRPCError(
1303 'Error occurred locking repository `%s`' % repo.repo_name
1305 'Error occurred locking repository `%s`' % repo.repo_name
1304 )
1306 )
1305
1307
1306
1308
1307 @jsonrpc_method()
1309 @jsonrpc_method()
1308 def comment_commit(
1310 def comment_commit(
1309 request, apiuser, repoid, commit_id, message,
1311 request, apiuser, repoid, commit_id, message,
1310 userid=Optional(OAttr('apiuser')), status=Optional(None)):
1312 userid=Optional(OAttr('apiuser')), status=Optional(None)):
1311 """
1313 """
1312 Set a commit comment, and optionally change the status of the commit.
1314 Set a commit comment, and optionally change the status of the commit.
1313 This command can be executed only using api_key belonging to user
1315 This command can be executed only using api_key belonging to user
1314 with admin rights, or repository administrator.
1316 with admin rights, or repository administrator.
1315
1317
1316 :param apiuser: This is filled automatically from the |authtoken|.
1318 :param apiuser: This is filled automatically from the |authtoken|.
1317 :type apiuser: AuthUser
1319 :type apiuser: AuthUser
1318 :param repoid: Set the repository name or repository ID.
1320 :param repoid: Set the repository name or repository ID.
1319 :type repoid: str or int
1321 :type repoid: str or int
1320 :param commit_id: Specify the commit_id for which to set a comment.
1322 :param commit_id: Specify the commit_id for which to set a comment.
1321 :type commit_id: str
1323 :type commit_id: str
1322 :param message: The comment text.
1324 :param message: The comment text.
1323 :type message: str
1325 :type message: str
1324 :param userid: Set the user name of the comment creator.
1326 :param userid: Set the user name of the comment creator.
1325 :type userid: Optional(str or int)
1327 :type userid: Optional(str or int)
1326 :param status: status, one of 'not_reviewed', 'approved', 'rejected',
1328 :param status: status, one of 'not_reviewed', 'approved', 'rejected',
1327 'under_review'
1329 'under_review'
1328 :type status: str
1330 :type status: str
1329
1331
1330 Example error output:
1332 Example error output:
1331
1333
1332 .. code-block:: json
1334 .. code-block:: json
1333
1335
1334 {
1336 {
1335 "id" : <id_given_in_input>,
1337 "id" : <id_given_in_input>,
1336 "result" : {
1338 "result" : {
1337 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1339 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1338 "status_change": null or <status>,
1340 "status_change": null or <status>,
1339 "success": true
1341 "success": true
1340 },
1342 },
1341 "error" : null
1343 "error" : null
1342 }
1344 }
1343
1345
1344 """
1346 """
1345 repo = get_repo_or_error(repoid)
1347 repo = get_repo_or_error(repoid)
1346 if not has_superadmin_permission(apiuser):
1348 if not has_superadmin_permission(apiuser):
1347 _perms = ('repository.admin',)
1349 _perms = ('repository.admin',)
1348 has_repo_permissions(apiuser, repoid, repo, _perms)
1350 has_repo_permissions(apiuser, repoid, repo, _perms)
1349
1351
1350 if isinstance(userid, Optional):
1352 if isinstance(userid, Optional):
1351 userid = apiuser.user_id
1353 userid = apiuser.user_id
1352
1354
1353 user = get_user_or_error(userid)
1355 user = get_user_or_error(userid)
1354 status = Optional.extract(status)
1356 status = Optional.extract(status)
1355
1357
1356 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1358 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1357 if status and status not in allowed_statuses:
1359 if status and status not in allowed_statuses:
1358 raise JSONRPCError('Bad status, must be on '
1360 raise JSONRPCError('Bad status, must be on '
1359 'of %s got %s' % (allowed_statuses, status,))
1361 'of %s got %s' % (allowed_statuses, status,))
1360
1362
1361 try:
1363 try:
1362 rc_config = SettingsModel().get_all_settings()
1364 rc_config = SettingsModel().get_all_settings()
1363 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1365 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1364
1366
1365 comm = ChangesetCommentsModel().create(
1367 comm = ChangesetCommentsModel().create(
1366 message, repo, user, revision=commit_id, status_change=status,
1368 message, repo, user, revision=commit_id, status_change=status,
1367 renderer=renderer)
1369 renderer=renderer)
1368 if status:
1370 if status:
1369 # also do a status change
1371 # also do a status change
1370 try:
1372 try:
1371 ChangesetStatusModel().set_status(
1373 ChangesetStatusModel().set_status(
1372 repo, status, user, comm, revision=commit_id,
1374 repo, status, user, comm, revision=commit_id,
1373 dont_allow_on_closed_pull_request=True
1375 dont_allow_on_closed_pull_request=True
1374 )
1376 )
1375 except StatusChangeOnClosedPullRequestError:
1377 except StatusChangeOnClosedPullRequestError:
1376 log.exception(
1378 log.exception(
1377 "Exception occurred while trying to change repo commit status")
1379 "Exception occurred while trying to change repo commit status")
1378 msg = ('Changing status on a changeset associated with '
1380 msg = ('Changing status on a changeset associated with '
1379 'a closed pull request is not allowed')
1381 'a closed pull request is not allowed')
1380 raise JSONRPCError(msg)
1382 raise JSONRPCError(msg)
1381
1383
1382 Session().commit()
1384 Session().commit()
1383 return {
1385 return {
1384 'msg': (
1386 'msg': (
1385 'Commented on commit `%s` for repository `%s`' % (
1387 'Commented on commit `%s` for repository `%s`' % (
1386 comm.revision, repo.repo_name)),
1388 comm.revision, repo.repo_name)),
1387 'status_change': status,
1389 'status_change': status,
1388 'success': True,
1390 'success': True,
1389 }
1391 }
1390 except JSONRPCError:
1392 except JSONRPCError:
1391 # catch any inside errors, and re-raise them to prevent from
1393 # catch any inside errors, and re-raise them to prevent from
1392 # below global catch to silence them
1394 # below global catch to silence them
1393 raise
1395 raise
1394 except Exception:
1396 except Exception:
1395 log.exception("Exception occurred while trying to comment on commit")
1397 log.exception("Exception occurred while trying to comment on commit")
1396 raise JSONRPCError(
1398 raise JSONRPCError(
1397 'failed to set comment on repository `%s`' % (repo.repo_name,)
1399 'failed to set comment on repository `%s`' % (repo.repo_name,)
1398 )
1400 )
1399
1401
1400
1402
1401 @jsonrpc_method()
1403 @jsonrpc_method()
1402 def grant_user_permission(request, apiuser, repoid, userid, perm):
1404 def grant_user_permission(request, apiuser, repoid, userid, perm):
1403 """
1405 """
1404 Grant permissions for the specified user on the given repository,
1406 Grant permissions for the specified user on the given repository,
1405 or update existing permissions if found.
1407 or update existing permissions if found.
1406
1408
1407 This command can only be run using an |authtoken| with admin
1409 This command can only be run using an |authtoken| with admin
1408 permissions on the |repo|.
1410 permissions on the |repo|.
1409
1411
1410 :param apiuser: This is filled automatically from the |authtoken|.
1412 :param apiuser: This is filled automatically from the |authtoken|.
1411 :type apiuser: AuthUser
1413 :type apiuser: AuthUser
1412 :param repoid: Set the repository name or repository ID.
1414 :param repoid: Set the repository name or repository ID.
1413 :type repoid: str or int
1415 :type repoid: str or int
1414 :param userid: Set the user name.
1416 :param userid: Set the user name.
1415 :type userid: str
1417 :type userid: str
1416 :param perm: Set the user permissions, using the following format
1418 :param perm: Set the user permissions, using the following format
1417 ``(repository.(none|read|write|admin))``
1419 ``(repository.(none|read|write|admin))``
1418 :type perm: str
1420 :type perm: str
1419
1421
1420 Example output:
1422 Example output:
1421
1423
1422 .. code-block:: bash
1424 .. code-block:: bash
1423
1425
1424 id : <id_given_in_input>
1426 id : <id_given_in_input>
1425 result: {
1427 result: {
1426 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1428 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1427 "success": true
1429 "success": true
1428 }
1430 }
1429 error: null
1431 error: null
1430 """
1432 """
1431
1433
1432 repo = get_repo_or_error(repoid)
1434 repo = get_repo_or_error(repoid)
1433 user = get_user_or_error(userid)
1435 user = get_user_or_error(userid)
1434 perm = get_perm_or_error(perm)
1436 perm = get_perm_or_error(perm)
1435 if not has_superadmin_permission(apiuser):
1437 if not has_superadmin_permission(apiuser):
1436 _perms = ('repository.admin',)
1438 _perms = ('repository.admin',)
1437 has_repo_permissions(apiuser, repoid, repo, _perms)
1439 has_repo_permissions(apiuser, repoid, repo, _perms)
1438
1440
1439 try:
1441 try:
1440
1442
1441 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1443 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1442
1444
1443 Session().commit()
1445 Session().commit()
1444 return {
1446 return {
1445 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1447 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1446 perm.permission_name, user.username, repo.repo_name
1448 perm.permission_name, user.username, repo.repo_name
1447 ),
1449 ),
1448 'success': True
1450 'success': True
1449 }
1451 }
1450 except Exception:
1452 except Exception:
1451 log.exception(
1453 log.exception(
1452 "Exception occurred while trying edit permissions for repo")
1454 "Exception occurred while trying edit permissions for repo")
1453 raise JSONRPCError(
1455 raise JSONRPCError(
1454 'failed to edit permission for user: `%s` in repo: `%s`' % (
1456 'failed to edit permission for user: `%s` in repo: `%s`' % (
1455 userid, repoid
1457 userid, repoid
1456 )
1458 )
1457 )
1459 )
1458
1460
1459
1461
1460 @jsonrpc_method()
1462 @jsonrpc_method()
1461 def revoke_user_permission(request, apiuser, repoid, userid):
1463 def revoke_user_permission(request, apiuser, repoid, userid):
1462 """
1464 """
1463 Revoke permission for a user on the specified repository.
1465 Revoke permission for a user on the specified repository.
1464
1466
1465 This command can only be run using an |authtoken| with admin
1467 This command can only be run using an |authtoken| with admin
1466 permissions on the |repo|.
1468 permissions on the |repo|.
1467
1469
1468 :param apiuser: This is filled automatically from the |authtoken|.
1470 :param apiuser: This is filled automatically from the |authtoken|.
1469 :type apiuser: AuthUser
1471 :type apiuser: AuthUser
1470 :param repoid: Set the repository name or repository ID.
1472 :param repoid: Set the repository name or repository ID.
1471 :type repoid: str or int
1473 :type repoid: str or int
1472 :param userid: Set the user name of revoked user.
1474 :param userid: Set the user name of revoked user.
1473 :type userid: str or int
1475 :type userid: str or int
1474
1476
1475 Example error output:
1477 Example error output:
1476
1478
1477 .. code-block:: bash
1479 .. code-block:: bash
1478
1480
1479 id : <id_given_in_input>
1481 id : <id_given_in_input>
1480 result: {
1482 result: {
1481 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1483 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1482 "success": true
1484 "success": true
1483 }
1485 }
1484 error: null
1486 error: null
1485 """
1487 """
1486
1488
1487 repo = get_repo_or_error(repoid)
1489 repo = get_repo_or_error(repoid)
1488 user = get_user_or_error(userid)
1490 user = get_user_or_error(userid)
1489 if not has_superadmin_permission(apiuser):
1491 if not has_superadmin_permission(apiuser):
1490 _perms = ('repository.admin',)
1492 _perms = ('repository.admin',)
1491 has_repo_permissions(apiuser, repoid, repo, _perms)
1493 has_repo_permissions(apiuser, repoid, repo, _perms)
1492
1494
1493 try:
1495 try:
1494 RepoModel().revoke_user_permission(repo=repo, user=user)
1496 RepoModel().revoke_user_permission(repo=repo, user=user)
1495 Session().commit()
1497 Session().commit()
1496 return {
1498 return {
1497 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1499 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1498 user.username, repo.repo_name
1500 user.username, repo.repo_name
1499 ),
1501 ),
1500 'success': True
1502 'success': True
1501 }
1503 }
1502 except Exception:
1504 except Exception:
1503 log.exception(
1505 log.exception(
1504 "Exception occurred while trying revoke permissions to repo")
1506 "Exception occurred while trying revoke permissions to repo")
1505 raise JSONRPCError(
1507 raise JSONRPCError(
1506 'failed to edit permission for user: `%s` in repo: `%s`' % (
1508 'failed to edit permission for user: `%s` in repo: `%s`' % (
1507 userid, repoid
1509 userid, repoid
1508 )
1510 )
1509 )
1511 )
1510
1512
1511
1513
1512 @jsonrpc_method()
1514 @jsonrpc_method()
1513 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1515 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1514 """
1516 """
1515 Grant permission for a user group on the specified repository,
1517 Grant permission for a user group on the specified repository,
1516 or update existing permissions.
1518 or update existing permissions.
1517
1519
1518 This command can only be run using an |authtoken| with admin
1520 This command can only be run using an |authtoken| with admin
1519 permissions on the |repo|.
1521 permissions on the |repo|.
1520
1522
1521 :param apiuser: This is filled automatically from the |authtoken|.
1523 :param apiuser: This is filled automatically from the |authtoken|.
1522 :type apiuser: AuthUser
1524 :type apiuser: AuthUser
1523 :param repoid: Set the repository name or repository ID.
1525 :param repoid: Set the repository name or repository ID.
1524 :type repoid: str or int
1526 :type repoid: str or int
1525 :param usergroupid: Specify the ID of the user group.
1527 :param usergroupid: Specify the ID of the user group.
1526 :type usergroupid: str or int
1528 :type usergroupid: str or int
1527 :param perm: Set the user group permissions using the following
1529 :param perm: Set the user group permissions using the following
1528 format: (repository.(none|read|write|admin))
1530 format: (repository.(none|read|write|admin))
1529 :type perm: str
1531 :type perm: str
1530
1532
1531 Example output:
1533 Example output:
1532
1534
1533 .. code-block:: bash
1535 .. code-block:: bash
1534
1536
1535 id : <id_given_in_input>
1537 id : <id_given_in_input>
1536 result : {
1538 result : {
1537 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1539 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1538 "success": true
1540 "success": true
1539
1541
1540 }
1542 }
1541 error : null
1543 error : null
1542
1544
1543 Example error output:
1545 Example error output:
1544
1546
1545 .. code-block:: bash
1547 .. code-block:: bash
1546
1548
1547 id : <id_given_in_input>
1549 id : <id_given_in_input>
1548 result : null
1550 result : null
1549 error : {
1551 error : {
1550 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1552 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1551 }
1553 }
1552
1554
1553 """
1555 """
1554
1556
1555 repo = get_repo_or_error(repoid)
1557 repo = get_repo_or_error(repoid)
1556 perm = get_perm_or_error(perm)
1558 perm = get_perm_or_error(perm)
1557 if not has_superadmin_permission(apiuser):
1559 if not has_superadmin_permission(apiuser):
1558 _perms = ('repository.admin',)
1560 _perms = ('repository.admin',)
1559 has_repo_permissions(apiuser, repoid, repo, _perms)
1561 has_repo_permissions(apiuser, repoid, repo, _perms)
1560
1562
1561 user_group = get_user_group_or_error(usergroupid)
1563 user_group = get_user_group_or_error(usergroupid)
1562 if not has_superadmin_permission(apiuser):
1564 if not has_superadmin_permission(apiuser):
1563 # check if we have at least read permission for this user group !
1565 # check if we have at least read permission for this user group !
1564 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1566 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1565 if not HasUserGroupPermissionAnyApi(*_perms)(
1567 if not HasUserGroupPermissionAnyApi(*_perms)(
1566 user=apiuser, user_group_name=user_group.users_group_name):
1568 user=apiuser, user_group_name=user_group.users_group_name):
1567 raise JSONRPCError(
1569 raise JSONRPCError(
1568 'user group `%s` does not exist' % (usergroupid,))
1570 'user group `%s` does not exist' % (usergroupid,))
1569
1571
1570 try:
1572 try:
1571 RepoModel().grant_user_group_permission(
1573 RepoModel().grant_user_group_permission(
1572 repo=repo, group_name=user_group, perm=perm)
1574 repo=repo, group_name=user_group, perm=perm)
1573
1575
1574 Session().commit()
1576 Session().commit()
1575 return {
1577 return {
1576 'msg': 'Granted perm: `%s` for user group: `%s` in '
1578 'msg': 'Granted perm: `%s` for user group: `%s` in '
1577 'repo: `%s`' % (
1579 'repo: `%s`' % (
1578 perm.permission_name, user_group.users_group_name,
1580 perm.permission_name, user_group.users_group_name,
1579 repo.repo_name
1581 repo.repo_name
1580 ),
1582 ),
1581 'success': True
1583 'success': True
1582 }
1584 }
1583 except Exception:
1585 except Exception:
1584 log.exception(
1586 log.exception(
1585 "Exception occurred while trying change permission on repo")
1587 "Exception occurred while trying change permission on repo")
1586 raise JSONRPCError(
1588 raise JSONRPCError(
1587 'failed to edit permission for user group: `%s` in '
1589 'failed to edit permission for user group: `%s` in '
1588 'repo: `%s`' % (
1590 'repo: `%s`' % (
1589 usergroupid, repo.repo_name
1591 usergroupid, repo.repo_name
1590 )
1592 )
1591 )
1593 )
1592
1594
1593
1595
1594 @jsonrpc_method()
1596 @jsonrpc_method()
1595 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1597 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1596 """
1598 """
1597 Revoke the permissions of a user group on a given repository.
1599 Revoke the permissions of a user group on a given repository.
1598
1600
1599 This command can only be run using an |authtoken| with admin
1601 This command can only be run using an |authtoken| with admin
1600 permissions on the |repo|.
1602 permissions on the |repo|.
1601
1603
1602 :param apiuser: This is filled automatically from the |authtoken|.
1604 :param apiuser: This is filled automatically from the |authtoken|.
1603 :type apiuser: AuthUser
1605 :type apiuser: AuthUser
1604 :param repoid: Set the repository name or repository ID.
1606 :param repoid: Set the repository name or repository ID.
1605 :type repoid: str or int
1607 :type repoid: str or int
1606 :param usergroupid: Specify the user group ID.
1608 :param usergroupid: Specify the user group ID.
1607 :type usergroupid: str or int
1609 :type usergroupid: str or int
1608
1610
1609 Example output:
1611 Example output:
1610
1612
1611 .. code-block:: bash
1613 .. code-block:: bash
1612
1614
1613 id : <id_given_in_input>
1615 id : <id_given_in_input>
1614 result: {
1616 result: {
1615 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1617 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1616 "success": true
1618 "success": true
1617 }
1619 }
1618 error: null
1620 error: null
1619 """
1621 """
1620
1622
1621 repo = get_repo_or_error(repoid)
1623 repo = get_repo_or_error(repoid)
1622 if not has_superadmin_permission(apiuser):
1624 if not has_superadmin_permission(apiuser):
1623 _perms = ('repository.admin',)
1625 _perms = ('repository.admin',)
1624 has_repo_permissions(apiuser, repoid, repo, _perms)
1626 has_repo_permissions(apiuser, repoid, repo, _perms)
1625
1627
1626 user_group = get_user_group_or_error(usergroupid)
1628 user_group = get_user_group_or_error(usergroupid)
1627 if not has_superadmin_permission(apiuser):
1629 if not has_superadmin_permission(apiuser):
1628 # check if we have at least read permission for this user group !
1630 # check if we have at least read permission for this user group !
1629 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1631 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1630 if not HasUserGroupPermissionAnyApi(*_perms)(
1632 if not HasUserGroupPermissionAnyApi(*_perms)(
1631 user=apiuser, user_group_name=user_group.users_group_name):
1633 user=apiuser, user_group_name=user_group.users_group_name):
1632 raise JSONRPCError(
1634 raise JSONRPCError(
1633 'user group `%s` does not exist' % (usergroupid,))
1635 'user group `%s` does not exist' % (usergroupid,))
1634
1636
1635 try:
1637 try:
1636 RepoModel().revoke_user_group_permission(
1638 RepoModel().revoke_user_group_permission(
1637 repo=repo, group_name=user_group)
1639 repo=repo, group_name=user_group)
1638
1640
1639 Session().commit()
1641 Session().commit()
1640 return {
1642 return {
1641 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1643 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1642 user_group.users_group_name, repo.repo_name
1644 user_group.users_group_name, repo.repo_name
1643 ),
1645 ),
1644 'success': True
1646 'success': True
1645 }
1647 }
1646 except Exception:
1648 except Exception:
1647 log.exception("Exception occurred while trying revoke "
1649 log.exception("Exception occurred while trying revoke "
1648 "user group permission on repo")
1650 "user group permission on repo")
1649 raise JSONRPCError(
1651 raise JSONRPCError(
1650 'failed to edit permission for user group: `%s` in '
1652 'failed to edit permission for user group: `%s` in '
1651 'repo: `%s`' % (
1653 'repo: `%s`' % (
1652 user_group.users_group_name, repo.repo_name
1654 user_group.users_group_name, repo.repo_name
1653 )
1655 )
1654 )
1656 )
1655
1657
1656
1658
1657 @jsonrpc_method()
1659 @jsonrpc_method()
1658 def pull(request, apiuser, repoid):
1660 def pull(request, apiuser, repoid):
1659 """
1661 """
1660 Triggers a pull on the given repository from a remote location. You
1662 Triggers a pull on the given repository from a remote location. You
1661 can use this to keep remote repositories up-to-date.
1663 can use this to keep remote repositories up-to-date.
1662
1664
1663 This command can only be run using an |authtoken| with admin
1665 This command can only be run using an |authtoken| with admin
1664 rights to the specified repository. For more information,
1666 rights to the specified repository. For more information,
1665 see :ref:`config-token-ref`.
1667 see :ref:`config-token-ref`.
1666
1668
1667 This command takes the following options:
1669 This command takes the following options:
1668
1670
1669 :param apiuser: This is filled automatically from the |authtoken|.
1671 :param apiuser: This is filled automatically from the |authtoken|.
1670 :type apiuser: AuthUser
1672 :type apiuser: AuthUser
1671 :param repoid: The repository name or repository ID.
1673 :param repoid: The repository name or repository ID.
1672 :type repoid: str or int
1674 :type repoid: str or int
1673
1675
1674 Example output:
1676 Example output:
1675
1677
1676 .. code-block:: bash
1678 .. code-block:: bash
1677
1679
1678 id : <id_given_in_input>
1680 id : <id_given_in_input>
1679 result : {
1681 result : {
1680 "msg": "Pulled from `<repository name>`"
1682 "msg": "Pulled from `<repository name>`"
1681 "repository": "<repository name>"
1683 "repository": "<repository name>"
1682 }
1684 }
1683 error : null
1685 error : null
1684
1686
1685 Example error output:
1687 Example error output:
1686
1688
1687 .. code-block:: bash
1689 .. code-block:: bash
1688
1690
1689 id : <id_given_in_input>
1691 id : <id_given_in_input>
1690 result : null
1692 result : null
1691 error : {
1693 error : {
1692 "Unable to pull changes from `<reponame>`"
1694 "Unable to pull changes from `<reponame>`"
1693 }
1695 }
1694
1696
1695 """
1697 """
1696
1698
1697 repo = get_repo_or_error(repoid)
1699 repo = get_repo_or_error(repoid)
1698 if not has_superadmin_permission(apiuser):
1700 if not has_superadmin_permission(apiuser):
1699 _perms = ('repository.admin',)
1701 _perms = ('repository.admin',)
1700 has_repo_permissions(apiuser, repoid, repo, _perms)
1702 has_repo_permissions(apiuser, repoid, repo, _perms)
1701
1703
1702 try:
1704 try:
1703 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1705 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1704 return {
1706 return {
1705 'msg': 'Pulled from `%s`' % repo.repo_name,
1707 'msg': 'Pulled from `%s`' % repo.repo_name,
1706 'repository': repo.repo_name
1708 'repository': repo.repo_name
1707 }
1709 }
1708 except Exception:
1710 except Exception:
1709 log.exception("Exception occurred while trying to "
1711 log.exception("Exception occurred while trying to "
1710 "pull changes from remote location")
1712 "pull changes from remote location")
1711 raise JSONRPCError(
1713 raise JSONRPCError(
1712 'Unable to pull changes from `%s`' % repo.repo_name
1714 'Unable to pull changes from `%s`' % repo.repo_name
1713 )
1715 )
1714
1716
1715
1717
1716 @jsonrpc_method()
1718 @jsonrpc_method()
1717 def strip(request, apiuser, repoid, revision, branch):
1719 def strip(request, apiuser, repoid, revision, branch):
1718 """
1720 """
1719 Strips the given revision from the specified repository.
1721 Strips the given revision from the specified repository.
1720
1722
1721 * This will remove the revision and all of its decendants.
1723 * This will remove the revision and all of its decendants.
1722
1724
1723 This command can only be run using an |authtoken| with admin rights to
1725 This command can only be run using an |authtoken| with admin rights to
1724 the specified repository.
1726 the specified repository.
1725
1727
1726 This command takes the following options:
1728 This command takes the following options:
1727
1729
1728 :param apiuser: This is filled automatically from the |authtoken|.
1730 :param apiuser: This is filled automatically from the |authtoken|.
1729 :type apiuser: AuthUser
1731 :type apiuser: AuthUser
1730 :param repoid: The repository name or repository ID.
1732 :param repoid: The repository name or repository ID.
1731 :type repoid: str or int
1733 :type repoid: str or int
1732 :param revision: The revision you wish to strip.
1734 :param revision: The revision you wish to strip.
1733 :type revision: str
1735 :type revision: str
1734 :param branch: The branch from which to strip the revision.
1736 :param branch: The branch from which to strip the revision.
1735 :type branch: str
1737 :type branch: str
1736
1738
1737 Example output:
1739 Example output:
1738
1740
1739 .. code-block:: bash
1741 .. code-block:: bash
1740
1742
1741 id : <id_given_in_input>
1743 id : <id_given_in_input>
1742 result : {
1744 result : {
1743 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1745 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1744 "repository": "<repository name>"
1746 "repository": "<repository name>"
1745 }
1747 }
1746 error : null
1748 error : null
1747
1749
1748 Example error output:
1750 Example error output:
1749
1751
1750 .. code-block:: bash
1752 .. code-block:: bash
1751
1753
1752 id : <id_given_in_input>
1754 id : <id_given_in_input>
1753 result : null
1755 result : null
1754 error : {
1756 error : {
1755 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1757 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1756 }
1758 }
1757
1759
1758 """
1760 """
1759
1761
1760 repo = get_repo_or_error(repoid)
1762 repo = get_repo_or_error(repoid)
1761 if not has_superadmin_permission(apiuser):
1763 if not has_superadmin_permission(apiuser):
1762 _perms = ('repository.admin',)
1764 _perms = ('repository.admin',)
1763 has_repo_permissions(apiuser, repoid, repo, _perms)
1765 has_repo_permissions(apiuser, repoid, repo, _perms)
1764
1766
1765 try:
1767 try:
1766 ScmModel().strip(repo, revision, branch)
1768 ScmModel().strip(repo, revision, branch)
1767 return {
1769 return {
1768 'msg': 'Stripped commit %s from repo `%s`' % (
1770 'msg': 'Stripped commit %s from repo `%s`' % (
1769 revision, repo.repo_name),
1771 revision, repo.repo_name),
1770 'repository': repo.repo_name
1772 'repository': repo.repo_name
1771 }
1773 }
1772 except Exception:
1774 except Exception:
1773 log.exception("Exception while trying to strip")
1775 log.exception("Exception while trying to strip")
1774 raise JSONRPCError(
1776 raise JSONRPCError(
1775 'Unable to strip commit %s from repo `%s`' % (
1777 'Unable to strip commit %s from repo `%s`' % (
1776 revision, repo.repo_name)
1778 revision, repo.repo_name)
1777 )
1779 )
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,511 +1,517 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2016 RhodeCode GmbH
3 # Copyright (C) 2011-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 user group model for RhodeCode
23 user group model for RhodeCode
24 """
24 """
25
25
26
26
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from rhodecode.lib.utils2 import safe_str
30 from rhodecode.lib.utils2 import safe_str
31 from rhodecode.model import BaseModel
31 from rhodecode.model import BaseModel
32 from rhodecode.model.db import UserGroupMember, UserGroup,\
32 from rhodecode.model.db import UserGroupMember, UserGroup,\
33 UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm,\
33 UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm,\
34 UserGroupUserGroupToPerm, UserGroupRepoGroupToPerm
34 UserGroupUserGroupToPerm, UserGroupRepoGroupToPerm
35 from rhodecode.lib.exceptions import UserGroupAssignedException,\
35 from rhodecode.lib.exceptions import UserGroupAssignedException,\
36 RepoGroupAssignmentError
36 RepoGroupAssignmentError
37 from rhodecode.lib.utils2 import get_current_rhodecode_user, action_logger_generic
37 from rhodecode.lib.utils2 import get_current_rhodecode_user, action_logger_generic
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 class UserGroupModel(BaseModel):
42 class UserGroupModel(BaseModel):
43
43
44 cls = UserGroup
44 cls = UserGroup
45
45
46 def _get_user_group(self, user_group):
46 def _get_user_group(self, user_group):
47 return self._get_instance(UserGroup, user_group,
47 return self._get_instance(UserGroup, user_group,
48 callback=UserGroup.get_by_group_name)
48 callback=UserGroup.get_by_group_name)
49
49
50 def _create_default_perms(self, user_group):
50 def _create_default_perms(self, user_group):
51 # create default permission
51 # create default permission
52 default_perm = 'usergroup.read'
52 default_perm = 'usergroup.read'
53 def_user = User.get_default_user()
53 def_user = User.get_default_user()
54 for p in def_user.user_perms:
54 for p in def_user.user_perms:
55 if p.permission.permission_name.startswith('usergroup.'):
55 if p.permission.permission_name.startswith('usergroup.'):
56 default_perm = p.permission.permission_name
56 default_perm = p.permission.permission_name
57 break
57 break
58
58
59 user_group_to_perm = UserUserGroupToPerm()
59 user_group_to_perm = UserUserGroupToPerm()
60 user_group_to_perm.permission = Permission.get_by_key(default_perm)
60 user_group_to_perm.permission = Permission.get_by_key(default_perm)
61
61
62 user_group_to_perm.user_group = user_group
62 user_group_to_perm.user_group = user_group
63 user_group_to_perm.user_id = def_user.user_id
63 user_group_to_perm.user_id = def_user.user_id
64 return user_group_to_perm
64 return user_group_to_perm
65
65
66 def update_permissions(self, user_group, perm_additions=None, perm_updates=None,
66 def update_permissions(self, user_group, perm_additions=None, perm_updates=None,
67 perm_deletions=None, check_perms=True, cur_user=None):
67 perm_deletions=None, check_perms=True, cur_user=None):
68 from rhodecode.lib.auth import HasUserGroupPermissionAny
68 from rhodecode.lib.auth import HasUserGroupPermissionAny
69 if not perm_additions:
69 if not perm_additions:
70 perm_additions = []
70 perm_additions = []
71 if not perm_updates:
71 if not perm_updates:
72 perm_updates = []
72 perm_updates = []
73 if not perm_deletions:
73 if not perm_deletions:
74 perm_deletions = []
74 perm_deletions = []
75
75
76 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
76 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
77
77
78 # update permissions
78 # update permissions
79 for member_id, perm, member_type in perm_updates:
79 for member_id, perm, member_type in perm_updates:
80 member_id = int(member_id)
80 member_id = int(member_id)
81 if member_type == 'user':
81 if member_type == 'user':
82 # this updates existing one
82 # this updates existing one
83 self.grant_user_permission(
83 self.grant_user_permission(
84 user_group=user_group, user=member_id, perm=perm
84 user_group=user_group, user=member_id, perm=perm
85 )
85 )
86 else:
86 else:
87 # check if we have permissions to alter this usergroup
87 # check if we have permissions to alter this usergroup
88 member_name = UserGroup.get(member_id).users_group_name
88 member_name = UserGroup.get(member_id).users_group_name
89 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
89 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
90 self.grant_user_group_permission(
90 self.grant_user_group_permission(
91 target_user_group=user_group, user_group=member_id, perm=perm
91 target_user_group=user_group, user_group=member_id, perm=perm
92 )
92 )
93
93
94 # set new permissions
94 # set new permissions
95 for member_id, perm, member_type in perm_additions:
95 for member_id, perm, member_type in perm_additions:
96 member_id = int(member_id)
96 member_id = int(member_id)
97 if member_type == 'user':
97 if member_type == 'user':
98 self.grant_user_permission(
98 self.grant_user_permission(
99 user_group=user_group, user=member_id, perm=perm
99 user_group=user_group, user=member_id, perm=perm
100 )
100 )
101 else:
101 else:
102 # check if we have permissions to alter this usergroup
102 # check if we have permissions to alter this usergroup
103 member_name = UserGroup.get(member_id).users_group_name
103 member_name = UserGroup.get(member_id).users_group_name
104 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
104 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
105 self.grant_user_group_permission(
105 self.grant_user_group_permission(
106 target_user_group=user_group, user_group=member_id, perm=perm
106 target_user_group=user_group, user_group=member_id, perm=perm
107 )
107 )
108
108
109 # delete permissions
109 # delete permissions
110 for member_id, perm, member_type in perm_deletions:
110 for member_id, perm, member_type in perm_deletions:
111 member_id = int(member_id)
111 member_id = int(member_id)
112 if member_type == 'user':
112 if member_type == 'user':
113 self.revoke_user_permission(user_group=user_group, user=member_id)
113 self.revoke_user_permission(user_group=user_group, user=member_id)
114 else:
114 else:
115 #check if we have permissions to alter this usergroup
115 #check if we have permissions to alter this usergroup
116 member_name = UserGroup.get(member_id).users_group_name
116 member_name = UserGroup.get(member_id).users_group_name
117 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
117 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
118 self.revoke_user_group_permission(
118 self.revoke_user_group_permission(
119 target_user_group=user_group, user_group=member_id
119 target_user_group=user_group, user_group=member_id
120 )
120 )
121
121
122 def get(self, user_group_id, cache=False):
122 def get(self, user_group_id, cache=False):
123 return UserGroup.get(user_group_id)
123 return UserGroup.get(user_group_id)
124
124
125 def get_group(self, user_group):
125 def get_group(self, user_group):
126 return self._get_user_group(user_group)
126 return self._get_user_group(user_group)
127
127
128 def get_by_name(self, name, cache=False, case_insensitive=False):
128 def get_by_name(self, name, cache=False, case_insensitive=False):
129 return UserGroup.get_by_group_name(name, cache, case_insensitive)
129 return UserGroup.get_by_group_name(name, cache, case_insensitive)
130
130
131 def create(self, name, description, owner, active=True, group_data=None):
131 def create(self, name, description, owner, active=True, group_data=None):
132 try:
132 try:
133 new_user_group = UserGroup()
133 new_user_group = UserGroup()
134 new_user_group.user = self._get_user(owner)
134 new_user_group.user = self._get_user(owner)
135 new_user_group.users_group_name = name
135 new_user_group.users_group_name = name
136 new_user_group.user_group_description = description
136 new_user_group.user_group_description = description
137 new_user_group.users_group_active = active
137 new_user_group.users_group_active = active
138 if group_data:
138 if group_data:
139 new_user_group.group_data = group_data
139 new_user_group.group_data = group_data
140 self.sa.add(new_user_group)
140 self.sa.add(new_user_group)
141 perm_obj = self._create_default_perms(new_user_group)
141 perm_obj = self._create_default_perms(new_user_group)
142 self.sa.add(perm_obj)
142 self.sa.add(perm_obj)
143
143
144 self.grant_user_permission(user_group=new_user_group,
144 self.grant_user_permission(user_group=new_user_group,
145 user=owner, perm='usergroup.admin')
145 user=owner, perm='usergroup.admin')
146
146
147 return new_user_group
147 return new_user_group
148 except Exception:
148 except Exception:
149 log.error(traceback.format_exc())
149 log.error(traceback.format_exc())
150 raise
150 raise
151
151
152 def _get_memberships_for_user_ids(self, user_group, user_id_list):
152 def _get_memberships_for_user_ids(self, user_group, user_id_list):
153 members = []
153 members = []
154 for user_id in user_id_list:
154 for user_id in user_id_list:
155 member = self._get_membership(user_group.users_group_id, user_id)
155 member = self._get_membership(user_group.users_group_id, user_id)
156 members.append(member)
156 members.append(member)
157 return members
157 return members
158
158
159 def _get_added_and_removed_user_ids(self, user_group, user_id_list):
159 def _get_added_and_removed_user_ids(self, user_group, user_id_list):
160 current_members = user_group.members or []
160 current_members = user_group.members or []
161 current_members_ids = [m.user.user_id for m in current_members]
161 current_members_ids = [m.user.user_id for m in current_members]
162
162
163 added_members = [
163 added_members = [
164 user_id for user_id in user_id_list
164 user_id for user_id in user_id_list
165 if user_id not in current_members_ids]
165 if user_id not in current_members_ids]
166 if user_id_list == []:
166 if user_id_list == []:
167 # all members were deleted
167 # all members were deleted
168 deleted_members = current_members_ids
168 deleted_members = current_members_ids
169 else:
169 else:
170 deleted_members = [
170 deleted_members = [
171 user_id for user_id in current_members_ids
171 user_id for user_id in current_members_ids
172 if user_id not in user_id_list]
172 if user_id not in user_id_list]
173
173
174 return (added_members, deleted_members)
174 return (added_members, deleted_members)
175
175
176 def _set_users_as_members(self, user_group, user_ids):
176 def _set_users_as_members(self, user_group, user_ids):
177 user_group.members = []
177 user_group.members = []
178 self.sa.flush()
178 self.sa.flush()
179 members = self._get_memberships_for_user_ids(
179 members = self._get_memberships_for_user_ids(
180 user_group, user_ids)
180 user_group, user_ids)
181 user_group.members = members
181 user_group.members = members
182 self.sa.add(user_group)
182 self.sa.add(user_group)
183
183
184 def _update_members_from_user_ids(self, user_group, user_ids):
184 def _update_members_from_user_ids(self, user_group, user_ids):
185 added, removed = self._get_added_and_removed_user_ids(
185 added, removed = self._get_added_and_removed_user_ids(
186 user_group, user_ids)
186 user_group, user_ids)
187 self._set_users_as_members(user_group, user_ids)
187 self._set_users_as_members(user_group, user_ids)
188 self._log_user_changes('added to', user_group, added)
188 self._log_user_changes('added to', user_group, added)
189 self._log_user_changes('removed from', user_group, removed)
189 self._log_user_changes('removed from', user_group, removed)
190
190
191 def _clean_members_data(self, members_data):
191 def _clean_members_data(self, members_data):
192 # TODO: anderson: this should be in the form validation but I couldn't
192 # TODO: anderson: this should be in the form validation but I couldn't
193 # make it work there as it conflicts with the other validator
193 # make it work there as it conflicts with the other validator
194 if not members_data:
194 if not members_data:
195 members_data = []
195 members_data = []
196
196
197 if isinstance(members_data, basestring):
197 if isinstance(members_data, basestring):
198 new_members = [members_data]
198 new_members = [members_data]
199 else:
199 else:
200 new_members = members_data
200 new_members = members_data
201
201
202 new_members = [int(uid) for uid in new_members]
202 new_members = [int(uid) for uid in new_members]
203 return new_members
203 return new_members
204
204
205 def update(self, user_group, form_data):
205 def update(self, user_group, form_data):
206 user_group = self._get_user_group(user_group)
206 user_group = self._get_user_group(user_group)
207 if 'users_group_name' in form_data:
207 if 'users_group_name' in form_data:
208 user_group.users_group_name = form_data['users_group_name']
208 user_group.users_group_name = form_data['users_group_name']
209 if 'users_group_active' in form_data:
209 if 'users_group_active' in form_data:
210 user_group.users_group_active = form_data['users_group_active']
210 user_group.users_group_active = form_data['users_group_active']
211 if 'user_group_description' in form_data:
211 if 'user_group_description' in form_data:
212 user_group.user_group_description = form_data[
212 user_group.user_group_description = form_data[
213 'user_group_description']
213 'user_group_description']
214
214
215 # handle owner change
215 # handle owner change
216 if 'user' in form_data:
216 if 'user' in form_data:
217 owner = form_data['user']
217 owner = form_data['user']
218 if isinstance(owner, basestring):
218 if isinstance(owner, basestring):
219 user_group.user = User.get_by_username(form_data['user'])
219 owner = User.get_by_username(form_data['user'])
220
221 if not isinstance(owner, User):
222 raise ValueError(
223 'invalid owner for user group: %s' % form_data['user'])
224
225 user_group.user = owner
220
226
221 if 'users_group_members' in form_data:
227 if 'users_group_members' in form_data:
222 members_id_list = self._clean_members_data(
228 members_id_list = self._clean_members_data(
223 form_data['users_group_members'])
229 form_data['users_group_members'])
224 self._update_members_from_user_ids(user_group, members_id_list)
230 self._update_members_from_user_ids(user_group, members_id_list)
225
231
226 self.sa.add(user_group)
232 self.sa.add(user_group)
227
233
228 def delete(self, user_group, force=False):
234 def delete(self, user_group, force=False):
229 """
235 """
230 Deletes repository group, unless force flag is used
236 Deletes repository group, unless force flag is used
231 raises exception if there are members in that group, else deletes
237 raises exception if there are members in that group, else deletes
232 group and users
238 group and users
233
239
234 :param user_group:
240 :param user_group:
235 :param force:
241 :param force:
236 """
242 """
237 user_group = self._get_user_group(user_group)
243 user_group = self._get_user_group(user_group)
238 try:
244 try:
239 # check if this group is not assigned to repo
245 # check if this group is not assigned to repo
240 assigned_to_repo = [x.repository for x in UserGroupRepoToPerm.query()\
246 assigned_to_repo = [x.repository for x in UserGroupRepoToPerm.query()\
241 .filter(UserGroupRepoToPerm.users_group == user_group).all()]
247 .filter(UserGroupRepoToPerm.users_group == user_group).all()]
242 # check if this group is not assigned to repo
248 # check if this group is not assigned to repo
243 assigned_to_repo_group = [x.group for x in UserGroupRepoGroupToPerm.query()\
249 assigned_to_repo_group = [x.group for x in UserGroupRepoGroupToPerm.query()\
244 .filter(UserGroupRepoGroupToPerm.users_group == user_group).all()]
250 .filter(UserGroupRepoGroupToPerm.users_group == user_group).all()]
245
251
246 if (assigned_to_repo or assigned_to_repo_group) and not force:
252 if (assigned_to_repo or assigned_to_repo_group) and not force:
247 assigned = ','.join(map(safe_str,
253 assigned = ','.join(map(safe_str,
248 assigned_to_repo+assigned_to_repo_group))
254 assigned_to_repo+assigned_to_repo_group))
249
255
250 raise UserGroupAssignedException(
256 raise UserGroupAssignedException(
251 'UserGroup assigned to %s' % (assigned,))
257 'UserGroup assigned to %s' % (assigned,))
252 self.sa.delete(user_group)
258 self.sa.delete(user_group)
253 except Exception:
259 except Exception:
254 log.error(traceback.format_exc())
260 log.error(traceback.format_exc())
255 raise
261 raise
256
262
257 def _log_user_changes(self, action, user_group, user_or_users):
263 def _log_user_changes(self, action, user_group, user_or_users):
258 users = user_or_users
264 users = user_or_users
259 if not isinstance(users, (list, tuple)):
265 if not isinstance(users, (list, tuple)):
260 users = [users]
266 users = [users]
261 rhodecode_user = get_current_rhodecode_user()
267 rhodecode_user = get_current_rhodecode_user()
262 ipaddr = getattr(rhodecode_user, 'ip_addr', '')
268 ipaddr = getattr(rhodecode_user, 'ip_addr', '')
263 group_name = user_group.users_group_name
269 group_name = user_group.users_group_name
264
270
265 for user_or_user_id in users:
271 for user_or_user_id in users:
266 user = self._get_user(user_or_user_id)
272 user = self._get_user(user_or_user_id)
267 log_text = 'User {user} {action} {group}'.format(
273 log_text = 'User {user} {action} {group}'.format(
268 action=action, user=user.username, group=group_name)
274 action=action, user=user.username, group=group_name)
269 log.info('Logging action: {0} by {1} ip:{2}'.format(
275 log.info('Logging action: {0} by {1} ip:{2}'.format(
270 log_text, rhodecode_user, ipaddr))
276 log_text, rhodecode_user, ipaddr))
271
277
272 def _find_user_in_group(self, user, user_group):
278 def _find_user_in_group(self, user, user_group):
273 user_group_member = None
279 user_group_member = None
274 for m in user_group.members:
280 for m in user_group.members:
275 if m.user_id == user.user_id:
281 if m.user_id == user.user_id:
276 # Found this user's membership row
282 # Found this user's membership row
277 user_group_member = m
283 user_group_member = m
278 break
284 break
279
285
280 return user_group_member
286 return user_group_member
281
287
282 def _get_membership(self, user_group_id, user_id):
288 def _get_membership(self, user_group_id, user_id):
283 user_group_member = UserGroupMember(user_group_id, user_id)
289 user_group_member = UserGroupMember(user_group_id, user_id)
284 return user_group_member
290 return user_group_member
285
291
286 def add_user_to_group(self, user_group, user):
292 def add_user_to_group(self, user_group, user):
287 user_group = self._get_user_group(user_group)
293 user_group = self._get_user_group(user_group)
288 user = self._get_user(user)
294 user = self._get_user(user)
289 user_member = self._find_user_in_group(user, user_group)
295 user_member = self._find_user_in_group(user, user_group)
290 if user_member:
296 if user_member:
291 # user already in the group, skip
297 # user already in the group, skip
292 return True
298 return True
293
299
294 member = self._get_membership(
300 member = self._get_membership(
295 user_group.users_group_id, user.user_id)
301 user_group.users_group_id, user.user_id)
296 user_group.members.append(member)
302 user_group.members.append(member)
297
303
298 try:
304 try:
299 self.sa.add(member)
305 self.sa.add(member)
300 except Exception:
306 except Exception:
301 # what could go wrong here?
307 # what could go wrong here?
302 log.error(traceback.format_exc())
308 log.error(traceback.format_exc())
303 raise
309 raise
304
310
305 self._log_user_changes('added to', user_group, user)
311 self._log_user_changes('added to', user_group, user)
306 return member
312 return member
307
313
308 def remove_user_from_group(self, user_group, user):
314 def remove_user_from_group(self, user_group, user):
309 user_group = self._get_user_group(user_group)
315 user_group = self._get_user_group(user_group)
310 user = self._get_user(user)
316 user = self._get_user(user)
311 user_group_member = self._find_user_in_group(user, user_group)
317 user_group_member = self._find_user_in_group(user, user_group)
312
318
313 if not user_group_member:
319 if not user_group_member:
314 # User isn't in that group
320 # User isn't in that group
315 return False
321 return False
316
322
317 try:
323 try:
318 self.sa.delete(user_group_member)
324 self.sa.delete(user_group_member)
319 except Exception:
325 except Exception:
320 log.error(traceback.format_exc())
326 log.error(traceback.format_exc())
321 raise
327 raise
322
328
323 self._log_user_changes('removed from', user_group, user)
329 self._log_user_changes('removed from', user_group, user)
324 return True
330 return True
325
331
326 def has_perm(self, user_group, perm):
332 def has_perm(self, user_group, perm):
327 user_group = self._get_user_group(user_group)
333 user_group = self._get_user_group(user_group)
328 perm = self._get_perm(perm)
334 perm = self._get_perm(perm)
329
335
330 return UserGroupToPerm.query()\
336 return UserGroupToPerm.query()\
331 .filter(UserGroupToPerm.users_group == user_group)\
337 .filter(UserGroupToPerm.users_group == user_group)\
332 .filter(UserGroupToPerm.permission == perm).scalar() is not None
338 .filter(UserGroupToPerm.permission == perm).scalar() is not None
333
339
334 def grant_perm(self, user_group, perm):
340 def grant_perm(self, user_group, perm):
335 user_group = self._get_user_group(user_group)
341 user_group = self._get_user_group(user_group)
336 perm = self._get_perm(perm)
342 perm = self._get_perm(perm)
337
343
338 # if this permission is already granted skip it
344 # if this permission is already granted skip it
339 _perm = UserGroupToPerm.query()\
345 _perm = UserGroupToPerm.query()\
340 .filter(UserGroupToPerm.users_group == user_group)\
346 .filter(UserGroupToPerm.users_group == user_group)\
341 .filter(UserGroupToPerm.permission == perm)\
347 .filter(UserGroupToPerm.permission == perm)\
342 .scalar()
348 .scalar()
343 if _perm:
349 if _perm:
344 return
350 return
345
351
346 new = UserGroupToPerm()
352 new = UserGroupToPerm()
347 new.users_group = user_group
353 new.users_group = user_group
348 new.permission = perm
354 new.permission = perm
349 self.sa.add(new)
355 self.sa.add(new)
350 return new
356 return new
351
357
352 def revoke_perm(self, user_group, perm):
358 def revoke_perm(self, user_group, perm):
353 user_group = self._get_user_group(user_group)
359 user_group = self._get_user_group(user_group)
354 perm = self._get_perm(perm)
360 perm = self._get_perm(perm)
355
361
356 obj = UserGroupToPerm.query()\
362 obj = UserGroupToPerm.query()\
357 .filter(UserGroupToPerm.users_group == user_group)\
363 .filter(UserGroupToPerm.users_group == user_group)\
358 .filter(UserGroupToPerm.permission == perm).scalar()
364 .filter(UserGroupToPerm.permission == perm).scalar()
359 if obj:
365 if obj:
360 self.sa.delete(obj)
366 self.sa.delete(obj)
361
367
362 def grant_user_permission(self, user_group, user, perm):
368 def grant_user_permission(self, user_group, user, perm):
363 """
369 """
364 Grant permission for user on given user group, or update
370 Grant permission for user on given user group, or update
365 existing one if found
371 existing one if found
366
372
367 :param user_group: Instance of UserGroup, users_group_id,
373 :param user_group: Instance of UserGroup, users_group_id,
368 or users_group_name
374 or users_group_name
369 :param user: Instance of User, user_id or username
375 :param user: Instance of User, user_id or username
370 :param perm: Instance of Permission, or permission_name
376 :param perm: Instance of Permission, or permission_name
371 """
377 """
372
378
373 user_group = self._get_user_group(user_group)
379 user_group = self._get_user_group(user_group)
374 user = self._get_user(user)
380 user = self._get_user(user)
375 permission = self._get_perm(perm)
381 permission = self._get_perm(perm)
376
382
377 # check if we have that permission already
383 # check if we have that permission already
378 obj = self.sa.query(UserUserGroupToPerm)\
384 obj = self.sa.query(UserUserGroupToPerm)\
379 .filter(UserUserGroupToPerm.user == user)\
385 .filter(UserUserGroupToPerm.user == user)\
380 .filter(UserUserGroupToPerm.user_group == user_group)\
386 .filter(UserUserGroupToPerm.user_group == user_group)\
381 .scalar()
387 .scalar()
382 if obj is None:
388 if obj is None:
383 # create new !
389 # create new !
384 obj = UserUserGroupToPerm()
390 obj = UserUserGroupToPerm()
385 obj.user_group = user_group
391 obj.user_group = user_group
386 obj.user = user
392 obj.user = user
387 obj.permission = permission
393 obj.permission = permission
388 self.sa.add(obj)
394 self.sa.add(obj)
389 log.debug('Granted perm %s to %s on %s', perm, user, user_group)
395 log.debug('Granted perm %s to %s on %s', perm, user, user_group)
390 action_logger_generic(
396 action_logger_generic(
391 'granted permission: {} to user: {} on usergroup: {}'.format(
397 'granted permission: {} to user: {} on usergroup: {}'.format(
392 perm, user, user_group), namespace='security.usergroup')
398 perm, user, user_group), namespace='security.usergroup')
393
399
394 return obj
400 return obj
395
401
396 def revoke_user_permission(self, user_group, user):
402 def revoke_user_permission(self, user_group, user):
397 """
403 """
398 Revoke permission for user on given user group
404 Revoke permission for user on given user group
399
405
400 :param user_group: Instance of UserGroup, users_group_id,
406 :param user_group: Instance of UserGroup, users_group_id,
401 or users_group name
407 or users_group name
402 :param user: Instance of User, user_id or username
408 :param user: Instance of User, user_id or username
403 """
409 """
404
410
405 user_group = self._get_user_group(user_group)
411 user_group = self._get_user_group(user_group)
406 user = self._get_user(user)
412 user = self._get_user(user)
407
413
408 obj = self.sa.query(UserUserGroupToPerm)\
414 obj = self.sa.query(UserUserGroupToPerm)\
409 .filter(UserUserGroupToPerm.user == user)\
415 .filter(UserUserGroupToPerm.user == user)\
410 .filter(UserUserGroupToPerm.user_group == user_group)\
416 .filter(UserUserGroupToPerm.user_group == user_group)\
411 .scalar()
417 .scalar()
412 if obj:
418 if obj:
413 self.sa.delete(obj)
419 self.sa.delete(obj)
414 log.debug('Revoked perm on %s on %s', user_group, user)
420 log.debug('Revoked perm on %s on %s', user_group, user)
415 action_logger_generic(
421 action_logger_generic(
416 'revoked permission from user: {} on usergroup: {}'.format(
422 'revoked permission from user: {} on usergroup: {}'.format(
417 user, user_group), namespace='security.usergroup')
423 user, user_group), namespace='security.usergroup')
418
424
419 def grant_user_group_permission(self, target_user_group, user_group, perm):
425 def grant_user_group_permission(self, target_user_group, user_group, perm):
420 """
426 """
421 Grant user group permission for given target_user_group
427 Grant user group permission for given target_user_group
422
428
423 :param target_user_group:
429 :param target_user_group:
424 :param user_group:
430 :param user_group:
425 :param perm:
431 :param perm:
426 """
432 """
427 target_user_group = self._get_user_group(target_user_group)
433 target_user_group = self._get_user_group(target_user_group)
428 user_group = self._get_user_group(user_group)
434 user_group = self._get_user_group(user_group)
429 permission = self._get_perm(perm)
435 permission = self._get_perm(perm)
430 # forbid assigning same user group to itself
436 # forbid assigning same user group to itself
431 if target_user_group == user_group:
437 if target_user_group == user_group:
432 raise RepoGroupAssignmentError('target repo:%s cannot be '
438 raise RepoGroupAssignmentError('target repo:%s cannot be '
433 'assigned to itself' % target_user_group)
439 'assigned to itself' % target_user_group)
434
440
435 # check if we have that permission already
441 # check if we have that permission already
436 obj = self.sa.query(UserGroupUserGroupToPerm)\
442 obj = self.sa.query(UserGroupUserGroupToPerm)\
437 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
443 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
438 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
444 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
439 .scalar()
445 .scalar()
440 if obj is None:
446 if obj is None:
441 # create new !
447 # create new !
442 obj = UserGroupUserGroupToPerm()
448 obj = UserGroupUserGroupToPerm()
443 obj.user_group = user_group
449 obj.user_group = user_group
444 obj.target_user_group = target_user_group
450 obj.target_user_group = target_user_group
445 obj.permission = permission
451 obj.permission = permission
446 self.sa.add(obj)
452 self.sa.add(obj)
447 log.debug(
453 log.debug(
448 'Granted perm %s to %s on %s', perm, target_user_group, user_group)
454 'Granted perm %s to %s on %s', perm, target_user_group, user_group)
449 action_logger_generic(
455 action_logger_generic(
450 'granted permission: {} to usergroup: {} on usergroup: {}'.format(
456 'granted permission: {} to usergroup: {} on usergroup: {}'.format(
451 perm, user_group, target_user_group),
457 perm, user_group, target_user_group),
452 namespace='security.usergroup')
458 namespace='security.usergroup')
453
459
454 return obj
460 return obj
455
461
456 def revoke_user_group_permission(self, target_user_group, user_group):
462 def revoke_user_group_permission(self, target_user_group, user_group):
457 """
463 """
458 Revoke user group permission for given target_user_group
464 Revoke user group permission for given target_user_group
459
465
460 :param target_user_group:
466 :param target_user_group:
461 :param user_group:
467 :param user_group:
462 """
468 """
463 target_user_group = self._get_user_group(target_user_group)
469 target_user_group = self._get_user_group(target_user_group)
464 user_group = self._get_user_group(user_group)
470 user_group = self._get_user_group(user_group)
465
471
466 obj = self.sa.query(UserGroupUserGroupToPerm)\
472 obj = self.sa.query(UserGroupUserGroupToPerm)\
467 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
473 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
468 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
474 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
469 .scalar()
475 .scalar()
470 if obj:
476 if obj:
471 self.sa.delete(obj)
477 self.sa.delete(obj)
472 log.debug(
478 log.debug(
473 'Revoked perm on %s on %s', target_user_group, user_group)
479 'Revoked perm on %s on %s', target_user_group, user_group)
474 action_logger_generic(
480 action_logger_generic(
475 'revoked permission from usergroup: {} on usergroup: {}'.format(
481 'revoked permission from usergroup: {} on usergroup: {}'.format(
476 user_group, target_user_group),
482 user_group, target_user_group),
477 namespace='security.repogroup')
483 namespace='security.repogroup')
478
484
479 def enforce_groups(self, user, groups, extern_type=None):
485 def enforce_groups(self, user, groups, extern_type=None):
480 user = self._get_user(user)
486 user = self._get_user(user)
481 log.debug('Enforcing groups %s on user %s', groups, user)
487 log.debug('Enforcing groups %s on user %s', groups, user)
482 current_groups = user.group_member
488 current_groups = user.group_member
483 # find the external created groups
489 # find the external created groups
484 externals = [x.users_group for x in current_groups
490 externals = [x.users_group for x in current_groups
485 if 'extern_type' in x.users_group.group_data]
491 if 'extern_type' in x.users_group.group_data]
486
492
487 # calculate from what groups user should be removed
493 # calculate from what groups user should be removed
488 # externals that are not in groups
494 # externals that are not in groups
489 for gr in externals:
495 for gr in externals:
490 if gr.users_group_name not in groups:
496 if gr.users_group_name not in groups:
491 log.debug('Removing user %s from user group %s', user, gr)
497 log.debug('Removing user %s from user group %s', user, gr)
492 self.remove_user_from_group(gr, user)
498 self.remove_user_from_group(gr, user)
493
499
494 # now we calculate in which groups user should be == groups params
500 # now we calculate in which groups user should be == groups params
495 owner = User.get_first_admin().username
501 owner = User.get_first_admin().username
496 for gr in set(groups):
502 for gr in set(groups):
497 existing_group = UserGroup.get_by_group_name(gr)
503 existing_group = UserGroup.get_by_group_name(gr)
498 if not existing_group:
504 if not existing_group:
499 desc = 'Automatically created from plugin:%s' % extern_type
505 desc = 'Automatically created from plugin:%s' % extern_type
500 # we use first admin account to set the owner of the group
506 # we use first admin account to set the owner of the group
501 existing_group = UserGroupModel().create(gr, desc, owner,
507 existing_group = UserGroupModel().create(gr, desc, owner,
502 group_data={'extern_type': extern_type})
508 group_data={'extern_type': extern_type})
503
509
504 # we can only add users to special groups created via plugins
510 # we can only add users to special groups created via plugins
505 managed = 'extern_type' in existing_group.group_data
511 managed = 'extern_type' in existing_group.group_data
506 if managed:
512 if managed:
507 log.debug('Adding user %s to user group %s', user, gr)
513 log.debug('Adding user %s to user group %s', user, gr)
508 UserGroupModel().add_user_to_group(existing_group, user)
514 UserGroupModel().add_user_to_group(existing_group, user)
509 else:
515 else:
510 log.debug('Skipping addition to group %s since it is '
516 log.debug('Skipping addition to group %s since it is '
511 'not managed by auth plugins' % gr)
517 'not managed by auth plugins' % gr)
General Comments 0
You need to be logged in to leave comments. Login now