##// END OF EJS Templates
api: fixed tests and update docstrings.
marcink -
r2438:b3aeea31 default
parent child Browse files
Show More
@@ -1,139 +1,137 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 import pytest
22 import pytest
23
23
24 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.model.repo import RepoModel
25 from rhodecode.model.repo import RepoModel
26 from rhodecode.model.user import UserModel
26 from rhodecode.model.user import UserModel
27 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
27 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
28 from rhodecode.api.tests.utils import (
28 from rhodecode.api.tests.utils import (
29 build_data, api_call, assert_ok, assert_error, expected_permissions)
29 build_data, api_call, assert_ok, assert_error, expected_permissions)
30
30
31
31
32 @pytest.mark.usefixtures("testuser_api", "app")
32 @pytest.mark.usefixtures("testuser_api", "app")
33 class TestGetRepo(object):
33 class TestGetRepo(object):
34 @pytest.mark.parametrize("apikey_attr, expect_secrets", [
34 @pytest.mark.parametrize("apikey_attr, expect_secrets", [
35 ('apikey', True),
35 ('apikey', True),
36 ('apikey_regular', False),
36 ('apikey_regular', False),
37 ])
37 ])
38 @pytest.mark.parametrize("cache_param", [
38 @pytest.mark.parametrize("cache_param", [
39 True,
39 True,
40 False,
40 False,
41 None,
41 None,
42 ])
42 ])
43 def test_api_get_repo(
43 def test_api_get_repo(
44 self, apikey_attr, expect_secrets, cache_param, backend,
44 self, apikey_attr, expect_secrets, cache_param, backend,
45 user_util):
45 user_util):
46 repo = backend.create_repo()
46 repo = backend.create_repo()
47 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
47 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
48 group = user_util.create_user_group(members=[usr])
48 group = user_util.create_user_group(members=[usr])
49 user_util.grant_user_group_permission_to_repo(
49 user_util.grant_user_group_permission_to_repo(
50 repo=repo, user_group=group, permission_name='repository.read')
50 repo=repo, user_group=group, permission_name='repository.read')
51 Session().commit()
51 Session().commit()
52 kwargs = {
52 kwargs = {
53 'repoid': repo.repo_name,
53 'repoid': repo.repo_name,
54 }
54 }
55 if cache_param is not None:
55 if cache_param is not None:
56 kwargs['cache'] = cache_param
56 kwargs['cache'] = cache_param
57
57
58 apikey = getattr(self, apikey_attr)
58 apikey = getattr(self, apikey_attr)
59 id_, params = build_data(apikey, 'get_repo', **kwargs)
59 id_, params = build_data(apikey, 'get_repo', **kwargs)
60 response = api_call(self.app, params)
60 response = api_call(self.app, params)
61
61
62 ret = repo.get_api_data()
62 ret = repo.get_api_data()
63
63
64 permissions = expected_permissions(repo)
64 permissions = expected_permissions(repo)
65
65
66 followers = []
66 followers = []
67 for user in repo.followers:
67 for user in repo.followers:
68 followers.append(user.user.get_api_data(
68 followers.append(user.user.get_api_data(
69 include_secrets=expect_secrets))
69 include_secrets=expect_secrets))
70
70
71 ret['members'] = permissions
72 ret['permissions'] = permissions
71 ret['permissions'] = permissions
73 ret['followers'] = followers
72 ret['followers'] = followers
74
73
75 expected = ret
74 expected = ret
76
75
77 assert_ok(id_, expected, given=response.body)
76 assert_ok(id_, expected, given=response.body)
78
77
79 @pytest.mark.parametrize("grant_perm", [
78 @pytest.mark.parametrize("grant_perm", [
80 'repository.admin',
79 'repository.admin',
81 'repository.write',
80 'repository.write',
82 'repository.read',
81 'repository.read',
83 ])
82 ])
84 def test_api_get_repo_by_non_admin(self, grant_perm, backend):
83 def test_api_get_repo_by_non_admin(self, grant_perm, backend):
85 # TODO: Depending on which tests are running before this one, we
84 # TODO: Depending on which tests are running before this one, we
86 # start with a different number of permissions in the database.
85 # start with a different number of permissions in the database.
87 repo = RepoModel().get_by_repo_name(backend.repo_name)
86 repo = RepoModel().get_by_repo_name(backend.repo_name)
88 permission_count = len(repo.repo_to_perm)
87 permission_count = len(repo.repo_to_perm)
89
88
90 RepoModel().grant_user_permission(repo=backend.repo_name,
89 RepoModel().grant_user_permission(repo=backend.repo_name,
91 user=self.TEST_USER_LOGIN,
90 user=self.TEST_USER_LOGIN,
92 perm=grant_perm)
91 perm=grant_perm)
93 Session().commit()
92 Session().commit()
94 id_, params = build_data(
93 id_, params = build_data(
95 self.apikey_regular, 'get_repo', repoid=backend.repo_name)
94 self.apikey_regular, 'get_repo', repoid=backend.repo_name)
96 response = api_call(self.app, params)
95 response = api_call(self.app, params)
97
96
98 repo = RepoModel().get_by_repo_name(backend.repo_name)
97 repo = RepoModel().get_by_repo_name(backend.repo_name)
99 ret = repo.get_api_data()
98 ret = repo.get_api_data()
100
99
101 assert permission_count + 1, len(repo.repo_to_perm)
100 assert permission_count + 1, len(repo.repo_to_perm)
102
101
103 permissions = expected_permissions(repo)
102 permissions = expected_permissions(repo)
104
103
105 followers = []
104 followers = []
106 for user in repo.followers:
105 for user in repo.followers:
107 followers.append(user.user.get_api_data())
106 followers.append(user.user.get_api_data())
108
107
109 ret['members'] = permissions
110 ret['permissions'] = permissions
108 ret['permissions'] = permissions
111 ret['followers'] = followers
109 ret['followers'] = followers
112
110
113 expected = ret
111 expected = ret
114 try:
112 try:
115 assert_ok(id_, expected, given=response.body)
113 assert_ok(id_, expected, given=response.body)
116 finally:
114 finally:
117 RepoModel().revoke_user_permission(
115 RepoModel().revoke_user_permission(
118 backend.repo_name, self.TEST_USER_LOGIN)
116 backend.repo_name, self.TEST_USER_LOGIN)
119
117
120 def test_api_get_repo_by_non_admin_no_permission_to_repo(self, backend):
118 def test_api_get_repo_by_non_admin_no_permission_to_repo(self, backend):
121 RepoModel().grant_user_permission(repo=backend.repo_name,
119 RepoModel().grant_user_permission(repo=backend.repo_name,
122 user=self.TEST_USER_LOGIN,
120 user=self.TEST_USER_LOGIN,
123 perm='repository.none')
121 perm='repository.none')
124
122
125 id_, params = build_data(
123 id_, params = build_data(
126 self.apikey_regular, 'get_repo', repoid=backend.repo_name)
124 self.apikey_regular, 'get_repo', repoid=backend.repo_name)
127 response = api_call(self.app, params)
125 response = api_call(self.app, params)
128
126
129 expected = 'repository `%s` does not exist' % (backend.repo_name)
127 expected = 'repository `%s` does not exist' % (backend.repo_name)
130 assert_error(id_, expected, given=response.body)
128 assert_error(id_, expected, given=response.body)
131
129
132 def test_api_get_repo_not_existing(self):
130 def test_api_get_repo_not_existing(self):
133 id_, params = build_data(
131 id_, params = build_data(
134 self.apikey, 'get_repo', repoid='no-such-repo')
132 self.apikey, 'get_repo', repoid='no-such-repo')
135 response = api_call(self.app, params)
133 response = api_call(self.app, params)
136
134
137 ret = 'repository `%s` does not exist' % 'no-such-repo'
135 ret = 'repository `%s` does not exist' % 'no-such-repo'
138 expected = ret
136 expected = ret
139 assert_error(id_, expected, given=response.body)
137 assert_error(id_, expected, given=response.body)
@@ -1,55 +1,55 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 import pytest
22 import pytest
23
23
24 from rhodecode.model.repo_group import RepoGroupModel
24 from rhodecode.model.repo_group import RepoGroupModel
25 from rhodecode.api.tests.utils import (
25 from rhodecode.api.tests.utils import (
26 build_data, api_call, assert_ok, assert_error, expected_permissions)
26 build_data, api_call, assert_ok, assert_error, expected_permissions)
27
27
28
28
29 @pytest.mark.usefixtures("testuser_api", "app")
29 @pytest.mark.usefixtures("testuser_api", "app")
30 class TestApiGetRepoGroup(object):
30 class TestApiGetRepoGroup(object):
31 def test_api_get_repo_group(self, user_util):
31 def test_api_get_repo_group(self, user_util):
32 repo_group = user_util.create_repo_group()
32 repo_group = user_util.create_repo_group()
33 repo_group_name = repo_group.group_name
33 repo_group_name = repo_group.group_name
34
34
35 id_, params = build_data(
35 id_, params = build_data(
36 self.apikey, 'get_repo_group', repogroupid=repo_group_name)
36 self.apikey, 'get_repo_group', repogroupid=repo_group_name)
37 response = api_call(self.app, params)
37 response = api_call(self.app, params)
38
38
39 repo_group = RepoGroupModel()._get_repo_group(repo_group_name)
39 repo_group = RepoGroupModel()._get_repo_group(repo_group_name)
40 ret = repo_group.get_api_data()
40 ret = repo_group.get_api_data()
41
41
42 permissions = expected_permissions(repo_group)
42 permissions = expected_permissions(repo_group)
43
43
44 ret['members'] = permissions
44 ret['permissions'] = permissions
45 expected = ret
45 expected = ret
46 assert_ok(id_, expected, given=response.body)
46 assert_ok(id_, expected, given=response.body)
47
47
48 def test_api_get_repo_group_not_existing(self):
48 def test_api_get_repo_group_not_existing(self):
49 id_, params = build_data(
49 id_, params = build_data(
50 self.apikey, 'get_repo_group', repogroupid='no-such-repo-group')
50 self.apikey, 'get_repo_group', repogroupid='no-such-repo-group')
51 response = api_call(self.app, params)
51 response = api_call(self.app, params)
52
52
53 ret = 'repository group `%s` does not exist' % 'no-such-repo-group'
53 ret = 'repository group `%s` does not exist' % 'no-such-repo-group'
54 expected = ret
54 expected = ret
55 assert_error(id_, expected, given=response.body)
55 assert_error(id_, expected, given=response.body)
@@ -1,80 +1,86 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 pytest
21 import pytest
22
22
23 from rhodecode.lib.auth import AuthUser
23 from rhodecode.lib.auth import AuthUser
24 from rhodecode.model.user import UserModel
24 from rhodecode.model.user import UserModel
25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
26 from rhodecode.api.tests.utils import (
26 from rhodecode.api.tests.utils import (
27 build_data, api_call, assert_ok, assert_error)
27 build_data, api_call, assert_ok, assert_error)
28
28
29
29
30 @pytest.mark.usefixtures("testuser_api", "app")
30 @pytest.mark.usefixtures("testuser_api", "app")
31 class TestGetUser(object):
31 class TestGetUser(object):
32 def test_api_get_user(self):
32 def test_api_get_user(self):
33 id_, params = build_data(
33 id_, params = build_data(
34 self.apikey, 'get_user', userid=TEST_USER_ADMIN_LOGIN)
34 self.apikey, 'get_user', userid=TEST_USER_ADMIN_LOGIN)
35 response = api_call(self.app, params)
35 response = api_call(self.app, params)
36
36
37 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
37 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
38 ret = usr.get_api_data(include_secrets=True)
38 ret = usr.get_api_data(include_secrets=True)
39 ret['permissions'] = AuthUser(usr.user_id).permissions
39 permissions = AuthUser(usr.user_id).permissions
40 ret['permissions'] = permissions
41 ret['permissions_summary'] = permissions
40
42
41 expected = ret
43 expected = ret
42 assert_ok(id_, expected, given=response.body)
44 assert_ok(id_, expected, given=response.body)
43
45
44 def test_api_get_user_not_existing(self):
46 def test_api_get_user_not_existing(self):
45 id_, params = build_data(self.apikey, 'get_user', userid='trololo')
47 id_, params = build_data(self.apikey, 'get_user', userid='trololo')
46 response = api_call(self.app, params)
48 response = api_call(self.app, params)
47
49
48 expected = "user `%s` does not exist" % 'trololo'
50 expected = "user `%s` does not exist" % 'trololo'
49 assert_error(id_, expected, given=response.body)
51 assert_error(id_, expected, given=response.body)
50
52
51 def test_api_get_user_without_giving_userid(self):
53 def test_api_get_user_without_giving_userid(self):
52 id_, params = build_data(self.apikey, 'get_user')
54 id_, params = build_data(self.apikey, 'get_user')
53 response = api_call(self.app, params)
55 response = api_call(self.app, params)
54
56
55 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
57 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
56 ret = usr.get_api_data(include_secrets=True)
58 ret = usr.get_api_data(include_secrets=True)
57 ret['permissions'] = AuthUser(usr.user_id).permissions
59 permissions = AuthUser(usr.user_id).permissions
60 ret['permissions'] = permissions
61 ret['permissions_summary'] = permissions
58
62
59 expected = ret
63 expected = ret
60 assert_ok(id_, expected, given=response.body)
64 assert_ok(id_, expected, given=response.body)
61
65
62 def test_api_get_user_without_giving_userid_non_admin(self):
66 def test_api_get_user_without_giving_userid_non_admin(self):
63 id_, params = build_data(self.apikey_regular, 'get_user')
67 id_, params = build_data(self.apikey_regular, 'get_user')
64 response = api_call(self.app, params)
68 response = api_call(self.app, params)
65
69
66 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
70 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
67 ret = usr.get_api_data(include_secrets=True)
71 ret = usr.get_api_data(include_secrets=True)
68 ret['permissions'] = AuthUser(usr.user_id).permissions
72 permissions = AuthUser(usr.user_id).permissions
73 ret['permissions'] = permissions
74 ret['permissions_summary'] = permissions
69
75
70 expected = ret
76 expected = ret
71 assert_ok(id_, expected, given=response.body)
77 assert_ok(id_, expected, given=response.body)
72
78
73 def test_api_get_user_with_giving_userid_non_admin(self):
79 def test_api_get_user_with_giving_userid_non_admin(self):
74 id_, params = build_data(
80 id_, params = build_data(
75 self.apikey_regular, 'get_user',
81 self.apikey_regular, 'get_user',
76 userid=self.TEST_USER_LOGIN)
82 userid=self.TEST_USER_LOGIN)
77 response = api_call(self.app, params)
83 response = api_call(self.app, params)
78
84
79 expected = 'userid is not the same as your user'
85 expected = 'userid is not the same as your user'
80 assert_error(id_, expected, given=response.body)
86 assert_error(id_, expected, given=response.body)
@@ -1,74 +1,76 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 pytest
21 import pytest
22
22
23 from rhodecode.model.user import UserModel
23 from rhodecode.model.user import UserModel
24 from rhodecode.api.tests.utils import (
24 from rhodecode.api.tests.utils import (
25 build_data, api_call, assert_ok, assert_error, expected_permissions)
25 build_data, api_call, assert_ok, assert_error, expected_permissions)
26
26
27
27
28 @pytest.mark.usefixtures("testuser_api", "app")
28 @pytest.mark.usefixtures("testuser_api", "app")
29 class TestGetUserGroups(object):
29 class TestGetUserGroups(object):
30 def test_api_get_user_group(self, user_util):
30 def test_api_get_user_group(self, user_util):
31 user, group = user_util.create_user_with_group()
31 user, group = user_util.create_user_with_group()
32 id_, params = build_data(
32 id_, params = build_data(
33 self.apikey, 'get_user_group', usergroupid=group.users_group_name)
33 self.apikey, 'get_user_group', usergroupid=group.users_group_name)
34 response = api_call(self.app, params)
34 response = api_call(self.app, params)
35
35
36 ret = group.get_api_data()
36 ret = group.get_api_data()
37 ret['users'] = [user.get_api_data()]
37 ret['users'] = [user.get_api_data()]
38
38
39 permissions = expected_permissions(group)
39 permissions = expected_permissions(group)
40
40
41 ret['members'] = permissions
41 ret['permissions'] = permissions
42 ret['permissions_summary'] = response.json['result']['permissions_summary']
42 expected = ret
43 expected = ret
43 assert_ok(id_, expected, given=response.body)
44 assert_ok(id_, expected, given=response.body)
44
45
45 def test_api_get_user_group_regular_user(self, user_util):
46 def test_api_get_user_group_regular_user(self, user_util):
46 user, group = user_util.create_user_with_group()
47 user, group = user_util.create_user_with_group()
47 id_, params = build_data(
48 id_, params = build_data(
48 self.apikey_regular, 'get_user_group',
49 self.apikey_regular, 'get_user_group',
49 usergroupid=group.users_group_name)
50 usergroupid=group.users_group_name)
50 response = api_call(self.app, params)
51 response = api_call(self.app, params)
51
52
52 ret = group.get_api_data()
53 ret = group.get_api_data()
53 ret['users'] = [user.get_api_data()]
54 ret['users'] = [user.get_api_data()]
54
55
55 permissions = expected_permissions(group)
56 permissions = expected_permissions(group)
56
57
57 ret['members'] = permissions
58 ret['permissions'] = permissions
59 ret['permissions_summary'] = response.json['result']['permissions_summary']
58 expected = ret
60 expected = ret
59 assert_ok(id_, expected, given=response.body)
61 assert_ok(id_, expected, given=response.body)
60
62
61 def test_api_get_user_group_regular_user_permission_denied(
63 def test_api_get_user_group_regular_user_permission_denied(
62 self, user_util):
64 self, user_util):
63 group = user_util.create_user_group()
65 group = user_util.create_user_group()
64 user = UserModel().get_by_username(self.TEST_USER_LOGIN)
66 user = UserModel().get_by_username(self.TEST_USER_LOGIN)
65 group_name = group.users_group_name
67 group_name = group.users_group_name
66 user_util.grant_user_permission_to_user_group(
68 user_util.grant_user_permission_to_user_group(
67 group, user, 'usergroup.none')
69 group, user, 'usergroup.none')
68
70
69 id_, params = build_data(
71 id_, params = build_data(
70 self.apikey_regular, 'get_user_group', usergroupid=group_name)
72 self.apikey_regular, 'get_user_group', usergroupid=group_name)
71 response = api_call(self.app, params)
73 response = api_call(self.app, params)
72
74
73 expected = 'user group `%s` does not exist' % (group_name,)
75 expected = 'user group `%s` does not exist' % (group_name,)
74 assert_error(id_, expected, given=response.body)
76 assert_error(id_, expected, given=response.body)
@@ -1,2062 +1,2042 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 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 rhodecode
24 import rhodecode
25 from rhodecode.api import (
25 from rhodecode.api import (
26 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
26 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
27 from rhodecode.api.utils import (
27 from rhodecode.api.utils import (
28 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
28 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
29 get_user_group_or_error, get_user_or_error, validate_repo_permissions,
29 get_user_group_or_error, get_user_or_error, validate_repo_permissions,
30 get_perm_or_error, parse_args, get_origin, build_commit_data,
30 get_perm_or_error, parse_args, get_origin, build_commit_data,
31 validate_set_owner_permissions)
31 validate_set_owner_permissions)
32 from rhodecode.lib import audit_logger
32 from rhodecode.lib import audit_logger
33 from rhodecode.lib import repo_maintenance
33 from rhodecode.lib import repo_maintenance
34 from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi
34 from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi
35 from rhodecode.lib.celerylib.utils import get_task_id
35 from rhodecode.lib.celerylib.utils import get_task_id
36 from rhodecode.lib.utils2 import str2bool, time_to_datetime
36 from rhodecode.lib.utils2 import str2bool, time_to_datetime
37 from rhodecode.lib.ext_json import json
37 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
38 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
39 from rhodecode.model.changeset_status import ChangesetStatusModel
39 from rhodecode.model.changeset_status import ChangesetStatusModel
40 from rhodecode.model.comment import CommentsModel
40 from rhodecode.model.comment import CommentsModel
41 from rhodecode.model.db import (
41 from rhodecode.model.db import (
42 Session, ChangesetStatus, RepositoryField, Repository, RepoGroup,
42 Session, ChangesetStatus, RepositoryField, Repository, RepoGroup,
43 ChangesetComment)
43 ChangesetComment)
44 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.repo import RepoModel
45 from rhodecode.model.scm import ScmModel, RepoList
45 from rhodecode.model.scm import ScmModel, RepoList
46 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
46 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
47 from rhodecode.model import validation_schema
47 from rhodecode.model import validation_schema
48 from rhodecode.model.validation_schema.schemas import repo_schema
48 from rhodecode.model.validation_schema.schemas import repo_schema
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 @jsonrpc_method()
53 @jsonrpc_method()
54 def get_repo(request, apiuser, repoid, cache=Optional(True)):
54 def get_repo(request, apiuser, repoid, cache=Optional(True)):
55 """
55 """
56 Gets an existing repository by its name or repository_id.
56 Gets an existing repository by its name or repository_id.
57
57
58 The members section so the output returns users groups or users
58 The members section so the output returns users groups or users
59 associated with that repository.
59 associated with that repository.
60
60
61 This command can only be run using an |authtoken| with admin rights,
61 This command can only be run using an |authtoken| with admin rights,
62 or users with at least read rights to the |repo|.
62 or users with at least read rights to the |repo|.
63
63
64 :param apiuser: This is filled automatically from the |authtoken|.
64 :param apiuser: This is filled automatically from the |authtoken|.
65 :type apiuser: AuthUser
65 :type apiuser: AuthUser
66 :param repoid: The repository name or repository id.
66 :param repoid: The repository name or repository id.
67 :type repoid: str or int
67 :type repoid: str or int
68 :param cache: use the cached value for last changeset
68 :param cache: use the cached value for last changeset
69 :type: cache: Optional(bool)
69 :type: cache: Optional(bool)
70
70
71 Example output:
71 Example output:
72
72
73 .. code-block:: bash
73 .. code-block:: bash
74
74
75 {
75 {
76 "error": null,
76 "error": null,
77 "id": <repo_id>,
77 "id": <repo_id>,
78 "result": {
78 "result": {
79 "clone_uri": null,
79 "clone_uri": null,
80 "created_on": "timestamp",
80 "created_on": "timestamp",
81 "description": "repo description",
81 "description": "repo description",
82 "enable_downloads": false,
82 "enable_downloads": false,
83 "enable_locking": false,
83 "enable_locking": false,
84 "enable_statistics": false,
84 "enable_statistics": false,
85 "followers": [
85 "followers": [
86 {
86 {
87 "active": true,
87 "active": true,
88 "admin": false,
88 "admin": false,
89 "api_key": "****************************************",
89 "api_key": "****************************************",
90 "api_keys": [
90 "api_keys": [
91 "****************************************"
91 "****************************************"
92 ],
92 ],
93 "email": "user@example.com",
93 "email": "user@example.com",
94 "emails": [
94 "emails": [
95 "user@example.com"
95 "user@example.com"
96 ],
96 ],
97 "extern_name": "rhodecode",
97 "extern_name": "rhodecode",
98 "extern_type": "rhodecode",
98 "extern_type": "rhodecode",
99 "firstname": "username",
99 "firstname": "username",
100 "ip_addresses": [],
100 "ip_addresses": [],
101 "language": null,
101 "language": null,
102 "last_login": "2015-09-16T17:16:35.854",
102 "last_login": "2015-09-16T17:16:35.854",
103 "lastname": "surname",
103 "lastname": "surname",
104 "user_id": <user_id>,
104 "user_id": <user_id>,
105 "username": "name"
105 "username": "name"
106 }
106 }
107 ],
107 ],
108 "fork_of": "parent-repo",
108 "fork_of": "parent-repo",
109 "landing_rev": [
109 "landing_rev": [
110 "rev",
110 "rev",
111 "tip"
111 "tip"
112 ],
112 ],
113 "last_changeset": {
113 "last_changeset": {
114 "author": "User <user@example.com>",
114 "author": "User <user@example.com>",
115 "branch": "default",
115 "branch": "default",
116 "date": "timestamp",
116 "date": "timestamp",
117 "message": "last commit message",
117 "message": "last commit message",
118 "parents": [
118 "parents": [
119 {
119 {
120 "raw_id": "commit-id"
120 "raw_id": "commit-id"
121 }
121 }
122 ],
122 ],
123 "raw_id": "commit-id",
123 "raw_id": "commit-id",
124 "revision": <revision number>,
124 "revision": <revision number>,
125 "short_id": "short id"
125 "short_id": "short id"
126 },
126 },
127 "lock_reason": null,
127 "lock_reason": null,
128 "locked_by": null,
128 "locked_by": null,
129 "locked_date": null,
129 "locked_date": null,
130 "members": [
131 {
132 "name": "super-admin-name",
133 "origin": "super-admin",
134 "permission": "repository.admin",
135 "type": "user"
136 },
137 {
138 "name": "owner-name",
139 "origin": "owner",
140 "permission": "repository.admin",
141 "type": "user"
142 },
143 {
144 "name": "user-group-name",
145 "origin": "permission",
146 "permission": "repository.write",
147 "type": "user_group"
148 }
149 ],
150 "owner": "owner-name",
130 "owner": "owner-name",
151 "permissions": [
131 "permissions": [
152 {
132 {
153 "name": "super-admin-name",
133 "name": "super-admin-name",
154 "origin": "super-admin",
134 "origin": "super-admin",
155 "permission": "repository.admin",
135 "permission": "repository.admin",
156 "type": "user"
136 "type": "user"
157 },
137 },
158 {
138 {
159 "name": "owner-name",
139 "name": "owner-name",
160 "origin": "owner",
140 "origin": "owner",
161 "permission": "repository.admin",
141 "permission": "repository.admin",
162 "type": "user"
142 "type": "user"
163 },
143 },
164 {
144 {
165 "name": "user-group-name",
145 "name": "user-group-name",
166 "origin": "permission",
146 "origin": "permission",
167 "permission": "repository.write",
147 "permission": "repository.write",
168 "type": "user_group"
148 "type": "user_group"
169 }
149 }
170 ],
150 ],
171 "private": true,
151 "private": true,
172 "repo_id": 676,
152 "repo_id": 676,
173 "repo_name": "user-group/repo-name",
153 "repo_name": "user-group/repo-name",
174 "repo_type": "hg"
154 "repo_type": "hg"
175 }
155 }
176 }
156 }
177 """
157 """
178
158
179 repo = get_repo_or_error(repoid)
159 repo = get_repo_or_error(repoid)
180 cache = Optional.extract(cache)
160 cache = Optional.extract(cache)
181
161
182 include_secrets = False
162 include_secrets = False
183 if has_superadmin_permission(apiuser):
163 if has_superadmin_permission(apiuser):
184 include_secrets = True
164 include_secrets = True
185 else:
165 else:
186 # check if we have at least read permission for this repo !
166 # check if we have at least read permission for this repo !
187 _perms = (
167 _perms = (
188 'repository.admin', 'repository.write', 'repository.read',)
168 'repository.admin', 'repository.write', 'repository.read',)
189 validate_repo_permissions(apiuser, repoid, repo, _perms)
169 validate_repo_permissions(apiuser, repoid, repo, _perms)
190
170
191 permissions = []
171 permissions = []
192 for _user in repo.permissions():
172 for _user in repo.permissions():
193 user_data = {
173 user_data = {
194 'name': _user.username,
174 'name': _user.username,
195 'permission': _user.permission,
175 'permission': _user.permission,
196 'origin': get_origin(_user),
176 'origin': get_origin(_user),
197 'type': "user",
177 'type': "user",
198 }
178 }
199 permissions.append(user_data)
179 permissions.append(user_data)
200
180
201 for _user_group in repo.permission_user_groups():
181 for _user_group in repo.permission_user_groups():
202 user_group_data = {
182 user_group_data = {
203 'name': _user_group.users_group_name,
183 'name': _user_group.users_group_name,
204 'permission': _user_group.permission,
184 'permission': _user_group.permission,
205 'origin': get_origin(_user_group),
185 'origin': get_origin(_user_group),
206 'type': "user_group",
186 'type': "user_group",
207 }
187 }
208 permissions.append(user_group_data)
188 permissions.append(user_group_data)
209
189
210 following_users = [
190 following_users = [
211 user.user.get_api_data(include_secrets=include_secrets)
191 user.user.get_api_data(include_secrets=include_secrets)
212 for user in repo.followers]
192 for user in repo.followers]
213
193
214 if not cache:
194 if not cache:
215 repo.update_commit_cache()
195 repo.update_commit_cache()
216 data = repo.get_api_data(include_secrets=include_secrets)
196 data = repo.get_api_data(include_secrets=include_secrets)
217 data['permissions'] = permissions
197 data['permissions'] = permissions
218 data['followers'] = following_users
198 data['followers'] = following_users
219 return data
199 return data
220
200
221
201
222 @jsonrpc_method()
202 @jsonrpc_method()
223 def get_repos(request, apiuser, root=Optional(None), traverse=Optional(True)):
203 def get_repos(request, apiuser, root=Optional(None), traverse=Optional(True)):
224 """
204 """
225 Lists all existing repositories.
205 Lists all existing repositories.
226
206
227 This command can only be run using an |authtoken| with admin rights,
207 This command can only be run using an |authtoken| with admin rights,
228 or users with at least read rights to |repos|.
208 or users with at least read rights to |repos|.
229
209
230 :param apiuser: This is filled automatically from the |authtoken|.
210 :param apiuser: This is filled automatically from the |authtoken|.
231 :type apiuser: AuthUser
211 :type apiuser: AuthUser
232 :param root: specify root repository group to fetch repositories.
212 :param root: specify root repository group to fetch repositories.
233 filters the returned repositories to be members of given root group.
213 filters the returned repositories to be members of given root group.
234 :type root: Optional(None)
214 :type root: Optional(None)
235 :param traverse: traverse given root into subrepositories. With this flag
215 :param traverse: traverse given root into subrepositories. With this flag
236 set to False, it will only return top-level repositories from `root`.
216 set to False, it will only return top-level repositories from `root`.
237 if root is empty it will return just top-level repositories.
217 if root is empty it will return just top-level repositories.
238 :type traverse: Optional(True)
218 :type traverse: Optional(True)
239
219
240
220
241 Example output:
221 Example output:
242
222
243 .. code-block:: bash
223 .. code-block:: bash
244
224
245 id : <id_given_in_input>
225 id : <id_given_in_input>
246 result: [
226 result: [
247 {
227 {
248 "repo_id" : "<repo_id>",
228 "repo_id" : "<repo_id>",
249 "repo_name" : "<reponame>"
229 "repo_name" : "<reponame>"
250 "repo_type" : "<repo_type>",
230 "repo_type" : "<repo_type>",
251 "clone_uri" : "<clone_uri>",
231 "clone_uri" : "<clone_uri>",
252 "private": : "<bool>",
232 "private": : "<bool>",
253 "created_on" : "<datetimecreated>",
233 "created_on" : "<datetimecreated>",
254 "description" : "<description>",
234 "description" : "<description>",
255 "landing_rev": "<landing_rev>",
235 "landing_rev": "<landing_rev>",
256 "owner": "<repo_owner>",
236 "owner": "<repo_owner>",
257 "fork_of": "<name_of_fork_parent>",
237 "fork_of": "<name_of_fork_parent>",
258 "enable_downloads": "<bool>",
238 "enable_downloads": "<bool>",
259 "enable_locking": "<bool>",
239 "enable_locking": "<bool>",
260 "enable_statistics": "<bool>",
240 "enable_statistics": "<bool>",
261 },
241 },
262 ...
242 ...
263 ]
243 ]
264 error: null
244 error: null
265 """
245 """
266
246
267 include_secrets = has_superadmin_permission(apiuser)
247 include_secrets = has_superadmin_permission(apiuser)
268 _perms = ('repository.read', 'repository.write', 'repository.admin',)
248 _perms = ('repository.read', 'repository.write', 'repository.admin',)
269 extras = {'user': apiuser}
249 extras = {'user': apiuser}
270
250
271 root = Optional.extract(root)
251 root = Optional.extract(root)
272 traverse = Optional.extract(traverse, binary=True)
252 traverse = Optional.extract(traverse, binary=True)
273
253
274 if root:
254 if root:
275 # verify parent existance, if it's empty return an error
255 # verify parent existance, if it's empty return an error
276 parent = RepoGroup.get_by_group_name(root)
256 parent = RepoGroup.get_by_group_name(root)
277 if not parent:
257 if not parent:
278 raise JSONRPCError(
258 raise JSONRPCError(
279 'Root repository group `{}` does not exist'.format(root))
259 'Root repository group `{}` does not exist'.format(root))
280
260
281 if traverse:
261 if traverse:
282 repos = RepoModel().get_repos_for_root(root=root, traverse=traverse)
262 repos = RepoModel().get_repos_for_root(root=root, traverse=traverse)
283 else:
263 else:
284 repos = RepoModel().get_repos_for_root(root=parent)
264 repos = RepoModel().get_repos_for_root(root=parent)
285 else:
265 else:
286 if traverse:
266 if traverse:
287 repos = RepoModel().get_all()
267 repos = RepoModel().get_all()
288 else:
268 else:
289 # return just top-level
269 # return just top-level
290 repos = RepoModel().get_repos_for_root(root=None)
270 repos = RepoModel().get_repos_for_root(root=None)
291
271
292 repo_list = RepoList(repos, perm_set=_perms, extra_kwargs=extras)
272 repo_list = RepoList(repos, perm_set=_perms, extra_kwargs=extras)
293 return [repo.get_api_data(include_secrets=include_secrets)
273 return [repo.get_api_data(include_secrets=include_secrets)
294 for repo in repo_list]
274 for repo in repo_list]
295
275
296
276
297 @jsonrpc_method()
277 @jsonrpc_method()
298 def get_repo_changeset(request, apiuser, repoid, revision,
278 def get_repo_changeset(request, apiuser, repoid, revision,
299 details=Optional('basic')):
279 details=Optional('basic')):
300 """
280 """
301 Returns information about a changeset.
281 Returns information about a changeset.
302
282
303 Additionally parameters define the amount of details returned by
283 Additionally parameters define the amount of details returned by
304 this function.
284 this function.
305
285
306 This command can only be run using an |authtoken| with admin rights,
286 This command can only be run using an |authtoken| with admin rights,
307 or users with at least read rights to the |repo|.
287 or users with at least read rights to the |repo|.
308
288
309 :param apiuser: This is filled automatically from the |authtoken|.
289 :param apiuser: This is filled automatically from the |authtoken|.
310 :type apiuser: AuthUser
290 :type apiuser: AuthUser
311 :param repoid: The repository name or repository id
291 :param repoid: The repository name or repository id
312 :type repoid: str or int
292 :type repoid: str or int
313 :param revision: revision for which listing should be done
293 :param revision: revision for which listing should be done
314 :type revision: str
294 :type revision: str
315 :param details: details can be 'basic|extended|full' full gives diff
295 :param details: details can be 'basic|extended|full' full gives diff
316 info details like the diff itself, and number of changed files etc.
296 info details like the diff itself, and number of changed files etc.
317 :type details: Optional(str)
297 :type details: Optional(str)
318
298
319 """
299 """
320 repo = get_repo_or_error(repoid)
300 repo = get_repo_or_error(repoid)
321 if not has_superadmin_permission(apiuser):
301 if not has_superadmin_permission(apiuser):
322 _perms = (
302 _perms = (
323 'repository.admin', 'repository.write', 'repository.read',)
303 'repository.admin', 'repository.write', 'repository.read',)
324 validate_repo_permissions(apiuser, repoid, repo, _perms)
304 validate_repo_permissions(apiuser, repoid, repo, _perms)
325
305
326 changes_details = Optional.extract(details)
306 changes_details = Optional.extract(details)
327 _changes_details_types = ['basic', 'extended', 'full']
307 _changes_details_types = ['basic', 'extended', 'full']
328 if changes_details not in _changes_details_types:
308 if changes_details not in _changes_details_types:
329 raise JSONRPCError(
309 raise JSONRPCError(
330 'ret_type must be one of %s' % (
310 'ret_type must be one of %s' % (
331 ','.join(_changes_details_types)))
311 ','.join(_changes_details_types)))
332
312
333 pre_load = ['author', 'branch', 'date', 'message', 'parents',
313 pre_load = ['author', 'branch', 'date', 'message', 'parents',
334 'status', '_commit', '_file_paths']
314 'status', '_commit', '_file_paths']
335
315
336 try:
316 try:
337 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
317 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
338 except TypeError as e:
318 except TypeError as e:
339 raise JSONRPCError(e.message)
319 raise JSONRPCError(e.message)
340 _cs_json = cs.__json__()
320 _cs_json = cs.__json__()
341 _cs_json['diff'] = build_commit_data(cs, changes_details)
321 _cs_json['diff'] = build_commit_data(cs, changes_details)
342 if changes_details == 'full':
322 if changes_details == 'full':
343 _cs_json['refs'] = cs._get_refs()
323 _cs_json['refs'] = cs._get_refs()
344 return _cs_json
324 return _cs_json
345
325
346
326
347 @jsonrpc_method()
327 @jsonrpc_method()
348 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
328 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
349 details=Optional('basic')):
329 details=Optional('basic')):
350 """
330 """
351 Returns a set of commits limited by the number starting
331 Returns a set of commits limited by the number starting
352 from the `start_rev` option.
332 from the `start_rev` option.
353
333
354 Additional parameters define the amount of details returned by this
334 Additional parameters define the amount of details returned by this
355 function.
335 function.
356
336
357 This command can only be run using an |authtoken| with admin rights,
337 This command can only be run using an |authtoken| with admin rights,
358 or users with at least read rights to |repos|.
338 or users with at least read rights to |repos|.
359
339
360 :param apiuser: This is filled automatically from the |authtoken|.
340 :param apiuser: This is filled automatically from the |authtoken|.
361 :type apiuser: AuthUser
341 :type apiuser: AuthUser
362 :param repoid: The repository name or repository ID.
342 :param repoid: The repository name or repository ID.
363 :type repoid: str or int
343 :type repoid: str or int
364 :param start_rev: The starting revision from where to get changesets.
344 :param start_rev: The starting revision from where to get changesets.
365 :type start_rev: str
345 :type start_rev: str
366 :param limit: Limit the number of commits to this amount
346 :param limit: Limit the number of commits to this amount
367 :type limit: str or int
347 :type limit: str or int
368 :param details: Set the level of detail returned. Valid option are:
348 :param details: Set the level of detail returned. Valid option are:
369 ``basic``, ``extended`` and ``full``.
349 ``basic``, ``extended`` and ``full``.
370 :type details: Optional(str)
350 :type details: Optional(str)
371
351
372 .. note::
352 .. note::
373
353
374 Setting the parameter `details` to the value ``full`` is extensive
354 Setting the parameter `details` to the value ``full`` is extensive
375 and returns details like the diff itself, and the number
355 and returns details like the diff itself, and the number
376 of changed files.
356 of changed files.
377
357
378 """
358 """
379 repo = get_repo_or_error(repoid)
359 repo = get_repo_or_error(repoid)
380 if not has_superadmin_permission(apiuser):
360 if not has_superadmin_permission(apiuser):
381 _perms = (
361 _perms = (
382 'repository.admin', 'repository.write', 'repository.read',)
362 'repository.admin', 'repository.write', 'repository.read',)
383 validate_repo_permissions(apiuser, repoid, repo, _perms)
363 validate_repo_permissions(apiuser, repoid, repo, _perms)
384
364
385 changes_details = Optional.extract(details)
365 changes_details = Optional.extract(details)
386 _changes_details_types = ['basic', 'extended', 'full']
366 _changes_details_types = ['basic', 'extended', 'full']
387 if changes_details not in _changes_details_types:
367 if changes_details not in _changes_details_types:
388 raise JSONRPCError(
368 raise JSONRPCError(
389 'ret_type must be one of %s' % (
369 'ret_type must be one of %s' % (
390 ','.join(_changes_details_types)))
370 ','.join(_changes_details_types)))
391
371
392 limit = int(limit)
372 limit = int(limit)
393 pre_load = ['author', 'branch', 'date', 'message', 'parents',
373 pre_load = ['author', 'branch', 'date', 'message', 'parents',
394 'status', '_commit', '_file_paths']
374 'status', '_commit', '_file_paths']
395
375
396 vcs_repo = repo.scm_instance()
376 vcs_repo = repo.scm_instance()
397 # SVN needs a special case to distinguish its index and commit id
377 # SVN needs a special case to distinguish its index and commit id
398 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
378 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
399 start_rev = vcs_repo.commit_ids[0]
379 start_rev = vcs_repo.commit_ids[0]
400
380
401 try:
381 try:
402 commits = vcs_repo.get_commits(
382 commits = vcs_repo.get_commits(
403 start_id=start_rev, pre_load=pre_load)
383 start_id=start_rev, pre_load=pre_load)
404 except TypeError as e:
384 except TypeError as e:
405 raise JSONRPCError(e.message)
385 raise JSONRPCError(e.message)
406 except Exception:
386 except Exception:
407 log.exception('Fetching of commits failed')
387 log.exception('Fetching of commits failed')
408 raise JSONRPCError('Error occurred during commit fetching')
388 raise JSONRPCError('Error occurred during commit fetching')
409
389
410 ret = []
390 ret = []
411 for cnt, commit in enumerate(commits):
391 for cnt, commit in enumerate(commits):
412 if cnt >= limit != -1:
392 if cnt >= limit != -1:
413 break
393 break
414 _cs_json = commit.__json__()
394 _cs_json = commit.__json__()
415 _cs_json['diff'] = build_commit_data(commit, changes_details)
395 _cs_json['diff'] = build_commit_data(commit, changes_details)
416 if changes_details == 'full':
396 if changes_details == 'full':
417 _cs_json['refs'] = {
397 _cs_json['refs'] = {
418 'branches': [commit.branch],
398 'branches': [commit.branch],
419 'bookmarks': getattr(commit, 'bookmarks', []),
399 'bookmarks': getattr(commit, 'bookmarks', []),
420 'tags': commit.tags
400 'tags': commit.tags
421 }
401 }
422 ret.append(_cs_json)
402 ret.append(_cs_json)
423 return ret
403 return ret
424
404
425
405
426 @jsonrpc_method()
406 @jsonrpc_method()
427 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
407 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
428 ret_type=Optional('all'), details=Optional('basic'),
408 ret_type=Optional('all'), details=Optional('basic'),
429 max_file_bytes=Optional(None)):
409 max_file_bytes=Optional(None)):
430 """
410 """
431 Returns a list of nodes and children in a flat list for a given
411 Returns a list of nodes and children in a flat list for a given
432 path at given revision.
412 path at given revision.
433
413
434 It's possible to specify ret_type to show only `files` or `dirs`.
414 It's possible to specify ret_type to show only `files` or `dirs`.
435
415
436 This command can only be run using an |authtoken| with admin rights,
416 This command can only be run using an |authtoken| with admin rights,
437 or users with at least read rights to |repos|.
417 or users with at least read rights to |repos|.
438
418
439 :param apiuser: This is filled automatically from the |authtoken|.
419 :param apiuser: This is filled automatically from the |authtoken|.
440 :type apiuser: AuthUser
420 :type apiuser: AuthUser
441 :param repoid: The repository name or repository ID.
421 :param repoid: The repository name or repository ID.
442 :type repoid: str or int
422 :type repoid: str or int
443 :param revision: The revision for which listing should be done.
423 :param revision: The revision for which listing should be done.
444 :type revision: str
424 :type revision: str
445 :param root_path: The path from which to start displaying.
425 :param root_path: The path from which to start displaying.
446 :type root_path: str
426 :type root_path: str
447 :param ret_type: Set the return type. Valid options are
427 :param ret_type: Set the return type. Valid options are
448 ``all`` (default), ``files`` and ``dirs``.
428 ``all`` (default), ``files`` and ``dirs``.
449 :type ret_type: Optional(str)
429 :type ret_type: Optional(str)
450 :param details: Returns extended information about nodes, such as
430 :param details: Returns extended information about nodes, such as
451 md5, binary, and or content. The valid options are ``basic`` and
431 md5, binary, and or content. The valid options are ``basic`` and
452 ``full``.
432 ``full``.
453 :type details: Optional(str)
433 :type details: Optional(str)
454 :param max_file_bytes: Only return file content under this file size bytes
434 :param max_file_bytes: Only return file content under this file size bytes
455 :type details: Optional(int)
435 :type details: Optional(int)
456
436
457 Example output:
437 Example output:
458
438
459 .. code-block:: bash
439 .. code-block:: bash
460
440
461 id : <id_given_in_input>
441 id : <id_given_in_input>
462 result: [
442 result: [
463 {
443 {
464 "name" : "<name>"
444 "name" : "<name>"
465 "type" : "<type>",
445 "type" : "<type>",
466 "binary": "<true|false>" (only in extended mode)
446 "binary": "<true|false>" (only in extended mode)
467 "md5" : "<md5 of file content>" (only in extended mode)
447 "md5" : "<md5 of file content>" (only in extended mode)
468 },
448 },
469 ...
449 ...
470 ]
450 ]
471 error: null
451 error: null
472 """
452 """
473
453
474 repo = get_repo_or_error(repoid)
454 repo = get_repo_or_error(repoid)
475 if not has_superadmin_permission(apiuser):
455 if not has_superadmin_permission(apiuser):
476 _perms = (
456 _perms = (
477 'repository.admin', 'repository.write', 'repository.read',)
457 'repository.admin', 'repository.write', 'repository.read',)
478 validate_repo_permissions(apiuser, repoid, repo, _perms)
458 validate_repo_permissions(apiuser, repoid, repo, _perms)
479
459
480 ret_type = Optional.extract(ret_type)
460 ret_type = Optional.extract(ret_type)
481 details = Optional.extract(details)
461 details = Optional.extract(details)
482 _extended_types = ['basic', 'full']
462 _extended_types = ['basic', 'full']
483 if details not in _extended_types:
463 if details not in _extended_types:
484 raise JSONRPCError(
464 raise JSONRPCError(
485 'ret_type must be one of %s' % (','.join(_extended_types)))
465 'ret_type must be one of %s' % (','.join(_extended_types)))
486 extended_info = False
466 extended_info = False
487 content = False
467 content = False
488 if details == 'basic':
468 if details == 'basic':
489 extended_info = True
469 extended_info = True
490
470
491 if details == 'full':
471 if details == 'full':
492 extended_info = content = True
472 extended_info = content = True
493
473
494 _map = {}
474 _map = {}
495 try:
475 try:
496 # check if repo is not empty by any chance, skip quicker if it is.
476 # check if repo is not empty by any chance, skip quicker if it is.
497 _scm = repo.scm_instance()
477 _scm = repo.scm_instance()
498 if _scm.is_empty():
478 if _scm.is_empty():
499 return []
479 return []
500
480
501 _d, _f = ScmModel().get_nodes(
481 _d, _f = ScmModel().get_nodes(
502 repo, revision, root_path, flat=False,
482 repo, revision, root_path, flat=False,
503 extended_info=extended_info, content=content,
483 extended_info=extended_info, content=content,
504 max_file_bytes=max_file_bytes)
484 max_file_bytes=max_file_bytes)
505 _map = {
485 _map = {
506 'all': _d + _f,
486 'all': _d + _f,
507 'files': _f,
487 'files': _f,
508 'dirs': _d,
488 'dirs': _d,
509 }
489 }
510 return _map[ret_type]
490 return _map[ret_type]
511 except KeyError:
491 except KeyError:
512 raise JSONRPCError(
492 raise JSONRPCError(
513 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
493 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
514 except Exception:
494 except Exception:
515 log.exception("Exception occurred while trying to get repo nodes")
495 log.exception("Exception occurred while trying to get repo nodes")
516 raise JSONRPCError(
496 raise JSONRPCError(
517 'failed to get repo: `%s` nodes' % repo.repo_name
497 'failed to get repo: `%s` nodes' % repo.repo_name
518 )
498 )
519
499
520
500
521 @jsonrpc_method()
501 @jsonrpc_method()
522 def get_repo_refs(request, apiuser, repoid):
502 def get_repo_refs(request, apiuser, repoid):
523 """
503 """
524 Returns a dictionary of current references. It returns
504 Returns a dictionary of current references. It returns
525 bookmarks, branches, closed_branches, and tags for given repository
505 bookmarks, branches, closed_branches, and tags for given repository
526
506
527 It's possible to specify ret_type to show only `files` or `dirs`.
507 It's possible to specify ret_type to show only `files` or `dirs`.
528
508
529 This command can only be run using an |authtoken| with admin rights,
509 This command can only be run using an |authtoken| with admin rights,
530 or users with at least read rights to |repos|.
510 or users with at least read rights to |repos|.
531
511
532 :param apiuser: This is filled automatically from the |authtoken|.
512 :param apiuser: This is filled automatically from the |authtoken|.
533 :type apiuser: AuthUser
513 :type apiuser: AuthUser
534 :param repoid: The repository name or repository ID.
514 :param repoid: The repository name or repository ID.
535 :type repoid: str or int
515 :type repoid: str or int
536
516
537 Example output:
517 Example output:
538
518
539 .. code-block:: bash
519 .. code-block:: bash
540
520
541 id : <id_given_in_input>
521 id : <id_given_in_input>
542 "result": {
522 "result": {
543 "bookmarks": {
523 "bookmarks": {
544 "dev": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
524 "dev": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
545 "master": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
525 "master": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
546 },
526 },
547 "branches": {
527 "branches": {
548 "default": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
528 "default": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
549 "stable": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
529 "stable": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
550 },
530 },
551 "branches_closed": {},
531 "branches_closed": {},
552 "tags": {
532 "tags": {
553 "tip": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
533 "tip": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
554 "v4.4.0": "1232313f9e6adac5ce5399c2a891dc1e72b79022",
534 "v4.4.0": "1232313f9e6adac5ce5399c2a891dc1e72b79022",
555 "v4.4.1": "cbb9f1d329ae5768379cdec55a62ebdd546c4e27",
535 "v4.4.1": "cbb9f1d329ae5768379cdec55a62ebdd546c4e27",
556 "v4.4.2": "24ffe44a27fcd1c5b6936144e176b9f6dd2f3a17",
536 "v4.4.2": "24ffe44a27fcd1c5b6936144e176b9f6dd2f3a17",
557 }
537 }
558 }
538 }
559 error: null
539 error: null
560 """
540 """
561
541
562 repo = get_repo_or_error(repoid)
542 repo = get_repo_or_error(repoid)
563 if not has_superadmin_permission(apiuser):
543 if not has_superadmin_permission(apiuser):
564 _perms = ('repository.admin', 'repository.write', 'repository.read',)
544 _perms = ('repository.admin', 'repository.write', 'repository.read',)
565 validate_repo_permissions(apiuser, repoid, repo, _perms)
545 validate_repo_permissions(apiuser, repoid, repo, _perms)
566
546
567 try:
547 try:
568 # check if repo is not empty by any chance, skip quicker if it is.
548 # check if repo is not empty by any chance, skip quicker if it is.
569 vcs_instance = repo.scm_instance()
549 vcs_instance = repo.scm_instance()
570 refs = vcs_instance.refs()
550 refs = vcs_instance.refs()
571 return refs
551 return refs
572 except Exception:
552 except Exception:
573 log.exception("Exception occurred while trying to get repo refs")
553 log.exception("Exception occurred while trying to get repo refs")
574 raise JSONRPCError(
554 raise JSONRPCError(
575 'failed to get repo: `%s` references' % repo.repo_name
555 'failed to get repo: `%s` references' % repo.repo_name
576 )
556 )
577
557
578
558
579 @jsonrpc_method()
559 @jsonrpc_method()
580 def create_repo(
560 def create_repo(
581 request, apiuser, repo_name, repo_type,
561 request, apiuser, repo_name, repo_type,
582 owner=Optional(OAttr('apiuser')),
562 owner=Optional(OAttr('apiuser')),
583 description=Optional(''),
563 description=Optional(''),
584 private=Optional(False),
564 private=Optional(False),
585 clone_uri=Optional(None),
565 clone_uri=Optional(None),
586 landing_rev=Optional('rev:tip'),
566 landing_rev=Optional('rev:tip'),
587 enable_statistics=Optional(False),
567 enable_statistics=Optional(False),
588 enable_locking=Optional(False),
568 enable_locking=Optional(False),
589 enable_downloads=Optional(False),
569 enable_downloads=Optional(False),
590 copy_permissions=Optional(False)):
570 copy_permissions=Optional(False)):
591 """
571 """
592 Creates a repository.
572 Creates a repository.
593
573
594 * If the repository name contains "/", repository will be created inside
574 * If the repository name contains "/", repository will be created inside
595 a repository group or nested repository groups
575 a repository group or nested repository groups
596
576
597 For example "foo/bar/repo1" will create |repo| called "repo1" inside
577 For example "foo/bar/repo1" will create |repo| called "repo1" inside
598 group "foo/bar". You have to have permissions to access and write to
578 group "foo/bar". You have to have permissions to access and write to
599 the last repository group ("bar" in this example)
579 the last repository group ("bar" in this example)
600
580
601 This command can only be run using an |authtoken| with at least
581 This command can only be run using an |authtoken| with at least
602 permissions to create repositories, or write permissions to
582 permissions to create repositories, or write permissions to
603 parent repository groups.
583 parent repository groups.
604
584
605 :param apiuser: This is filled automatically from the |authtoken|.
585 :param apiuser: This is filled automatically from the |authtoken|.
606 :type apiuser: AuthUser
586 :type apiuser: AuthUser
607 :param repo_name: Set the repository name.
587 :param repo_name: Set the repository name.
608 :type repo_name: str
588 :type repo_name: str
609 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
589 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
610 :type repo_type: str
590 :type repo_type: str
611 :param owner: user_id or username
591 :param owner: user_id or username
612 :type owner: Optional(str)
592 :type owner: Optional(str)
613 :param description: Set the repository description.
593 :param description: Set the repository description.
614 :type description: Optional(str)
594 :type description: Optional(str)
615 :param private: set repository as private
595 :param private: set repository as private
616 :type private: bool
596 :type private: bool
617 :param clone_uri: set clone_uri
597 :param clone_uri: set clone_uri
618 :type clone_uri: str
598 :type clone_uri: str
619 :param landing_rev: <rev_type>:<rev>
599 :param landing_rev: <rev_type>:<rev>
620 :type landing_rev: str
600 :type landing_rev: str
621 :param enable_locking:
601 :param enable_locking:
622 :type enable_locking: bool
602 :type enable_locking: bool
623 :param enable_downloads:
603 :param enable_downloads:
624 :type enable_downloads: bool
604 :type enable_downloads: bool
625 :param enable_statistics:
605 :param enable_statistics:
626 :type enable_statistics: bool
606 :type enable_statistics: bool
627 :param copy_permissions: Copy permission from group in which the
607 :param copy_permissions: Copy permission from group in which the
628 repository is being created.
608 repository is being created.
629 :type copy_permissions: bool
609 :type copy_permissions: bool
630
610
631
611
632 Example output:
612 Example output:
633
613
634 .. code-block:: bash
614 .. code-block:: bash
635
615
636 id : <id_given_in_input>
616 id : <id_given_in_input>
637 result: {
617 result: {
638 "msg": "Created new repository `<reponame>`",
618 "msg": "Created new repository `<reponame>`",
639 "success": true,
619 "success": true,
640 "task": "<celery task id or None if done sync>"
620 "task": "<celery task id or None if done sync>"
641 }
621 }
642 error: null
622 error: null
643
623
644
624
645 Example error output:
625 Example error output:
646
626
647 .. code-block:: bash
627 .. code-block:: bash
648
628
649 id : <id_given_in_input>
629 id : <id_given_in_input>
650 result : null
630 result : null
651 error : {
631 error : {
652 'failed to create repository `<repo_name>`'
632 'failed to create repository `<repo_name>`'
653 }
633 }
654
634
655 """
635 """
656
636
657 owner = validate_set_owner_permissions(apiuser, owner)
637 owner = validate_set_owner_permissions(apiuser, owner)
658
638
659 description = Optional.extract(description)
639 description = Optional.extract(description)
660 copy_permissions = Optional.extract(copy_permissions)
640 copy_permissions = Optional.extract(copy_permissions)
661 clone_uri = Optional.extract(clone_uri)
641 clone_uri = Optional.extract(clone_uri)
662 landing_commit_ref = Optional.extract(landing_rev)
642 landing_commit_ref = Optional.extract(landing_rev)
663
643
664 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
644 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
665 if isinstance(private, Optional):
645 if isinstance(private, Optional):
666 private = defs.get('repo_private') or Optional.extract(private)
646 private = defs.get('repo_private') or Optional.extract(private)
667 if isinstance(repo_type, Optional):
647 if isinstance(repo_type, Optional):
668 repo_type = defs.get('repo_type')
648 repo_type = defs.get('repo_type')
669 if isinstance(enable_statistics, Optional):
649 if isinstance(enable_statistics, Optional):
670 enable_statistics = defs.get('repo_enable_statistics')
650 enable_statistics = defs.get('repo_enable_statistics')
671 if isinstance(enable_locking, Optional):
651 if isinstance(enable_locking, Optional):
672 enable_locking = defs.get('repo_enable_locking')
652 enable_locking = defs.get('repo_enable_locking')
673 if isinstance(enable_downloads, Optional):
653 if isinstance(enable_downloads, Optional):
674 enable_downloads = defs.get('repo_enable_downloads')
654 enable_downloads = defs.get('repo_enable_downloads')
675
655
676 schema = repo_schema.RepoSchema().bind(
656 schema = repo_schema.RepoSchema().bind(
677 repo_type_options=rhodecode.BACKENDS.keys(),
657 repo_type_options=rhodecode.BACKENDS.keys(),
678 # user caller
658 # user caller
679 user=apiuser)
659 user=apiuser)
680
660
681 try:
661 try:
682 schema_data = schema.deserialize(dict(
662 schema_data = schema.deserialize(dict(
683 repo_name=repo_name,
663 repo_name=repo_name,
684 repo_type=repo_type,
664 repo_type=repo_type,
685 repo_owner=owner.username,
665 repo_owner=owner.username,
686 repo_description=description,
666 repo_description=description,
687 repo_landing_commit_ref=landing_commit_ref,
667 repo_landing_commit_ref=landing_commit_ref,
688 repo_clone_uri=clone_uri,
668 repo_clone_uri=clone_uri,
689 repo_private=private,
669 repo_private=private,
690 repo_copy_permissions=copy_permissions,
670 repo_copy_permissions=copy_permissions,
691 repo_enable_statistics=enable_statistics,
671 repo_enable_statistics=enable_statistics,
692 repo_enable_downloads=enable_downloads,
672 repo_enable_downloads=enable_downloads,
693 repo_enable_locking=enable_locking))
673 repo_enable_locking=enable_locking))
694 except validation_schema.Invalid as err:
674 except validation_schema.Invalid as err:
695 raise JSONRPCValidationError(colander_exc=err)
675 raise JSONRPCValidationError(colander_exc=err)
696
676
697 try:
677 try:
698 data = {
678 data = {
699 'owner': owner,
679 'owner': owner,
700 'repo_name': schema_data['repo_group']['repo_name_without_group'],
680 'repo_name': schema_data['repo_group']['repo_name_without_group'],
701 'repo_name_full': schema_data['repo_name'],
681 'repo_name_full': schema_data['repo_name'],
702 'repo_group': schema_data['repo_group']['repo_group_id'],
682 'repo_group': schema_data['repo_group']['repo_group_id'],
703 'repo_type': schema_data['repo_type'],
683 'repo_type': schema_data['repo_type'],
704 'repo_description': schema_data['repo_description'],
684 'repo_description': schema_data['repo_description'],
705 'repo_private': schema_data['repo_private'],
685 'repo_private': schema_data['repo_private'],
706 'clone_uri': schema_data['repo_clone_uri'],
686 'clone_uri': schema_data['repo_clone_uri'],
707 'repo_landing_rev': schema_data['repo_landing_commit_ref'],
687 'repo_landing_rev': schema_data['repo_landing_commit_ref'],
708 'enable_statistics': schema_data['repo_enable_statistics'],
688 'enable_statistics': schema_data['repo_enable_statistics'],
709 'enable_locking': schema_data['repo_enable_locking'],
689 'enable_locking': schema_data['repo_enable_locking'],
710 'enable_downloads': schema_data['repo_enable_downloads'],
690 'enable_downloads': schema_data['repo_enable_downloads'],
711 'repo_copy_permissions': schema_data['repo_copy_permissions'],
691 'repo_copy_permissions': schema_data['repo_copy_permissions'],
712 }
692 }
713
693
714 task = RepoModel().create(form_data=data, cur_user=owner)
694 task = RepoModel().create(form_data=data, cur_user=owner)
715 task_id = get_task_id(task)
695 task_id = get_task_id(task)
716 # no commit, it's done in RepoModel, or async via celery
696 # no commit, it's done in RepoModel, or async via celery
717 return {
697 return {
718 'msg': "Created new repository `%s`" % (schema_data['repo_name'],),
698 'msg': "Created new repository `%s`" % (schema_data['repo_name'],),
719 'success': True, # cannot return the repo data here since fork
699 'success': True, # cannot return the repo data here since fork
720 # can be done async
700 # can be done async
721 'task': task_id
701 'task': task_id
722 }
702 }
723 except Exception:
703 except Exception:
724 log.exception(
704 log.exception(
725 u"Exception while trying to create the repository %s",
705 u"Exception while trying to create the repository %s",
726 schema_data['repo_name'])
706 schema_data['repo_name'])
727 raise JSONRPCError(
707 raise JSONRPCError(
728 'failed to create repository `%s`' % (schema_data['repo_name'],))
708 'failed to create repository `%s`' % (schema_data['repo_name'],))
729
709
730
710
731 @jsonrpc_method()
711 @jsonrpc_method()
732 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
712 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
733 description=Optional('')):
713 description=Optional('')):
734 """
714 """
735 Adds an extra field to a repository.
715 Adds an extra field to a repository.
736
716
737 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
738 write permissions to the |repo|.
718 write permissions to the |repo|.
739
719
740 :param apiuser: This is filled automatically from the |authtoken|.
720 :param apiuser: This is filled automatically from the |authtoken|.
741 :type apiuser: AuthUser
721 :type apiuser: AuthUser
742 :param repoid: Set the repository name or repository id.
722 :param repoid: Set the repository name or repository id.
743 :type repoid: str or int
723 :type repoid: str or int
744 :param key: Create a unique field key for this repository.
724 :param key: Create a unique field key for this repository.
745 :type key: str
725 :type key: str
746 :param label:
726 :param label:
747 :type label: Optional(str)
727 :type label: Optional(str)
748 :param description:
728 :param description:
749 :type description: Optional(str)
729 :type description: Optional(str)
750 """
730 """
751 repo = get_repo_or_error(repoid)
731 repo = get_repo_or_error(repoid)
752 if not has_superadmin_permission(apiuser):
732 if not has_superadmin_permission(apiuser):
753 _perms = ('repository.admin',)
733 _perms = ('repository.admin',)
754 validate_repo_permissions(apiuser, repoid, repo, _perms)
734 validate_repo_permissions(apiuser, repoid, repo, _perms)
755
735
756 label = Optional.extract(label) or key
736 label = Optional.extract(label) or key
757 description = Optional.extract(description)
737 description = Optional.extract(description)
758
738
759 field = RepositoryField.get_by_key_name(key, repo)
739 field = RepositoryField.get_by_key_name(key, repo)
760 if field:
740 if field:
761 raise JSONRPCError('Field with key '
741 raise JSONRPCError('Field with key '
762 '`%s` exists for repo `%s`' % (key, repoid))
742 '`%s` exists for repo `%s`' % (key, repoid))
763
743
764 try:
744 try:
765 RepoModel().add_repo_field(repo, key, field_label=label,
745 RepoModel().add_repo_field(repo, key, field_label=label,
766 field_desc=description)
746 field_desc=description)
767 Session().commit()
747 Session().commit()
768 return {
748 return {
769 'msg': "Added new repository field `%s`" % (key,),
749 'msg': "Added new repository field `%s`" % (key,),
770 'success': True,
750 'success': True,
771 }
751 }
772 except Exception:
752 except Exception:
773 log.exception("Exception occurred while trying to add field to repo")
753 log.exception("Exception occurred while trying to add field to repo")
774 raise JSONRPCError(
754 raise JSONRPCError(
775 'failed to create new field for repository `%s`' % (repoid,))
755 'failed to create new field for repository `%s`' % (repoid,))
776
756
777
757
778 @jsonrpc_method()
758 @jsonrpc_method()
779 def remove_field_from_repo(request, apiuser, repoid, key):
759 def remove_field_from_repo(request, apiuser, repoid, key):
780 """
760 """
781 Removes an extra field from a repository.
761 Removes an extra field from a repository.
782
762
783 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
784 write permissions to the |repo|.
764 write permissions to the |repo|.
785
765
786 :param apiuser: This is filled automatically from the |authtoken|.
766 :param apiuser: This is filled automatically from the |authtoken|.
787 :type apiuser: AuthUser
767 :type apiuser: AuthUser
788 :param repoid: Set the repository name or repository ID.
768 :param repoid: Set the repository name or repository ID.
789 :type repoid: str or int
769 :type repoid: str or int
790 :param key: Set the unique field key for this repository.
770 :param key: Set the unique field key for this repository.
791 :type key: str
771 :type key: str
792 """
772 """
793
773
794 repo = get_repo_or_error(repoid)
774 repo = get_repo_or_error(repoid)
795 if not has_superadmin_permission(apiuser):
775 if not has_superadmin_permission(apiuser):
796 _perms = ('repository.admin',)
776 _perms = ('repository.admin',)
797 validate_repo_permissions(apiuser, repoid, repo, _perms)
777 validate_repo_permissions(apiuser, repoid, repo, _perms)
798
778
799 field = RepositoryField.get_by_key_name(key, repo)
779 field = RepositoryField.get_by_key_name(key, repo)
800 if not field:
780 if not field:
801 raise JSONRPCError('Field with key `%s` does not '
781 raise JSONRPCError('Field with key `%s` does not '
802 'exists for repo `%s`' % (key, repoid))
782 'exists for repo `%s`' % (key, repoid))
803
783
804 try:
784 try:
805 RepoModel().delete_repo_field(repo, field_key=key)
785 RepoModel().delete_repo_field(repo, field_key=key)
806 Session().commit()
786 Session().commit()
807 return {
787 return {
808 'msg': "Deleted repository field `%s`" % (key,),
788 'msg': "Deleted repository field `%s`" % (key,),
809 'success': True,
789 'success': True,
810 }
790 }
811 except Exception:
791 except Exception:
812 log.exception(
792 log.exception(
813 "Exception occurred while trying to delete field from repo")
793 "Exception occurred while trying to delete field from repo")
814 raise JSONRPCError(
794 raise JSONRPCError(
815 'failed to delete field for repository `%s`' % (repoid,))
795 'failed to delete field for repository `%s`' % (repoid,))
816
796
817
797
818 @jsonrpc_method()
798 @jsonrpc_method()
819 def update_repo(
799 def update_repo(
820 request, apiuser, repoid, repo_name=Optional(None),
800 request, apiuser, repoid, repo_name=Optional(None),
821 owner=Optional(OAttr('apiuser')), description=Optional(''),
801 owner=Optional(OAttr('apiuser')), description=Optional(''),
822 private=Optional(False), clone_uri=Optional(None),
802 private=Optional(False), clone_uri=Optional(None),
823 landing_rev=Optional('rev:tip'), fork_of=Optional(None),
803 landing_rev=Optional('rev:tip'), fork_of=Optional(None),
824 enable_statistics=Optional(False),
804 enable_statistics=Optional(False),
825 enable_locking=Optional(False),
805 enable_locking=Optional(False),
826 enable_downloads=Optional(False), fields=Optional('')):
806 enable_downloads=Optional(False), fields=Optional('')):
827 """
807 """
828 Updates a repository with the given information.
808 Updates a repository with the given information.
829
809
830 This command can only be run using an |authtoken| with at least
810 This command can only be run using an |authtoken| with at least
831 admin permissions to the |repo|.
811 admin permissions to the |repo|.
832
812
833 * If the repository name contains "/", repository will be updated
813 * If the repository name contains "/", repository will be updated
834 accordingly with a repository group or nested repository groups
814 accordingly with a repository group or nested repository groups
835
815
836 For example repoid=repo-test name="foo/bar/repo-test" will update |repo|
816 For example repoid=repo-test name="foo/bar/repo-test" will update |repo|
837 called "repo-test" and place it inside group "foo/bar".
817 called "repo-test" and place it inside group "foo/bar".
838 You have to have permissions to access and write to the last repository
818 You have to have permissions to access and write to the last repository
839 group ("bar" in this example)
819 group ("bar" in this example)
840
820
841 :param apiuser: This is filled automatically from the |authtoken|.
821 :param apiuser: This is filled automatically from the |authtoken|.
842 :type apiuser: AuthUser
822 :type apiuser: AuthUser
843 :param repoid: repository name or repository ID.
823 :param repoid: repository name or repository ID.
844 :type repoid: str or int
824 :type repoid: str or int
845 :param repo_name: Update the |repo| name, including the
825 :param repo_name: Update the |repo| name, including the
846 repository group it's in.
826 repository group it's in.
847 :type repo_name: str
827 :type repo_name: str
848 :param owner: Set the |repo| owner.
828 :param owner: Set the |repo| owner.
849 :type owner: str
829 :type owner: str
850 :param fork_of: Set the |repo| as fork of another |repo|.
830 :param fork_of: Set the |repo| as fork of another |repo|.
851 :type fork_of: str
831 :type fork_of: str
852 :param description: Update the |repo| description.
832 :param description: Update the |repo| description.
853 :type description: str
833 :type description: str
854 :param private: Set the |repo| as private. (True | False)
834 :param private: Set the |repo| as private. (True | False)
855 :type private: bool
835 :type private: bool
856 :param clone_uri: Update the |repo| clone URI.
836 :param clone_uri: Update the |repo| clone URI.
857 :type clone_uri: str
837 :type clone_uri: str
858 :param landing_rev: Set the |repo| landing revision. Default is ``rev:tip``.
838 :param landing_rev: Set the |repo| landing revision. Default is ``rev:tip``.
859 :type landing_rev: str
839 :type landing_rev: str
860 :param enable_statistics: Enable statistics on the |repo|, (True | False).
840 :param enable_statistics: Enable statistics on the |repo|, (True | False).
861 :type enable_statistics: bool
841 :type enable_statistics: bool
862 :param enable_locking: Enable |repo| locking.
842 :param enable_locking: Enable |repo| locking.
863 :type enable_locking: bool
843 :type enable_locking: bool
864 :param enable_downloads: Enable downloads from the |repo|, (True | False).
844 :param enable_downloads: Enable downloads from the |repo|, (True | False).
865 :type enable_downloads: bool
845 :type enable_downloads: bool
866 :param fields: Add extra fields to the |repo|. Use the following
846 :param fields: Add extra fields to the |repo|. Use the following
867 example format: ``field_key=field_val,field_key2=fieldval2``.
847 example format: ``field_key=field_val,field_key2=fieldval2``.
868 Escape ', ' with \,
848 Escape ', ' with \,
869 :type fields: str
849 :type fields: str
870 """
850 """
871
851
872 repo = get_repo_or_error(repoid)
852 repo = get_repo_or_error(repoid)
873
853
874 include_secrets = False
854 include_secrets = False
875 if not has_superadmin_permission(apiuser):
855 if not has_superadmin_permission(apiuser):
876 validate_repo_permissions(apiuser, repoid, repo, ('repository.admin',))
856 validate_repo_permissions(apiuser, repoid, repo, ('repository.admin',))
877 else:
857 else:
878 include_secrets = True
858 include_secrets = True
879
859
880 updates = dict(
860 updates = dict(
881 repo_name=repo_name
861 repo_name=repo_name
882 if not isinstance(repo_name, Optional) else repo.repo_name,
862 if not isinstance(repo_name, Optional) else repo.repo_name,
883
863
884 fork_id=fork_of
864 fork_id=fork_of
885 if not isinstance(fork_of, Optional) else repo.fork.repo_name if repo.fork else None,
865 if not isinstance(fork_of, Optional) else repo.fork.repo_name if repo.fork else None,
886
866
887 user=owner
867 user=owner
888 if not isinstance(owner, Optional) else repo.user.username,
868 if not isinstance(owner, Optional) else repo.user.username,
889
869
890 repo_description=description
870 repo_description=description
891 if not isinstance(description, Optional) else repo.description,
871 if not isinstance(description, Optional) else repo.description,
892
872
893 repo_private=private
873 repo_private=private
894 if not isinstance(private, Optional) else repo.private,
874 if not isinstance(private, Optional) else repo.private,
895
875
896 clone_uri=clone_uri
876 clone_uri=clone_uri
897 if not isinstance(clone_uri, Optional) else repo.clone_uri,
877 if not isinstance(clone_uri, Optional) else repo.clone_uri,
898
878
899 repo_landing_rev=landing_rev
879 repo_landing_rev=landing_rev
900 if not isinstance(landing_rev, Optional) else repo._landing_revision,
880 if not isinstance(landing_rev, Optional) else repo._landing_revision,
901
881
902 repo_enable_statistics=enable_statistics
882 repo_enable_statistics=enable_statistics
903 if not isinstance(enable_statistics, Optional) else repo.enable_statistics,
883 if not isinstance(enable_statistics, Optional) else repo.enable_statistics,
904
884
905 repo_enable_locking=enable_locking
885 repo_enable_locking=enable_locking
906 if not isinstance(enable_locking, Optional) else repo.enable_locking,
886 if not isinstance(enable_locking, Optional) else repo.enable_locking,
907
887
908 repo_enable_downloads=enable_downloads
888 repo_enable_downloads=enable_downloads
909 if not isinstance(enable_downloads, Optional) else repo.enable_downloads)
889 if not isinstance(enable_downloads, Optional) else repo.enable_downloads)
910
890
911 ref_choices, _labels = ScmModel().get_repo_landing_revs(
891 ref_choices, _labels = ScmModel().get_repo_landing_revs(
912 request.translate, repo=repo)
892 request.translate, repo=repo)
913
893
914 old_values = repo.get_api_data()
894 old_values = repo.get_api_data()
915 schema = repo_schema.RepoSchema().bind(
895 schema = repo_schema.RepoSchema().bind(
916 repo_type_options=rhodecode.BACKENDS.keys(),
896 repo_type_options=rhodecode.BACKENDS.keys(),
917 repo_ref_options=ref_choices,
897 repo_ref_options=ref_choices,
918 # user caller
898 # user caller
919 user=apiuser,
899 user=apiuser,
920 old_values=old_values)
900 old_values=old_values)
921 try:
901 try:
922 schema_data = schema.deserialize(dict(
902 schema_data = schema.deserialize(dict(
923 # we save old value, users cannot change type
903 # we save old value, users cannot change type
924 repo_type=repo.repo_type,
904 repo_type=repo.repo_type,
925
905
926 repo_name=updates['repo_name'],
906 repo_name=updates['repo_name'],
927 repo_owner=updates['user'],
907 repo_owner=updates['user'],
928 repo_description=updates['repo_description'],
908 repo_description=updates['repo_description'],
929 repo_clone_uri=updates['clone_uri'],
909 repo_clone_uri=updates['clone_uri'],
930 repo_fork_of=updates['fork_id'],
910 repo_fork_of=updates['fork_id'],
931 repo_private=updates['repo_private'],
911 repo_private=updates['repo_private'],
932 repo_landing_commit_ref=updates['repo_landing_rev'],
912 repo_landing_commit_ref=updates['repo_landing_rev'],
933 repo_enable_statistics=updates['repo_enable_statistics'],
913 repo_enable_statistics=updates['repo_enable_statistics'],
934 repo_enable_downloads=updates['repo_enable_downloads'],
914 repo_enable_downloads=updates['repo_enable_downloads'],
935 repo_enable_locking=updates['repo_enable_locking']))
915 repo_enable_locking=updates['repo_enable_locking']))
936 except validation_schema.Invalid as err:
916 except validation_schema.Invalid as err:
937 raise JSONRPCValidationError(colander_exc=err)
917 raise JSONRPCValidationError(colander_exc=err)
938
918
939 # save validated data back into the updates dict
919 # save validated data back into the updates dict
940 validated_updates = dict(
920 validated_updates = dict(
941 repo_name=schema_data['repo_group']['repo_name_without_group'],
921 repo_name=schema_data['repo_group']['repo_name_without_group'],
942 repo_group=schema_data['repo_group']['repo_group_id'],
922 repo_group=schema_data['repo_group']['repo_group_id'],
943
923
944 user=schema_data['repo_owner'],
924 user=schema_data['repo_owner'],
945 repo_description=schema_data['repo_description'],
925 repo_description=schema_data['repo_description'],
946 repo_private=schema_data['repo_private'],
926 repo_private=schema_data['repo_private'],
947 clone_uri=schema_data['repo_clone_uri'],
927 clone_uri=schema_data['repo_clone_uri'],
948 repo_landing_rev=schema_data['repo_landing_commit_ref'],
928 repo_landing_rev=schema_data['repo_landing_commit_ref'],
949 repo_enable_statistics=schema_data['repo_enable_statistics'],
929 repo_enable_statistics=schema_data['repo_enable_statistics'],
950 repo_enable_locking=schema_data['repo_enable_locking'],
930 repo_enable_locking=schema_data['repo_enable_locking'],
951 repo_enable_downloads=schema_data['repo_enable_downloads'],
931 repo_enable_downloads=schema_data['repo_enable_downloads'],
952 )
932 )
953
933
954 if schema_data['repo_fork_of']:
934 if schema_data['repo_fork_of']:
955 fork_repo = get_repo_or_error(schema_data['repo_fork_of'])
935 fork_repo = get_repo_or_error(schema_data['repo_fork_of'])
956 validated_updates['fork_id'] = fork_repo.repo_id
936 validated_updates['fork_id'] = fork_repo.repo_id
957
937
958 # extra fields
938 # extra fields
959 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
939 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
960 if fields:
940 if fields:
961 validated_updates.update(fields)
941 validated_updates.update(fields)
962
942
963 try:
943 try:
964 RepoModel().update(repo, **validated_updates)
944 RepoModel().update(repo, **validated_updates)
965 audit_logger.store_api(
945 audit_logger.store_api(
966 'repo.edit', action_data={'old_data': old_values},
946 'repo.edit', action_data={'old_data': old_values},
967 user=apiuser, repo=repo)
947 user=apiuser, repo=repo)
968 Session().commit()
948 Session().commit()
969 return {
949 return {
970 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
950 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
971 'repository': repo.get_api_data(include_secrets=include_secrets)
951 'repository': repo.get_api_data(include_secrets=include_secrets)
972 }
952 }
973 except Exception:
953 except Exception:
974 log.exception(
954 log.exception(
975 u"Exception while trying to update the repository %s",
955 u"Exception while trying to update the repository %s",
976 repoid)
956 repoid)
977 raise JSONRPCError('failed to update repo `%s`' % repoid)
957 raise JSONRPCError('failed to update repo `%s`' % repoid)
978
958
979
959
980 @jsonrpc_method()
960 @jsonrpc_method()
981 def fork_repo(request, apiuser, repoid, fork_name,
961 def fork_repo(request, apiuser, repoid, fork_name,
982 owner=Optional(OAttr('apiuser')),
962 owner=Optional(OAttr('apiuser')),
983 description=Optional(''),
963 description=Optional(''),
984 private=Optional(False),
964 private=Optional(False),
985 clone_uri=Optional(None),
965 clone_uri=Optional(None),
986 landing_rev=Optional('rev:tip'),
966 landing_rev=Optional('rev:tip'),
987 copy_permissions=Optional(False)):
967 copy_permissions=Optional(False)):
988 """
968 """
989 Creates a fork of the specified |repo|.
969 Creates a fork of the specified |repo|.
990
970
991 * If the fork_name contains "/", fork will be created inside
971 * If the fork_name contains "/", fork will be created inside
992 a repository group or nested repository groups
972 a repository group or nested repository groups
993
973
994 For example "foo/bar/fork-repo" will create fork called "fork-repo"
974 For example "foo/bar/fork-repo" will create fork called "fork-repo"
995 inside group "foo/bar". You have to have permissions to access and
975 inside group "foo/bar". You have to have permissions to access and
996 write to the last repository group ("bar" in this example)
976 write to the last repository group ("bar" in this example)
997
977
998 This command can only be run using an |authtoken| with minimum
978 This command can only be run using an |authtoken| with minimum
999 read permissions of the forked repo, create fork permissions for an user.
979 read permissions of the forked repo, create fork permissions for an user.
1000
980
1001 :param apiuser: This is filled automatically from the |authtoken|.
981 :param apiuser: This is filled automatically from the |authtoken|.
1002 :type apiuser: AuthUser
982 :type apiuser: AuthUser
1003 :param repoid: Set repository name or repository ID.
983 :param repoid: Set repository name or repository ID.
1004 :type repoid: str or int
984 :type repoid: str or int
1005 :param fork_name: Set the fork name, including it's repository group membership.
985 :param fork_name: Set the fork name, including it's repository group membership.
1006 :type fork_name: str
986 :type fork_name: str
1007 :param owner: Set the fork owner.
987 :param owner: Set the fork owner.
1008 :type owner: str
988 :type owner: str
1009 :param description: Set the fork description.
989 :param description: Set the fork description.
1010 :type description: str
990 :type description: str
1011 :param copy_permissions: Copy permissions from parent |repo|. The
991 :param copy_permissions: Copy permissions from parent |repo|. The
1012 default is False.
992 default is False.
1013 :type copy_permissions: bool
993 :type copy_permissions: bool
1014 :param private: Make the fork private. The default is False.
994 :param private: Make the fork private. The default is False.
1015 :type private: bool
995 :type private: bool
1016 :param landing_rev: Set the landing revision. The default is tip.
996 :param landing_rev: Set the landing revision. The default is tip.
1017
997
1018 Example output:
998 Example output:
1019
999
1020 .. code-block:: bash
1000 .. code-block:: bash
1021
1001
1022 id : <id_for_response>
1002 id : <id_for_response>
1023 api_key : "<api_key>"
1003 api_key : "<api_key>"
1024 args: {
1004 args: {
1025 "repoid" : "<reponame or repo_id>",
1005 "repoid" : "<reponame or repo_id>",
1026 "fork_name": "<forkname>",
1006 "fork_name": "<forkname>",
1027 "owner": "<username or user_id = Optional(=apiuser)>",
1007 "owner": "<username or user_id = Optional(=apiuser)>",
1028 "description": "<description>",
1008 "description": "<description>",
1029 "copy_permissions": "<bool>",
1009 "copy_permissions": "<bool>",
1030 "private": "<bool>",
1010 "private": "<bool>",
1031 "landing_rev": "<landing_rev>"
1011 "landing_rev": "<landing_rev>"
1032 }
1012 }
1033
1013
1034 Example error output:
1014 Example error output:
1035
1015
1036 .. code-block:: bash
1016 .. code-block:: bash
1037
1017
1038 id : <id_given_in_input>
1018 id : <id_given_in_input>
1039 result: {
1019 result: {
1040 "msg": "Created fork of `<reponame>` as `<forkname>`",
1020 "msg": "Created fork of `<reponame>` as `<forkname>`",
1041 "success": true,
1021 "success": true,
1042 "task": "<celery task id or None if done sync>"
1022 "task": "<celery task id or None if done sync>"
1043 }
1023 }
1044 error: null
1024 error: null
1045
1025
1046 """
1026 """
1047
1027
1048 repo = get_repo_or_error(repoid)
1028 repo = get_repo_or_error(repoid)
1049 repo_name = repo.repo_name
1029 repo_name = repo.repo_name
1050
1030
1051 if not has_superadmin_permission(apiuser):
1031 if not has_superadmin_permission(apiuser):
1052 # check if we have at least read permission for
1032 # check if we have at least read permission for
1053 # this repo that we fork !
1033 # this repo that we fork !
1054 _perms = (
1034 _perms = (
1055 'repository.admin', 'repository.write', 'repository.read')
1035 'repository.admin', 'repository.write', 'repository.read')
1056 validate_repo_permissions(apiuser, repoid, repo, _perms)
1036 validate_repo_permissions(apiuser, repoid, repo, _perms)
1057
1037
1058 # check if the regular user has at least fork permissions as well
1038 # check if the regular user has at least fork permissions as well
1059 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
1039 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
1060 raise JSONRPCForbidden()
1040 raise JSONRPCForbidden()
1061
1041
1062 # check if user can set owner parameter
1042 # check if user can set owner parameter
1063 owner = validate_set_owner_permissions(apiuser, owner)
1043 owner = validate_set_owner_permissions(apiuser, owner)
1064
1044
1065 description = Optional.extract(description)
1045 description = Optional.extract(description)
1066 copy_permissions = Optional.extract(copy_permissions)
1046 copy_permissions = Optional.extract(copy_permissions)
1067 clone_uri = Optional.extract(clone_uri)
1047 clone_uri = Optional.extract(clone_uri)
1068 landing_commit_ref = Optional.extract(landing_rev)
1048 landing_commit_ref = Optional.extract(landing_rev)
1069 private = Optional.extract(private)
1049 private = Optional.extract(private)
1070
1050
1071 schema = repo_schema.RepoSchema().bind(
1051 schema = repo_schema.RepoSchema().bind(
1072 repo_type_options=rhodecode.BACKENDS.keys(),
1052 repo_type_options=rhodecode.BACKENDS.keys(),
1073 # user caller
1053 # user caller
1074 user=apiuser)
1054 user=apiuser)
1075
1055
1076 try:
1056 try:
1077 schema_data = schema.deserialize(dict(
1057 schema_data = schema.deserialize(dict(
1078 repo_name=fork_name,
1058 repo_name=fork_name,
1079 repo_type=repo.repo_type,
1059 repo_type=repo.repo_type,
1080 repo_owner=owner.username,
1060 repo_owner=owner.username,
1081 repo_description=description,
1061 repo_description=description,
1082 repo_landing_commit_ref=landing_commit_ref,
1062 repo_landing_commit_ref=landing_commit_ref,
1083 repo_clone_uri=clone_uri,
1063 repo_clone_uri=clone_uri,
1084 repo_private=private,
1064 repo_private=private,
1085 repo_copy_permissions=copy_permissions))
1065 repo_copy_permissions=copy_permissions))
1086 except validation_schema.Invalid as err:
1066 except validation_schema.Invalid as err:
1087 raise JSONRPCValidationError(colander_exc=err)
1067 raise JSONRPCValidationError(colander_exc=err)
1088
1068
1089 try:
1069 try:
1090 data = {
1070 data = {
1091 'fork_parent_id': repo.repo_id,
1071 'fork_parent_id': repo.repo_id,
1092
1072
1093 'repo_name': schema_data['repo_group']['repo_name_without_group'],
1073 'repo_name': schema_data['repo_group']['repo_name_without_group'],
1094 'repo_name_full': schema_data['repo_name'],
1074 'repo_name_full': schema_data['repo_name'],
1095 'repo_group': schema_data['repo_group']['repo_group_id'],
1075 'repo_group': schema_data['repo_group']['repo_group_id'],
1096 'repo_type': schema_data['repo_type'],
1076 'repo_type': schema_data['repo_type'],
1097 'description': schema_data['repo_description'],
1077 'description': schema_data['repo_description'],
1098 'private': schema_data['repo_private'],
1078 'private': schema_data['repo_private'],
1099 'copy_permissions': schema_data['repo_copy_permissions'],
1079 'copy_permissions': schema_data['repo_copy_permissions'],
1100 'landing_rev': schema_data['repo_landing_commit_ref'],
1080 'landing_rev': schema_data['repo_landing_commit_ref'],
1101 }
1081 }
1102
1082
1103 task = RepoModel().create_fork(data, cur_user=owner)
1083 task = RepoModel().create_fork(data, cur_user=owner)
1104 # no commit, it's done in RepoModel, or async via celery
1084 # no commit, it's done in RepoModel, or async via celery
1105 task_id = get_task_id(task)
1085 task_id = get_task_id(task)
1106
1086
1107 return {
1087 return {
1108 'msg': 'Created fork of `%s` as `%s`' % (
1088 'msg': 'Created fork of `%s` as `%s`' % (
1109 repo.repo_name, schema_data['repo_name']),
1089 repo.repo_name, schema_data['repo_name']),
1110 'success': True, # cannot return the repo data here since fork
1090 'success': True, # cannot return the repo data here since fork
1111 # can be done async
1091 # can be done async
1112 'task': task_id
1092 'task': task_id
1113 }
1093 }
1114 except Exception:
1094 except Exception:
1115 log.exception(
1095 log.exception(
1116 u"Exception while trying to create fork %s",
1096 u"Exception while trying to create fork %s",
1117 schema_data['repo_name'])
1097 schema_data['repo_name'])
1118 raise JSONRPCError(
1098 raise JSONRPCError(
1119 'failed to fork repository `%s` as `%s`' % (
1099 'failed to fork repository `%s` as `%s`' % (
1120 repo_name, schema_data['repo_name']))
1100 repo_name, schema_data['repo_name']))
1121
1101
1122
1102
1123 @jsonrpc_method()
1103 @jsonrpc_method()
1124 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1104 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1125 """
1105 """
1126 Deletes a repository.
1106 Deletes a repository.
1127
1107
1128 * When the `forks` parameter is set it's possible to detach or delete
1108 * When the `forks` parameter is set it's possible to detach or delete
1129 forks of deleted repository.
1109 forks of deleted repository.
1130
1110
1131 This command can only be run using an |authtoken| with admin
1111 This command can only be run using an |authtoken| with admin
1132 permissions on the |repo|.
1112 permissions on the |repo|.
1133
1113
1134 :param apiuser: This is filled automatically from the |authtoken|.
1114 :param apiuser: This is filled automatically from the |authtoken|.
1135 :type apiuser: AuthUser
1115 :type apiuser: AuthUser
1136 :param repoid: Set the repository name or repository ID.
1116 :param repoid: Set the repository name or repository ID.
1137 :type repoid: str or int
1117 :type repoid: str or int
1138 :param forks: Set to `detach` or `delete` forks from the |repo|.
1118 :param forks: Set to `detach` or `delete` forks from the |repo|.
1139 :type forks: Optional(str)
1119 :type forks: Optional(str)
1140
1120
1141 Example error output:
1121 Example error output:
1142
1122
1143 .. code-block:: bash
1123 .. code-block:: bash
1144
1124
1145 id : <id_given_in_input>
1125 id : <id_given_in_input>
1146 result: {
1126 result: {
1147 "msg": "Deleted repository `<reponame>`",
1127 "msg": "Deleted repository `<reponame>`",
1148 "success": true
1128 "success": true
1149 }
1129 }
1150 error: null
1130 error: null
1151 """
1131 """
1152
1132
1153 repo = get_repo_or_error(repoid)
1133 repo = get_repo_or_error(repoid)
1154 repo_name = repo.repo_name
1134 repo_name = repo.repo_name
1155 if not has_superadmin_permission(apiuser):
1135 if not has_superadmin_permission(apiuser):
1156 _perms = ('repository.admin',)
1136 _perms = ('repository.admin',)
1157 validate_repo_permissions(apiuser, repoid, repo, _perms)
1137 validate_repo_permissions(apiuser, repoid, repo, _perms)
1158
1138
1159 try:
1139 try:
1160 handle_forks = Optional.extract(forks)
1140 handle_forks = Optional.extract(forks)
1161 _forks_msg = ''
1141 _forks_msg = ''
1162 _forks = [f for f in repo.forks]
1142 _forks = [f for f in repo.forks]
1163 if handle_forks == 'detach':
1143 if handle_forks == 'detach':
1164 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1144 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1165 elif handle_forks == 'delete':
1145 elif handle_forks == 'delete':
1166 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1146 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1167 elif _forks:
1147 elif _forks:
1168 raise JSONRPCError(
1148 raise JSONRPCError(
1169 'Cannot delete `%s` it still contains attached forks' %
1149 'Cannot delete `%s` it still contains attached forks' %
1170 (repo.repo_name,)
1150 (repo.repo_name,)
1171 )
1151 )
1172 old_data = repo.get_api_data()
1152 old_data = repo.get_api_data()
1173 RepoModel().delete(repo, forks=forks)
1153 RepoModel().delete(repo, forks=forks)
1174
1154
1175 repo = audit_logger.RepoWrap(repo_id=None,
1155 repo = audit_logger.RepoWrap(repo_id=None,
1176 repo_name=repo.repo_name)
1156 repo_name=repo.repo_name)
1177
1157
1178 audit_logger.store_api(
1158 audit_logger.store_api(
1179 'repo.delete', action_data={'old_data': old_data},
1159 'repo.delete', action_data={'old_data': old_data},
1180 user=apiuser, repo=repo)
1160 user=apiuser, repo=repo)
1181
1161
1182 ScmModel().mark_for_invalidation(repo_name, delete=True)
1162 ScmModel().mark_for_invalidation(repo_name, delete=True)
1183 Session().commit()
1163 Session().commit()
1184 return {
1164 return {
1185 'msg': 'Deleted repository `%s`%s' % (repo_name, _forks_msg),
1165 'msg': 'Deleted repository `%s`%s' % (repo_name, _forks_msg),
1186 'success': True
1166 'success': True
1187 }
1167 }
1188 except Exception:
1168 except Exception:
1189 log.exception("Exception occurred while trying to delete repo")
1169 log.exception("Exception occurred while trying to delete repo")
1190 raise JSONRPCError(
1170 raise JSONRPCError(
1191 'failed to delete repository `%s`' % (repo_name,)
1171 'failed to delete repository `%s`' % (repo_name,)
1192 )
1172 )
1193
1173
1194
1174
1195 #TODO: marcink, change name ?
1175 #TODO: marcink, change name ?
1196 @jsonrpc_method()
1176 @jsonrpc_method()
1197 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1177 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1198 """
1178 """
1199 Invalidates the cache for the specified repository.
1179 Invalidates the cache for the specified repository.
1200
1180
1201 This command can only be run using an |authtoken| with admin rights to
1181 This command can only be run using an |authtoken| with admin rights to
1202 the specified repository.
1182 the specified repository.
1203
1183
1204 This command takes the following options:
1184 This command takes the following options:
1205
1185
1206 :param apiuser: This is filled automatically from |authtoken|.
1186 :param apiuser: This is filled automatically from |authtoken|.
1207 :type apiuser: AuthUser
1187 :type apiuser: AuthUser
1208 :param repoid: Sets the repository name or repository ID.
1188 :param repoid: Sets the repository name or repository ID.
1209 :type repoid: str or int
1189 :type repoid: str or int
1210 :param delete_keys: This deletes the invalidated keys instead of
1190 :param delete_keys: This deletes the invalidated keys instead of
1211 just flagging them.
1191 just flagging them.
1212 :type delete_keys: Optional(``True`` | ``False``)
1192 :type delete_keys: Optional(``True`` | ``False``)
1213
1193
1214 Example output:
1194 Example output:
1215
1195
1216 .. code-block:: bash
1196 .. code-block:: bash
1217
1197
1218 id : <id_given_in_input>
1198 id : <id_given_in_input>
1219 result : {
1199 result : {
1220 'msg': Cache for repository `<repository name>` was invalidated,
1200 'msg': Cache for repository `<repository name>` was invalidated,
1221 'repository': <repository name>
1201 'repository': <repository name>
1222 }
1202 }
1223 error : null
1203 error : null
1224
1204
1225 Example error output:
1205 Example error output:
1226
1206
1227 .. code-block:: bash
1207 .. code-block:: bash
1228
1208
1229 id : <id_given_in_input>
1209 id : <id_given_in_input>
1230 result : null
1210 result : null
1231 error : {
1211 error : {
1232 'Error occurred during cache invalidation action'
1212 'Error occurred during cache invalidation action'
1233 }
1213 }
1234
1214
1235 """
1215 """
1236
1216
1237 repo = get_repo_or_error(repoid)
1217 repo = get_repo_or_error(repoid)
1238 if not has_superadmin_permission(apiuser):
1218 if not has_superadmin_permission(apiuser):
1239 _perms = ('repository.admin', 'repository.write',)
1219 _perms = ('repository.admin', 'repository.write',)
1240 validate_repo_permissions(apiuser, repoid, repo, _perms)
1220 validate_repo_permissions(apiuser, repoid, repo, _perms)
1241
1221
1242 delete = Optional.extract(delete_keys)
1222 delete = Optional.extract(delete_keys)
1243 try:
1223 try:
1244 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1224 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1245 return {
1225 return {
1246 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1226 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1247 'repository': repo.repo_name
1227 'repository': repo.repo_name
1248 }
1228 }
1249 except Exception:
1229 except Exception:
1250 log.exception(
1230 log.exception(
1251 "Exception occurred while trying to invalidate repo cache")
1231 "Exception occurred while trying to invalidate repo cache")
1252 raise JSONRPCError(
1232 raise JSONRPCError(
1253 'Error occurred during cache invalidation action'
1233 'Error occurred during cache invalidation action'
1254 )
1234 )
1255
1235
1256
1236
1257 #TODO: marcink, change name ?
1237 #TODO: marcink, change name ?
1258 @jsonrpc_method()
1238 @jsonrpc_method()
1259 def lock(request, apiuser, repoid, locked=Optional(None),
1239 def lock(request, apiuser, repoid, locked=Optional(None),
1260 userid=Optional(OAttr('apiuser'))):
1240 userid=Optional(OAttr('apiuser'))):
1261 """
1241 """
1262 Sets the lock state of the specified |repo| by the given user.
1242 Sets the lock state of the specified |repo| by the given user.
1263 From more information, see :ref:`repo-locking`.
1243 From more information, see :ref:`repo-locking`.
1264
1244
1265 * If the ``userid`` option is not set, the repository is locked to the
1245 * If the ``userid`` option is not set, the repository is locked to the
1266 user who called the method.
1246 user who called the method.
1267 * If the ``locked`` parameter is not set, the current lock state of the
1247 * If the ``locked`` parameter is not set, the current lock state of the
1268 repository is displayed.
1248 repository is displayed.
1269
1249
1270 This command can only be run using an |authtoken| with admin rights to
1250 This command can only be run using an |authtoken| with admin rights to
1271 the specified repository.
1251 the specified repository.
1272
1252
1273 This command takes the following options:
1253 This command takes the following options:
1274
1254
1275 :param apiuser: This is filled automatically from the |authtoken|.
1255 :param apiuser: This is filled automatically from the |authtoken|.
1276 :type apiuser: AuthUser
1256 :type apiuser: AuthUser
1277 :param repoid: Sets the repository name or repository ID.
1257 :param repoid: Sets the repository name or repository ID.
1278 :type repoid: str or int
1258 :type repoid: str or int
1279 :param locked: Sets the lock state.
1259 :param locked: Sets the lock state.
1280 :type locked: Optional(``True`` | ``False``)
1260 :type locked: Optional(``True`` | ``False``)
1281 :param userid: Set the repository lock to this user.
1261 :param userid: Set the repository lock to this user.
1282 :type userid: Optional(str or int)
1262 :type userid: Optional(str or int)
1283
1263
1284 Example error output:
1264 Example error output:
1285
1265
1286 .. code-block:: bash
1266 .. code-block:: bash
1287
1267
1288 id : <id_given_in_input>
1268 id : <id_given_in_input>
1289 result : {
1269 result : {
1290 'repo': '<reponame>',
1270 'repo': '<reponame>',
1291 'locked': <bool: lock state>,
1271 'locked': <bool: lock state>,
1292 'locked_since': <int: lock timestamp>,
1272 'locked_since': <int: lock timestamp>,
1293 'locked_by': <username of person who made the lock>,
1273 'locked_by': <username of person who made the lock>,
1294 'lock_reason': <str: reason for locking>,
1274 'lock_reason': <str: reason for locking>,
1295 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1275 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1296 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1276 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1297 or
1277 or
1298 'msg': 'Repo `<repository name>` not locked.'
1278 'msg': 'Repo `<repository name>` not locked.'
1299 or
1279 or
1300 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1280 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1301 }
1281 }
1302 error : null
1282 error : null
1303
1283
1304 Example error output:
1284 Example error output:
1305
1285
1306 .. code-block:: bash
1286 .. code-block:: bash
1307
1287
1308 id : <id_given_in_input>
1288 id : <id_given_in_input>
1309 result : null
1289 result : null
1310 error : {
1290 error : {
1311 'Error occurred locking repository `<reponame>`'
1291 'Error occurred locking repository `<reponame>`'
1312 }
1292 }
1313 """
1293 """
1314
1294
1315 repo = get_repo_or_error(repoid)
1295 repo = get_repo_or_error(repoid)
1316 if not has_superadmin_permission(apiuser):
1296 if not has_superadmin_permission(apiuser):
1317 # check if we have at least write permission for this repo !
1297 # check if we have at least write permission for this repo !
1318 _perms = ('repository.admin', 'repository.write',)
1298 _perms = ('repository.admin', 'repository.write',)
1319 validate_repo_permissions(apiuser, repoid, repo, _perms)
1299 validate_repo_permissions(apiuser, repoid, repo, _perms)
1320
1300
1321 # make sure normal user does not pass someone else userid,
1301 # make sure normal user does not pass someone else userid,
1322 # he is not allowed to do that
1302 # he is not allowed to do that
1323 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1303 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1324 raise JSONRPCError('userid is not the same as your user')
1304 raise JSONRPCError('userid is not the same as your user')
1325
1305
1326 if isinstance(userid, Optional):
1306 if isinstance(userid, Optional):
1327 userid = apiuser.user_id
1307 userid = apiuser.user_id
1328
1308
1329 user = get_user_or_error(userid)
1309 user = get_user_or_error(userid)
1330
1310
1331 if isinstance(locked, Optional):
1311 if isinstance(locked, Optional):
1332 lockobj = repo.locked
1312 lockobj = repo.locked
1333
1313
1334 if lockobj[0] is None:
1314 if lockobj[0] is None:
1335 _d = {
1315 _d = {
1336 'repo': repo.repo_name,
1316 'repo': repo.repo_name,
1337 'locked': False,
1317 'locked': False,
1338 'locked_since': None,
1318 'locked_since': None,
1339 'locked_by': None,
1319 'locked_by': None,
1340 'lock_reason': None,
1320 'lock_reason': None,
1341 'lock_state_changed': False,
1321 'lock_state_changed': False,
1342 'msg': 'Repo `%s` not locked.' % repo.repo_name
1322 'msg': 'Repo `%s` not locked.' % repo.repo_name
1343 }
1323 }
1344 return _d
1324 return _d
1345 else:
1325 else:
1346 _user_id, _time, _reason = lockobj
1326 _user_id, _time, _reason = lockobj
1347 lock_user = get_user_or_error(userid)
1327 lock_user = get_user_or_error(userid)
1348 _d = {
1328 _d = {
1349 'repo': repo.repo_name,
1329 'repo': repo.repo_name,
1350 'locked': True,
1330 'locked': True,
1351 'locked_since': _time,
1331 'locked_since': _time,
1352 'locked_by': lock_user.username,
1332 'locked_by': lock_user.username,
1353 'lock_reason': _reason,
1333 'lock_reason': _reason,
1354 'lock_state_changed': False,
1334 'lock_state_changed': False,
1355 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1335 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1356 % (repo.repo_name, lock_user.username,
1336 % (repo.repo_name, lock_user.username,
1357 json.dumps(time_to_datetime(_time))))
1337 json.dumps(time_to_datetime(_time))))
1358 }
1338 }
1359 return _d
1339 return _d
1360
1340
1361 # force locked state through a flag
1341 # force locked state through a flag
1362 else:
1342 else:
1363 locked = str2bool(locked)
1343 locked = str2bool(locked)
1364 lock_reason = Repository.LOCK_API
1344 lock_reason = Repository.LOCK_API
1365 try:
1345 try:
1366 if locked:
1346 if locked:
1367 lock_time = time.time()
1347 lock_time = time.time()
1368 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1348 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1369 else:
1349 else:
1370 lock_time = None
1350 lock_time = None
1371 Repository.unlock(repo)
1351 Repository.unlock(repo)
1372 _d = {
1352 _d = {
1373 'repo': repo.repo_name,
1353 'repo': repo.repo_name,
1374 'locked': locked,
1354 'locked': locked,
1375 'locked_since': lock_time,
1355 'locked_since': lock_time,
1376 'locked_by': user.username,
1356 'locked_by': user.username,
1377 'lock_reason': lock_reason,
1357 'lock_reason': lock_reason,
1378 'lock_state_changed': True,
1358 'lock_state_changed': True,
1379 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1359 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1380 % (user.username, repo.repo_name, locked))
1360 % (user.username, repo.repo_name, locked))
1381 }
1361 }
1382 return _d
1362 return _d
1383 except Exception:
1363 except Exception:
1384 log.exception(
1364 log.exception(
1385 "Exception occurred while trying to lock repository")
1365 "Exception occurred while trying to lock repository")
1386 raise JSONRPCError(
1366 raise JSONRPCError(
1387 'Error occurred locking repository `%s`' % repo.repo_name
1367 'Error occurred locking repository `%s`' % repo.repo_name
1388 )
1368 )
1389
1369
1390
1370
1391 @jsonrpc_method()
1371 @jsonrpc_method()
1392 def comment_commit(
1372 def comment_commit(
1393 request, apiuser, repoid, commit_id, message, status=Optional(None),
1373 request, apiuser, repoid, commit_id, message, status=Optional(None),
1394 comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
1374 comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
1395 resolves_comment_id=Optional(None),
1375 resolves_comment_id=Optional(None),
1396 userid=Optional(OAttr('apiuser'))):
1376 userid=Optional(OAttr('apiuser'))):
1397 """
1377 """
1398 Set a commit comment, and optionally change the status of the commit.
1378 Set a commit comment, and optionally change the status of the commit.
1399
1379
1400 :param apiuser: This is filled automatically from the |authtoken|.
1380 :param apiuser: This is filled automatically from the |authtoken|.
1401 :type apiuser: AuthUser
1381 :type apiuser: AuthUser
1402 :param repoid: Set the repository name or repository ID.
1382 :param repoid: Set the repository name or repository ID.
1403 :type repoid: str or int
1383 :type repoid: str or int
1404 :param commit_id: Specify the commit_id for which to set a comment.
1384 :param commit_id: Specify the commit_id for which to set a comment.
1405 :type commit_id: str
1385 :type commit_id: str
1406 :param message: The comment text.
1386 :param message: The comment text.
1407 :type message: str
1387 :type message: str
1408 :param status: (**Optional**) status of commit, one of: 'not_reviewed',
1388 :param status: (**Optional**) status of commit, one of: 'not_reviewed',
1409 'approved', 'rejected', 'under_review'
1389 'approved', 'rejected', 'under_review'
1410 :type status: str
1390 :type status: str
1411 :param comment_type: Comment type, one of: 'note', 'todo'
1391 :param comment_type: Comment type, one of: 'note', 'todo'
1412 :type comment_type: Optional(str), default: 'note'
1392 :type comment_type: Optional(str), default: 'note'
1413 :param userid: Set the user name of the comment creator.
1393 :param userid: Set the user name of the comment creator.
1414 :type userid: Optional(str or int)
1394 :type userid: Optional(str or int)
1415
1395
1416 Example error output:
1396 Example error output:
1417
1397
1418 .. code-block:: bash
1398 .. code-block:: bash
1419
1399
1420 {
1400 {
1421 "id" : <id_given_in_input>,
1401 "id" : <id_given_in_input>,
1422 "result" : {
1402 "result" : {
1423 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1403 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1424 "status_change": null or <status>,
1404 "status_change": null or <status>,
1425 "success": true
1405 "success": true
1426 },
1406 },
1427 "error" : null
1407 "error" : null
1428 }
1408 }
1429
1409
1430 """
1410 """
1431 repo = get_repo_or_error(repoid)
1411 repo = get_repo_or_error(repoid)
1432 if not has_superadmin_permission(apiuser):
1412 if not has_superadmin_permission(apiuser):
1433 _perms = ('repository.read', 'repository.write', 'repository.admin')
1413 _perms = ('repository.read', 'repository.write', 'repository.admin')
1434 validate_repo_permissions(apiuser, repoid, repo, _perms)
1414 validate_repo_permissions(apiuser, repoid, repo, _perms)
1435
1415
1436 try:
1416 try:
1437 commit_id = repo.scm_instance().get_commit(commit_id=commit_id).raw_id
1417 commit_id = repo.scm_instance().get_commit(commit_id=commit_id).raw_id
1438 except Exception as e:
1418 except Exception as e:
1439 log.exception('Failed to fetch commit')
1419 log.exception('Failed to fetch commit')
1440 raise JSONRPCError(e.message)
1420 raise JSONRPCError(e.message)
1441
1421
1442 if isinstance(userid, Optional):
1422 if isinstance(userid, Optional):
1443 userid = apiuser.user_id
1423 userid = apiuser.user_id
1444
1424
1445 user = get_user_or_error(userid)
1425 user = get_user_or_error(userid)
1446 status = Optional.extract(status)
1426 status = Optional.extract(status)
1447 comment_type = Optional.extract(comment_type)
1427 comment_type = Optional.extract(comment_type)
1448 resolves_comment_id = Optional.extract(resolves_comment_id)
1428 resolves_comment_id = Optional.extract(resolves_comment_id)
1449
1429
1450 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1430 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1451 if status and status not in allowed_statuses:
1431 if status and status not in allowed_statuses:
1452 raise JSONRPCError('Bad status, must be on '
1432 raise JSONRPCError('Bad status, must be on '
1453 'of %s got %s' % (allowed_statuses, status,))
1433 'of %s got %s' % (allowed_statuses, status,))
1454
1434
1455 if resolves_comment_id:
1435 if resolves_comment_id:
1456 comment = ChangesetComment.get(resolves_comment_id)
1436 comment = ChangesetComment.get(resolves_comment_id)
1457 if not comment:
1437 if not comment:
1458 raise JSONRPCError(
1438 raise JSONRPCError(
1459 'Invalid resolves_comment_id `%s` for this commit.'
1439 'Invalid resolves_comment_id `%s` for this commit.'
1460 % resolves_comment_id)
1440 % resolves_comment_id)
1461 if comment.comment_type != ChangesetComment.COMMENT_TYPE_TODO:
1441 if comment.comment_type != ChangesetComment.COMMENT_TYPE_TODO:
1462 raise JSONRPCError(
1442 raise JSONRPCError(
1463 'Comment `%s` is wrong type for setting status to resolved.'
1443 'Comment `%s` is wrong type for setting status to resolved.'
1464 % resolves_comment_id)
1444 % resolves_comment_id)
1465
1445
1466 try:
1446 try:
1467 rc_config = SettingsModel().get_all_settings()
1447 rc_config = SettingsModel().get_all_settings()
1468 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1448 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1469 status_change_label = ChangesetStatus.get_status_lbl(status)
1449 status_change_label = ChangesetStatus.get_status_lbl(status)
1470 comment = CommentsModel().create(
1450 comment = CommentsModel().create(
1471 message, repo, user, commit_id=commit_id,
1451 message, repo, user, commit_id=commit_id,
1472 status_change=status_change_label,
1452 status_change=status_change_label,
1473 status_change_type=status,
1453 status_change_type=status,
1474 renderer=renderer,
1454 renderer=renderer,
1475 comment_type=comment_type,
1455 comment_type=comment_type,
1476 resolves_comment_id=resolves_comment_id
1456 resolves_comment_id=resolves_comment_id
1477 )
1457 )
1478 if status:
1458 if status:
1479 # also do a status change
1459 # also do a status change
1480 try:
1460 try:
1481 ChangesetStatusModel().set_status(
1461 ChangesetStatusModel().set_status(
1482 repo, status, user, comment, revision=commit_id,
1462 repo, status, user, comment, revision=commit_id,
1483 dont_allow_on_closed_pull_request=True
1463 dont_allow_on_closed_pull_request=True
1484 )
1464 )
1485 except StatusChangeOnClosedPullRequestError:
1465 except StatusChangeOnClosedPullRequestError:
1486 log.exception(
1466 log.exception(
1487 "Exception occurred while trying to change repo commit status")
1467 "Exception occurred while trying to change repo commit status")
1488 msg = ('Changing status on a changeset associated with '
1468 msg = ('Changing status on a changeset associated with '
1489 'a closed pull request is not allowed')
1469 'a closed pull request is not allowed')
1490 raise JSONRPCError(msg)
1470 raise JSONRPCError(msg)
1491
1471
1492 Session().commit()
1472 Session().commit()
1493 return {
1473 return {
1494 'msg': (
1474 'msg': (
1495 'Commented on commit `%s` for repository `%s`' % (
1475 'Commented on commit `%s` for repository `%s`' % (
1496 comment.revision, repo.repo_name)),
1476 comment.revision, repo.repo_name)),
1497 'status_change': status,
1477 'status_change': status,
1498 'success': True,
1478 'success': True,
1499 }
1479 }
1500 except JSONRPCError:
1480 except JSONRPCError:
1501 # catch any inside errors, and re-raise them to prevent from
1481 # catch any inside errors, and re-raise them to prevent from
1502 # below global catch to silence them
1482 # below global catch to silence them
1503 raise
1483 raise
1504 except Exception:
1484 except Exception:
1505 log.exception("Exception occurred while trying to comment on commit")
1485 log.exception("Exception occurred while trying to comment on commit")
1506 raise JSONRPCError(
1486 raise JSONRPCError(
1507 'failed to set comment on repository `%s`' % (repo.repo_name,)
1487 'failed to set comment on repository `%s`' % (repo.repo_name,)
1508 )
1488 )
1509
1489
1510
1490
1511 @jsonrpc_method()
1491 @jsonrpc_method()
1512 def grant_user_permission(request, apiuser, repoid, userid, perm):
1492 def grant_user_permission(request, apiuser, repoid, userid, perm):
1513 """
1493 """
1514 Grant permissions for the specified user on the given repository,
1494 Grant permissions for the specified user on the given repository,
1515 or update existing permissions if found.
1495 or update existing permissions if found.
1516
1496
1517 This command can only be run using an |authtoken| with admin
1497 This command can only be run using an |authtoken| with admin
1518 permissions on the |repo|.
1498 permissions on the |repo|.
1519
1499
1520 :param apiuser: This is filled automatically from the |authtoken|.
1500 :param apiuser: This is filled automatically from the |authtoken|.
1521 :type apiuser: AuthUser
1501 :type apiuser: AuthUser
1522 :param repoid: Set the repository name or repository ID.
1502 :param repoid: Set the repository name or repository ID.
1523 :type repoid: str or int
1503 :type repoid: str or int
1524 :param userid: Set the user name.
1504 :param userid: Set the user name.
1525 :type userid: str
1505 :type userid: str
1526 :param perm: Set the user permissions, using the following format
1506 :param perm: Set the user permissions, using the following format
1527 ``(repository.(none|read|write|admin))``
1507 ``(repository.(none|read|write|admin))``
1528 :type perm: str
1508 :type perm: str
1529
1509
1530 Example output:
1510 Example output:
1531
1511
1532 .. code-block:: bash
1512 .. code-block:: bash
1533
1513
1534 id : <id_given_in_input>
1514 id : <id_given_in_input>
1535 result: {
1515 result: {
1536 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1516 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1537 "success": true
1517 "success": true
1538 }
1518 }
1539 error: null
1519 error: null
1540 """
1520 """
1541
1521
1542 repo = get_repo_or_error(repoid)
1522 repo = get_repo_or_error(repoid)
1543 user = get_user_or_error(userid)
1523 user = get_user_or_error(userid)
1544 perm = get_perm_or_error(perm)
1524 perm = get_perm_or_error(perm)
1545 if not has_superadmin_permission(apiuser):
1525 if not has_superadmin_permission(apiuser):
1546 _perms = ('repository.admin',)
1526 _perms = ('repository.admin',)
1547 validate_repo_permissions(apiuser, repoid, repo, _perms)
1527 validate_repo_permissions(apiuser, repoid, repo, _perms)
1548
1528
1549 try:
1529 try:
1550
1530
1551 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1531 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1552
1532
1553 Session().commit()
1533 Session().commit()
1554 return {
1534 return {
1555 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1535 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1556 perm.permission_name, user.username, repo.repo_name
1536 perm.permission_name, user.username, repo.repo_name
1557 ),
1537 ),
1558 'success': True
1538 'success': True
1559 }
1539 }
1560 except Exception:
1540 except Exception:
1561 log.exception(
1541 log.exception(
1562 "Exception occurred while trying edit permissions for repo")
1542 "Exception occurred while trying edit permissions for repo")
1563 raise JSONRPCError(
1543 raise JSONRPCError(
1564 'failed to edit permission for user: `%s` in repo: `%s`' % (
1544 'failed to edit permission for user: `%s` in repo: `%s`' % (
1565 userid, repoid
1545 userid, repoid
1566 )
1546 )
1567 )
1547 )
1568
1548
1569
1549
1570 @jsonrpc_method()
1550 @jsonrpc_method()
1571 def revoke_user_permission(request, apiuser, repoid, userid):
1551 def revoke_user_permission(request, apiuser, repoid, userid):
1572 """
1552 """
1573 Revoke permission for a user on the specified repository.
1553 Revoke permission for a user on the specified repository.
1574
1554
1575 This command can only be run using an |authtoken| with admin
1555 This command can only be run using an |authtoken| with admin
1576 permissions on the |repo|.
1556 permissions on the |repo|.
1577
1557
1578 :param apiuser: This is filled automatically from the |authtoken|.
1558 :param apiuser: This is filled automatically from the |authtoken|.
1579 :type apiuser: AuthUser
1559 :type apiuser: AuthUser
1580 :param repoid: Set the repository name or repository ID.
1560 :param repoid: Set the repository name or repository ID.
1581 :type repoid: str or int
1561 :type repoid: str or int
1582 :param userid: Set the user name of revoked user.
1562 :param userid: Set the user name of revoked user.
1583 :type userid: str or int
1563 :type userid: str or int
1584
1564
1585 Example error output:
1565 Example error output:
1586
1566
1587 .. code-block:: bash
1567 .. code-block:: bash
1588
1568
1589 id : <id_given_in_input>
1569 id : <id_given_in_input>
1590 result: {
1570 result: {
1591 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1571 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1592 "success": true
1572 "success": true
1593 }
1573 }
1594 error: null
1574 error: null
1595 """
1575 """
1596
1576
1597 repo = get_repo_or_error(repoid)
1577 repo = get_repo_or_error(repoid)
1598 user = get_user_or_error(userid)
1578 user = get_user_or_error(userid)
1599 if not has_superadmin_permission(apiuser):
1579 if not has_superadmin_permission(apiuser):
1600 _perms = ('repository.admin',)
1580 _perms = ('repository.admin',)
1601 validate_repo_permissions(apiuser, repoid, repo, _perms)
1581 validate_repo_permissions(apiuser, repoid, repo, _perms)
1602
1582
1603 try:
1583 try:
1604 RepoModel().revoke_user_permission(repo=repo, user=user)
1584 RepoModel().revoke_user_permission(repo=repo, user=user)
1605 Session().commit()
1585 Session().commit()
1606 return {
1586 return {
1607 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1587 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1608 user.username, repo.repo_name
1588 user.username, repo.repo_name
1609 ),
1589 ),
1610 'success': True
1590 'success': True
1611 }
1591 }
1612 except Exception:
1592 except Exception:
1613 log.exception(
1593 log.exception(
1614 "Exception occurred while trying revoke permissions to repo")
1594 "Exception occurred while trying revoke permissions to repo")
1615 raise JSONRPCError(
1595 raise JSONRPCError(
1616 'failed to edit permission for user: `%s` in repo: `%s`' % (
1596 'failed to edit permission for user: `%s` in repo: `%s`' % (
1617 userid, repoid
1597 userid, repoid
1618 )
1598 )
1619 )
1599 )
1620
1600
1621
1601
1622 @jsonrpc_method()
1602 @jsonrpc_method()
1623 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1603 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1624 """
1604 """
1625 Grant permission for a user group on the specified repository,
1605 Grant permission for a user group on the specified repository,
1626 or update existing permissions.
1606 or update existing permissions.
1627
1607
1628 This command can only be run using an |authtoken| with admin
1608 This command can only be run using an |authtoken| with admin
1629 permissions on the |repo|.
1609 permissions on the |repo|.
1630
1610
1631 :param apiuser: This is filled automatically from the |authtoken|.
1611 :param apiuser: This is filled automatically from the |authtoken|.
1632 :type apiuser: AuthUser
1612 :type apiuser: AuthUser
1633 :param repoid: Set the repository name or repository ID.
1613 :param repoid: Set the repository name or repository ID.
1634 :type repoid: str or int
1614 :type repoid: str or int
1635 :param usergroupid: Specify the ID of the user group.
1615 :param usergroupid: Specify the ID of the user group.
1636 :type usergroupid: str or int
1616 :type usergroupid: str or int
1637 :param perm: Set the user group permissions using the following
1617 :param perm: Set the user group permissions using the following
1638 format: (repository.(none|read|write|admin))
1618 format: (repository.(none|read|write|admin))
1639 :type perm: str
1619 :type perm: str
1640
1620
1641 Example output:
1621 Example output:
1642
1622
1643 .. code-block:: bash
1623 .. code-block:: bash
1644
1624
1645 id : <id_given_in_input>
1625 id : <id_given_in_input>
1646 result : {
1626 result : {
1647 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1627 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1648 "success": true
1628 "success": true
1649
1629
1650 }
1630 }
1651 error : null
1631 error : null
1652
1632
1653 Example error output:
1633 Example error output:
1654
1634
1655 .. code-block:: bash
1635 .. code-block:: bash
1656
1636
1657 id : <id_given_in_input>
1637 id : <id_given_in_input>
1658 result : null
1638 result : null
1659 error : {
1639 error : {
1660 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1640 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1661 }
1641 }
1662
1642
1663 """
1643 """
1664
1644
1665 repo = get_repo_or_error(repoid)
1645 repo = get_repo_or_error(repoid)
1666 perm = get_perm_or_error(perm)
1646 perm = get_perm_or_error(perm)
1667 if not has_superadmin_permission(apiuser):
1647 if not has_superadmin_permission(apiuser):
1668 _perms = ('repository.admin',)
1648 _perms = ('repository.admin',)
1669 validate_repo_permissions(apiuser, repoid, repo, _perms)
1649 validate_repo_permissions(apiuser, repoid, repo, _perms)
1670
1650
1671 user_group = get_user_group_or_error(usergroupid)
1651 user_group = get_user_group_or_error(usergroupid)
1672 if not has_superadmin_permission(apiuser):
1652 if not has_superadmin_permission(apiuser):
1673 # check if we have at least read permission for this user group !
1653 # check if we have at least read permission for this user group !
1674 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1654 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1675 if not HasUserGroupPermissionAnyApi(*_perms)(
1655 if not HasUserGroupPermissionAnyApi(*_perms)(
1676 user=apiuser, user_group_name=user_group.users_group_name):
1656 user=apiuser, user_group_name=user_group.users_group_name):
1677 raise JSONRPCError(
1657 raise JSONRPCError(
1678 'user group `%s` does not exist' % (usergroupid,))
1658 'user group `%s` does not exist' % (usergroupid,))
1679
1659
1680 try:
1660 try:
1681 RepoModel().grant_user_group_permission(
1661 RepoModel().grant_user_group_permission(
1682 repo=repo, group_name=user_group, perm=perm)
1662 repo=repo, group_name=user_group, perm=perm)
1683
1663
1684 Session().commit()
1664 Session().commit()
1685 return {
1665 return {
1686 'msg': 'Granted perm: `%s` for user group: `%s` in '
1666 'msg': 'Granted perm: `%s` for user group: `%s` in '
1687 'repo: `%s`' % (
1667 'repo: `%s`' % (
1688 perm.permission_name, user_group.users_group_name,
1668 perm.permission_name, user_group.users_group_name,
1689 repo.repo_name
1669 repo.repo_name
1690 ),
1670 ),
1691 'success': True
1671 'success': True
1692 }
1672 }
1693 except Exception:
1673 except Exception:
1694 log.exception(
1674 log.exception(
1695 "Exception occurred while trying change permission on repo")
1675 "Exception occurred while trying change permission on repo")
1696 raise JSONRPCError(
1676 raise JSONRPCError(
1697 'failed to edit permission for user group: `%s` in '
1677 'failed to edit permission for user group: `%s` in '
1698 'repo: `%s`' % (
1678 'repo: `%s`' % (
1699 usergroupid, repo.repo_name
1679 usergroupid, repo.repo_name
1700 )
1680 )
1701 )
1681 )
1702
1682
1703
1683
1704 @jsonrpc_method()
1684 @jsonrpc_method()
1705 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1685 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1706 """
1686 """
1707 Revoke the permissions of a user group on a given repository.
1687 Revoke the permissions of a user group on a given repository.
1708
1688
1709 This command can only be run using an |authtoken| with admin
1689 This command can only be run using an |authtoken| with admin
1710 permissions on the |repo|.
1690 permissions on the |repo|.
1711
1691
1712 :param apiuser: This is filled automatically from the |authtoken|.
1692 :param apiuser: This is filled automatically from the |authtoken|.
1713 :type apiuser: AuthUser
1693 :type apiuser: AuthUser
1714 :param repoid: Set the repository name or repository ID.
1694 :param repoid: Set the repository name or repository ID.
1715 :type repoid: str or int
1695 :type repoid: str or int
1716 :param usergroupid: Specify the user group ID.
1696 :param usergroupid: Specify the user group ID.
1717 :type usergroupid: str or int
1697 :type usergroupid: str or int
1718
1698
1719 Example output:
1699 Example output:
1720
1700
1721 .. code-block:: bash
1701 .. code-block:: bash
1722
1702
1723 id : <id_given_in_input>
1703 id : <id_given_in_input>
1724 result: {
1704 result: {
1725 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1705 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1726 "success": true
1706 "success": true
1727 }
1707 }
1728 error: null
1708 error: null
1729 """
1709 """
1730
1710
1731 repo = get_repo_or_error(repoid)
1711 repo = get_repo_or_error(repoid)
1732 if not has_superadmin_permission(apiuser):
1712 if not has_superadmin_permission(apiuser):
1733 _perms = ('repository.admin',)
1713 _perms = ('repository.admin',)
1734 validate_repo_permissions(apiuser, repoid, repo, _perms)
1714 validate_repo_permissions(apiuser, repoid, repo, _perms)
1735
1715
1736 user_group = get_user_group_or_error(usergroupid)
1716 user_group = get_user_group_or_error(usergroupid)
1737 if not has_superadmin_permission(apiuser):
1717 if not has_superadmin_permission(apiuser):
1738 # check if we have at least read permission for this user group !
1718 # check if we have at least read permission for this user group !
1739 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1719 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1740 if not HasUserGroupPermissionAnyApi(*_perms)(
1720 if not HasUserGroupPermissionAnyApi(*_perms)(
1741 user=apiuser, user_group_name=user_group.users_group_name):
1721 user=apiuser, user_group_name=user_group.users_group_name):
1742 raise JSONRPCError(
1722 raise JSONRPCError(
1743 'user group `%s` does not exist' % (usergroupid,))
1723 'user group `%s` does not exist' % (usergroupid,))
1744
1724
1745 try:
1725 try:
1746 RepoModel().revoke_user_group_permission(
1726 RepoModel().revoke_user_group_permission(
1747 repo=repo, group_name=user_group)
1727 repo=repo, group_name=user_group)
1748
1728
1749 Session().commit()
1729 Session().commit()
1750 return {
1730 return {
1751 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1731 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1752 user_group.users_group_name, repo.repo_name
1732 user_group.users_group_name, repo.repo_name
1753 ),
1733 ),
1754 'success': True
1734 'success': True
1755 }
1735 }
1756 except Exception:
1736 except Exception:
1757 log.exception("Exception occurred while trying revoke "
1737 log.exception("Exception occurred while trying revoke "
1758 "user group permission on repo")
1738 "user group permission on repo")
1759 raise JSONRPCError(
1739 raise JSONRPCError(
1760 'failed to edit permission for user group: `%s` in '
1740 'failed to edit permission for user group: `%s` in '
1761 'repo: `%s`' % (
1741 'repo: `%s`' % (
1762 user_group.users_group_name, repo.repo_name
1742 user_group.users_group_name, repo.repo_name
1763 )
1743 )
1764 )
1744 )
1765
1745
1766
1746
1767 @jsonrpc_method()
1747 @jsonrpc_method()
1768 def pull(request, apiuser, repoid):
1748 def pull(request, apiuser, repoid):
1769 """
1749 """
1770 Triggers a pull on the given repository from a remote location. You
1750 Triggers a pull on the given repository from a remote location. You
1771 can use this to keep remote repositories up-to-date.
1751 can use this to keep remote repositories up-to-date.
1772
1752
1773 This command can only be run using an |authtoken| with admin
1753 This command can only be run using an |authtoken| with admin
1774 rights to the specified repository. For more information,
1754 rights to the specified repository. For more information,
1775 see :ref:`config-token-ref`.
1755 see :ref:`config-token-ref`.
1776
1756
1777 This command takes the following options:
1757 This command takes the following options:
1778
1758
1779 :param apiuser: This is filled automatically from the |authtoken|.
1759 :param apiuser: This is filled automatically from the |authtoken|.
1780 :type apiuser: AuthUser
1760 :type apiuser: AuthUser
1781 :param repoid: The repository name or repository ID.
1761 :param repoid: The repository name or repository ID.
1782 :type repoid: str or int
1762 :type repoid: str or int
1783
1763
1784 Example output:
1764 Example output:
1785
1765
1786 .. code-block:: bash
1766 .. code-block:: bash
1787
1767
1788 id : <id_given_in_input>
1768 id : <id_given_in_input>
1789 result : {
1769 result : {
1790 "msg": "Pulled from `<repository name>`"
1770 "msg": "Pulled from `<repository name>`"
1791 "repository": "<repository name>"
1771 "repository": "<repository name>"
1792 }
1772 }
1793 error : null
1773 error : null
1794
1774
1795 Example error output:
1775 Example error output:
1796
1776
1797 .. code-block:: bash
1777 .. code-block:: bash
1798
1778
1799 id : <id_given_in_input>
1779 id : <id_given_in_input>
1800 result : null
1780 result : null
1801 error : {
1781 error : {
1802 "Unable to pull changes from `<reponame>`"
1782 "Unable to pull changes from `<reponame>`"
1803 }
1783 }
1804
1784
1805 """
1785 """
1806
1786
1807 repo = get_repo_or_error(repoid)
1787 repo = get_repo_or_error(repoid)
1808 if not has_superadmin_permission(apiuser):
1788 if not has_superadmin_permission(apiuser):
1809 _perms = ('repository.admin',)
1789 _perms = ('repository.admin',)
1810 validate_repo_permissions(apiuser, repoid, repo, _perms)
1790 validate_repo_permissions(apiuser, repoid, repo, _perms)
1811
1791
1812 try:
1792 try:
1813 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1793 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1814 return {
1794 return {
1815 'msg': 'Pulled from `%s`' % repo.repo_name,
1795 'msg': 'Pulled from `%s`' % repo.repo_name,
1816 'repository': repo.repo_name
1796 'repository': repo.repo_name
1817 }
1797 }
1818 except Exception:
1798 except Exception:
1819 log.exception("Exception occurred while trying to "
1799 log.exception("Exception occurred while trying to "
1820 "pull changes from remote location")
1800 "pull changes from remote location")
1821 raise JSONRPCError(
1801 raise JSONRPCError(
1822 'Unable to pull changes from `%s`' % repo.repo_name
1802 'Unable to pull changes from `%s`' % repo.repo_name
1823 )
1803 )
1824
1804
1825
1805
1826 @jsonrpc_method()
1806 @jsonrpc_method()
1827 def strip(request, apiuser, repoid, revision, branch):
1807 def strip(request, apiuser, repoid, revision, branch):
1828 """
1808 """
1829 Strips the given revision from the specified repository.
1809 Strips the given revision from the specified repository.
1830
1810
1831 * This will remove the revision and all of its decendants.
1811 * This will remove the revision and all of its decendants.
1832
1812
1833 This command can only be run using an |authtoken| with admin rights to
1813 This command can only be run using an |authtoken| with admin rights to
1834 the specified repository.
1814 the specified repository.
1835
1815
1836 This command takes the following options:
1816 This command takes the following options:
1837
1817
1838 :param apiuser: This is filled automatically from the |authtoken|.
1818 :param apiuser: This is filled automatically from the |authtoken|.
1839 :type apiuser: AuthUser
1819 :type apiuser: AuthUser
1840 :param repoid: The repository name or repository ID.
1820 :param repoid: The repository name or repository ID.
1841 :type repoid: str or int
1821 :type repoid: str or int
1842 :param revision: The revision you wish to strip.
1822 :param revision: The revision you wish to strip.
1843 :type revision: str
1823 :type revision: str
1844 :param branch: The branch from which to strip the revision.
1824 :param branch: The branch from which to strip the revision.
1845 :type branch: str
1825 :type branch: str
1846
1826
1847 Example output:
1827 Example output:
1848
1828
1849 .. code-block:: bash
1829 .. code-block:: bash
1850
1830
1851 id : <id_given_in_input>
1831 id : <id_given_in_input>
1852 result : {
1832 result : {
1853 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1833 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1854 "repository": "<repository name>"
1834 "repository": "<repository name>"
1855 }
1835 }
1856 error : null
1836 error : null
1857
1837
1858 Example error output:
1838 Example error output:
1859
1839
1860 .. code-block:: bash
1840 .. code-block:: bash
1861
1841
1862 id : <id_given_in_input>
1842 id : <id_given_in_input>
1863 result : null
1843 result : null
1864 error : {
1844 error : {
1865 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1845 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1866 }
1846 }
1867
1847
1868 """
1848 """
1869
1849
1870 repo = get_repo_or_error(repoid)
1850 repo = get_repo_or_error(repoid)
1871 if not has_superadmin_permission(apiuser):
1851 if not has_superadmin_permission(apiuser):
1872 _perms = ('repository.admin',)
1852 _perms = ('repository.admin',)
1873 validate_repo_permissions(apiuser, repoid, repo, _perms)
1853 validate_repo_permissions(apiuser, repoid, repo, _perms)
1874
1854
1875 try:
1855 try:
1876 ScmModel().strip(repo, revision, branch)
1856 ScmModel().strip(repo, revision, branch)
1877 audit_logger.store_api(
1857 audit_logger.store_api(
1878 'repo.commit.strip', action_data={'commit_id': revision},
1858 'repo.commit.strip', action_data={'commit_id': revision},
1879 repo=repo,
1859 repo=repo,
1880 user=apiuser, commit=True)
1860 user=apiuser, commit=True)
1881
1861
1882 return {
1862 return {
1883 'msg': 'Stripped commit %s from repo `%s`' % (
1863 'msg': 'Stripped commit %s from repo `%s`' % (
1884 revision, repo.repo_name),
1864 revision, repo.repo_name),
1885 'repository': repo.repo_name
1865 'repository': repo.repo_name
1886 }
1866 }
1887 except Exception:
1867 except Exception:
1888 log.exception("Exception while trying to strip")
1868 log.exception("Exception while trying to strip")
1889 raise JSONRPCError(
1869 raise JSONRPCError(
1890 'Unable to strip commit %s from repo `%s`' % (
1870 'Unable to strip commit %s from repo `%s`' % (
1891 revision, repo.repo_name)
1871 revision, repo.repo_name)
1892 )
1872 )
1893
1873
1894
1874
1895 @jsonrpc_method()
1875 @jsonrpc_method()
1896 def get_repo_settings(request, apiuser, repoid, key=Optional(None)):
1876 def get_repo_settings(request, apiuser, repoid, key=Optional(None)):
1897 """
1877 """
1898 Returns all settings for a repository. If key is given it only returns the
1878 Returns all settings for a repository. If key is given it only returns the
1899 setting identified by the key or null.
1879 setting identified by the key or null.
1900
1880
1901 :param apiuser: This is filled automatically from the |authtoken|.
1881 :param apiuser: This is filled automatically from the |authtoken|.
1902 :type apiuser: AuthUser
1882 :type apiuser: AuthUser
1903 :param repoid: The repository name or repository id.
1883 :param repoid: The repository name or repository id.
1904 :type repoid: str or int
1884 :type repoid: str or int
1905 :param key: Key of the setting to return.
1885 :param key: Key of the setting to return.
1906 :type: key: Optional(str)
1886 :type: key: Optional(str)
1907
1887
1908 Example output:
1888 Example output:
1909
1889
1910 .. code-block:: bash
1890 .. code-block:: bash
1911
1891
1912 {
1892 {
1913 "error": null,
1893 "error": null,
1914 "id": 237,
1894 "id": 237,
1915 "result": {
1895 "result": {
1916 "extensions_largefiles": true,
1896 "extensions_largefiles": true,
1917 "extensions_evolve": true,
1897 "extensions_evolve": true,
1918 "hooks_changegroup_push_logger": true,
1898 "hooks_changegroup_push_logger": true,
1919 "hooks_changegroup_repo_size": false,
1899 "hooks_changegroup_repo_size": false,
1920 "hooks_outgoing_pull_logger": true,
1900 "hooks_outgoing_pull_logger": true,
1921 "phases_publish": "True",
1901 "phases_publish": "True",
1922 "rhodecode_hg_use_rebase_for_merging": true,
1902 "rhodecode_hg_use_rebase_for_merging": true,
1923 "rhodecode_pr_merge_enabled": true,
1903 "rhodecode_pr_merge_enabled": true,
1924 "rhodecode_use_outdated_comments": true
1904 "rhodecode_use_outdated_comments": true
1925 }
1905 }
1926 }
1906 }
1927 """
1907 """
1928
1908
1929 # Restrict access to this api method to admins only.
1909 # Restrict access to this api method to admins only.
1930 if not has_superadmin_permission(apiuser):
1910 if not has_superadmin_permission(apiuser):
1931 raise JSONRPCForbidden()
1911 raise JSONRPCForbidden()
1932
1912
1933 try:
1913 try:
1934 repo = get_repo_or_error(repoid)
1914 repo = get_repo_or_error(repoid)
1935 settings_model = VcsSettingsModel(repo=repo)
1915 settings_model = VcsSettingsModel(repo=repo)
1936 settings = settings_model.get_global_settings()
1916 settings = settings_model.get_global_settings()
1937 settings.update(settings_model.get_repo_settings())
1917 settings.update(settings_model.get_repo_settings())
1938
1918
1939 # If only a single setting is requested fetch it from all settings.
1919 # If only a single setting is requested fetch it from all settings.
1940 key = Optional.extract(key)
1920 key = Optional.extract(key)
1941 if key is not None:
1921 if key is not None:
1942 settings = settings.get(key, None)
1922 settings = settings.get(key, None)
1943 except Exception:
1923 except Exception:
1944 msg = 'Failed to fetch settings for repository `{}`'.format(repoid)
1924 msg = 'Failed to fetch settings for repository `{}`'.format(repoid)
1945 log.exception(msg)
1925 log.exception(msg)
1946 raise JSONRPCError(msg)
1926 raise JSONRPCError(msg)
1947
1927
1948 return settings
1928 return settings
1949
1929
1950
1930
1951 @jsonrpc_method()
1931 @jsonrpc_method()
1952 def set_repo_settings(request, apiuser, repoid, settings):
1932 def set_repo_settings(request, apiuser, repoid, settings):
1953 """
1933 """
1954 Update repository settings. Returns true on success.
1934 Update repository settings. Returns true on success.
1955
1935
1956 :param apiuser: This is filled automatically from the |authtoken|.
1936 :param apiuser: This is filled automatically from the |authtoken|.
1957 :type apiuser: AuthUser
1937 :type apiuser: AuthUser
1958 :param repoid: The repository name or repository id.
1938 :param repoid: The repository name or repository id.
1959 :type repoid: str or int
1939 :type repoid: str or int
1960 :param settings: The new settings for the repository.
1940 :param settings: The new settings for the repository.
1961 :type: settings: dict
1941 :type: settings: dict
1962
1942
1963 Example output:
1943 Example output:
1964
1944
1965 .. code-block:: bash
1945 .. code-block:: bash
1966
1946
1967 {
1947 {
1968 "error": null,
1948 "error": null,
1969 "id": 237,
1949 "id": 237,
1970 "result": true
1950 "result": true
1971 }
1951 }
1972 """
1952 """
1973 # Restrict access to this api method to admins only.
1953 # Restrict access to this api method to admins only.
1974 if not has_superadmin_permission(apiuser):
1954 if not has_superadmin_permission(apiuser):
1975 raise JSONRPCForbidden()
1955 raise JSONRPCForbidden()
1976
1956
1977 if type(settings) is not dict:
1957 if type(settings) is not dict:
1978 raise JSONRPCError('Settings have to be a JSON Object.')
1958 raise JSONRPCError('Settings have to be a JSON Object.')
1979
1959
1980 try:
1960 try:
1981 settings_model = VcsSettingsModel(repo=repoid)
1961 settings_model = VcsSettingsModel(repo=repoid)
1982
1962
1983 # Merge global, repo and incoming settings.
1963 # Merge global, repo and incoming settings.
1984 new_settings = settings_model.get_global_settings()
1964 new_settings = settings_model.get_global_settings()
1985 new_settings.update(settings_model.get_repo_settings())
1965 new_settings.update(settings_model.get_repo_settings())
1986 new_settings.update(settings)
1966 new_settings.update(settings)
1987
1967
1988 # Update the settings.
1968 # Update the settings.
1989 inherit_global_settings = new_settings.get(
1969 inherit_global_settings = new_settings.get(
1990 'inherit_global_settings', False)
1970 'inherit_global_settings', False)
1991 settings_model.create_or_update_repo_settings(
1971 settings_model.create_or_update_repo_settings(
1992 new_settings, inherit_global_settings=inherit_global_settings)
1972 new_settings, inherit_global_settings=inherit_global_settings)
1993 Session().commit()
1973 Session().commit()
1994 except Exception:
1974 except Exception:
1995 msg = 'Failed to update settings for repository `{}`'.format(repoid)
1975 msg = 'Failed to update settings for repository `{}`'.format(repoid)
1996 log.exception(msg)
1976 log.exception(msg)
1997 raise JSONRPCError(msg)
1977 raise JSONRPCError(msg)
1998
1978
1999 # Indicate success.
1979 # Indicate success.
2000 return True
1980 return True
2001
1981
2002
1982
2003 @jsonrpc_method()
1983 @jsonrpc_method()
2004 def maintenance(request, apiuser, repoid):
1984 def maintenance(request, apiuser, repoid):
2005 """
1985 """
2006 Triggers a maintenance on the given repository.
1986 Triggers a maintenance on the given repository.
2007
1987
2008 This command can only be run using an |authtoken| with admin
1988 This command can only be run using an |authtoken| with admin
2009 rights to the specified repository. For more information,
1989 rights to the specified repository. For more information,
2010 see :ref:`config-token-ref`.
1990 see :ref:`config-token-ref`.
2011
1991
2012 This command takes the following options:
1992 This command takes the following options:
2013
1993
2014 :param apiuser: This is filled automatically from the |authtoken|.
1994 :param apiuser: This is filled automatically from the |authtoken|.
2015 :type apiuser: AuthUser
1995 :type apiuser: AuthUser
2016 :param repoid: The repository name or repository ID.
1996 :param repoid: The repository name or repository ID.
2017 :type repoid: str or int
1997 :type repoid: str or int
2018
1998
2019 Example output:
1999 Example output:
2020
2000
2021 .. code-block:: bash
2001 .. code-block:: bash
2022
2002
2023 id : <id_given_in_input>
2003 id : <id_given_in_input>
2024 result : {
2004 result : {
2025 "msg": "executed maintenance command",
2005 "msg": "executed maintenance command",
2026 "executed_actions": [
2006 "executed_actions": [
2027 <action_message>, <action_message2>...
2007 <action_message>, <action_message2>...
2028 ],
2008 ],
2029 "repository": "<repository name>"
2009 "repository": "<repository name>"
2030 }
2010 }
2031 error : null
2011 error : null
2032
2012
2033 Example error output:
2013 Example error output:
2034
2014
2035 .. code-block:: bash
2015 .. code-block:: bash
2036
2016
2037 id : <id_given_in_input>
2017 id : <id_given_in_input>
2038 result : null
2018 result : null
2039 error : {
2019 error : {
2040 "Unable to execute maintenance on `<reponame>`"
2020 "Unable to execute maintenance on `<reponame>`"
2041 }
2021 }
2042
2022
2043 """
2023 """
2044
2024
2045 repo = get_repo_or_error(repoid)
2025 repo = get_repo_or_error(repoid)
2046 if not has_superadmin_permission(apiuser):
2026 if not has_superadmin_permission(apiuser):
2047 _perms = ('repository.admin',)
2027 _perms = ('repository.admin',)
2048 validate_repo_permissions(apiuser, repoid, repo, _perms)
2028 validate_repo_permissions(apiuser, repoid, repo, _perms)
2049
2029
2050 try:
2030 try:
2051 maintenance = repo_maintenance.RepoMaintenance()
2031 maintenance = repo_maintenance.RepoMaintenance()
2052 executed_actions = maintenance.execute(repo)
2032 executed_actions = maintenance.execute(repo)
2053
2033
2054 return {
2034 return {
2055 'msg': 'executed maintenance command',
2035 'msg': 'executed maintenance command',
2056 'executed_actions': executed_actions,
2036 'executed_actions': executed_actions,
2057 'repository': repo.repo_name
2037 'repository': repo.repo_name
2058 }
2038 }
2059 except Exception:
2039 except Exception:
2060 log.exception("Exception occurred while trying to run maintenance")
2040 log.exception("Exception occurred while trying to run maintenance")
2061 raise JSONRPCError(
2041 raise JSONRPCError(
2062 'Unable to execute maintenance on `%s`' % repo.repo_name)
2042 'Unable to execute maintenance on `%s`' % repo.repo_name)
@@ -1,719 +1,719 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 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 import logging
22 import logging
23
23
24 from rhodecode.api import JSONRPCValidationError
24 from rhodecode.api import JSONRPCValidationError
25 from rhodecode.api import jsonrpc_method, JSONRPCError
25 from rhodecode.api import jsonrpc_method, JSONRPCError
26 from rhodecode.api.utils import (
26 from rhodecode.api.utils import (
27 has_superadmin_permission, Optional, OAttr, get_user_or_error,
27 has_superadmin_permission, Optional, OAttr, get_user_or_error,
28 get_repo_group_or_error, get_perm_or_error, get_user_group_or_error,
28 get_repo_group_or_error, get_perm_or_error, get_user_group_or_error,
29 get_origin, validate_repo_group_permissions, validate_set_owner_permissions)
29 get_origin, validate_repo_group_permissions, validate_set_owner_permissions)
30 from rhodecode.lib import audit_logger
30 from rhodecode.lib import audit_logger
31 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
32 HasRepoGroupPermissionAnyApi, HasUserGroupPermissionAnyApi)
32 HasRepoGroupPermissionAnyApi, HasUserGroupPermissionAnyApi)
33 from rhodecode.model.db import Session
33 from rhodecode.model.db import Session
34 from rhodecode.model.repo_group import RepoGroupModel
34 from rhodecode.model.repo_group import RepoGroupModel
35 from rhodecode.model.scm import RepoGroupList
35 from rhodecode.model.scm import RepoGroupList
36 from rhodecode.model import validation_schema
36 from rhodecode.model import validation_schema
37 from rhodecode.model.validation_schema.schemas import repo_group_schema
37 from rhodecode.model.validation_schema.schemas import repo_group_schema
38
38
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42
42
43 @jsonrpc_method()
43 @jsonrpc_method()
44 def get_repo_group(request, apiuser, repogroupid):
44 def get_repo_group(request, apiuser, repogroupid):
45 """
45 """
46 Return the specified |repo| group, along with permissions,
46 Return the specified |repo| group, along with permissions,
47 and repositories inside the group
47 and repositories inside the group
48
48
49 :param apiuser: This is filled automatically from the |authtoken|.
49 :param apiuser: This is filled automatically from the |authtoken|.
50 :type apiuser: AuthUser
50 :type apiuser: AuthUser
51 :param repogroupid: Specify the name of ID of the repository group.
51 :param repogroupid: Specify the name of ID of the repository group.
52 :type repogroupid: str or int
52 :type repogroupid: str or int
53
53
54
54
55 Example output:
55 Example output:
56
56
57 .. code-block:: bash
57 .. code-block:: bash
58
58
59 {
59 {
60 "error": null,
60 "error": null,
61 "id": repo-group-id,
61 "id": repo-group-id,
62 "result": {
62 "result": {
63 "group_description": "repo group description",
63 "group_description": "repo group description",
64 "group_id": 14,
64 "group_id": 14,
65 "group_name": "group name",
65 "group_name": "group name",
66 "members": [
66 "permissions": [
67 {
67 {
68 "name": "super-admin-username",
68 "name": "super-admin-username",
69 "origin": "super-admin",
69 "origin": "super-admin",
70 "permission": "group.admin",
70 "permission": "group.admin",
71 "type": "user"
71 "type": "user"
72 },
72 },
73 {
73 {
74 "name": "owner-name",
74 "name": "owner-name",
75 "origin": "owner",
75 "origin": "owner",
76 "permission": "group.admin",
76 "permission": "group.admin",
77 "type": "user"
77 "type": "user"
78 },
78 },
79 {
79 {
80 "name": "user-group-name",
80 "name": "user-group-name",
81 "origin": "permission",
81 "origin": "permission",
82 "permission": "group.write",
82 "permission": "group.write",
83 "type": "user_group"
83 "type": "user_group"
84 }
84 }
85 ],
85 ],
86 "owner": "owner-name",
86 "owner": "owner-name",
87 "parent_group": null,
87 "parent_group": null,
88 "repositories": [ repo-list ]
88 "repositories": [ repo-list ]
89 }
89 }
90 }
90 }
91 """
91 """
92
92
93 repo_group = get_repo_group_or_error(repogroupid)
93 repo_group = get_repo_group_or_error(repogroupid)
94 if not has_superadmin_permission(apiuser):
94 if not has_superadmin_permission(apiuser):
95 # check if we have at least read permission for this repo group !
95 # check if we have at least read permission for this repo group !
96 _perms = ('group.admin', 'group.write', 'group.read',)
96 _perms = ('group.admin', 'group.write', 'group.read',)
97 if not HasRepoGroupPermissionAnyApi(*_perms)(
97 if not HasRepoGroupPermissionAnyApi(*_perms)(
98 user=apiuser, group_name=repo_group.group_name):
98 user=apiuser, group_name=repo_group.group_name):
99 raise JSONRPCError(
99 raise JSONRPCError(
100 'repository group `%s` does not exist' % (repogroupid,))
100 'repository group `%s` does not exist' % (repogroupid,))
101
101
102 permissions = []
102 permissions = []
103 for _user in repo_group.permissions():
103 for _user in repo_group.permissions():
104 user_data = {
104 user_data = {
105 'name': _user.username,
105 'name': _user.username,
106 'permission': _user.permission,
106 'permission': _user.permission,
107 'origin': get_origin(_user),
107 'origin': get_origin(_user),
108 'type': "user",
108 'type': "user",
109 }
109 }
110 permissions.append(user_data)
110 permissions.append(user_data)
111
111
112 for _user_group in repo_group.permission_user_groups():
112 for _user_group in repo_group.permission_user_groups():
113 user_group_data = {
113 user_group_data = {
114 'name': _user_group.users_group_name,
114 'name': _user_group.users_group_name,
115 'permission': _user_group.permission,
115 'permission': _user_group.permission,
116 'origin': get_origin(_user_group),
116 'origin': get_origin(_user_group),
117 'type': "user_group",
117 'type': "user_group",
118 }
118 }
119 permissions.append(user_group_data)
119 permissions.append(user_group_data)
120
120
121 data = repo_group.get_api_data()
121 data = repo_group.get_api_data()
122 data["permissions"] = permissions
122 data["permissions"] = permissions
123 return data
123 return data
124
124
125
125
126 @jsonrpc_method()
126 @jsonrpc_method()
127 def get_repo_groups(request, apiuser):
127 def get_repo_groups(request, apiuser):
128 """
128 """
129 Returns all repository groups.
129 Returns all repository groups.
130
130
131 :param apiuser: This is filled automatically from the |authtoken|.
131 :param apiuser: This is filled automatically from the |authtoken|.
132 :type apiuser: AuthUser
132 :type apiuser: AuthUser
133 """
133 """
134
134
135 result = []
135 result = []
136 _perms = ('group.read', 'group.write', 'group.admin',)
136 _perms = ('group.read', 'group.write', 'group.admin',)
137 extras = {'user': apiuser}
137 extras = {'user': apiuser}
138 for repo_group in RepoGroupList(RepoGroupModel().get_all(),
138 for repo_group in RepoGroupList(RepoGroupModel().get_all(),
139 perm_set=_perms, extra_kwargs=extras):
139 perm_set=_perms, extra_kwargs=extras):
140 result.append(repo_group.get_api_data())
140 result.append(repo_group.get_api_data())
141 return result
141 return result
142
142
143
143
144 @jsonrpc_method()
144 @jsonrpc_method()
145 def create_repo_group(
145 def create_repo_group(
146 request, apiuser, group_name,
146 request, apiuser, group_name,
147 owner=Optional(OAttr('apiuser')),
147 owner=Optional(OAttr('apiuser')),
148 description=Optional(''),
148 description=Optional(''),
149 copy_permissions=Optional(False)):
149 copy_permissions=Optional(False)):
150 """
150 """
151 Creates a repository group.
151 Creates a repository group.
152
152
153 * If the repository group name contains "/", repository group will be
153 * If the repository group name contains "/", repository group will be
154 created inside a repository group or nested repository groups
154 created inside a repository group or nested repository groups
155
155
156 For example "foo/bar/group1" will create repository group called "group1"
156 For example "foo/bar/group1" will create repository group called "group1"
157 inside group "foo/bar". You have to have permissions to access and
157 inside group "foo/bar". You have to have permissions to access and
158 write to the last repository group ("bar" in this example)
158 write to the last repository group ("bar" in this example)
159
159
160 This command can only be run using an |authtoken| with at least
160 This command can only be run using an |authtoken| with at least
161 permissions to create repository groups, or admin permissions to
161 permissions to create repository groups, or admin permissions to
162 parent repository groups.
162 parent repository groups.
163
163
164 :param apiuser: This is filled automatically from the |authtoken|.
164 :param apiuser: This is filled automatically from the |authtoken|.
165 :type apiuser: AuthUser
165 :type apiuser: AuthUser
166 :param group_name: Set the repository group name.
166 :param group_name: Set the repository group name.
167 :type group_name: str
167 :type group_name: str
168 :param description: Set the |repo| group description.
168 :param description: Set the |repo| group description.
169 :type description: str
169 :type description: str
170 :param owner: Set the |repo| group owner.
170 :param owner: Set the |repo| group owner.
171 :type owner: str
171 :type owner: str
172 :param copy_permissions:
172 :param copy_permissions:
173 :type copy_permissions:
173 :type copy_permissions:
174
174
175 Example output:
175 Example output:
176
176
177 .. code-block:: bash
177 .. code-block:: bash
178
178
179 id : <id_given_in_input>
179 id : <id_given_in_input>
180 result : {
180 result : {
181 "msg": "Created new repo group `<repo_group_name>`"
181 "msg": "Created new repo group `<repo_group_name>`"
182 "repo_group": <repogroup_object>
182 "repo_group": <repogroup_object>
183 }
183 }
184 error : null
184 error : null
185
185
186
186
187 Example error output:
187 Example error output:
188
188
189 .. code-block:: bash
189 .. code-block:: bash
190
190
191 id : <id_given_in_input>
191 id : <id_given_in_input>
192 result : null
192 result : null
193 error : {
193 error : {
194 failed to create repo group `<repogroupid>`
194 failed to create repo group `<repogroupid>`
195 }
195 }
196
196
197 """
197 """
198
198
199 owner = validate_set_owner_permissions(apiuser, owner)
199 owner = validate_set_owner_permissions(apiuser, owner)
200
200
201 description = Optional.extract(description)
201 description = Optional.extract(description)
202 copy_permissions = Optional.extract(copy_permissions)
202 copy_permissions = Optional.extract(copy_permissions)
203
203
204 schema = repo_group_schema.RepoGroupSchema().bind(
204 schema = repo_group_schema.RepoGroupSchema().bind(
205 # user caller
205 # user caller
206 user=apiuser)
206 user=apiuser)
207
207
208 try:
208 try:
209 schema_data = schema.deserialize(dict(
209 schema_data = schema.deserialize(dict(
210 repo_group_name=group_name,
210 repo_group_name=group_name,
211 repo_group_owner=owner.username,
211 repo_group_owner=owner.username,
212 repo_group_description=description,
212 repo_group_description=description,
213 repo_group_copy_permissions=copy_permissions,
213 repo_group_copy_permissions=copy_permissions,
214 ))
214 ))
215 except validation_schema.Invalid as err:
215 except validation_schema.Invalid as err:
216 raise JSONRPCValidationError(colander_exc=err)
216 raise JSONRPCValidationError(colander_exc=err)
217
217
218 validated_group_name = schema_data['repo_group_name']
218 validated_group_name = schema_data['repo_group_name']
219
219
220 try:
220 try:
221 repo_group = RepoGroupModel().create(
221 repo_group = RepoGroupModel().create(
222 owner=owner,
222 owner=owner,
223 group_name=validated_group_name,
223 group_name=validated_group_name,
224 group_description=schema_data['repo_group_name'],
224 group_description=schema_data['repo_group_name'],
225 copy_permissions=schema_data['repo_group_copy_permissions'])
225 copy_permissions=schema_data['repo_group_copy_permissions'])
226 Session().flush()
226 Session().flush()
227
227
228 repo_group_data = repo_group.get_api_data()
228 repo_group_data = repo_group.get_api_data()
229 audit_logger.store_api(
229 audit_logger.store_api(
230 'repo_group.create', action_data={'data': repo_group_data},
230 'repo_group.create', action_data={'data': repo_group_data},
231 user=apiuser)
231 user=apiuser)
232
232
233 Session().commit()
233 Session().commit()
234 return {
234 return {
235 'msg': 'Created new repo group `%s`' % validated_group_name,
235 'msg': 'Created new repo group `%s`' % validated_group_name,
236 'repo_group': repo_group.get_api_data()
236 'repo_group': repo_group.get_api_data()
237 }
237 }
238 except Exception:
238 except Exception:
239 log.exception("Exception occurred while trying create repo group")
239 log.exception("Exception occurred while trying create repo group")
240 raise JSONRPCError(
240 raise JSONRPCError(
241 'failed to create repo group `%s`' % (validated_group_name,))
241 'failed to create repo group `%s`' % (validated_group_name,))
242
242
243
243
244 @jsonrpc_method()
244 @jsonrpc_method()
245 def update_repo_group(
245 def update_repo_group(
246 request, apiuser, repogroupid, group_name=Optional(''),
246 request, apiuser, repogroupid, group_name=Optional(''),
247 description=Optional(''), owner=Optional(OAttr('apiuser')),
247 description=Optional(''), owner=Optional(OAttr('apiuser')),
248 enable_locking=Optional(False)):
248 enable_locking=Optional(False)):
249 """
249 """
250 Updates repository group with the details given.
250 Updates repository group with the details given.
251
251
252 This command can only be run using an |authtoken| with admin
252 This command can only be run using an |authtoken| with admin
253 permissions.
253 permissions.
254
254
255 * If the group_name name contains "/", repository group will be updated
255 * If the group_name name contains "/", repository group will be updated
256 accordingly with a repository group or nested repository groups
256 accordingly with a repository group or nested repository groups
257
257
258 For example repogroupid=group-test group_name="foo/bar/group-test"
258 For example repogroupid=group-test group_name="foo/bar/group-test"
259 will update repository group called "group-test" and place it
259 will update repository group called "group-test" and place it
260 inside group "foo/bar".
260 inside group "foo/bar".
261 You have to have permissions to access and write to the last repository
261 You have to have permissions to access and write to the last repository
262 group ("bar" in this example)
262 group ("bar" in this example)
263
263
264 :param apiuser: This is filled automatically from the |authtoken|.
264 :param apiuser: This is filled automatically from the |authtoken|.
265 :type apiuser: AuthUser
265 :type apiuser: AuthUser
266 :param repogroupid: Set the ID of repository group.
266 :param repogroupid: Set the ID of repository group.
267 :type repogroupid: str or int
267 :type repogroupid: str or int
268 :param group_name: Set the name of the |repo| group.
268 :param group_name: Set the name of the |repo| group.
269 :type group_name: str
269 :type group_name: str
270 :param description: Set a description for the group.
270 :param description: Set a description for the group.
271 :type description: str
271 :type description: str
272 :param owner: Set the |repo| group owner.
272 :param owner: Set the |repo| group owner.
273 :type owner: str
273 :type owner: str
274 :param enable_locking: Enable |repo| locking. The default is false.
274 :param enable_locking: Enable |repo| locking. The default is false.
275 :type enable_locking: bool
275 :type enable_locking: bool
276 """
276 """
277
277
278 repo_group = get_repo_group_or_error(repogroupid)
278 repo_group = get_repo_group_or_error(repogroupid)
279
279
280 if not has_superadmin_permission(apiuser):
280 if not has_superadmin_permission(apiuser):
281 validate_repo_group_permissions(
281 validate_repo_group_permissions(
282 apiuser, repogroupid, repo_group, ('group.admin',))
282 apiuser, repogroupid, repo_group, ('group.admin',))
283
283
284 updates = dict(
284 updates = dict(
285 group_name=group_name
285 group_name=group_name
286 if not isinstance(group_name, Optional) else repo_group.group_name,
286 if not isinstance(group_name, Optional) else repo_group.group_name,
287
287
288 group_description=description
288 group_description=description
289 if not isinstance(description, Optional) else repo_group.group_description,
289 if not isinstance(description, Optional) else repo_group.group_description,
290
290
291 user=owner
291 user=owner
292 if not isinstance(owner, Optional) else repo_group.user.username,
292 if not isinstance(owner, Optional) else repo_group.user.username,
293
293
294 enable_locking=enable_locking
294 enable_locking=enable_locking
295 if not isinstance(enable_locking, Optional) else repo_group.enable_locking
295 if not isinstance(enable_locking, Optional) else repo_group.enable_locking
296 )
296 )
297
297
298 schema = repo_group_schema.RepoGroupSchema().bind(
298 schema = repo_group_schema.RepoGroupSchema().bind(
299 # user caller
299 # user caller
300 user=apiuser,
300 user=apiuser,
301 old_values=repo_group.get_api_data())
301 old_values=repo_group.get_api_data())
302
302
303 try:
303 try:
304 schema_data = schema.deserialize(dict(
304 schema_data = schema.deserialize(dict(
305 repo_group_name=updates['group_name'],
305 repo_group_name=updates['group_name'],
306 repo_group_owner=updates['user'],
306 repo_group_owner=updates['user'],
307 repo_group_description=updates['group_description'],
307 repo_group_description=updates['group_description'],
308 repo_group_enable_locking=updates['enable_locking'],
308 repo_group_enable_locking=updates['enable_locking'],
309 ))
309 ))
310 except validation_schema.Invalid as err:
310 except validation_schema.Invalid as err:
311 raise JSONRPCValidationError(colander_exc=err)
311 raise JSONRPCValidationError(colander_exc=err)
312
312
313 validated_updates = dict(
313 validated_updates = dict(
314 group_name=schema_data['repo_group']['repo_group_name_without_group'],
314 group_name=schema_data['repo_group']['repo_group_name_without_group'],
315 group_parent_id=schema_data['repo_group']['repo_group_id'],
315 group_parent_id=schema_data['repo_group']['repo_group_id'],
316 user=schema_data['repo_group_owner'],
316 user=schema_data['repo_group_owner'],
317 group_description=schema_data['repo_group_description'],
317 group_description=schema_data['repo_group_description'],
318 enable_locking=schema_data['repo_group_enable_locking'],
318 enable_locking=schema_data['repo_group_enable_locking'],
319 )
319 )
320
320
321 old_data = repo_group.get_api_data()
321 old_data = repo_group.get_api_data()
322 try:
322 try:
323 RepoGroupModel().update(repo_group, validated_updates)
323 RepoGroupModel().update(repo_group, validated_updates)
324 audit_logger.store_api(
324 audit_logger.store_api(
325 'repo_group.edit', action_data={'old_data': old_data},
325 'repo_group.edit', action_data={'old_data': old_data},
326 user=apiuser)
326 user=apiuser)
327
327
328 Session().commit()
328 Session().commit()
329 return {
329 return {
330 'msg': 'updated repository group ID:%s %s' % (
330 'msg': 'updated repository group ID:%s %s' % (
331 repo_group.group_id, repo_group.group_name),
331 repo_group.group_id, repo_group.group_name),
332 'repo_group': repo_group.get_api_data()
332 'repo_group': repo_group.get_api_data()
333 }
333 }
334 except Exception:
334 except Exception:
335 log.exception(
335 log.exception(
336 u"Exception occurred while trying update repo group %s",
336 u"Exception occurred while trying update repo group %s",
337 repogroupid)
337 repogroupid)
338 raise JSONRPCError('failed to update repository group `%s`'
338 raise JSONRPCError('failed to update repository group `%s`'
339 % (repogroupid,))
339 % (repogroupid,))
340
340
341
341
342 @jsonrpc_method()
342 @jsonrpc_method()
343 def delete_repo_group(request, apiuser, repogroupid):
343 def delete_repo_group(request, apiuser, repogroupid):
344 """
344 """
345 Deletes a |repo| group.
345 Deletes a |repo| group.
346
346
347 :param apiuser: This is filled automatically from the |authtoken|.
347 :param apiuser: This is filled automatically from the |authtoken|.
348 :type apiuser: AuthUser
348 :type apiuser: AuthUser
349 :param repogroupid: Set the name or ID of repository group to be
349 :param repogroupid: Set the name or ID of repository group to be
350 deleted.
350 deleted.
351 :type repogroupid: str or int
351 :type repogroupid: str or int
352
352
353 Example output:
353 Example output:
354
354
355 .. code-block:: bash
355 .. code-block:: bash
356
356
357 id : <id_given_in_input>
357 id : <id_given_in_input>
358 result : {
358 result : {
359 'msg': 'deleted repo group ID:<repogroupid> <repogroupname>'
359 'msg': 'deleted repo group ID:<repogroupid> <repogroupname>'
360 'repo_group': null
360 'repo_group': null
361 }
361 }
362 error : null
362 error : null
363
363
364 Example error output:
364 Example error output:
365
365
366 .. code-block:: bash
366 .. code-block:: bash
367
367
368 id : <id_given_in_input>
368 id : <id_given_in_input>
369 result : null
369 result : null
370 error : {
370 error : {
371 "failed to delete repo group ID:<repogroupid> <repogroupname>"
371 "failed to delete repo group ID:<repogroupid> <repogroupname>"
372 }
372 }
373
373
374 """
374 """
375
375
376 repo_group = get_repo_group_or_error(repogroupid)
376 repo_group = get_repo_group_or_error(repogroupid)
377 if not has_superadmin_permission(apiuser):
377 if not has_superadmin_permission(apiuser):
378 validate_repo_group_permissions(
378 validate_repo_group_permissions(
379 apiuser, repogroupid, repo_group, ('group.admin',))
379 apiuser, repogroupid, repo_group, ('group.admin',))
380
380
381 old_data = repo_group.get_api_data()
381 old_data = repo_group.get_api_data()
382 try:
382 try:
383 RepoGroupModel().delete(repo_group)
383 RepoGroupModel().delete(repo_group)
384 audit_logger.store_api(
384 audit_logger.store_api(
385 'repo_group.delete', action_data={'old_data': old_data},
385 'repo_group.delete', action_data={'old_data': old_data},
386 user=apiuser)
386 user=apiuser)
387 Session().commit()
387 Session().commit()
388 return {
388 return {
389 'msg': 'deleted repo group ID:%s %s' %
389 'msg': 'deleted repo group ID:%s %s' %
390 (repo_group.group_id, repo_group.group_name),
390 (repo_group.group_id, repo_group.group_name),
391 'repo_group': None
391 'repo_group': None
392 }
392 }
393 except Exception:
393 except Exception:
394 log.exception("Exception occurred while trying to delete repo group")
394 log.exception("Exception occurred while trying to delete repo group")
395 raise JSONRPCError('failed to delete repo group ID:%s %s' %
395 raise JSONRPCError('failed to delete repo group ID:%s %s' %
396 (repo_group.group_id, repo_group.group_name))
396 (repo_group.group_id, repo_group.group_name))
397
397
398
398
399 @jsonrpc_method()
399 @jsonrpc_method()
400 def grant_user_permission_to_repo_group(
400 def grant_user_permission_to_repo_group(
401 request, apiuser, repogroupid, userid, perm,
401 request, apiuser, repogroupid, userid, perm,
402 apply_to_children=Optional('none')):
402 apply_to_children=Optional('none')):
403 """
403 """
404 Grant permission for a user on the given repository group, or update
404 Grant permission for a user on the given repository group, or update
405 existing permissions if found.
405 existing permissions if found.
406
406
407 This command can only be run using an |authtoken| with admin
407 This command can only be run using an |authtoken| with admin
408 permissions.
408 permissions.
409
409
410 :param apiuser: This is filled automatically from the |authtoken|.
410 :param apiuser: This is filled automatically from the |authtoken|.
411 :type apiuser: AuthUser
411 :type apiuser: AuthUser
412 :param repogroupid: Set the name or ID of repository group.
412 :param repogroupid: Set the name or ID of repository group.
413 :type repogroupid: str or int
413 :type repogroupid: str or int
414 :param userid: Set the user name.
414 :param userid: Set the user name.
415 :type userid: str
415 :type userid: str
416 :param perm: (group.(none|read|write|admin))
416 :param perm: (group.(none|read|write|admin))
417 :type perm: str
417 :type perm: str
418 :param apply_to_children: 'none', 'repos', 'groups', 'all'
418 :param apply_to_children: 'none', 'repos', 'groups', 'all'
419 :type apply_to_children: str
419 :type apply_to_children: str
420
420
421 Example output:
421 Example output:
422
422
423 .. code-block:: bash
423 .. code-block:: bash
424
424
425 id : <id_given_in_input>
425 id : <id_given_in_input>
426 result: {
426 result: {
427 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
427 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
428 "success": true
428 "success": true
429 }
429 }
430 error: null
430 error: null
431
431
432 Example error output:
432 Example error output:
433
433
434 .. code-block:: bash
434 .. code-block:: bash
435
435
436 id : <id_given_in_input>
436 id : <id_given_in_input>
437 result : null
437 result : null
438 error : {
438 error : {
439 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
439 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
440 }
440 }
441
441
442 """
442 """
443
443
444 repo_group = get_repo_group_or_error(repogroupid)
444 repo_group = get_repo_group_or_error(repogroupid)
445
445
446 if not has_superadmin_permission(apiuser):
446 if not has_superadmin_permission(apiuser):
447 validate_repo_group_permissions(
447 validate_repo_group_permissions(
448 apiuser, repogroupid, repo_group, ('group.admin',))
448 apiuser, repogroupid, repo_group, ('group.admin',))
449
449
450 user = get_user_or_error(userid)
450 user = get_user_or_error(userid)
451 perm = get_perm_or_error(perm, prefix='group.')
451 perm = get_perm_or_error(perm, prefix='group.')
452 apply_to_children = Optional.extract(apply_to_children)
452 apply_to_children = Optional.extract(apply_to_children)
453
453
454 perm_additions = [[user.user_id, perm, "user"]]
454 perm_additions = [[user.user_id, perm, "user"]]
455 try:
455 try:
456 RepoGroupModel().update_permissions(repo_group=repo_group,
456 RepoGroupModel().update_permissions(repo_group=repo_group,
457 perm_additions=perm_additions,
457 perm_additions=perm_additions,
458 recursive=apply_to_children,
458 recursive=apply_to_children,
459 cur_user=apiuser)
459 cur_user=apiuser)
460 Session().commit()
460 Session().commit()
461 return {
461 return {
462 'msg': 'Granted perm: `%s` (recursive:%s) for user: '
462 'msg': 'Granted perm: `%s` (recursive:%s) for user: '
463 '`%s` in repo group: `%s`' % (
463 '`%s` in repo group: `%s`' % (
464 perm.permission_name, apply_to_children, user.username,
464 perm.permission_name, apply_to_children, user.username,
465 repo_group.name
465 repo_group.name
466 ),
466 ),
467 'success': True
467 'success': True
468 }
468 }
469 except Exception:
469 except Exception:
470 log.exception("Exception occurred while trying to grant "
470 log.exception("Exception occurred while trying to grant "
471 "user permissions to repo group")
471 "user permissions to repo group")
472 raise JSONRPCError(
472 raise JSONRPCError(
473 'failed to edit permission for user: '
473 'failed to edit permission for user: '
474 '`%s` in repo group: `%s`' % (userid, repo_group.name))
474 '`%s` in repo group: `%s`' % (userid, repo_group.name))
475
475
476
476
477 @jsonrpc_method()
477 @jsonrpc_method()
478 def revoke_user_permission_from_repo_group(
478 def revoke_user_permission_from_repo_group(
479 request, apiuser, repogroupid, userid,
479 request, apiuser, repogroupid, userid,
480 apply_to_children=Optional('none')):
480 apply_to_children=Optional('none')):
481 """
481 """
482 Revoke permission for a user in a given repository group.
482 Revoke permission for a user in a given repository group.
483
483
484 This command can only be run using an |authtoken| with admin
484 This command can only be run using an |authtoken| with admin
485 permissions on the |repo| group.
485 permissions on the |repo| group.
486
486
487 :param apiuser: This is filled automatically from the |authtoken|.
487 :param apiuser: This is filled automatically from the |authtoken|.
488 :type apiuser: AuthUser
488 :type apiuser: AuthUser
489 :param repogroupid: Set the name or ID of the repository group.
489 :param repogroupid: Set the name or ID of the repository group.
490 :type repogroupid: str or int
490 :type repogroupid: str or int
491 :param userid: Set the user name to revoke.
491 :param userid: Set the user name to revoke.
492 :type userid: str
492 :type userid: str
493 :param apply_to_children: 'none', 'repos', 'groups', 'all'
493 :param apply_to_children: 'none', 'repos', 'groups', 'all'
494 :type apply_to_children: str
494 :type apply_to_children: str
495
495
496 Example output:
496 Example output:
497
497
498 .. code-block:: bash
498 .. code-block:: bash
499
499
500 id : <id_given_in_input>
500 id : <id_given_in_input>
501 result: {
501 result: {
502 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
502 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
503 "success": true
503 "success": true
504 }
504 }
505 error: null
505 error: null
506
506
507 Example error output:
507 Example error output:
508
508
509 .. code-block:: bash
509 .. code-block:: bash
510
510
511 id : <id_given_in_input>
511 id : <id_given_in_input>
512 result : null
512 result : null
513 error : {
513 error : {
514 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
514 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
515 }
515 }
516
516
517 """
517 """
518
518
519 repo_group = get_repo_group_or_error(repogroupid)
519 repo_group = get_repo_group_or_error(repogroupid)
520
520
521 if not has_superadmin_permission(apiuser):
521 if not has_superadmin_permission(apiuser):
522 validate_repo_group_permissions(
522 validate_repo_group_permissions(
523 apiuser, repogroupid, repo_group, ('group.admin',))
523 apiuser, repogroupid, repo_group, ('group.admin',))
524
524
525 user = get_user_or_error(userid)
525 user = get_user_or_error(userid)
526 apply_to_children = Optional.extract(apply_to_children)
526 apply_to_children = Optional.extract(apply_to_children)
527
527
528 perm_deletions = [[user.user_id, None, "user"]]
528 perm_deletions = [[user.user_id, None, "user"]]
529 try:
529 try:
530 RepoGroupModel().update_permissions(repo_group=repo_group,
530 RepoGroupModel().update_permissions(repo_group=repo_group,
531 perm_deletions=perm_deletions,
531 perm_deletions=perm_deletions,
532 recursive=apply_to_children,
532 recursive=apply_to_children,
533 cur_user=apiuser)
533 cur_user=apiuser)
534 Session().commit()
534 Session().commit()
535 return {
535 return {
536 'msg': 'Revoked perm (recursive:%s) for user: '
536 'msg': 'Revoked perm (recursive:%s) for user: '
537 '`%s` in repo group: `%s`' % (
537 '`%s` in repo group: `%s`' % (
538 apply_to_children, user.username, repo_group.name
538 apply_to_children, user.username, repo_group.name
539 ),
539 ),
540 'success': True
540 'success': True
541 }
541 }
542 except Exception:
542 except Exception:
543 log.exception("Exception occurred while trying revoke user "
543 log.exception("Exception occurred while trying revoke user "
544 "permission from repo group")
544 "permission from repo group")
545 raise JSONRPCError(
545 raise JSONRPCError(
546 'failed to edit permission for user: '
546 'failed to edit permission for user: '
547 '`%s` in repo group: `%s`' % (userid, repo_group.name))
547 '`%s` in repo group: `%s`' % (userid, repo_group.name))
548
548
549
549
550 @jsonrpc_method()
550 @jsonrpc_method()
551 def grant_user_group_permission_to_repo_group(
551 def grant_user_group_permission_to_repo_group(
552 request, apiuser, repogroupid, usergroupid, perm,
552 request, apiuser, repogroupid, usergroupid, perm,
553 apply_to_children=Optional('none'), ):
553 apply_to_children=Optional('none'), ):
554 """
554 """
555 Grant permission for a user group on given repository group, or update
555 Grant permission for a user group on given repository group, or update
556 existing permissions if found.
556 existing permissions if found.
557
557
558 This command can only be run using an |authtoken| with admin
558 This command can only be run using an |authtoken| with admin
559 permissions on the |repo| group.
559 permissions on the |repo| group.
560
560
561 :param apiuser: This is filled automatically from the |authtoken|.
561 :param apiuser: This is filled automatically from the |authtoken|.
562 :type apiuser: AuthUser
562 :type apiuser: AuthUser
563 :param repogroupid: Set the name or id of repository group
563 :param repogroupid: Set the name or id of repository group
564 :type repogroupid: str or int
564 :type repogroupid: str or int
565 :param usergroupid: id of usergroup
565 :param usergroupid: id of usergroup
566 :type usergroupid: str or int
566 :type usergroupid: str or int
567 :param perm: (group.(none|read|write|admin))
567 :param perm: (group.(none|read|write|admin))
568 :type perm: str
568 :type perm: str
569 :param apply_to_children: 'none', 'repos', 'groups', 'all'
569 :param apply_to_children: 'none', 'repos', 'groups', 'all'
570 :type apply_to_children: str
570 :type apply_to_children: str
571
571
572 Example output:
572 Example output:
573
573
574 .. code-block:: bash
574 .. code-block:: bash
575
575
576 id : <id_given_in_input>
576 id : <id_given_in_input>
577 result : {
577 result : {
578 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
578 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
579 "success": true
579 "success": true
580
580
581 }
581 }
582 error : null
582 error : null
583
583
584 Example error output:
584 Example error output:
585
585
586 .. code-block:: bash
586 .. code-block:: bash
587
587
588 id : <id_given_in_input>
588 id : <id_given_in_input>
589 result : null
589 result : null
590 error : {
590 error : {
591 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
591 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
592 }
592 }
593
593
594 """
594 """
595
595
596 repo_group = get_repo_group_or_error(repogroupid)
596 repo_group = get_repo_group_or_error(repogroupid)
597 perm = get_perm_or_error(perm, prefix='group.')
597 perm = get_perm_or_error(perm, prefix='group.')
598 user_group = get_user_group_or_error(usergroupid)
598 user_group = get_user_group_or_error(usergroupid)
599 if not has_superadmin_permission(apiuser):
599 if not has_superadmin_permission(apiuser):
600 validate_repo_group_permissions(
600 validate_repo_group_permissions(
601 apiuser, repogroupid, repo_group, ('group.admin',))
601 apiuser, repogroupid, repo_group, ('group.admin',))
602
602
603 # check if we have at least read permission for this user group !
603 # check if we have at least read permission for this user group !
604 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
604 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
605 if not HasUserGroupPermissionAnyApi(*_perms)(
605 if not HasUserGroupPermissionAnyApi(*_perms)(
606 user=apiuser, user_group_name=user_group.users_group_name):
606 user=apiuser, user_group_name=user_group.users_group_name):
607 raise JSONRPCError(
607 raise JSONRPCError(
608 'user group `%s` does not exist' % (usergroupid,))
608 'user group `%s` does not exist' % (usergroupid,))
609
609
610 apply_to_children = Optional.extract(apply_to_children)
610 apply_to_children = Optional.extract(apply_to_children)
611
611
612 perm_additions = [[user_group.users_group_id, perm, "user_group"]]
612 perm_additions = [[user_group.users_group_id, perm, "user_group"]]
613 try:
613 try:
614 RepoGroupModel().update_permissions(repo_group=repo_group,
614 RepoGroupModel().update_permissions(repo_group=repo_group,
615 perm_additions=perm_additions,
615 perm_additions=perm_additions,
616 recursive=apply_to_children,
616 recursive=apply_to_children,
617 cur_user=apiuser)
617 cur_user=apiuser)
618 Session().commit()
618 Session().commit()
619 return {
619 return {
620 'msg': 'Granted perm: `%s` (recursive:%s) '
620 'msg': 'Granted perm: `%s` (recursive:%s) '
621 'for user group: `%s` in repo group: `%s`' % (
621 'for user group: `%s` in repo group: `%s`' % (
622 perm.permission_name, apply_to_children,
622 perm.permission_name, apply_to_children,
623 user_group.users_group_name, repo_group.name
623 user_group.users_group_name, repo_group.name
624 ),
624 ),
625 'success': True
625 'success': True
626 }
626 }
627 except Exception:
627 except Exception:
628 log.exception("Exception occurred while trying to grant user "
628 log.exception("Exception occurred while trying to grant user "
629 "group permissions to repo group")
629 "group permissions to repo group")
630 raise JSONRPCError(
630 raise JSONRPCError(
631 'failed to edit permission for user group: `%s` in '
631 'failed to edit permission for user group: `%s` in '
632 'repo group: `%s`' % (
632 'repo group: `%s`' % (
633 usergroupid, repo_group.name
633 usergroupid, repo_group.name
634 )
634 )
635 )
635 )
636
636
637
637
638 @jsonrpc_method()
638 @jsonrpc_method()
639 def revoke_user_group_permission_from_repo_group(
639 def revoke_user_group_permission_from_repo_group(
640 request, apiuser, repogroupid, usergroupid,
640 request, apiuser, repogroupid, usergroupid,
641 apply_to_children=Optional('none')):
641 apply_to_children=Optional('none')):
642 """
642 """
643 Revoke permission for user group on given repository.
643 Revoke permission for user group on given repository.
644
644
645 This command can only be run using an |authtoken| with admin
645 This command can only be run using an |authtoken| with admin
646 permissions on the |repo| group.
646 permissions on the |repo| group.
647
647
648 :param apiuser: This is filled automatically from the |authtoken|.
648 :param apiuser: This is filled automatically from the |authtoken|.
649 :type apiuser: AuthUser
649 :type apiuser: AuthUser
650 :param repogroupid: name or id of repository group
650 :param repogroupid: name or id of repository group
651 :type repogroupid: str or int
651 :type repogroupid: str or int
652 :param usergroupid:
652 :param usergroupid:
653 :param apply_to_children: 'none', 'repos', 'groups', 'all'
653 :param apply_to_children: 'none', 'repos', 'groups', 'all'
654 :type apply_to_children: str
654 :type apply_to_children: str
655
655
656 Example output:
656 Example output:
657
657
658 .. code-block:: bash
658 .. code-block:: bash
659
659
660 id : <id_given_in_input>
660 id : <id_given_in_input>
661 result: {
661 result: {
662 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
662 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
663 "success": true
663 "success": true
664 }
664 }
665 error: null
665 error: null
666
666
667 Example error output:
667 Example error output:
668
668
669 .. code-block:: bash
669 .. code-block:: bash
670
670
671 id : <id_given_in_input>
671 id : <id_given_in_input>
672 result : null
672 result : null
673 error : {
673 error : {
674 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
674 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
675 }
675 }
676
676
677
677
678 """
678 """
679
679
680 repo_group = get_repo_group_or_error(repogroupid)
680 repo_group = get_repo_group_or_error(repogroupid)
681 user_group = get_user_group_or_error(usergroupid)
681 user_group = get_user_group_or_error(usergroupid)
682 if not has_superadmin_permission(apiuser):
682 if not has_superadmin_permission(apiuser):
683 validate_repo_group_permissions(
683 validate_repo_group_permissions(
684 apiuser, repogroupid, repo_group, ('group.admin',))
684 apiuser, repogroupid, repo_group, ('group.admin',))
685
685
686 # check if we have at least read permission for this user group !
686 # check if we have at least read permission for this user group !
687 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
687 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
688 if not HasUserGroupPermissionAnyApi(*_perms)(
688 if not HasUserGroupPermissionAnyApi(*_perms)(
689 user=apiuser, user_group_name=user_group.users_group_name):
689 user=apiuser, user_group_name=user_group.users_group_name):
690 raise JSONRPCError(
690 raise JSONRPCError(
691 'user group `%s` does not exist' % (usergroupid,))
691 'user group `%s` does not exist' % (usergroupid,))
692
692
693 apply_to_children = Optional.extract(apply_to_children)
693 apply_to_children = Optional.extract(apply_to_children)
694
694
695 perm_deletions = [[user_group.users_group_id, None, "user_group"]]
695 perm_deletions = [[user_group.users_group_id, None, "user_group"]]
696 try:
696 try:
697 RepoGroupModel().update_permissions(repo_group=repo_group,
697 RepoGroupModel().update_permissions(repo_group=repo_group,
698 perm_deletions=perm_deletions,
698 perm_deletions=perm_deletions,
699 recursive=apply_to_children,
699 recursive=apply_to_children,
700 cur_user=apiuser)
700 cur_user=apiuser)
701 Session().commit()
701 Session().commit()
702 return {
702 return {
703 'msg': 'Revoked perm (recursive:%s) for user group: '
703 'msg': 'Revoked perm (recursive:%s) for user group: '
704 '`%s` in repo group: `%s`' % (
704 '`%s` in repo group: `%s`' % (
705 apply_to_children, user_group.users_group_name,
705 apply_to_children, user_group.users_group_name,
706 repo_group.name
706 repo_group.name
707 ),
707 ),
708 'success': True
708 'success': True
709 }
709 }
710 except Exception:
710 except Exception:
711 log.exception("Exception occurred while trying revoke user group "
711 log.exception("Exception occurred while trying revoke user group "
712 "permissions from repo group")
712 "permissions from repo group")
713 raise JSONRPCError(
713 raise JSONRPCError(
714 'failed to edit permission for user group: '
714 'failed to edit permission for user group: '
715 '`%s` in repo group: `%s`' % (
715 '`%s` in repo group: `%s`' % (
716 user_group.users_group_name, repo_group.name
716 user_group.users_group_name, repo_group.name
717 )
717 )
718 )
718 )
719
719
@@ -1,562 +1,563 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 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
22
23 from rhodecode.api import (
23 from rhodecode.api import (
24 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
24 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
25 from rhodecode.api.utils import (
25 from rhodecode.api.utils import (
26 Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
26 Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
27 from rhodecode.lib import audit_logger
27 from rhodecode.lib import audit_logger
28 from rhodecode.lib.auth import AuthUser, PasswordGenerator
28 from rhodecode.lib.auth import AuthUser, PasswordGenerator
29 from rhodecode.lib.exceptions import DefaultUserException
29 from rhodecode.lib.exceptions import DefaultUserException
30 from rhodecode.lib.utils2 import safe_int, str2bool
30 from rhodecode.lib.utils2 import safe_int, str2bool
31 from rhodecode.model.db import Session, User, Repository
31 from rhodecode.model.db import Session, User, Repository
32 from rhodecode.model.user import UserModel
32 from rhodecode.model.user import UserModel
33 from rhodecode.model import validation_schema
33 from rhodecode.model import validation_schema
34 from rhodecode.model.validation_schema.schemas import user_schema
34 from rhodecode.model.validation_schema.schemas import user_schema
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 @jsonrpc_method()
39 @jsonrpc_method()
40 def get_user(request, apiuser, userid=Optional(OAttr('apiuser'))):
40 def get_user(request, apiuser, userid=Optional(OAttr('apiuser'))):
41 """
41 """
42 Returns the information associated with a username or userid.
42 Returns the information associated with a username or userid.
43
43
44 * If the ``userid`` is not set, this command returns the information
44 * If the ``userid`` is not set, this command returns the information
45 for the ``userid`` calling the method.
45 for the ``userid`` calling the method.
46
46
47 .. note::
47 .. note::
48
48
49 Normal users may only run this command against their ``userid``. For
49 Normal users may only run this command against their ``userid``. For
50 full privileges you must run this command using an |authtoken| with
50 full privileges you must run this command using an |authtoken| with
51 admin rights.
51 admin rights.
52
52
53 :param apiuser: This is filled automatically from the |authtoken|.
53 :param apiuser: This is filled automatically from the |authtoken|.
54 :type apiuser: AuthUser
54 :type apiuser: AuthUser
55 :param userid: Sets the userid for which data will be returned.
55 :param userid: Sets the userid for which data will be returned.
56 :type userid: Optional(str or int)
56 :type userid: Optional(str or int)
57
57
58 Example output:
58 Example output:
59
59
60 .. code-block:: bash
60 .. code-block:: bash
61
61
62 {
62 {
63 "error": null,
63 "error": null,
64 "id": <id>,
64 "id": <id>,
65 "result": {
65 "result": {
66 "active": true,
66 "active": true,
67 "admin": false,
67 "admin": false,
68 "api_keys": [ list of keys ],
68 "api_keys": [ list of keys ],
69 "auth_tokens": [ list of tokens with details ],
69 "auth_tokens": [ list of tokens with details ],
70 "email": "user@example.com",
70 "email": "user@example.com",
71 "emails": [
71 "emails": [
72 "user@example.com"
72 "user@example.com"
73 ],
73 ],
74 "extern_name": "rhodecode",
74 "extern_name": "rhodecode",
75 "extern_type": "rhodecode",
75 "extern_type": "rhodecode",
76 "firstname": "username",
76 "firstname": "username",
77 "ip_addresses": [],
77 "ip_addresses": [],
78 "language": null,
78 "language": null,
79 "last_login": "Timestamp",
79 "last_login": "Timestamp",
80 "last_activity": "Timestamp",
80 "last_activity": "Timestamp",
81 "lastname": "surnae",
81 "lastname": "surnae",
82 "permissions": {
82 "permissions": <deprecated>,
83 "permissions_summary": {
83 "global": [
84 "global": [
84 "hg.inherit_default_perms.true",
85 "hg.inherit_default_perms.true",
85 "usergroup.read",
86 "usergroup.read",
86 "hg.repogroup.create.false",
87 "hg.repogroup.create.false",
87 "hg.create.none",
88 "hg.create.none",
88 "hg.password_reset.enabled",
89 "hg.password_reset.enabled",
89 "hg.extern_activate.manual",
90 "hg.extern_activate.manual",
90 "hg.create.write_on_repogroup.false",
91 "hg.create.write_on_repogroup.false",
91 "hg.usergroup.create.false",
92 "hg.usergroup.create.false",
92 "group.none",
93 "group.none",
93 "repository.none",
94 "repository.none",
94 "hg.register.none",
95 "hg.register.none",
95 "hg.fork.repository"
96 "hg.fork.repository"
96 ],
97 ],
97 "repositories": { "username/example": "repository.write"},
98 "repositories": { "username/example": "repository.write"},
98 "repositories_groups": { "user-group/repo": "group.none" },
99 "repositories_groups": { "user-group/repo": "group.none" },
99 "user_groups": { "user_group_name": "usergroup.read" }
100 "user_groups": { "user_group_name": "usergroup.read" }
100 },
101 }
101 "user_id": 32,
102 "user_id": 32,
102 "username": "username"
103 "username": "username"
103 }
104 }
104 }
105 }
105 """
106 """
106
107
107 if not has_superadmin_permission(apiuser):
108 if not has_superadmin_permission(apiuser):
108 # make sure normal user does not pass someone else userid,
109 # make sure normal user does not pass someone else userid,
109 # he is not allowed to do that
110 # he is not allowed to do that
110 if not isinstance(userid, Optional) and userid != apiuser.user_id:
111 if not isinstance(userid, Optional) and userid != apiuser.user_id:
111 raise JSONRPCError('userid is not the same as your user')
112 raise JSONRPCError('userid is not the same as your user')
112
113
113 userid = Optional.extract(userid, evaluate_locals=locals())
114 userid = Optional.extract(userid, evaluate_locals=locals())
114 userid = getattr(userid, 'user_id', userid)
115 userid = getattr(userid, 'user_id', userid)
115
116
116 user = get_user_or_error(userid)
117 user = get_user_or_error(userid)
117 data = user.get_api_data(include_secrets=True)
118 data = user.get_api_data(include_secrets=True)
118 permissions = AuthUser(user_id=user.user_id).permissions
119 permissions = AuthUser(user_id=user.user_id).permissions
119 data['permissions'] = permissions # TODO(marcink): should be deprecated
120 data['permissions'] = permissions # TODO(marcink): should be deprecated
120 data['permissions_summary'] = permissions
121 data['permissions_summary'] = permissions
121 return data
122 return data
122
123
123
124
124 @jsonrpc_method()
125 @jsonrpc_method()
125 def get_users(request, apiuser):
126 def get_users(request, apiuser):
126 """
127 """
127 Lists all users in the |RCE| user database.
128 Lists all users in the |RCE| user database.
128
129
129 This command can only be run using an |authtoken| with admin rights to
130 This command can only be run using an |authtoken| with admin rights to
130 the specified repository.
131 the specified repository.
131
132
132 This command takes the following options:
133 This command takes the following options:
133
134
134 :param apiuser: This is filled automatically from the |authtoken|.
135 :param apiuser: This is filled automatically from the |authtoken|.
135 :type apiuser: AuthUser
136 :type apiuser: AuthUser
136
137
137 Example output:
138 Example output:
138
139
139 .. code-block:: bash
140 .. code-block:: bash
140
141
141 id : <id_given_in_input>
142 id : <id_given_in_input>
142 result: [<user_object>, ...]
143 result: [<user_object>, ...]
143 error: null
144 error: null
144 """
145 """
145
146
146 if not has_superadmin_permission(apiuser):
147 if not has_superadmin_permission(apiuser):
147 raise JSONRPCForbidden()
148 raise JSONRPCForbidden()
148
149
149 result = []
150 result = []
150 users_list = User.query().order_by(User.username) \
151 users_list = User.query().order_by(User.username) \
151 .filter(User.username != User.DEFAULT_USER) \
152 .filter(User.username != User.DEFAULT_USER) \
152 .all()
153 .all()
153 for user in users_list:
154 for user in users_list:
154 result.append(user.get_api_data(include_secrets=True))
155 result.append(user.get_api_data(include_secrets=True))
155 return result
156 return result
156
157
157
158
158 @jsonrpc_method()
159 @jsonrpc_method()
159 def create_user(request, apiuser, username, email, password=Optional(''),
160 def create_user(request, apiuser, username, email, password=Optional(''),
160 firstname=Optional(''), lastname=Optional(''),
161 firstname=Optional(''), lastname=Optional(''),
161 active=Optional(True), admin=Optional(False),
162 active=Optional(True), admin=Optional(False),
162 extern_name=Optional('rhodecode'),
163 extern_name=Optional('rhodecode'),
163 extern_type=Optional('rhodecode'),
164 extern_type=Optional('rhodecode'),
164 force_password_change=Optional(False),
165 force_password_change=Optional(False),
165 create_personal_repo_group=Optional(None)):
166 create_personal_repo_group=Optional(None)):
166 """
167 """
167 Creates a new user and returns the new user object.
168 Creates a new user and returns the new user object.
168
169
169 This command can only be run using an |authtoken| with admin rights to
170 This command can only be run using an |authtoken| with admin rights to
170 the specified repository.
171 the specified repository.
171
172
172 This command takes the following options:
173 This command takes the following options:
173
174
174 :param apiuser: This is filled automatically from the |authtoken|.
175 :param apiuser: This is filled automatically from the |authtoken|.
175 :type apiuser: AuthUser
176 :type apiuser: AuthUser
176 :param username: Set the new username.
177 :param username: Set the new username.
177 :type username: str or int
178 :type username: str or int
178 :param email: Set the user email address.
179 :param email: Set the user email address.
179 :type email: str
180 :type email: str
180 :param password: Set the new user password.
181 :param password: Set the new user password.
181 :type password: Optional(str)
182 :type password: Optional(str)
182 :param firstname: Set the new user firstname.
183 :param firstname: Set the new user firstname.
183 :type firstname: Optional(str)
184 :type firstname: Optional(str)
184 :param lastname: Set the new user surname.
185 :param lastname: Set the new user surname.
185 :type lastname: Optional(str)
186 :type lastname: Optional(str)
186 :param active: Set the user as active.
187 :param active: Set the user as active.
187 :type active: Optional(``True`` | ``False``)
188 :type active: Optional(``True`` | ``False``)
188 :param admin: Give the new user admin rights.
189 :param admin: Give the new user admin rights.
189 :type admin: Optional(``True`` | ``False``)
190 :type admin: Optional(``True`` | ``False``)
190 :param extern_name: Set the authentication plugin name.
191 :param extern_name: Set the authentication plugin name.
191 Using LDAP this is filled with LDAP UID.
192 Using LDAP this is filled with LDAP UID.
192 :type extern_name: Optional(str)
193 :type extern_name: Optional(str)
193 :param extern_type: Set the new user authentication plugin.
194 :param extern_type: Set the new user authentication plugin.
194 :type extern_type: Optional(str)
195 :type extern_type: Optional(str)
195 :param force_password_change: Force the new user to change password
196 :param force_password_change: Force the new user to change password
196 on next login.
197 on next login.
197 :type force_password_change: Optional(``True`` | ``False``)
198 :type force_password_change: Optional(``True`` | ``False``)
198 :param create_personal_repo_group: Create personal repo group for this user
199 :param create_personal_repo_group: Create personal repo group for this user
199 :type create_personal_repo_group: Optional(``True`` | ``False``)
200 :type create_personal_repo_group: Optional(``True`` | ``False``)
200
201
201 Example output:
202 Example output:
202
203
203 .. code-block:: bash
204 .. code-block:: bash
204
205
205 id : <id_given_in_input>
206 id : <id_given_in_input>
206 result: {
207 result: {
207 "msg" : "created new user `<username>`",
208 "msg" : "created new user `<username>`",
208 "user": <user_obj>
209 "user": <user_obj>
209 }
210 }
210 error: null
211 error: null
211
212
212 Example error output:
213 Example error output:
213
214
214 .. code-block:: bash
215 .. code-block:: bash
215
216
216 id : <id_given_in_input>
217 id : <id_given_in_input>
217 result : null
218 result : null
218 error : {
219 error : {
219 "user `<username>` already exist"
220 "user `<username>` already exist"
220 or
221 or
221 "email `<email>` already exist"
222 "email `<email>` already exist"
222 or
223 or
223 "failed to create user `<username>`"
224 "failed to create user `<username>`"
224 }
225 }
225
226
226 """
227 """
227 if not has_superadmin_permission(apiuser):
228 if not has_superadmin_permission(apiuser):
228 raise JSONRPCForbidden()
229 raise JSONRPCForbidden()
229
230
230 if UserModel().get_by_username(username):
231 if UserModel().get_by_username(username):
231 raise JSONRPCError("user `%s` already exist" % (username,))
232 raise JSONRPCError("user `%s` already exist" % (username,))
232
233
233 if UserModel().get_by_email(email, case_insensitive=True):
234 if UserModel().get_by_email(email, case_insensitive=True):
234 raise JSONRPCError("email `%s` already exist" % (email,))
235 raise JSONRPCError("email `%s` already exist" % (email,))
235
236
236 # generate random password if we actually given the
237 # generate random password if we actually given the
237 # extern_name and it's not rhodecode
238 # extern_name and it's not rhodecode
238 if (not isinstance(extern_name, Optional) and
239 if (not isinstance(extern_name, Optional) and
239 Optional.extract(extern_name) != 'rhodecode'):
240 Optional.extract(extern_name) != 'rhodecode'):
240 # generate temporary password if user is external
241 # generate temporary password if user is external
241 password = PasswordGenerator().gen_password(length=16)
242 password = PasswordGenerator().gen_password(length=16)
242 create_repo_group = Optional.extract(create_personal_repo_group)
243 create_repo_group = Optional.extract(create_personal_repo_group)
243 if isinstance(create_repo_group, basestring):
244 if isinstance(create_repo_group, basestring):
244 create_repo_group = str2bool(create_repo_group)
245 create_repo_group = str2bool(create_repo_group)
245
246
246 username = Optional.extract(username)
247 username = Optional.extract(username)
247 password = Optional.extract(password)
248 password = Optional.extract(password)
248 email = Optional.extract(email)
249 email = Optional.extract(email)
249 first_name = Optional.extract(firstname)
250 first_name = Optional.extract(firstname)
250 last_name = Optional.extract(lastname)
251 last_name = Optional.extract(lastname)
251 active = Optional.extract(active)
252 active = Optional.extract(active)
252 admin = Optional.extract(admin)
253 admin = Optional.extract(admin)
253 extern_type = Optional.extract(extern_type)
254 extern_type = Optional.extract(extern_type)
254 extern_name = Optional.extract(extern_name)
255 extern_name = Optional.extract(extern_name)
255
256
256 schema = user_schema.UserSchema().bind(
257 schema = user_schema.UserSchema().bind(
257 # user caller
258 # user caller
258 user=apiuser)
259 user=apiuser)
259 try:
260 try:
260 schema_data = schema.deserialize(dict(
261 schema_data = schema.deserialize(dict(
261 username=username,
262 username=username,
262 email=email,
263 email=email,
263 password=password,
264 password=password,
264 first_name=first_name,
265 first_name=first_name,
265 last_name=last_name,
266 last_name=last_name,
266 active=active,
267 active=active,
267 admin=admin,
268 admin=admin,
268 extern_type=extern_type,
269 extern_type=extern_type,
269 extern_name=extern_name,
270 extern_name=extern_name,
270 ))
271 ))
271 except validation_schema.Invalid as err:
272 except validation_schema.Invalid as err:
272 raise JSONRPCValidationError(colander_exc=err)
273 raise JSONRPCValidationError(colander_exc=err)
273
274
274 try:
275 try:
275 user = UserModel().create_or_update(
276 user = UserModel().create_or_update(
276 username=schema_data['username'],
277 username=schema_data['username'],
277 password=schema_data['password'],
278 password=schema_data['password'],
278 email=schema_data['email'],
279 email=schema_data['email'],
279 firstname=schema_data['first_name'],
280 firstname=schema_data['first_name'],
280 lastname=schema_data['last_name'],
281 lastname=schema_data['last_name'],
281 active=schema_data['active'],
282 active=schema_data['active'],
282 admin=schema_data['admin'],
283 admin=schema_data['admin'],
283 extern_type=schema_data['extern_type'],
284 extern_type=schema_data['extern_type'],
284 extern_name=schema_data['extern_name'],
285 extern_name=schema_data['extern_name'],
285 force_password_change=Optional.extract(force_password_change),
286 force_password_change=Optional.extract(force_password_change),
286 create_repo_group=create_repo_group
287 create_repo_group=create_repo_group
287 )
288 )
288 Session().flush()
289 Session().flush()
289 creation_data = user.get_api_data()
290 creation_data = user.get_api_data()
290 audit_logger.store_api(
291 audit_logger.store_api(
291 'user.create', action_data={'data': creation_data},
292 'user.create', action_data={'data': creation_data},
292 user=apiuser)
293 user=apiuser)
293
294
294 Session().commit()
295 Session().commit()
295 return {
296 return {
296 'msg': 'created new user `%s`' % username,
297 'msg': 'created new user `%s`' % username,
297 'user': user.get_api_data(include_secrets=True)
298 'user': user.get_api_data(include_secrets=True)
298 }
299 }
299 except Exception:
300 except Exception:
300 log.exception('Error occurred during creation of user')
301 log.exception('Error occurred during creation of user')
301 raise JSONRPCError('failed to create user `%s`' % (username,))
302 raise JSONRPCError('failed to create user `%s`' % (username,))
302
303
303
304
304 @jsonrpc_method()
305 @jsonrpc_method()
305 def update_user(request, apiuser, userid, username=Optional(None),
306 def update_user(request, apiuser, userid, username=Optional(None),
306 email=Optional(None), password=Optional(None),
307 email=Optional(None), password=Optional(None),
307 firstname=Optional(None), lastname=Optional(None),
308 firstname=Optional(None), lastname=Optional(None),
308 active=Optional(None), admin=Optional(None),
309 active=Optional(None), admin=Optional(None),
309 extern_type=Optional(None), extern_name=Optional(None), ):
310 extern_type=Optional(None), extern_name=Optional(None), ):
310 """
311 """
311 Updates the details for the specified user, if that user exists.
312 Updates the details for the specified user, if that user exists.
312
313
313 This command can only be run using an |authtoken| with admin rights to
314 This command can only be run using an |authtoken| with admin rights to
314 the specified repository.
315 the specified repository.
315
316
316 This command takes the following options:
317 This command takes the following options:
317
318
318 :param apiuser: This is filled automatically from |authtoken|.
319 :param apiuser: This is filled automatically from |authtoken|.
319 :type apiuser: AuthUser
320 :type apiuser: AuthUser
320 :param userid: Set the ``userid`` to update.
321 :param userid: Set the ``userid`` to update.
321 :type userid: str or int
322 :type userid: str or int
322 :param username: Set the new username.
323 :param username: Set the new username.
323 :type username: str or int
324 :type username: str or int
324 :param email: Set the new email.
325 :param email: Set the new email.
325 :type email: str
326 :type email: str
326 :param password: Set the new password.
327 :param password: Set the new password.
327 :type password: Optional(str)
328 :type password: Optional(str)
328 :param firstname: Set the new first name.
329 :param firstname: Set the new first name.
329 :type firstname: Optional(str)
330 :type firstname: Optional(str)
330 :param lastname: Set the new surname.
331 :param lastname: Set the new surname.
331 :type lastname: Optional(str)
332 :type lastname: Optional(str)
332 :param active: Set the new user as active.
333 :param active: Set the new user as active.
333 :type active: Optional(``True`` | ``False``)
334 :type active: Optional(``True`` | ``False``)
334 :param admin: Give the user admin rights.
335 :param admin: Give the user admin rights.
335 :type admin: Optional(``True`` | ``False``)
336 :type admin: Optional(``True`` | ``False``)
336 :param extern_name: Set the authentication plugin user name.
337 :param extern_name: Set the authentication plugin user name.
337 Using LDAP this is filled with LDAP UID.
338 Using LDAP this is filled with LDAP UID.
338 :type extern_name: Optional(str)
339 :type extern_name: Optional(str)
339 :param extern_type: Set the authentication plugin type.
340 :param extern_type: Set the authentication plugin type.
340 :type extern_type: Optional(str)
341 :type extern_type: Optional(str)
341
342
342
343
343 Example output:
344 Example output:
344
345
345 .. code-block:: bash
346 .. code-block:: bash
346
347
347 id : <id_given_in_input>
348 id : <id_given_in_input>
348 result: {
349 result: {
349 "msg" : "updated user ID:<userid> <username>",
350 "msg" : "updated user ID:<userid> <username>",
350 "user": <user_object>,
351 "user": <user_object>,
351 }
352 }
352 error: null
353 error: null
353
354
354 Example error output:
355 Example error output:
355
356
356 .. code-block:: bash
357 .. code-block:: bash
357
358
358 id : <id_given_in_input>
359 id : <id_given_in_input>
359 result : null
360 result : null
360 error : {
361 error : {
361 "failed to update user `<username>`"
362 "failed to update user `<username>`"
362 }
363 }
363
364
364 """
365 """
365 if not has_superadmin_permission(apiuser):
366 if not has_superadmin_permission(apiuser):
366 raise JSONRPCForbidden()
367 raise JSONRPCForbidden()
367
368
368 user = get_user_or_error(userid)
369 user = get_user_or_error(userid)
369 old_data = user.get_api_data()
370 old_data = user.get_api_data()
370 # only non optional arguments will be stored in updates
371 # only non optional arguments will be stored in updates
371 updates = {}
372 updates = {}
372
373
373 try:
374 try:
374
375
375 store_update(updates, username, 'username')
376 store_update(updates, username, 'username')
376 store_update(updates, password, 'password')
377 store_update(updates, password, 'password')
377 store_update(updates, email, 'email')
378 store_update(updates, email, 'email')
378 store_update(updates, firstname, 'name')
379 store_update(updates, firstname, 'name')
379 store_update(updates, lastname, 'lastname')
380 store_update(updates, lastname, 'lastname')
380 store_update(updates, active, 'active')
381 store_update(updates, active, 'active')
381 store_update(updates, admin, 'admin')
382 store_update(updates, admin, 'admin')
382 store_update(updates, extern_name, 'extern_name')
383 store_update(updates, extern_name, 'extern_name')
383 store_update(updates, extern_type, 'extern_type')
384 store_update(updates, extern_type, 'extern_type')
384
385
385 user = UserModel().update_user(user, **updates)
386 user = UserModel().update_user(user, **updates)
386 audit_logger.store_api(
387 audit_logger.store_api(
387 'user.edit', action_data={'old_data': old_data},
388 'user.edit', action_data={'old_data': old_data},
388 user=apiuser)
389 user=apiuser)
389 Session().commit()
390 Session().commit()
390 return {
391 return {
391 'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
392 'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
392 'user': user.get_api_data(include_secrets=True)
393 'user': user.get_api_data(include_secrets=True)
393 }
394 }
394 except DefaultUserException:
395 except DefaultUserException:
395 log.exception("Default user edit exception")
396 log.exception("Default user edit exception")
396 raise JSONRPCError('editing default user is forbidden')
397 raise JSONRPCError('editing default user is forbidden')
397 except Exception:
398 except Exception:
398 log.exception("Error occurred during update of user")
399 log.exception("Error occurred during update of user")
399 raise JSONRPCError('failed to update user `%s`' % (userid,))
400 raise JSONRPCError('failed to update user `%s`' % (userid,))
400
401
401
402
402 @jsonrpc_method()
403 @jsonrpc_method()
403 def delete_user(request, apiuser, userid):
404 def delete_user(request, apiuser, userid):
404 """
405 """
405 Deletes the specified user from the |RCE| user database.
406 Deletes the specified user from the |RCE| user database.
406
407
407 This command can only be run using an |authtoken| with admin rights to
408 This command can only be run using an |authtoken| with admin rights to
408 the specified repository.
409 the specified repository.
409
410
410 .. important::
411 .. important::
411
412
412 Ensure all open pull requests and open code review
413 Ensure all open pull requests and open code review
413 requests to this user are close.
414 requests to this user are close.
414
415
415 Also ensure all repositories, or repository groups owned by this
416 Also ensure all repositories, or repository groups owned by this
416 user are reassigned before deletion.
417 user are reassigned before deletion.
417
418
418 This command takes the following options:
419 This command takes the following options:
419
420
420 :param apiuser: This is filled automatically from the |authtoken|.
421 :param apiuser: This is filled automatically from the |authtoken|.
421 :type apiuser: AuthUser
422 :type apiuser: AuthUser
422 :param userid: Set the user to delete.
423 :param userid: Set the user to delete.
423 :type userid: str or int
424 :type userid: str or int
424
425
425 Example output:
426 Example output:
426
427
427 .. code-block:: bash
428 .. code-block:: bash
428
429
429 id : <id_given_in_input>
430 id : <id_given_in_input>
430 result: {
431 result: {
431 "msg" : "deleted user ID:<userid> <username>",
432 "msg" : "deleted user ID:<userid> <username>",
432 "user": null
433 "user": null
433 }
434 }
434 error: null
435 error: null
435
436
436 Example error output:
437 Example error output:
437
438
438 .. code-block:: bash
439 .. code-block:: bash
439
440
440 id : <id_given_in_input>
441 id : <id_given_in_input>
441 result : null
442 result : null
442 error : {
443 error : {
443 "failed to delete user ID:<userid> <username>"
444 "failed to delete user ID:<userid> <username>"
444 }
445 }
445
446
446 """
447 """
447 if not has_superadmin_permission(apiuser):
448 if not has_superadmin_permission(apiuser):
448 raise JSONRPCForbidden()
449 raise JSONRPCForbidden()
449
450
450 user = get_user_or_error(userid)
451 user = get_user_or_error(userid)
451 old_data = user.get_api_data()
452 old_data = user.get_api_data()
452 try:
453 try:
453 UserModel().delete(userid)
454 UserModel().delete(userid)
454 audit_logger.store_api(
455 audit_logger.store_api(
455 'user.delete', action_data={'old_data': old_data},
456 'user.delete', action_data={'old_data': old_data},
456 user=apiuser)
457 user=apiuser)
457
458
458 Session().commit()
459 Session().commit()
459 return {
460 return {
460 'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),
461 'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),
461 'user': None
462 'user': None
462 }
463 }
463 except Exception:
464 except Exception:
464 log.exception("Error occurred during deleting of user")
465 log.exception("Error occurred during deleting of user")
465 raise JSONRPCError(
466 raise JSONRPCError(
466 'failed to delete user ID:%s %s' % (user.user_id, user.username))
467 'failed to delete user ID:%s %s' % (user.user_id, user.username))
467
468
468
469
469 @jsonrpc_method()
470 @jsonrpc_method()
470 def get_user_locks(request, apiuser, userid=Optional(OAttr('apiuser'))):
471 def get_user_locks(request, apiuser, userid=Optional(OAttr('apiuser'))):
471 """
472 """
472 Displays all repositories locked by the specified user.
473 Displays all repositories locked by the specified user.
473
474
474 * If this command is run by a non-admin user, it returns
475 * If this command is run by a non-admin user, it returns
475 a list of |repos| locked by that user.
476 a list of |repos| locked by that user.
476
477
477 This command takes the following options:
478 This command takes the following options:
478
479
479 :param apiuser: This is filled automatically from the |authtoken|.
480 :param apiuser: This is filled automatically from the |authtoken|.
480 :type apiuser: AuthUser
481 :type apiuser: AuthUser
481 :param userid: Sets the userid whose list of locked |repos| will be
482 :param userid: Sets the userid whose list of locked |repos| will be
482 displayed.
483 displayed.
483 :type userid: Optional(str or int)
484 :type userid: Optional(str or int)
484
485
485 Example output:
486 Example output:
486
487
487 .. code-block:: bash
488 .. code-block:: bash
488
489
489 id : <id_given_in_input>
490 id : <id_given_in_input>
490 result : {
491 result : {
491 [repo_object, repo_object,...]
492 [repo_object, repo_object,...]
492 }
493 }
493 error : null
494 error : null
494 """
495 """
495
496
496 include_secrets = False
497 include_secrets = False
497 if not has_superadmin_permission(apiuser):
498 if not has_superadmin_permission(apiuser):
498 # make sure normal user does not pass someone else userid,
499 # make sure normal user does not pass someone else userid,
499 # he is not allowed to do that
500 # he is not allowed to do that
500 if not isinstance(userid, Optional) and userid != apiuser.user_id:
501 if not isinstance(userid, Optional) and userid != apiuser.user_id:
501 raise JSONRPCError('userid is not the same as your user')
502 raise JSONRPCError('userid is not the same as your user')
502 else:
503 else:
503 include_secrets = True
504 include_secrets = True
504
505
505 userid = Optional.extract(userid, evaluate_locals=locals())
506 userid = Optional.extract(userid, evaluate_locals=locals())
506 userid = getattr(userid, 'user_id', userid)
507 userid = getattr(userid, 'user_id', userid)
507 user = get_user_or_error(userid)
508 user = get_user_or_error(userid)
508
509
509 ret = []
510 ret = []
510
511
511 # show all locks
512 # show all locks
512 for r in Repository.getAll():
513 for r in Repository.getAll():
513 _user_id, _time, _reason = r.locked
514 _user_id, _time, _reason = r.locked
514 if _user_id and _time:
515 if _user_id and _time:
515 _api_data = r.get_api_data(include_secrets=include_secrets)
516 _api_data = r.get_api_data(include_secrets=include_secrets)
516 # if we use user filter just show the locks for this user
517 # if we use user filter just show the locks for this user
517 if safe_int(_user_id) == user.user_id:
518 if safe_int(_user_id) == user.user_id:
518 ret.append(_api_data)
519 ret.append(_api_data)
519
520
520 return ret
521 return ret
521
522
522
523
523 @jsonrpc_method()
524 @jsonrpc_method()
524 def get_user_audit_logs(request, apiuser, userid=Optional(OAttr('apiuser'))):
525 def get_user_audit_logs(request, apiuser, userid=Optional(OAttr('apiuser'))):
525 """
526 """
526 Fetches all action logs made by the specified user.
527 Fetches all action logs made by the specified user.
527
528
528 This command takes the following options:
529 This command takes the following options:
529
530
530 :param apiuser: This is filled automatically from the |authtoken|.
531 :param apiuser: This is filled automatically from the |authtoken|.
531 :type apiuser: AuthUser
532 :type apiuser: AuthUser
532 :param userid: Sets the userid whose list of locked |repos| will be
533 :param userid: Sets the userid whose list of locked |repos| will be
533 displayed.
534 displayed.
534 :type userid: Optional(str or int)
535 :type userid: Optional(str or int)
535
536
536 Example output:
537 Example output:
537
538
538 .. code-block:: bash
539 .. code-block:: bash
539
540
540 id : <id_given_in_input>
541 id : <id_given_in_input>
541 result : {
542 result : {
542 [action, action,...]
543 [action, action,...]
543 }
544 }
544 error : null
545 error : null
545 """
546 """
546
547
547 if not has_superadmin_permission(apiuser):
548 if not has_superadmin_permission(apiuser):
548 # make sure normal user does not pass someone else userid,
549 # make sure normal user does not pass someone else userid,
549 # he is not allowed to do that
550 # he is not allowed to do that
550 if not isinstance(userid, Optional) and userid != apiuser.user_id:
551 if not isinstance(userid, Optional) and userid != apiuser.user_id:
551 raise JSONRPCError('userid is not the same as your user')
552 raise JSONRPCError('userid is not the same as your user')
552
553
553 userid = Optional.extract(userid, evaluate_locals=locals())
554 userid = Optional.extract(userid, evaluate_locals=locals())
554 userid = getattr(userid, 'user_id', userid)
555 userid = getattr(userid, 'user_id', userid)
555 user = get_user_or_error(userid)
556 user = get_user_or_error(userid)
556
557
557 ret = []
558 ret = []
558
559
559 # show all user actions
560 # show all user actions
560 for entry in UserModel().get_user_log(user, filter_term=None):
561 for entry in UserModel().get_user_log(user, filter_term=None):
561 ret.append(entry)
562 ret.append(entry)
562 return ret
563 return ret
@@ -1,823 +1,829 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 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
22
23 from rhodecode.api import (
23 from rhodecode.api import (
24 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
24 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
25 from rhodecode.api.utils import (
25 from rhodecode.api.utils import (
26 Optional, OAttr, store_update, has_superadmin_permission, get_origin,
26 Optional, OAttr, store_update, has_superadmin_permission, get_origin,
27 get_user_or_error, get_user_group_or_error, get_perm_or_error)
27 get_user_or_error, get_user_group_or_error, get_perm_or_error)
28 from rhodecode.lib import audit_logger
28 from rhodecode.lib import audit_logger
29 from rhodecode.lib.auth import HasUserGroupPermissionAnyApi, HasPermissionAnyApi
29 from rhodecode.lib.auth import HasUserGroupPermissionAnyApi, HasPermissionAnyApi
30 from rhodecode.lib.exceptions import UserGroupAssignedException
30 from rhodecode.lib.exceptions import UserGroupAssignedException
31 from rhodecode.model.db import Session
31 from rhodecode.model.db import Session
32 from rhodecode.model.scm import UserGroupList
32 from rhodecode.model.scm import UserGroupList
33 from rhodecode.model.user_group import UserGroupModel
33 from rhodecode.model.user_group import UserGroupModel
34 from rhodecode.model import validation_schema
34 from rhodecode.model import validation_schema
35 from rhodecode.model.validation_schema.schemas import user_group_schema
35 from rhodecode.model.validation_schema.schemas import user_group_schema
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39
39
40 @jsonrpc_method()
40 @jsonrpc_method()
41 def get_user_group(request, apiuser, usergroupid):
41 def get_user_group(request, apiuser, usergroupid):
42 """
42 """
43 Returns the data of an existing user group.
43 Returns the data of an existing user group.
44
44
45 This command can only be run using an |authtoken| with admin rights to
45 This command can only be run using an |authtoken| with admin rights to
46 the specified repository.
46 the specified repository.
47
47
48 :param apiuser: This is filled automatically from the |authtoken|.
48 :param apiuser: This is filled automatically from the |authtoken|.
49 :type apiuser: AuthUser
49 :type apiuser: AuthUser
50 :param usergroupid: Set the user group from which to return data.
50 :param usergroupid: Set the user group from which to return data.
51 :type usergroupid: str or int
51 :type usergroupid: str or int
52
52
53 Example error output:
53 Example error output:
54
54
55 .. code-block:: bash
55 .. code-block:: bash
56
56
57 {
57 {
58 "error": null,
58 "error": null,
59 "id": <id>,
59 "id": <id>,
60 "result": {
60 "result": {
61 "active": true,
61 "active": true,
62 "group_description": "group description",
62 "group_description": "group description",
63 "group_name": "group name",
63 "group_name": "group name",
64 "members": [
64 "permissions": [
65 {
65 {
66 "name": "owner-name",
66 "name": "owner-name",
67 "origin": "owner",
67 "origin": "owner",
68 "permission": "usergroup.admin",
68 "permission": "usergroup.admin",
69 "type": "user"
69 "type": "user"
70 },
70 },
71 {
71 {
72 {
72 {
73 "name": "user name",
73 "name": "user name",
74 "origin": "permission",
74 "origin": "permission",
75 "permission": "usergroup.admin",
75 "permission": "usergroup.admin",
76 "type": "user"
76 "type": "user"
77 },
77 },
78 {
78 {
79 "name": "user group name",
79 "name": "user group name",
80 "origin": "permission",
80 "origin": "permission",
81 "permission": "usergroup.write",
81 "permission": "usergroup.write",
82 "type": "user_group"
82 "type": "user_group"
83 }
83 }
84 ],
84 ],
85 "permissions_summary": {
86 "repositories": {
87 "aa-root-level-repo-1": "repository.admin"
88 },
89 "repositories_groups": {}
90 },
85 "owner": "owner name",
91 "owner": "owner name",
86 "users": [],
92 "users": [],
87 "users_group_id": 2
93 "users_group_id": 2
88 }
94 }
89 }
95 }
90
96
91 """
97 """
92
98
93 user_group = get_user_group_or_error(usergroupid)
99 user_group = get_user_group_or_error(usergroupid)
94 if not has_superadmin_permission(apiuser):
100 if not has_superadmin_permission(apiuser):
95 # check if we have at least read permission for this user group !
101 # check if we have at least read permission for this user group !
96 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
102 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
97 if not HasUserGroupPermissionAnyApi(*_perms)(
103 if not HasUserGroupPermissionAnyApi(*_perms)(
98 user=apiuser, user_group_name=user_group.users_group_name):
104 user=apiuser, user_group_name=user_group.users_group_name):
99 raise JSONRPCError('user group `%s` does not exist' % (
105 raise JSONRPCError('user group `%s` does not exist' % (
100 usergroupid,))
106 usergroupid,))
101
107
102 permissions = []
108 permissions = []
103 for _user in user_group.permissions():
109 for _user in user_group.permissions():
104 user_data = {
110 user_data = {
105 'name': _user.username,
111 'name': _user.username,
106 'permission': _user.permission,
112 'permission': _user.permission,
107 'origin': get_origin(_user),
113 'origin': get_origin(_user),
108 'type': "user",
114 'type': "user",
109 }
115 }
110 permissions.append(user_data)
116 permissions.append(user_data)
111
117
112 for _user_group in user_group.permission_user_groups():
118 for _user_group in user_group.permission_user_groups():
113 user_group_data = {
119 user_group_data = {
114 'name': _user_group.users_group_name,
120 'name': _user_group.users_group_name,
115 'permission': _user_group.permission,
121 'permission': _user_group.permission,
116 'origin': get_origin(_user_group),
122 'origin': get_origin(_user_group),
117 'type': "user_group",
123 'type': "user_group",
118 }
124 }
119 permissions.append(user_group_data)
125 permissions.append(user_group_data)
120
126
121 data = user_group.get_api_data()
127 data = user_group.get_api_data()
122 data["permissions"] = permissions
128 data["permissions"] = permissions
123 data["Permissions_summary"] = UserGroupModel().get_perms_summary(
129 data["permissions_summary"] = UserGroupModel().get_perms_summary(
124 user_group.users_group_id)
130 user_group.users_group_id)
125 return data
131 return data
126
132
127
133
128 @jsonrpc_method()
134 @jsonrpc_method()
129 def get_user_groups(request, apiuser):
135 def get_user_groups(request, apiuser):
130 """
136 """
131 Lists all the existing user groups within RhodeCode.
137 Lists all the existing user groups within RhodeCode.
132
138
133 This command can only be run using an |authtoken| with admin rights to
139 This command can only be run using an |authtoken| with admin rights to
134 the specified repository.
140 the specified repository.
135
141
136 This command takes the following options:
142 This command takes the following options:
137
143
138 :param apiuser: This is filled automatically from the |authtoken|.
144 :param apiuser: This is filled automatically from the |authtoken|.
139 :type apiuser: AuthUser
145 :type apiuser: AuthUser
140
146
141 Example error output:
147 Example error output:
142
148
143 .. code-block:: bash
149 .. code-block:: bash
144
150
145 id : <id_given_in_input>
151 id : <id_given_in_input>
146 result : [<user_group_obj>,...]
152 result : [<user_group_obj>,...]
147 error : null
153 error : null
148 """
154 """
149
155
150 include_secrets = has_superadmin_permission(apiuser)
156 include_secrets = has_superadmin_permission(apiuser)
151
157
152 result = []
158 result = []
153 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
159 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
154 extras = {'user': apiuser}
160 extras = {'user': apiuser}
155 for user_group in UserGroupList(UserGroupModel().get_all(),
161 for user_group in UserGroupList(UserGroupModel().get_all(),
156 perm_set=_perms, extra_kwargs=extras):
162 perm_set=_perms, extra_kwargs=extras):
157 result.append(
163 result.append(
158 user_group.get_api_data(include_secrets=include_secrets))
164 user_group.get_api_data(include_secrets=include_secrets))
159 return result
165 return result
160
166
161
167
162 @jsonrpc_method()
168 @jsonrpc_method()
163 def create_user_group(
169 def create_user_group(
164 request, apiuser, group_name, description=Optional(''),
170 request, apiuser, group_name, description=Optional(''),
165 owner=Optional(OAttr('apiuser')), active=Optional(True)):
171 owner=Optional(OAttr('apiuser')), active=Optional(True)):
166 """
172 """
167 Creates a new user group.
173 Creates a new user group.
168
174
169 This command can only be run using an |authtoken| with admin rights to
175 This command can only be run using an |authtoken| with admin rights to
170 the specified repository.
176 the specified repository.
171
177
172 This command takes the following options:
178 This command takes the following options:
173
179
174 :param apiuser: This is filled automatically from the |authtoken|.
180 :param apiuser: This is filled automatically from the |authtoken|.
175 :type apiuser: AuthUser
181 :type apiuser: AuthUser
176 :param group_name: Set the name of the new user group.
182 :param group_name: Set the name of the new user group.
177 :type group_name: str
183 :type group_name: str
178 :param description: Give a description of the new user group.
184 :param description: Give a description of the new user group.
179 :type description: str
185 :type description: str
180 :param owner: Set the owner of the new user group.
186 :param owner: Set the owner of the new user group.
181 If not set, the owner is the |authtoken| user.
187 If not set, the owner is the |authtoken| user.
182 :type owner: Optional(str or int)
188 :type owner: Optional(str or int)
183 :param active: Set this group as active.
189 :param active: Set this group as active.
184 :type active: Optional(``True`` | ``False``)
190 :type active: Optional(``True`` | ``False``)
185
191
186 Example output:
192 Example output:
187
193
188 .. code-block:: bash
194 .. code-block:: bash
189
195
190 id : <id_given_in_input>
196 id : <id_given_in_input>
191 result: {
197 result: {
192 "msg": "created new user group `<groupname>`",
198 "msg": "created new user group `<groupname>`",
193 "user_group": <user_group_object>
199 "user_group": <user_group_object>
194 }
200 }
195 error: null
201 error: null
196
202
197 Example error output:
203 Example error output:
198
204
199 .. code-block:: bash
205 .. code-block:: bash
200
206
201 id : <id_given_in_input>
207 id : <id_given_in_input>
202 result : null
208 result : null
203 error : {
209 error : {
204 "user group `<group name>` already exist"
210 "user group `<group name>` already exist"
205 or
211 or
206 "failed to create group `<group name>`"
212 "failed to create group `<group name>`"
207 }
213 }
208
214
209 """
215 """
210
216
211 if not has_superadmin_permission(apiuser):
217 if not has_superadmin_permission(apiuser):
212 if not HasPermissionAnyApi('hg.usergroup.create.true')(user=apiuser):
218 if not HasPermissionAnyApi('hg.usergroup.create.true')(user=apiuser):
213 raise JSONRPCForbidden()
219 raise JSONRPCForbidden()
214
220
215 if UserGroupModel().get_by_name(group_name):
221 if UserGroupModel().get_by_name(group_name):
216 raise JSONRPCError("user group `%s` already exist" % (group_name,))
222 raise JSONRPCError("user group `%s` already exist" % (group_name,))
217
223
218 if isinstance(owner, Optional):
224 if isinstance(owner, Optional):
219 owner = apiuser.user_id
225 owner = apiuser.user_id
220
226
221 owner = get_user_or_error(owner)
227 owner = get_user_or_error(owner)
222 active = Optional.extract(active)
228 active = Optional.extract(active)
223 description = Optional.extract(description)
229 description = Optional.extract(description)
224
230
225 schema = user_group_schema.UserGroupSchema().bind(
231 schema = user_group_schema.UserGroupSchema().bind(
226 # user caller
232 # user caller
227 user=apiuser)
233 user=apiuser)
228 try:
234 try:
229 schema_data = schema.deserialize(dict(
235 schema_data = schema.deserialize(dict(
230 user_group_name=group_name,
236 user_group_name=group_name,
231 user_group_description=description,
237 user_group_description=description,
232 user_group_owner=owner.username,
238 user_group_owner=owner.username,
233 user_group_active=active,
239 user_group_active=active,
234 ))
240 ))
235 except validation_schema.Invalid as err:
241 except validation_schema.Invalid as err:
236 raise JSONRPCValidationError(colander_exc=err)
242 raise JSONRPCValidationError(colander_exc=err)
237
243
238 try:
244 try:
239 user_group = UserGroupModel().create(
245 user_group = UserGroupModel().create(
240 name=schema_data['user_group_name'],
246 name=schema_data['user_group_name'],
241 description=schema_data['user_group_description'],
247 description=schema_data['user_group_description'],
242 owner=owner,
248 owner=owner,
243 active=schema_data['user_group_active'])
249 active=schema_data['user_group_active'])
244 Session().flush()
250 Session().flush()
245 creation_data = user_group.get_api_data()
251 creation_data = user_group.get_api_data()
246 audit_logger.store_api(
252 audit_logger.store_api(
247 'user_group.create', action_data={'data': creation_data},
253 'user_group.create', action_data={'data': creation_data},
248 user=apiuser)
254 user=apiuser)
249 Session().commit()
255 Session().commit()
250 return {
256 return {
251 'msg': 'created new user group `%s`' % group_name,
257 'msg': 'created new user group `%s`' % group_name,
252 'user_group': creation_data
258 'user_group': creation_data
253 }
259 }
254 except Exception:
260 except Exception:
255 log.exception("Error occurred during creation of user group")
261 log.exception("Error occurred during creation of user group")
256 raise JSONRPCError('failed to create group `%s`' % (group_name,))
262 raise JSONRPCError('failed to create group `%s`' % (group_name,))
257
263
258
264
259 @jsonrpc_method()
265 @jsonrpc_method()
260 def update_user_group(request, apiuser, usergroupid, group_name=Optional(''),
266 def update_user_group(request, apiuser, usergroupid, group_name=Optional(''),
261 description=Optional(''), owner=Optional(None),
267 description=Optional(''), owner=Optional(None),
262 active=Optional(True)):
268 active=Optional(True)):
263 """
269 """
264 Updates the specified `user group` with the details provided.
270 Updates the specified `user group` with the details provided.
265
271
266 This command can only be run using an |authtoken| with admin rights to
272 This command can only be run using an |authtoken| with admin rights to
267 the specified repository.
273 the specified repository.
268
274
269 :param apiuser: This is filled automatically from the |authtoken|.
275 :param apiuser: This is filled automatically from the |authtoken|.
270 :type apiuser: AuthUser
276 :type apiuser: AuthUser
271 :param usergroupid: Set the id of the `user group` to update.
277 :param usergroupid: Set the id of the `user group` to update.
272 :type usergroupid: str or int
278 :type usergroupid: str or int
273 :param group_name: Set the new name the `user group`
279 :param group_name: Set the new name the `user group`
274 :type group_name: str
280 :type group_name: str
275 :param description: Give a description for the `user group`
281 :param description: Give a description for the `user group`
276 :type description: str
282 :type description: str
277 :param owner: Set the owner of the `user group`.
283 :param owner: Set the owner of the `user group`.
278 :type owner: Optional(str or int)
284 :type owner: Optional(str or int)
279 :param active: Set the group as active.
285 :param active: Set the group as active.
280 :type active: Optional(``True`` | ``False``)
286 :type active: Optional(``True`` | ``False``)
281
287
282 Example output:
288 Example output:
283
289
284 .. code-block:: bash
290 .. code-block:: bash
285
291
286 id : <id_given_in_input>
292 id : <id_given_in_input>
287 result : {
293 result : {
288 "msg": 'updated user group ID:<user group id> <user group name>',
294 "msg": 'updated user group ID:<user group id> <user group name>',
289 "user_group": <user_group_object>
295 "user_group": <user_group_object>
290 }
296 }
291 error : null
297 error : null
292
298
293 Example error output:
299 Example error output:
294
300
295 .. code-block:: bash
301 .. code-block:: bash
296
302
297 id : <id_given_in_input>
303 id : <id_given_in_input>
298 result : null
304 result : null
299 error : {
305 error : {
300 "failed to update user group `<user group name>`"
306 "failed to update user group `<user group name>`"
301 }
307 }
302
308
303 """
309 """
304
310
305 user_group = get_user_group_or_error(usergroupid)
311 user_group = get_user_group_or_error(usergroupid)
306 include_secrets = False
312 include_secrets = False
307 if not has_superadmin_permission(apiuser):
313 if not has_superadmin_permission(apiuser):
308 # check if we have admin permission for this user group !
314 # check if we have admin permission for this user group !
309 _perms = ('usergroup.admin',)
315 _perms = ('usergroup.admin',)
310 if not HasUserGroupPermissionAnyApi(*_perms)(
316 if not HasUserGroupPermissionAnyApi(*_perms)(
311 user=apiuser, user_group_name=user_group.users_group_name):
317 user=apiuser, user_group_name=user_group.users_group_name):
312 raise JSONRPCError(
318 raise JSONRPCError(
313 'user group `%s` does not exist' % (usergroupid,))
319 'user group `%s` does not exist' % (usergroupid,))
314 else:
320 else:
315 include_secrets = True
321 include_secrets = True
316
322
317 if not isinstance(owner, Optional):
323 if not isinstance(owner, Optional):
318 owner = get_user_or_error(owner)
324 owner = get_user_or_error(owner)
319
325
320 old_data = user_group.get_api_data()
326 old_data = user_group.get_api_data()
321 updates = {}
327 updates = {}
322 store_update(updates, group_name, 'users_group_name')
328 store_update(updates, group_name, 'users_group_name')
323 store_update(updates, description, 'user_group_description')
329 store_update(updates, description, 'user_group_description')
324 store_update(updates, owner, 'user')
330 store_update(updates, owner, 'user')
325 store_update(updates, active, 'users_group_active')
331 store_update(updates, active, 'users_group_active')
326 try:
332 try:
327 UserGroupModel().update(user_group, updates)
333 UserGroupModel().update(user_group, updates)
328 audit_logger.store_api(
334 audit_logger.store_api(
329 'user_group.edit', action_data={'old_data': old_data},
335 'user_group.edit', action_data={'old_data': old_data},
330 user=apiuser)
336 user=apiuser)
331 Session().commit()
337 Session().commit()
332 return {
338 return {
333 'msg': 'updated user group ID:%s %s' % (
339 'msg': 'updated user group ID:%s %s' % (
334 user_group.users_group_id, user_group.users_group_name),
340 user_group.users_group_id, user_group.users_group_name),
335 'user_group': user_group.get_api_data(
341 'user_group': user_group.get_api_data(
336 include_secrets=include_secrets)
342 include_secrets=include_secrets)
337 }
343 }
338 except Exception:
344 except Exception:
339 log.exception("Error occurred during update of user group")
345 log.exception("Error occurred during update of user group")
340 raise JSONRPCError(
346 raise JSONRPCError(
341 'failed to update user group `%s`' % (usergroupid,))
347 'failed to update user group `%s`' % (usergroupid,))
342
348
343
349
344 @jsonrpc_method()
350 @jsonrpc_method()
345 def delete_user_group(request, apiuser, usergroupid):
351 def delete_user_group(request, apiuser, usergroupid):
346 """
352 """
347 Deletes the specified `user group`.
353 Deletes the specified `user group`.
348
354
349 This command can only be run using an |authtoken| with admin rights to
355 This command can only be run using an |authtoken| with admin rights to
350 the specified repository.
356 the specified repository.
351
357
352 This command takes the following options:
358 This command takes the following options:
353
359
354 :param apiuser: filled automatically from apikey
360 :param apiuser: filled automatically from apikey
355 :type apiuser: AuthUser
361 :type apiuser: AuthUser
356 :param usergroupid:
362 :param usergroupid:
357 :type usergroupid: int
363 :type usergroupid: int
358
364
359 Example output:
365 Example output:
360
366
361 .. code-block:: bash
367 .. code-block:: bash
362
368
363 id : <id_given_in_input>
369 id : <id_given_in_input>
364 result : {
370 result : {
365 "msg": "deleted user group ID:<user_group_id> <user_group_name>"
371 "msg": "deleted user group ID:<user_group_id> <user_group_name>"
366 }
372 }
367 error : null
373 error : null
368
374
369 Example error output:
375 Example error output:
370
376
371 .. code-block:: bash
377 .. code-block:: bash
372
378
373 id : <id_given_in_input>
379 id : <id_given_in_input>
374 result : null
380 result : null
375 error : {
381 error : {
376 "failed to delete user group ID:<user_group_id> <user_group_name>"
382 "failed to delete user group ID:<user_group_id> <user_group_name>"
377 or
383 or
378 "RepoGroup assigned to <repo_groups_list>"
384 "RepoGroup assigned to <repo_groups_list>"
379 }
385 }
380
386
381 """
387 """
382
388
383 user_group = get_user_group_or_error(usergroupid)
389 user_group = get_user_group_or_error(usergroupid)
384 if not has_superadmin_permission(apiuser):
390 if not has_superadmin_permission(apiuser):
385 # check if we have admin permission for this user group !
391 # check if we have admin permission for this user group !
386 _perms = ('usergroup.admin',)
392 _perms = ('usergroup.admin',)
387 if not HasUserGroupPermissionAnyApi(*_perms)(
393 if not HasUserGroupPermissionAnyApi(*_perms)(
388 user=apiuser, user_group_name=user_group.users_group_name):
394 user=apiuser, user_group_name=user_group.users_group_name):
389 raise JSONRPCError(
395 raise JSONRPCError(
390 'user group `%s` does not exist' % (usergroupid,))
396 'user group `%s` does not exist' % (usergroupid,))
391
397
392 old_data = user_group.get_api_data()
398 old_data = user_group.get_api_data()
393 try:
399 try:
394 UserGroupModel().delete(user_group)
400 UserGroupModel().delete(user_group)
395 audit_logger.store_api(
401 audit_logger.store_api(
396 'user_group.delete', action_data={'old_data': old_data},
402 'user_group.delete', action_data={'old_data': old_data},
397 user=apiuser)
403 user=apiuser)
398 Session().commit()
404 Session().commit()
399 return {
405 return {
400 'msg': 'deleted user group ID:%s %s' % (
406 'msg': 'deleted user group ID:%s %s' % (
401 user_group.users_group_id, user_group.users_group_name),
407 user_group.users_group_id, user_group.users_group_name),
402 'user_group': None
408 'user_group': None
403 }
409 }
404 except UserGroupAssignedException as e:
410 except UserGroupAssignedException as e:
405 log.exception("UserGroupAssigned error")
411 log.exception("UserGroupAssigned error")
406 raise JSONRPCError(str(e))
412 raise JSONRPCError(str(e))
407 except Exception:
413 except Exception:
408 log.exception("Error occurred during deletion of user group")
414 log.exception("Error occurred during deletion of user group")
409 raise JSONRPCError(
415 raise JSONRPCError(
410 'failed to delete user group ID:%s %s' %(
416 'failed to delete user group ID:%s %s' %(
411 user_group.users_group_id, user_group.users_group_name))
417 user_group.users_group_id, user_group.users_group_name))
412
418
413
419
414 @jsonrpc_method()
420 @jsonrpc_method()
415 def add_user_to_user_group(request, apiuser, usergroupid, userid):
421 def add_user_to_user_group(request, apiuser, usergroupid, userid):
416 """
422 """
417 Adds a user to a `user group`. If the user already exists in the group
423 Adds a user to a `user group`. If the user already exists in the group
418 this command will return false.
424 this command will return false.
419
425
420 This command can only be run using an |authtoken| with admin rights to
426 This command can only be run using an |authtoken| with admin rights to
421 the specified user group.
427 the specified user group.
422
428
423 This command takes the following options:
429 This command takes the following options:
424
430
425 :param apiuser: This is filled automatically from the |authtoken|.
431 :param apiuser: This is filled automatically from the |authtoken|.
426 :type apiuser: AuthUser
432 :type apiuser: AuthUser
427 :param usergroupid: Set the name of the `user group` to which a
433 :param usergroupid: Set the name of the `user group` to which a
428 user will be added.
434 user will be added.
429 :type usergroupid: int
435 :type usergroupid: int
430 :param userid: Set the `user_id` of the user to add to the group.
436 :param userid: Set the `user_id` of the user to add to the group.
431 :type userid: int
437 :type userid: int
432
438
433 Example output:
439 Example output:
434
440
435 .. code-block:: bash
441 .. code-block:: bash
436
442
437 id : <id_given_in_input>
443 id : <id_given_in_input>
438 result : {
444 result : {
439 "success": True|False # depends on if member is in group
445 "success": True|False # depends on if member is in group
440 "msg": "added member `<username>` to user group `<groupname>` |
446 "msg": "added member `<username>` to user group `<groupname>` |
441 User is already in that group"
447 User is already in that group"
442
448
443 }
449 }
444 error : null
450 error : null
445
451
446 Example error output:
452 Example error output:
447
453
448 .. code-block:: bash
454 .. code-block:: bash
449
455
450 id : <id_given_in_input>
456 id : <id_given_in_input>
451 result : null
457 result : null
452 error : {
458 error : {
453 "failed to add member to user group `<user_group_name>`"
459 "failed to add member to user group `<user_group_name>`"
454 }
460 }
455
461
456 """
462 """
457
463
458 user = get_user_or_error(userid)
464 user = get_user_or_error(userid)
459 user_group = get_user_group_or_error(usergroupid)
465 user_group = get_user_group_or_error(usergroupid)
460 if not has_superadmin_permission(apiuser):
466 if not has_superadmin_permission(apiuser):
461 # check if we have admin permission for this user group !
467 # check if we have admin permission for this user group !
462 _perms = ('usergroup.admin',)
468 _perms = ('usergroup.admin',)
463 if not HasUserGroupPermissionAnyApi(*_perms)(
469 if not HasUserGroupPermissionAnyApi(*_perms)(
464 user=apiuser, user_group_name=user_group.users_group_name):
470 user=apiuser, user_group_name=user_group.users_group_name):
465 raise JSONRPCError('user group `%s` does not exist' % (
471 raise JSONRPCError('user group `%s` does not exist' % (
466 usergroupid,))
472 usergroupid,))
467
473
468 old_values = user_group.get_api_data()
474 old_values = user_group.get_api_data()
469 try:
475 try:
470 ugm = UserGroupModel().add_user_to_group(user_group, user)
476 ugm = UserGroupModel().add_user_to_group(user_group, user)
471 success = True if ugm is not True else False
477 success = True if ugm is not True else False
472 msg = 'added member `%s` to user group `%s`' % (
478 msg = 'added member `%s` to user group `%s`' % (
473 user.username, user_group.users_group_name
479 user.username, user_group.users_group_name
474 )
480 )
475 msg = msg if success else 'User is already in that group'
481 msg = msg if success else 'User is already in that group'
476 if success:
482 if success:
477 user_data = user.get_api_data()
483 user_data = user.get_api_data()
478 audit_logger.store_api(
484 audit_logger.store_api(
479 'user_group.edit.member.add',
485 'user_group.edit.member.add',
480 action_data={'user': user_data, 'old_data': old_values},
486 action_data={'user': user_data, 'old_data': old_values},
481 user=apiuser)
487 user=apiuser)
482
488
483 Session().commit()
489 Session().commit()
484
490
485 return {
491 return {
486 'success': success,
492 'success': success,
487 'msg': msg
493 'msg': msg
488 }
494 }
489 except Exception:
495 except Exception:
490 log.exception("Error occurred during adding a member to user group")
496 log.exception("Error occurred during adding a member to user group")
491 raise JSONRPCError(
497 raise JSONRPCError(
492 'failed to add member to user group `%s`' % (
498 'failed to add member to user group `%s`' % (
493 user_group.users_group_name,
499 user_group.users_group_name,
494 )
500 )
495 )
501 )
496
502
497
503
498 @jsonrpc_method()
504 @jsonrpc_method()
499 def remove_user_from_user_group(request, apiuser, usergroupid, userid):
505 def remove_user_from_user_group(request, apiuser, usergroupid, userid):
500 """
506 """
501 Removes a user from a user group.
507 Removes a user from a user group.
502
508
503 * If the specified user is not in the group, this command will return
509 * If the specified user is not in the group, this command will return
504 `false`.
510 `false`.
505
511
506 This command can only be run using an |authtoken| with admin rights to
512 This command can only be run using an |authtoken| with admin rights to
507 the specified user group.
513 the specified user group.
508
514
509 :param apiuser: This is filled automatically from the |authtoken|.
515 :param apiuser: This is filled automatically from the |authtoken|.
510 :type apiuser: AuthUser
516 :type apiuser: AuthUser
511 :param usergroupid: Sets the user group name.
517 :param usergroupid: Sets the user group name.
512 :type usergroupid: str or int
518 :type usergroupid: str or int
513 :param userid: The user you wish to remove from |RCE|.
519 :param userid: The user you wish to remove from |RCE|.
514 :type userid: str or int
520 :type userid: str or int
515
521
516 Example output:
522 Example output:
517
523
518 .. code-block:: bash
524 .. code-block:: bash
519
525
520 id : <id_given_in_input>
526 id : <id_given_in_input>
521 result: {
527 result: {
522 "success": True|False, # depends on if member is in group
528 "success": True|False, # depends on if member is in group
523 "msg": "removed member <username> from user group <groupname> |
529 "msg": "removed member <username> from user group <groupname> |
524 User wasn't in group"
530 User wasn't in group"
525 }
531 }
526 error: null
532 error: null
527
533
528 """
534 """
529
535
530 user = get_user_or_error(userid)
536 user = get_user_or_error(userid)
531 user_group = get_user_group_or_error(usergroupid)
537 user_group = get_user_group_or_error(usergroupid)
532 if not has_superadmin_permission(apiuser):
538 if not has_superadmin_permission(apiuser):
533 # check if we have admin permission for this user group !
539 # check if we have admin permission for this user group !
534 _perms = ('usergroup.admin',)
540 _perms = ('usergroup.admin',)
535 if not HasUserGroupPermissionAnyApi(*_perms)(
541 if not HasUserGroupPermissionAnyApi(*_perms)(
536 user=apiuser, user_group_name=user_group.users_group_name):
542 user=apiuser, user_group_name=user_group.users_group_name):
537 raise JSONRPCError(
543 raise JSONRPCError(
538 'user group `%s` does not exist' % (usergroupid,))
544 'user group `%s` does not exist' % (usergroupid,))
539
545
540 old_values = user_group.get_api_data()
546 old_values = user_group.get_api_data()
541 try:
547 try:
542 success = UserGroupModel().remove_user_from_group(user_group, user)
548 success = UserGroupModel().remove_user_from_group(user_group, user)
543 msg = 'removed member `%s` from user group `%s`' % (
549 msg = 'removed member `%s` from user group `%s`' % (
544 user.username, user_group.users_group_name
550 user.username, user_group.users_group_name
545 )
551 )
546 msg = msg if success else "User wasn't in group"
552 msg = msg if success else "User wasn't in group"
547 if success:
553 if success:
548 user_data = user.get_api_data()
554 user_data = user.get_api_data()
549 audit_logger.store_api(
555 audit_logger.store_api(
550 'user_group.edit.member.delete',
556 'user_group.edit.member.delete',
551 action_data={'user': user_data, 'old_data': old_values},
557 action_data={'user': user_data, 'old_data': old_values},
552 user=apiuser)
558 user=apiuser)
553
559
554 Session().commit()
560 Session().commit()
555 return {'success': success, 'msg': msg}
561 return {'success': success, 'msg': msg}
556 except Exception:
562 except Exception:
557 log.exception("Error occurred during removing an member from user group")
563 log.exception("Error occurred during removing an member from user group")
558 raise JSONRPCError(
564 raise JSONRPCError(
559 'failed to remove member from user group `%s`' % (
565 'failed to remove member from user group `%s`' % (
560 user_group.users_group_name,
566 user_group.users_group_name,
561 )
567 )
562 )
568 )
563
569
564
570
565 @jsonrpc_method()
571 @jsonrpc_method()
566 def grant_user_permission_to_user_group(
572 def grant_user_permission_to_user_group(
567 request, apiuser, usergroupid, userid, perm):
573 request, apiuser, usergroupid, userid, perm):
568 """
574 """
569 Set permissions for a user in a user group.
575 Set permissions for a user in a user group.
570
576
571 :param apiuser: This is filled automatically from the |authtoken|.
577 :param apiuser: This is filled automatically from the |authtoken|.
572 :type apiuser: AuthUser
578 :type apiuser: AuthUser
573 :param usergroupid: Set the user group to edit permissions on.
579 :param usergroupid: Set the user group to edit permissions on.
574 :type usergroupid: str or int
580 :type usergroupid: str or int
575 :param userid: Set the user from whom you wish to set permissions.
581 :param userid: Set the user from whom you wish to set permissions.
576 :type userid: str
582 :type userid: str
577 :param perm: (usergroup.(none|read|write|admin))
583 :param perm: (usergroup.(none|read|write|admin))
578 :type perm: str
584 :type perm: str
579
585
580 Example output:
586 Example output:
581
587
582 .. code-block:: bash
588 .. code-block:: bash
583
589
584 id : <id_given_in_input>
590 id : <id_given_in_input>
585 result : {
591 result : {
586 "msg": "Granted perm: `<perm_name>` for user: `<username>` in user group: `<user_group_name>`",
592 "msg": "Granted perm: `<perm_name>` for user: `<username>` in user group: `<user_group_name>`",
587 "success": true
593 "success": true
588 }
594 }
589 error : null
595 error : null
590 """
596 """
591
597
592 user_group = get_user_group_or_error(usergroupid)
598 user_group = get_user_group_or_error(usergroupid)
593
599
594 if not has_superadmin_permission(apiuser):
600 if not has_superadmin_permission(apiuser):
595 # check if we have admin permission for this user group !
601 # check if we have admin permission for this user group !
596 _perms = ('usergroup.admin',)
602 _perms = ('usergroup.admin',)
597 if not HasUserGroupPermissionAnyApi(*_perms)(
603 if not HasUserGroupPermissionAnyApi(*_perms)(
598 user=apiuser, user_group_name=user_group.users_group_name):
604 user=apiuser, user_group_name=user_group.users_group_name):
599 raise JSONRPCError(
605 raise JSONRPCError(
600 'user group `%s` does not exist' % (usergroupid,))
606 'user group `%s` does not exist' % (usergroupid,))
601
607
602 user = get_user_or_error(userid)
608 user = get_user_or_error(userid)
603 perm = get_perm_or_error(perm, prefix='usergroup.')
609 perm = get_perm_or_error(perm, prefix='usergroup.')
604
610
605 try:
611 try:
606 UserGroupModel().grant_user_permission(
612 UserGroupModel().grant_user_permission(
607 user_group=user_group, user=user, perm=perm)
613 user_group=user_group, user=user, perm=perm)
608 Session().commit()
614 Session().commit()
609 return {
615 return {
610 'msg':
616 'msg':
611 'Granted perm: `%s` for user: `%s` in user group: `%s`' % (
617 'Granted perm: `%s` for user: `%s` in user group: `%s`' % (
612 perm.permission_name, user.username,
618 perm.permission_name, user.username,
613 user_group.users_group_name
619 user_group.users_group_name
614 ),
620 ),
615 'success': True
621 'success': True
616 }
622 }
617 except Exception:
623 except Exception:
618 log.exception("Error occurred during editing permissions "
624 log.exception("Error occurred during editing permissions "
619 "for user in user group")
625 "for user in user group")
620 raise JSONRPCError(
626 raise JSONRPCError(
621 'failed to edit permission for user: '
627 'failed to edit permission for user: '
622 '`%s` in user group: `%s`' % (
628 '`%s` in user group: `%s`' % (
623 userid, user_group.users_group_name))
629 userid, user_group.users_group_name))
624
630
625
631
626 @jsonrpc_method()
632 @jsonrpc_method()
627 def revoke_user_permission_from_user_group(
633 def revoke_user_permission_from_user_group(
628 request, apiuser, usergroupid, userid):
634 request, apiuser, usergroupid, userid):
629 """
635 """
630 Revoke a users permissions in a user group.
636 Revoke a users permissions in a user group.
631
637
632 :param apiuser: This is filled automatically from the |authtoken|.
638 :param apiuser: This is filled automatically from the |authtoken|.
633 :type apiuser: AuthUser
639 :type apiuser: AuthUser
634 :param usergroupid: Set the user group from which to revoke the user
640 :param usergroupid: Set the user group from which to revoke the user
635 permissions.
641 permissions.
636 :type: usergroupid: str or int
642 :type: usergroupid: str or int
637 :param userid: Set the userid of the user whose permissions will be
643 :param userid: Set the userid of the user whose permissions will be
638 revoked.
644 revoked.
639 :type userid: str
645 :type userid: str
640
646
641 Example output:
647 Example output:
642
648
643 .. code-block:: bash
649 .. code-block:: bash
644
650
645 id : <id_given_in_input>
651 id : <id_given_in_input>
646 result : {
652 result : {
647 "msg": "Revoked perm for user: `<username>` in user group: `<user_group_name>`",
653 "msg": "Revoked perm for user: `<username>` in user group: `<user_group_name>`",
648 "success": true
654 "success": true
649 }
655 }
650 error : null
656 error : null
651 """
657 """
652
658
653 user_group = get_user_group_or_error(usergroupid)
659 user_group = get_user_group_or_error(usergroupid)
654
660
655 if not has_superadmin_permission(apiuser):
661 if not has_superadmin_permission(apiuser):
656 # check if we have admin permission for this user group !
662 # check if we have admin permission for this user group !
657 _perms = ('usergroup.admin',)
663 _perms = ('usergroup.admin',)
658 if not HasUserGroupPermissionAnyApi(*_perms)(
664 if not HasUserGroupPermissionAnyApi(*_perms)(
659 user=apiuser, user_group_name=user_group.users_group_name):
665 user=apiuser, user_group_name=user_group.users_group_name):
660 raise JSONRPCError(
666 raise JSONRPCError(
661 'user group `%s` does not exist' % (usergroupid,))
667 'user group `%s` does not exist' % (usergroupid,))
662
668
663 user = get_user_or_error(userid)
669 user = get_user_or_error(userid)
664
670
665 try:
671 try:
666 UserGroupModel().revoke_user_permission(
672 UserGroupModel().revoke_user_permission(
667 user_group=user_group, user=user)
673 user_group=user_group, user=user)
668 Session().commit()
674 Session().commit()
669 return {
675 return {
670 'msg': 'Revoked perm for user: `%s` in user group: `%s`' % (
676 'msg': 'Revoked perm for user: `%s` in user group: `%s`' % (
671 user.username, user_group.users_group_name
677 user.username, user_group.users_group_name
672 ),
678 ),
673 'success': True
679 'success': True
674 }
680 }
675 except Exception:
681 except Exception:
676 log.exception("Error occurred during editing permissions "
682 log.exception("Error occurred during editing permissions "
677 "for user in user group")
683 "for user in user group")
678 raise JSONRPCError(
684 raise JSONRPCError(
679 'failed to edit permission for user: `%s` in user group: `%s`'
685 'failed to edit permission for user: `%s` in user group: `%s`'
680 % (userid, user_group.users_group_name))
686 % (userid, user_group.users_group_name))
681
687
682
688
683 @jsonrpc_method()
689 @jsonrpc_method()
684 def grant_user_group_permission_to_user_group(
690 def grant_user_group_permission_to_user_group(
685 request, apiuser, usergroupid, sourceusergroupid, perm):
691 request, apiuser, usergroupid, sourceusergroupid, perm):
686 """
692 """
687 Give one user group permissions to another user group.
693 Give one user group permissions to another user group.
688
694
689 :param apiuser: This is filled automatically from the |authtoken|.
695 :param apiuser: This is filled automatically from the |authtoken|.
690 :type apiuser: AuthUser
696 :type apiuser: AuthUser
691 :param usergroupid: Set the user group on which to edit permissions.
697 :param usergroupid: Set the user group on which to edit permissions.
692 :type usergroupid: str or int
698 :type usergroupid: str or int
693 :param sourceusergroupid: Set the source user group to which
699 :param sourceusergroupid: Set the source user group to which
694 access/permissions will be granted.
700 access/permissions will be granted.
695 :type sourceusergroupid: str or int
701 :type sourceusergroupid: str or int
696 :param perm: (usergroup.(none|read|write|admin))
702 :param perm: (usergroup.(none|read|write|admin))
697 :type perm: str
703 :type perm: str
698
704
699 Example output:
705 Example output:
700
706
701 .. code-block:: bash
707 .. code-block:: bash
702
708
703 id : <id_given_in_input>
709 id : <id_given_in_input>
704 result : {
710 result : {
705 "msg": "Granted perm: `<perm_name>` for user group: `<source_user_group_name>` in user group: `<user_group_name>`",
711 "msg": "Granted perm: `<perm_name>` for user group: `<source_user_group_name>` in user group: `<user_group_name>`",
706 "success": true
712 "success": true
707 }
713 }
708 error : null
714 error : null
709 """
715 """
710
716
711 user_group = get_user_group_or_error(sourceusergroupid)
717 user_group = get_user_group_or_error(sourceusergroupid)
712 target_user_group = get_user_group_or_error(usergroupid)
718 target_user_group = get_user_group_or_error(usergroupid)
713 perm = get_perm_or_error(perm, prefix='usergroup.')
719 perm = get_perm_or_error(perm, prefix='usergroup.')
714
720
715 if not has_superadmin_permission(apiuser):
721 if not has_superadmin_permission(apiuser):
716 # check if we have admin permission for this user group !
722 # check if we have admin permission for this user group !
717 _perms = ('usergroup.admin',)
723 _perms = ('usergroup.admin',)
718 if not HasUserGroupPermissionAnyApi(*_perms)(
724 if not HasUserGroupPermissionAnyApi(*_perms)(
719 user=apiuser,
725 user=apiuser,
720 user_group_name=target_user_group.users_group_name):
726 user_group_name=target_user_group.users_group_name):
721 raise JSONRPCError(
727 raise JSONRPCError(
722 'to user group `%s` does not exist' % (usergroupid,))
728 'to user group `%s` does not exist' % (usergroupid,))
723
729
724 # check if we have at least read permission for source user group !
730 # check if we have at least read permission for source user group !
725 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
731 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
726 if not HasUserGroupPermissionAnyApi(*_perms)(
732 if not HasUserGroupPermissionAnyApi(*_perms)(
727 user=apiuser, user_group_name=user_group.users_group_name):
733 user=apiuser, user_group_name=user_group.users_group_name):
728 raise JSONRPCError(
734 raise JSONRPCError(
729 'user group `%s` does not exist' % (sourceusergroupid,))
735 'user group `%s` does not exist' % (sourceusergroupid,))
730
736
731 try:
737 try:
732 UserGroupModel().grant_user_group_permission(
738 UserGroupModel().grant_user_group_permission(
733 target_user_group=target_user_group,
739 target_user_group=target_user_group,
734 user_group=user_group, perm=perm)
740 user_group=user_group, perm=perm)
735 Session().commit()
741 Session().commit()
736
742
737 return {
743 return {
738 'msg': 'Granted perm: `%s` for user group: `%s` '
744 'msg': 'Granted perm: `%s` for user group: `%s` '
739 'in user group: `%s`' % (
745 'in user group: `%s`' % (
740 perm.permission_name, user_group.users_group_name,
746 perm.permission_name, user_group.users_group_name,
741 target_user_group.users_group_name
747 target_user_group.users_group_name
742 ),
748 ),
743 'success': True
749 'success': True
744 }
750 }
745 except Exception:
751 except Exception:
746 log.exception("Error occurred during editing permissions "
752 log.exception("Error occurred during editing permissions "
747 "for user group in user group")
753 "for user group in user group")
748 raise JSONRPCError(
754 raise JSONRPCError(
749 'failed to edit permission for user group: `%s` in '
755 'failed to edit permission for user group: `%s` in '
750 'user group: `%s`' % (
756 'user group: `%s`' % (
751 sourceusergroupid, target_user_group.users_group_name
757 sourceusergroupid, target_user_group.users_group_name
752 )
758 )
753 )
759 )
754
760
755
761
756 @jsonrpc_method()
762 @jsonrpc_method()
757 def revoke_user_group_permission_from_user_group(
763 def revoke_user_group_permission_from_user_group(
758 request, apiuser, usergroupid, sourceusergroupid):
764 request, apiuser, usergroupid, sourceusergroupid):
759 """
765 """
760 Revoke the permissions that one user group has to another.
766 Revoke the permissions that one user group has to another.
761
767
762 :param apiuser: This is filled automatically from the |authtoken|.
768 :param apiuser: This is filled automatically from the |authtoken|.
763 :type apiuser: AuthUser
769 :type apiuser: AuthUser
764 :param usergroupid: Set the user group on which to edit permissions.
770 :param usergroupid: Set the user group on which to edit permissions.
765 :type usergroupid: str or int
771 :type usergroupid: str or int
766 :param sourceusergroupid: Set the user group from which permissions
772 :param sourceusergroupid: Set the user group from which permissions
767 are revoked.
773 are revoked.
768 :type sourceusergroupid: str or int
774 :type sourceusergroupid: str or int
769
775
770 Example output:
776 Example output:
771
777
772 .. code-block:: bash
778 .. code-block:: bash
773
779
774 id : <id_given_in_input>
780 id : <id_given_in_input>
775 result : {
781 result : {
776 "msg": "Revoked perm for user group: `<user_group_name>` in user group: `<target_user_group_name>`",
782 "msg": "Revoked perm for user group: `<user_group_name>` in user group: `<target_user_group_name>`",
777 "success": true
783 "success": true
778 }
784 }
779 error : null
785 error : null
780 """
786 """
781
787
782 user_group = get_user_group_or_error(sourceusergroupid)
788 user_group = get_user_group_or_error(sourceusergroupid)
783 target_user_group = get_user_group_or_error(usergroupid)
789 target_user_group = get_user_group_or_error(usergroupid)
784
790
785 if not has_superadmin_permission(apiuser):
791 if not has_superadmin_permission(apiuser):
786 # check if we have admin permission for this user group !
792 # check if we have admin permission for this user group !
787 _perms = ('usergroup.admin',)
793 _perms = ('usergroup.admin',)
788 if not HasUserGroupPermissionAnyApi(*_perms)(
794 if not HasUserGroupPermissionAnyApi(*_perms)(
789 user=apiuser,
795 user=apiuser,
790 user_group_name=target_user_group.users_group_name):
796 user_group_name=target_user_group.users_group_name):
791 raise JSONRPCError(
797 raise JSONRPCError(
792 'to user group `%s` does not exist' % (usergroupid,))
798 'to user group `%s` does not exist' % (usergroupid,))
793
799
794 # check if we have at least read permission
800 # check if we have at least read permission
795 # for the source user group !
801 # for the source user group !
796 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
802 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
797 if not HasUserGroupPermissionAnyApi(*_perms)(
803 if not HasUserGroupPermissionAnyApi(*_perms)(
798 user=apiuser, user_group_name=user_group.users_group_name):
804 user=apiuser, user_group_name=user_group.users_group_name):
799 raise JSONRPCError(
805 raise JSONRPCError(
800 'user group `%s` does not exist' % (sourceusergroupid,))
806 'user group `%s` does not exist' % (sourceusergroupid,))
801
807
802 try:
808 try:
803 UserGroupModel().revoke_user_group_permission(
809 UserGroupModel().revoke_user_group_permission(
804 target_user_group=target_user_group, user_group=user_group)
810 target_user_group=target_user_group, user_group=user_group)
805 Session().commit()
811 Session().commit()
806
812
807 return {
813 return {
808 'msg': 'Revoked perm for user group: '
814 'msg': 'Revoked perm for user group: '
809 '`%s` in user group: `%s`' % (
815 '`%s` in user group: `%s`' % (
810 user_group.users_group_name,
816 user_group.users_group_name,
811 target_user_group.users_group_name
817 target_user_group.users_group_name
812 ),
818 ),
813 'success': True
819 'success': True
814 }
820 }
815 except Exception:
821 except Exception:
816 log.exception("Error occurred during editing permissions "
822 log.exception("Error occurred during editing permissions "
817 "for user group in user group")
823 "for user group in user group")
818 raise JSONRPCError(
824 raise JSONRPCError(
819 'failed to edit permission for user group: '
825 'failed to edit permission for user group: '
820 '`%s` in user group: `%s`' % (
826 '`%s` in user group: `%s`' % (
821 sourceusergroupid, target_user_group.users_group_name
827 sourceusergroupid, target_user_group.users_group_name
822 )
828 )
823 )
829 )
General Comments 0
You need to be logged in to leave comments. Login now