Show More
@@ -0,0 +1,41 b'' | |||||
|
1 | |RCE| 4.10.4 |RNS| | |||
|
2 | ------------------ | |||
|
3 | ||||
|
4 | Release Date | |||
|
5 | ^^^^^^^^^^^^ | |||
|
6 | ||||
|
7 | - 2017-11-14 | |||
|
8 | ||||
|
9 | ||||
|
10 | New Features | |||
|
11 | ^^^^^^^^^^^^ | |||
|
12 | ||||
|
13 | ||||
|
14 | ||||
|
15 | General | |||
|
16 | ^^^^^^^ | |||
|
17 | ||||
|
18 | ||||
|
19 | ||||
|
20 | Security | |||
|
21 | ^^^^^^^^ | |||
|
22 | ||||
|
23 | ||||
|
24 | ||||
|
25 | Performance | |||
|
26 | ^^^^^^^^^^^ | |||
|
27 | ||||
|
28 | ||||
|
29 | ||||
|
30 | Fixes | |||
|
31 | ^^^^^ | |||
|
32 | ||||
|
33 | ||||
|
34 | - repository groups: fixed a regressions in update of nested repository groups. | |||
|
35 | ||||
|
36 | ||||
|
37 | Upgrade notes | |||
|
38 | ^^^^^^^^^^^^^ | |||
|
39 | ||||
|
40 | - Fixes regression in nested repository groups update. No upgrade problems should | |||
|
41 | be expected |
@@ -1,27 +1,28 b'' | |||||
1 | 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0 |
|
1 | 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0 | |
2 | 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1 |
|
2 | 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1 | |
3 | c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0 |
|
3 | c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0 | |
4 | 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1 |
|
4 | 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1 | |
5 | 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2 |
|
5 | 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2 | |
6 | baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0 |
|
6 | baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0 | |
7 | 32a70c7e56844a825f61df496ee5eaf8c3c4e189 v4.2.1 |
|
7 | 32a70c7e56844a825f61df496ee5eaf8c3c4e189 v4.2.1 | |
8 | fa695cdb411d294679ac081d595ac654e5613b03 v4.3.0 |
|
8 | fa695cdb411d294679ac081d595ac654e5613b03 v4.3.0 | |
9 | 0e4dc11b58cad833c513fe17bac39e6850edf959 v4.3.1 |
|
9 | 0e4dc11b58cad833c513fe17bac39e6850edf959 v4.3.1 | |
10 | 8a876f48f5cb1d018b837db28ff928500cb32cfb v4.4.0 |
|
10 | 8a876f48f5cb1d018b837db28ff928500cb32cfb v4.4.0 | |
11 | 8dd86b410b1aac086ffdfc524ef300f896af5047 v4.4.1 |
|
11 | 8dd86b410b1aac086ffdfc524ef300f896af5047 v4.4.1 | |
12 | d2514226abc8d3b4f6fb57765f47d1b6fb360a05 v4.4.2 |
|
12 | d2514226abc8d3b4f6fb57765f47d1b6fb360a05 v4.4.2 | |
13 | 27d783325930af6dad2741476c0d0b1b7c8415c2 v4.5.0 |
|
13 | 27d783325930af6dad2741476c0d0b1b7c8415c2 v4.5.0 | |
14 | 7f2016f352abcbdba4a19d4039c386e9629449da v4.5.1 |
|
14 | 7f2016f352abcbdba4a19d4039c386e9629449da v4.5.1 | |
15 | 416fec799314c70a5c780fb28b3357b08869333a v4.5.2 |
|
15 | 416fec799314c70a5c780fb28b3357b08869333a v4.5.2 | |
16 | 27c3b85fafc83143e6678fbc3da69e1615bcac55 v4.6.0 |
|
16 | 27c3b85fafc83143e6678fbc3da69e1615bcac55 v4.6.0 | |
17 | 5ad13deb9118c2a5243d4032d4d9cc174e5872db v4.6.1 |
|
17 | 5ad13deb9118c2a5243d4032d4d9cc174e5872db v4.6.1 | |
18 | 2be921e01fa24bb102696ada596f87464c3666f6 v4.7.0 |
|
18 | 2be921e01fa24bb102696ada596f87464c3666f6 v4.7.0 | |
19 | 7198bdec29c2872c974431d55200d0398354cdb1 v4.7.1 |
|
19 | 7198bdec29c2872c974431d55200d0398354cdb1 v4.7.1 | |
20 | bd1c8d230fe741c2dfd7100a0ef39fd0774fd581 v4.7.2 |
|
20 | bd1c8d230fe741c2dfd7100a0ef39fd0774fd581 v4.7.2 | |
21 | 9731914f89765d9628dc4dddc84bc9402aa124c8 v4.8.0 |
|
21 | 9731914f89765d9628dc4dddc84bc9402aa124c8 v4.8.0 | |
22 | c5a2b7d0e4bbdebc4a62d7b624befe375207b659 v4.9.0 |
|
22 | c5a2b7d0e4bbdebc4a62d7b624befe375207b659 v4.9.0 | |
23 | d9aa3b27ac9f7e78359775c75fedf7bfece232f1 v4.9.1 |
|
23 | d9aa3b27ac9f7e78359775c75fedf7bfece232f1 v4.9.1 | |
24 | 4ba4d74981cec5d6b28b158f875a2540952c2f74 v4.10.0 |
|
24 | 4ba4d74981cec5d6b28b158f875a2540952c2f74 v4.10.0 | |
25 | 0a6821cbd6b0b3c21503002f88800679fa35ab63 v4.10.1 |
|
25 | 0a6821cbd6b0b3c21503002f88800679fa35ab63 v4.10.1 | |
26 | 434ad90ec8d621f4416074b84f6e9ce03964defb v4.10.2 |
|
26 | 434ad90ec8d621f4416074b84f6e9ce03964defb v4.10.2 | |
27 | 68baee10e698da2724c6e0f698c03a6abb993bf2 v4.10.3 |
|
27 | 68baee10e698da2724c6e0f698c03a6abb993bf2 v4.10.3 | |
|
28 | 00821d3afd1dce3f4767cc353f84a17f7d5218a1 v4.10.4 |
@@ -1,104 +1,105 b'' | |||||
1 | .. _rhodecode-release-notes-ref: |
|
1 | .. _rhodecode-release-notes-ref: | |
2 |
|
2 | |||
3 | Release Notes |
|
3 | Release Notes | |
4 | ============= |
|
4 | ============= | |
5 |
|
5 | |||
6 | |RCE| 4.x Versions |
|
6 | |RCE| 4.x Versions | |
7 | ------------------ |
|
7 | ------------------ | |
8 |
|
8 | |||
9 | .. toctree:: |
|
9 | .. toctree:: | |
10 | :maxdepth: 1 |
|
10 | :maxdepth: 1 | |
11 |
|
11 | |||
|
12 | release-notes-4.10.4.rst | |||
12 | release-notes-4.10.3.rst |
|
13 | release-notes-4.10.3.rst | |
13 | release-notes-4.10.2.rst |
|
14 | release-notes-4.10.2.rst | |
14 | release-notes-4.10.1.rst |
|
15 | release-notes-4.10.1.rst | |
15 | release-notes-4.10.0.rst |
|
16 | release-notes-4.10.0.rst | |
16 | release-notes-4.9.1.rst |
|
17 | release-notes-4.9.1.rst | |
17 | release-notes-4.9.0.rst |
|
18 | release-notes-4.9.0.rst | |
18 | release-notes-4.8.0.rst |
|
19 | release-notes-4.8.0.rst | |
19 | release-notes-4.7.2.rst |
|
20 | release-notes-4.7.2.rst | |
20 | release-notes-4.7.1.rst |
|
21 | release-notes-4.7.1.rst | |
21 | release-notes-4.7.0.rst |
|
22 | release-notes-4.7.0.rst | |
22 | release-notes-4.6.1.rst |
|
23 | release-notes-4.6.1.rst | |
23 | release-notes-4.6.0.rst |
|
24 | release-notes-4.6.0.rst | |
24 | release-notes-4.5.2.rst |
|
25 | release-notes-4.5.2.rst | |
25 | release-notes-4.5.1.rst |
|
26 | release-notes-4.5.1.rst | |
26 | release-notes-4.5.0.rst |
|
27 | release-notes-4.5.0.rst | |
27 | release-notes-4.4.2.rst |
|
28 | release-notes-4.4.2.rst | |
28 | release-notes-4.4.1.rst |
|
29 | release-notes-4.4.1.rst | |
29 | release-notes-4.4.0.rst |
|
30 | release-notes-4.4.0.rst | |
30 | release-notes-4.3.1.rst |
|
31 | release-notes-4.3.1.rst | |
31 | release-notes-4.3.0.rst |
|
32 | release-notes-4.3.0.rst | |
32 | release-notes-4.2.1.rst |
|
33 | release-notes-4.2.1.rst | |
33 | release-notes-4.2.0.rst |
|
34 | release-notes-4.2.0.rst | |
34 | release-notes-4.1.2.rst |
|
35 | release-notes-4.1.2.rst | |
35 | release-notes-4.1.1.rst |
|
36 | release-notes-4.1.1.rst | |
36 | release-notes-4.1.0.rst |
|
37 | release-notes-4.1.0.rst | |
37 | release-notes-4.0.1.rst |
|
38 | release-notes-4.0.1.rst | |
38 | release-notes-4.0.0.rst |
|
39 | release-notes-4.0.0.rst | |
39 |
|
40 | |||
40 | |RCE| 3.x Versions |
|
41 | |RCE| 3.x Versions | |
41 | ------------------ |
|
42 | ------------------ | |
42 |
|
43 | |||
43 | .. toctree:: |
|
44 | .. toctree:: | |
44 | :maxdepth: 1 |
|
45 | :maxdepth: 1 | |
45 |
|
46 | |||
46 | release-notes-3.8.4.rst |
|
47 | release-notes-3.8.4.rst | |
47 | release-notes-3.8.3.rst |
|
48 | release-notes-3.8.3.rst | |
48 | release-notes-3.8.2.rst |
|
49 | release-notes-3.8.2.rst | |
49 | release-notes-3.8.1.rst |
|
50 | release-notes-3.8.1.rst | |
50 | release-notes-3.8.0.rst |
|
51 | release-notes-3.8.0.rst | |
51 | release-notes-3.7.1.rst |
|
52 | release-notes-3.7.1.rst | |
52 | release-notes-3.7.0.rst |
|
53 | release-notes-3.7.0.rst | |
53 | release-notes-3.6.1.rst |
|
54 | release-notes-3.6.1.rst | |
54 | release-notes-3.6.0.rst |
|
55 | release-notes-3.6.0.rst | |
55 | release-notes-3.5.2.rst |
|
56 | release-notes-3.5.2.rst | |
56 | release-notes-3.5.1.rst |
|
57 | release-notes-3.5.1.rst | |
57 | release-notes-3.5.0.rst |
|
58 | release-notes-3.5.0.rst | |
58 | release-notes-3.4.1.rst |
|
59 | release-notes-3.4.1.rst | |
59 | release-notes-3.4.0.rst |
|
60 | release-notes-3.4.0.rst | |
60 | release-notes-3.3.4.rst |
|
61 | release-notes-3.3.4.rst | |
61 | release-notes-3.3.3.rst |
|
62 | release-notes-3.3.3.rst | |
62 | release-notes-3.3.2.rst |
|
63 | release-notes-3.3.2.rst | |
63 | release-notes-3.3.1.rst |
|
64 | release-notes-3.3.1.rst | |
64 | release-notes-3.3.0.rst |
|
65 | release-notes-3.3.0.rst | |
65 | release-notes-3.2.3.rst |
|
66 | release-notes-3.2.3.rst | |
66 | release-notes-3.2.2.rst |
|
67 | release-notes-3.2.2.rst | |
67 | release-notes-3.2.1.rst |
|
68 | release-notes-3.2.1.rst | |
68 | release-notes-3.2.0.rst |
|
69 | release-notes-3.2.0.rst | |
69 | release-notes-3.1.1.rst |
|
70 | release-notes-3.1.1.rst | |
70 | release-notes-3.1.0.rst |
|
71 | release-notes-3.1.0.rst | |
71 | release-notes-3.0.2.rst |
|
72 | release-notes-3.0.2.rst | |
72 | release-notes-3.0.1.rst |
|
73 | release-notes-3.0.1.rst | |
73 | release-notes-3.0.0.rst |
|
74 | release-notes-3.0.0.rst | |
74 |
|
75 | |||
75 | |RCE| 2.x Versions |
|
76 | |RCE| 2.x Versions | |
76 | ------------------ |
|
77 | ------------------ | |
77 |
|
78 | |||
78 | .. toctree:: |
|
79 | .. toctree:: | |
79 | :maxdepth: 1 |
|
80 | :maxdepth: 1 | |
80 |
|
81 | |||
81 | release-notes-2.2.8.rst |
|
82 | release-notes-2.2.8.rst | |
82 | release-notes-2.2.7.rst |
|
83 | release-notes-2.2.7.rst | |
83 | release-notes-2.2.6.rst |
|
84 | release-notes-2.2.6.rst | |
84 | release-notes-2.2.5.rst |
|
85 | release-notes-2.2.5.rst | |
85 | release-notes-2.2.4.rst |
|
86 | release-notes-2.2.4.rst | |
86 | release-notes-2.2.3.rst |
|
87 | release-notes-2.2.3.rst | |
87 | release-notes-2.2.2.rst |
|
88 | release-notes-2.2.2.rst | |
88 | release-notes-2.2.1.rst |
|
89 | release-notes-2.2.1.rst | |
89 | release-notes-2.2.0.rst |
|
90 | release-notes-2.2.0.rst | |
90 | release-notes-2.1.0.rst |
|
91 | release-notes-2.1.0.rst | |
91 | release-notes-2.0.2.rst |
|
92 | release-notes-2.0.2.rst | |
92 | release-notes-2.0.1.rst |
|
93 | release-notes-2.0.1.rst | |
93 | release-notes-2.0.0.rst |
|
94 | release-notes-2.0.0.rst | |
94 |
|
95 | |||
95 | |RCE| 1.x Versions |
|
96 | |RCE| 1.x Versions | |
96 | ------------------ |
|
97 | ------------------ | |
97 |
|
98 | |||
98 | .. toctree:: |
|
99 | .. toctree:: | |
99 | :maxdepth: 1 |
|
100 | :maxdepth: 1 | |
100 |
|
101 | |||
101 | release-notes-1.7.2.rst |
|
102 | release-notes-1.7.2.rst | |
102 | release-notes-1.7.1.rst |
|
103 | release-notes-1.7.1.rst | |
103 | release-notes-1.7.0.rst |
|
104 | release-notes-1.7.0.rst | |
104 | release-notes-1.6.0.rst |
|
105 | release-notes-1.6.0.rst |
@@ -1,183 +1,183 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 deform |
|
22 | import deform | |
23 |
|
23 | |||
24 | from pyramid.view import view_config |
|
24 | from pyramid.view import view_config | |
25 | from pyramid.httpexceptions import HTTPFound |
|
25 | from pyramid.httpexceptions import HTTPFound | |
26 |
|
26 | |||
27 | from rhodecode.apps._base import RepoGroupAppView |
|
27 | from rhodecode.apps._base import RepoGroupAppView | |
28 | from rhodecode.forms import RcForm |
|
28 | from rhodecode.forms import RcForm | |
29 | from rhodecode.lib import helpers as h |
|
29 | from rhodecode.lib import helpers as h | |
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 | LoginRequired, HasPermissionAll, |
|
32 | LoginRequired, HasPermissionAll, | |
33 | HasRepoGroupPermissionAny, HasRepoGroupPermissionAnyDecorator, CSRFRequired) |
|
33 | HasRepoGroupPermissionAny, HasRepoGroupPermissionAnyDecorator, CSRFRequired) | |
34 | from rhodecode.model.db import Session, RepoGroup |
|
34 | from rhodecode.model.db import Session, RepoGroup | |
35 | from rhodecode.model.scm import RepoGroupList |
|
35 | from rhodecode.model.scm import RepoGroupList | |
36 | from rhodecode.model.repo_group import RepoGroupModel |
|
36 | from rhodecode.model.repo_group import RepoGroupModel | |
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 | log = logging.getLogger(__name__) |
|
39 | log = logging.getLogger(__name__) | |
40 |
|
40 | |||
41 |
|
41 | |||
42 | class RepoGroupSettingsView(RepoGroupAppView): |
|
42 | class RepoGroupSettingsView(RepoGroupAppView): | |
43 | def load_default_context(self): |
|
43 | def load_default_context(self): | |
44 | c = self._get_local_tmpl_context() |
|
44 | c = self._get_local_tmpl_context() | |
45 | c.repo_group = self.db_repo_group |
|
45 | c.repo_group = self.db_repo_group | |
46 | no_parrent = not c.repo_group.parent_group |
|
46 | no_parrent = not c.repo_group.parent_group | |
47 | can_create_in_root = self._can_create_repo_group() |
|
47 | can_create_in_root = self._can_create_repo_group() | |
48 |
|
48 | |||
49 | show_root_location = False |
|
49 | show_root_location = False | |
50 | if no_parrent or can_create_in_root: |
|
50 | if no_parrent or can_create_in_root: | |
51 | # we're global admin, we're ok and we can create TOP level groups |
|
51 | # we're global admin, we're ok and we can create TOP level groups | |
52 | # or in case this group is already at top-level we also allow |
|
52 | # or in case this group is already at top-level we also allow | |
53 | # creation in root |
|
53 | # creation in root | |
54 | show_root_location = True |
|
54 | show_root_location = True | |
55 |
|
55 | |||
56 | acl_groups = RepoGroupList( |
|
56 | acl_groups = RepoGroupList( | |
57 | RepoGroup.query().all(), |
|
57 | RepoGroup.query().all(), | |
58 | perm_set=['group.admin']) |
|
58 | perm_set=['group.admin']) | |
59 | c.repo_groups = RepoGroup.groups_choices( |
|
59 | c.repo_groups = RepoGroup.groups_choices( | |
60 | groups=acl_groups, |
|
60 | groups=acl_groups, | |
61 | show_empty_group=show_root_location) |
|
61 | show_empty_group=show_root_location) | |
62 | # filter out current repo group |
|
62 | # filter out current repo group | |
63 | exclude_group_ids = [c.repo_group.group_id] |
|
63 | exclude_group_ids = [c.repo_group.group_id] | |
64 | c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids, |
|
64 | c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids, | |
65 | c.repo_groups) |
|
65 | c.repo_groups) | |
66 | c.repo_groups_choices = map(lambda k: k[0], c.repo_groups) |
|
66 | c.repo_groups_choices = map(lambda k: k[0], c.repo_groups) | |
67 |
|
67 | |||
68 | parent_group = c.repo_group.parent_group |
|
68 | parent_group = c.repo_group.parent_group | |
69 |
|
69 | |||
70 | add_parent_group = (parent_group and ( |
|
70 | add_parent_group = (parent_group and ( | |
71 | parent_group.group_id not in c.repo_groups_choices)) |
|
71 | parent_group.group_id not in c.repo_groups_choices)) | |
72 | if add_parent_group: |
|
72 | if add_parent_group: | |
73 | c.repo_groups_choices.append(parent_group.group_id) |
|
73 | c.repo_groups_choices.append(parent_group.group_id) | |
74 | c.repo_groups.append(RepoGroup._generate_choice(parent_group)) |
|
74 | c.repo_groups.append(RepoGroup._generate_choice(parent_group)) | |
75 |
|
75 | |||
76 | self._register_global_c(c) |
|
76 | self._register_global_c(c) | |
77 | return c |
|
77 | return c | |
78 |
|
78 | |||
79 | def _can_create_repo_group(self, parent_group_id=None): |
|
79 | def _can_create_repo_group(self, parent_group_id=None): | |
80 | is_admin = HasPermissionAll('hg.admin')('group create controller') |
|
80 | is_admin = HasPermissionAll('hg.admin')('group create controller') | |
81 | create_repo_group = HasPermissionAll( |
|
81 | create_repo_group = HasPermissionAll( | |
82 | 'hg.repogroup.create.true')('group create controller') |
|
82 | 'hg.repogroup.create.true')('group create controller') | |
83 | if is_admin or (create_repo_group and not parent_group_id): |
|
83 | if is_admin or (create_repo_group and not parent_group_id): | |
84 | # we're global admin, or we have global repo group create |
|
84 | # we're global admin, or we have global repo group create | |
85 | # permission |
|
85 | # permission | |
86 | # we're ok and we can create TOP level groups |
|
86 | # we're ok and we can create TOP level groups | |
87 | return True |
|
87 | return True | |
88 | elif parent_group_id: |
|
88 | elif parent_group_id: | |
89 | # we check the permission if we can write to parent group |
|
89 | # we check the permission if we can write to parent group | |
90 | group = RepoGroup.get(parent_group_id) |
|
90 | group = RepoGroup.get(parent_group_id) | |
91 | group_name = group.group_name if group else None |
|
91 | group_name = group.group_name if group else None | |
92 | if HasRepoGroupPermissionAny('group.admin')( |
|
92 | if HasRepoGroupPermissionAny('group.admin')( | |
93 | group_name, 'check if user is an admin of group'): |
|
93 | group_name, 'check if user is an admin of group'): | |
94 | # we're an admin of passed in group, we're ok. |
|
94 | # we're an admin of passed in group, we're ok. | |
95 | return True |
|
95 | return True | |
96 | else: |
|
96 | else: | |
97 | return False |
|
97 | return False | |
98 | return False |
|
98 | return False | |
99 |
|
99 | |||
100 | def _get_schema(self, c, old_values=None): |
|
100 | def _get_schema(self, c, old_values=None): | |
101 | return repo_group_schema.RepoGroupSettingsSchema().bind( |
|
101 | return repo_group_schema.RepoGroupSettingsSchema().bind( | |
102 | repo_group_repo_group_options=c.repo_groups_choices, |
|
102 | repo_group_repo_group_options=c.repo_groups_choices, | |
103 | repo_group_repo_group_items=c.repo_groups, |
|
103 | repo_group_repo_group_items=c.repo_groups, | |
104 |
|
104 | |||
105 | # user caller |
|
105 | # user caller | |
106 | user=self._rhodecode_user, |
|
106 | user=self._rhodecode_user, | |
107 | old_values=old_values |
|
107 | old_values=old_values | |
108 | ) |
|
108 | ) | |
109 |
|
109 | |||
110 | @LoginRequired() |
|
110 | @LoginRequired() | |
111 | @HasRepoGroupPermissionAnyDecorator('group.admin') |
|
111 | @HasRepoGroupPermissionAnyDecorator('group.admin') | |
112 | @view_config( |
|
112 | @view_config( | |
113 | route_name='edit_repo_group', request_method='GET', |
|
113 | route_name='edit_repo_group', request_method='GET', | |
114 | renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako') |
|
114 | renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako') | |
115 | def edit_settings(self): |
|
115 | def edit_settings(self): | |
116 | c = self.load_default_context() |
|
116 | c = self.load_default_context() | |
117 | c.active = 'settings' |
|
117 | c.active = 'settings' | |
118 |
|
118 | |||
119 | defaults = RepoGroupModel()._get_defaults(self.db_repo_group_name) |
|
119 | defaults = RepoGroupModel()._get_defaults(self.db_repo_group_name) | |
120 | defaults['repo_group_owner'] = defaults['user'] |
|
120 | defaults['repo_group_owner'] = defaults['user'] | |
121 |
|
121 | |||
122 | schema = self._get_schema(c) |
|
122 | schema = self._get_schema(c) | |
123 | c.form = RcForm(schema, appstruct=defaults) |
|
123 | c.form = RcForm(schema, appstruct=defaults) | |
124 | return self._get_template_context(c) |
|
124 | return self._get_template_context(c) | |
125 |
|
125 | |||
126 | @LoginRequired() |
|
126 | @LoginRequired() | |
127 | @HasRepoGroupPermissionAnyDecorator('group.admin') |
|
127 | @HasRepoGroupPermissionAnyDecorator('group.admin') | |
128 | @CSRFRequired() |
|
128 | @CSRFRequired() | |
129 | @view_config( |
|
129 | @view_config( | |
130 | route_name='edit_repo_group', request_method='POST', |
|
130 | route_name='edit_repo_group', request_method='POST', | |
131 | renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako') |
|
131 | renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako') | |
132 | def edit_settings_update(self): |
|
132 | def edit_settings_update(self): | |
133 | _ = self.request.translate |
|
133 | _ = self.request.translate | |
134 | c = self.load_default_context() |
|
134 | c = self.load_default_context() | |
135 | c.active = 'settings' |
|
135 | c.active = 'settings' | |
136 |
|
136 | |||
137 | old_repo_group_name = self.db_repo_group_name |
|
137 | old_repo_group_name = self.db_repo_group_name | |
138 | new_repo_group_name = old_repo_group_name |
|
138 | new_repo_group_name = old_repo_group_name | |
139 |
|
139 | |||
140 | old_values = RepoGroupModel()._get_defaults(self.db_repo_group_name) |
|
140 | old_values = RepoGroupModel()._get_defaults(self.db_repo_group_name) | |
141 | schema = self._get_schema(c, old_values=old_values) |
|
141 | schema = self._get_schema(c, old_values=old_values) | |
142 |
|
142 | |||
143 | c.form = RcForm(schema) |
|
143 | c.form = RcForm(schema) | |
144 | pstruct = self.request.POST.items() |
|
144 | pstruct = self.request.POST.items() | |
145 |
|
145 | |||
146 | try: |
|
146 | try: | |
147 | schema_data = c.form.validate(pstruct) |
|
147 | schema_data = c.form.validate(pstruct) | |
148 | except deform.ValidationFailure as err_form: |
|
148 | except deform.ValidationFailure as err_form: | |
149 | return self._get_template_context(c) |
|
149 | return self._get_template_context(c) | |
150 |
|
150 | |||
151 | # data is now VALID, proceed with updates |
|
151 | # data is now VALID, proceed with updates | |
152 | # save validated data back into the updates dict |
|
152 | # save validated data back into the updates dict | |
153 | validated_updates = dict( |
|
153 | validated_updates = dict( | |
154 | group_name=schema_data['repo_group']['repo_group_name_without_group'], |
|
154 | group_name=schema_data['repo_group']['repo_group_name_without_group'], | |
155 | group_parent_id=schema_data['repo_group']['repo_group_id'], |
|
155 | group_parent_id=schema_data['repo_group']['repo_group_id'], | |
156 | user=schema_data['repo_group_owner'], |
|
156 | user=schema_data['repo_group_owner'], | |
157 | group_description=schema_data['repo_group_description'], |
|
157 | group_description=schema_data['repo_group_description'], | |
158 | enable_locking=schema_data['repo_group_enable_locking'], |
|
158 | enable_locking=schema_data['repo_group_enable_locking'], | |
159 | ) |
|
159 | ) | |
160 |
|
160 | |||
161 | try: |
|
161 | try: | |
162 | RepoGroupModel().update(self.db_repo_group, validated_updates) |
|
162 | RepoGroupModel().update(self.db_repo_group, validated_updates) | |
163 |
|
163 | |||
164 | audit_logger.store_web( |
|
164 | audit_logger.store_web( | |
165 | 'repo_group.edit', action_data={'old_data': old_values}, |
|
165 | 'repo_group.edit', action_data={'old_data': old_values}, | |
166 | user=c.rhodecode_user) |
|
166 | user=c.rhodecode_user) | |
167 |
|
167 | |||
168 | Session().commit() |
|
168 | Session().commit() | |
169 |
|
169 | |||
170 | # use the new full name for redirect once we know we updated |
|
170 | # use the new full name for redirect once we know we updated | |
171 | # the name on filesystem and in DB |
|
171 | # the name on filesystem and in DB | |
172 | new_repo_group_name = schema_data['repo_group_name'] |
|
172 | new_repo_group_name = schema_data['repo_group']['repo_group_name_with_group'] | |
173 |
|
173 | |||
174 | h.flash(_('Repository Group `{}` updated successfully').format( |
|
174 | h.flash(_('Repository Group `{}` updated successfully').format( | |
175 | old_repo_group_name), category='success') |
|
175 | old_repo_group_name), category='success') | |
176 |
|
176 | |||
177 | except Exception: |
|
177 | except Exception: | |
178 | log.exception("Exception during update or repository group") |
|
178 | log.exception("Exception during update or repository group") | |
179 | h.flash(_('Error occurred during update of repository group %s') |
|
179 | h.flash(_('Error occurred during update of repository group %s') | |
180 | % old_repo_group_name, category='error') |
|
180 | % old_repo_group_name, category='error') | |
181 |
|
181 | |||
182 | raise HTTPFound( |
|
182 | raise HTTPFound( | |
183 | h.route_path('edit_repo_group', repo_group_name=new_repo_group_name)) |
|
183 | h.route_path('edit_repo_group', repo_group_name=new_repo_group_name)) |
@@ -1,284 +1,298 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2017 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-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 colander |
|
22 | import colander | |
23 | import deform.widget |
|
23 | import deform.widget | |
24 |
|
24 | |||
25 | from rhodecode.translation import _ |
|
25 | from rhodecode.translation import _ | |
26 | from rhodecode.model.validation_schema import validators, preparers, types |
|
26 | from rhodecode.model.validation_schema import validators, preparers, types | |
27 |
|
27 | |||
28 |
|
28 | |||
29 | def get_group_and_repo(repo_name): |
|
29 | def get_group_and_repo(repo_name): | |
30 | from rhodecode.model.repo_group import RepoGroupModel |
|
30 | from rhodecode.model.repo_group import RepoGroupModel | |
31 | return RepoGroupModel()._get_group_name_and_parent( |
|
31 | return RepoGroupModel()._get_group_name_and_parent( | |
32 | repo_name, get_object=True) |
|
32 | repo_name, get_object=True) | |
33 |
|
33 | |||
34 |
|
34 | |||
|
35 | def get_repo_group(repo_group_id): | |||
|
36 | from rhodecode.model.repo_group import RepoGroup | |||
|
37 | return RepoGroup.get(repo_group_id), RepoGroup.CHOICES_SEPARATOR | |||
|
38 | ||||
|
39 | ||||
35 | @colander.deferred |
|
40 | @colander.deferred | |
36 | def deferred_can_write_to_group_validator(node, kw): |
|
41 | def deferred_can_write_to_group_validator(node, kw): | |
37 | old_values = kw.get('old_values') or {} |
|
42 | old_values = kw.get('old_values') or {} | |
38 | request_user = kw.get('user') |
|
43 | request_user = kw.get('user') | |
39 |
|
44 | |||
40 | def can_write_group_validator(node, value): |
|
45 | def can_write_group_validator(node, value): | |
41 | from rhodecode.lib.auth import ( |
|
46 | from rhodecode.lib.auth import ( | |
42 | HasPermissionAny, HasRepoGroupPermissionAny) |
|
47 | HasPermissionAny, HasRepoGroupPermissionAny) | |
43 | from rhodecode.model.repo_group import RepoGroupModel |
|
48 | from rhodecode.model.repo_group import RepoGroupModel | |
44 |
|
49 | |||
45 | messages = { |
|
50 | messages = { | |
46 | 'invalid_parent_repo_group': |
|
51 | 'invalid_parent_repo_group': | |
47 | _(u"Parent repository group `{}` does not exist"), |
|
52 | _(u"Parent repository group `{}` does not exist"), | |
48 | # permissions denied we expose as not existing, to prevent |
|
53 | # permissions denied we expose as not existing, to prevent | |
49 | # resource discovery |
|
54 | # resource discovery | |
50 | 'permission_denied_parent_group': |
|
55 | 'permission_denied_parent_group': | |
51 | _(u"Parent repository group `{}` does not exist"), |
|
56 | _(u"Parent repository group `{}` does not exist"), | |
52 | 'permission_denied_root': |
|
57 | 'permission_denied_root': | |
53 | _(u"You do not have the permission to store " |
|
58 | _(u"You do not have the permission to store " | |
54 | u"repository groups in the root location.") |
|
59 | u"repository groups in the root location.") | |
55 | } |
|
60 | } | |
56 |
|
61 | |||
57 | value = value['repo_group_name'] |
|
62 | value = value['repo_group_name'] | |
58 | parent_group_name = value |
|
63 | parent_group_name = value | |
59 |
|
64 | |||
60 | is_root_location = value is types.RootLocation |
|
65 | is_root_location = value is types.RootLocation | |
61 |
|
66 | |||
62 | # NOT initialized validators, we must call them |
|
67 | # NOT initialized validators, we must call them | |
63 | can_create_repo_groups_at_root = HasPermissionAny( |
|
68 | can_create_repo_groups_at_root = HasPermissionAny( | |
64 | 'hg.admin', 'hg.repogroup.create.true') |
|
69 | 'hg.admin', 'hg.repogroup.create.true') | |
65 |
|
70 | |||
66 | if is_root_location: |
|
71 | if is_root_location: | |
67 | if can_create_repo_groups_at_root(user=request_user): |
|
72 | if can_create_repo_groups_at_root(user=request_user): | |
68 | # we can create repo group inside tool-level. No more checks |
|
73 | # we can create repo group inside tool-level. No more checks | |
69 | # are required |
|
74 | # are required | |
70 | return |
|
75 | return | |
71 | else: |
|
76 | else: | |
72 | raise colander.Invalid(node, messages['permission_denied_root']) |
|
77 | raise colander.Invalid(node, messages['permission_denied_root']) | |
73 |
|
78 | |||
74 | # check if the parent repo group actually exists |
|
79 | # check if the parent repo group actually exists | |
75 | parent_group = None |
|
80 | parent_group = None | |
76 | if parent_group_name: |
|
81 | if parent_group_name: | |
77 | parent_group = RepoGroupModel().get_by_group_name(parent_group_name) |
|
82 | parent_group = RepoGroupModel().get_by_group_name(parent_group_name) | |
78 | if value and not parent_group: |
|
83 | if value and not parent_group: | |
79 | raise colander.Invalid( |
|
84 | raise colander.Invalid( | |
80 | node, messages['invalid_parent_repo_group'].format( |
|
85 | node, messages['invalid_parent_repo_group'].format( | |
81 | parent_group_name)) |
|
86 | parent_group_name)) | |
82 |
|
87 | |||
83 | # check if we have permissions to create new groups under |
|
88 | # check if we have permissions to create new groups under | |
84 | # parent repo group |
|
89 | # parent repo group | |
85 | # create repositories with write permission on group is set to true |
|
90 | # create repositories with write permission on group is set to true | |
86 | create_on_write = HasPermissionAny( |
|
91 | create_on_write = HasPermissionAny( | |
87 | 'hg.create.write_on_repogroup.true')(user=request_user) |
|
92 | 'hg.create.write_on_repogroup.true')(user=request_user) | |
88 |
|
93 | |||
89 | group_admin = HasRepoGroupPermissionAny('group.admin')( |
|
94 | group_admin = HasRepoGroupPermissionAny('group.admin')( | |
90 | parent_group_name, 'can write into group validator', user=request_user) |
|
95 | parent_group_name, 'can write into group validator', user=request_user) | |
91 | group_write = HasRepoGroupPermissionAny('group.write')( |
|
96 | group_write = HasRepoGroupPermissionAny('group.write')( | |
92 | parent_group_name, 'can write into group validator', user=request_user) |
|
97 | parent_group_name, 'can write into group validator', user=request_user) | |
93 |
|
98 | |||
94 | # creation by write access is currently disabled. Needs thinking if |
|
99 | # creation by write access is currently disabled. Needs thinking if | |
95 | # we want to allow this... |
|
100 | # we want to allow this... | |
96 | forbidden = not (group_admin or (group_write and create_on_write and 0)) |
|
101 | forbidden = not (group_admin or (group_write and create_on_write and 0)) | |
97 |
|
102 | |||
98 | if parent_group and forbidden: |
|
103 | if parent_group and forbidden: | |
99 | msg = messages['permission_denied_parent_group'].format( |
|
104 | msg = messages['permission_denied_parent_group'].format( | |
100 | parent_group_name) |
|
105 | parent_group_name) | |
101 | raise colander.Invalid(node, msg) |
|
106 | raise colander.Invalid(node, msg) | |
102 |
|
107 | |||
103 | return can_write_group_validator |
|
108 | return can_write_group_validator | |
104 |
|
109 | |||
105 |
|
110 | |||
106 | @colander.deferred |
|
111 | @colander.deferred | |
107 | def deferred_repo_group_owner_validator(node, kw): |
|
112 | def deferred_repo_group_owner_validator(node, kw): | |
108 |
|
113 | |||
109 | def repo_owner_validator(node, value): |
|
114 | def repo_owner_validator(node, value): | |
110 | from rhodecode.model.db import User |
|
115 | from rhodecode.model.db import User | |
111 | existing = User.get_by_username(value) |
|
116 | existing = User.get_by_username(value) | |
112 | if not existing: |
|
117 | if not existing: | |
113 | msg = _(u'Repo group owner with id `{}` does not exists').format( |
|
118 | msg = _(u'Repo group owner with id `{}` does not exists').format( | |
114 | value) |
|
119 | value) | |
115 | raise colander.Invalid(node, msg) |
|
120 | raise colander.Invalid(node, msg) | |
116 |
|
121 | |||
117 | return repo_owner_validator |
|
122 | return repo_owner_validator | |
118 |
|
123 | |||
119 |
|
124 | |||
120 | @colander.deferred |
|
125 | @colander.deferred | |
121 | def deferred_unique_name_validator(node, kw): |
|
126 | def deferred_unique_name_validator(node, kw): | |
122 | request_user = kw.get('user') |
|
127 | request_user = kw.get('user') | |
123 | old_values = kw.get('old_values') or {} |
|
128 | old_values = kw.get('old_values') or {} | |
124 |
|
129 | |||
125 | def unique_name_validator(node, value): |
|
130 | def unique_name_validator(node, value): | |
126 | from rhodecode.model.db import Repository, RepoGroup |
|
131 | from rhodecode.model.db import Repository, RepoGroup | |
127 | name_changed = value != old_values.get('group_name') |
|
132 | name_changed = value != old_values.get('group_name') | |
128 |
|
133 | |||
129 | existing = Repository.get_by_repo_name(value) |
|
134 | existing = Repository.get_by_repo_name(value) | |
130 | if name_changed and existing: |
|
135 | if name_changed and existing: | |
131 | msg = _(u'Repository with name `{}` already exists').format(value) |
|
136 | msg = _(u'Repository with name `{}` already exists').format(value) | |
132 | raise colander.Invalid(node, msg) |
|
137 | raise colander.Invalid(node, msg) | |
133 |
|
138 | |||
134 | existing_group = RepoGroup.get_by_group_name(value) |
|
139 | existing_group = RepoGroup.get_by_group_name(value) | |
135 | if name_changed and existing_group: |
|
140 | if name_changed and existing_group: | |
136 | msg = _(u'Repository group with name `{}` already exists').format( |
|
141 | msg = _(u'Repository group with name `{}` already exists').format( | |
137 | value) |
|
142 | value) | |
138 | raise colander.Invalid(node, msg) |
|
143 | raise colander.Invalid(node, msg) | |
139 | return unique_name_validator |
|
144 | return unique_name_validator | |
140 |
|
145 | |||
141 |
|
146 | |||
142 | @colander.deferred |
|
147 | @colander.deferred | |
143 | def deferred_repo_group_name_validator(node, kw): |
|
148 | def deferred_repo_group_name_validator(node, kw): | |
144 | return validators.valid_name_validator |
|
149 | return validators.valid_name_validator | |
145 |
|
150 | |||
146 |
|
151 | |||
147 | @colander.deferred |
|
152 | @colander.deferred | |
148 | def deferred_repo_group_validator(node, kw): |
|
153 | def deferred_repo_group_validator(node, kw): | |
149 | options = kw.get( |
|
154 | options = kw.get( | |
150 | 'repo_group_repo_group_options') |
|
155 | 'repo_group_repo_group_options') | |
151 | return colander.OneOf([x for x in options]) |
|
156 | return colander.OneOf([x for x in options]) | |
152 |
|
157 | |||
153 |
|
158 | |||
154 | @colander.deferred |
|
159 | @colander.deferred | |
155 | def deferred_repo_group_widget(node, kw): |
|
160 | def deferred_repo_group_widget(node, kw): | |
156 | items = kw.get('repo_group_repo_group_items') |
|
161 | items = kw.get('repo_group_repo_group_items') | |
157 | return deform.widget.Select2Widget(values=items) |
|
162 | return deform.widget.Select2Widget(values=items) | |
158 |
|
163 | |||
159 |
|
164 | |||
160 | class GroupType(colander.Mapping): |
|
165 | class GroupType(colander.Mapping): | |
161 | def _validate(self, node, value): |
|
166 | def _validate(self, node, value): | |
162 | try: |
|
167 | try: | |
163 | return dict(repo_group_name=value) |
|
168 | return dict(repo_group_name=value) | |
164 | except Exception as e: |
|
169 | except Exception as e: | |
165 | raise colander.Invalid( |
|
170 | raise colander.Invalid( | |
166 | node, '"${val}" is not a mapping type: ${err}'.format( |
|
171 | node, '"${val}" is not a mapping type: ${err}'.format( | |
167 | val=value, err=e)) |
|
172 | val=value, err=e)) | |
168 |
|
173 | |||
169 | def deserialize(self, node, cstruct): |
|
174 | def deserialize(self, node, cstruct): | |
170 | if cstruct is colander.null: |
|
175 | if cstruct is colander.null: | |
171 | return cstruct |
|
176 | return cstruct | |
172 |
|
177 | |||
173 | appstruct = super(GroupType, self).deserialize(node, cstruct) |
|
178 | appstruct = super(GroupType, self).deserialize(node, cstruct) | |
174 | validated_name = appstruct['repo_group_name'] |
|
179 | validated_name = appstruct['repo_group_name'] | |
175 |
|
180 | |||
176 | # inject group based on once deserialized data |
|
181 | # inject group based on once deserialized data | |
177 | (repo_group_name_without_group, |
|
182 | (repo_group_name_without_group, | |
178 | parent_group_name, |
|
183 | parent_group_name, | |
179 | parent_group) = get_group_and_repo(validated_name) |
|
184 | parent_group) = get_group_and_repo(validated_name) | |
180 |
|
185 | |||
|
186 | appstruct['repo_group_name_with_group'] = validated_name | |||
181 | appstruct['repo_group_name_without_group'] = repo_group_name_without_group |
|
187 | appstruct['repo_group_name_without_group'] = repo_group_name_without_group | |
182 | appstruct['repo_group_name'] = parent_group_name or types.RootLocation |
|
188 | appstruct['repo_group_name'] = parent_group_name or types.RootLocation | |
183 | if parent_group: |
|
189 | if parent_group: | |
184 | appstruct['repo_group_id'] = parent_group.group_id |
|
190 | appstruct['repo_group_id'] = parent_group.group_id | |
185 |
|
191 | |||
186 | return appstruct |
|
192 | return appstruct | |
187 |
|
193 | |||
188 |
|
194 | |||
189 | class GroupSchema(colander.SchemaNode): |
|
195 | class GroupSchema(colander.SchemaNode): | |
190 | schema_type = GroupType |
|
196 | schema_type = GroupType | |
191 | validator = deferred_can_write_to_group_validator |
|
197 | validator = deferred_can_write_to_group_validator | |
192 | missing = colander.null |
|
198 | missing = colander.null | |
193 |
|
199 | |||
194 |
|
200 | |||
195 | class RepoGroup(GroupSchema): |
|
201 | class RepoGroup(GroupSchema): | |
196 | repo_group_name = colander.SchemaNode( |
|
202 | repo_group_name = colander.SchemaNode( | |
197 | types.GroupNameType()) |
|
203 | types.GroupNameType()) | |
198 | repo_group_id = colander.SchemaNode( |
|
204 | repo_group_id = colander.SchemaNode( | |
199 | colander.String(), missing=None) |
|
205 | colander.String(), missing=None) | |
200 | repo_group_name_without_group = colander.SchemaNode( |
|
206 | repo_group_name_without_group = colander.SchemaNode( | |
201 | colander.String(), missing=None) |
|
207 | colander.String(), missing=None) | |
202 |
|
208 | |||
203 |
|
209 | |||
204 | class RepoGroupAccessSchema(colander.MappingSchema): |
|
210 | class RepoGroupAccessSchema(colander.MappingSchema): | |
205 | repo_group = RepoGroup() |
|
211 | repo_group = RepoGroup() | |
206 |
|
212 | |||
207 |
|
213 | |||
208 | class RepoGroupNameUniqueSchema(colander.MappingSchema): |
|
214 | class RepoGroupNameUniqueSchema(colander.MappingSchema): | |
209 | unique_repo_group_name = colander.SchemaNode( |
|
215 | unique_repo_group_name = colander.SchemaNode( | |
210 | colander.String(), |
|
216 | colander.String(), | |
211 | validator=deferred_unique_name_validator) |
|
217 | validator=deferred_unique_name_validator) | |
212 |
|
218 | |||
213 |
|
219 | |||
214 | class RepoGroupSchema(colander.Schema): |
|
220 | class RepoGroupSchema(colander.Schema): | |
215 |
|
221 | |||
216 | repo_group_name = colander.SchemaNode( |
|
222 | repo_group_name = colander.SchemaNode( | |
217 | types.GroupNameType(), |
|
223 | types.GroupNameType(), | |
218 | validator=deferred_repo_group_name_validator) |
|
224 | validator=deferred_repo_group_name_validator) | |
219 |
|
225 | |||
220 | repo_group_owner = colander.SchemaNode( |
|
226 | repo_group_owner = colander.SchemaNode( | |
221 | colander.String(), |
|
227 | colander.String(), | |
222 | validator=deferred_repo_group_owner_validator) |
|
228 | validator=deferred_repo_group_owner_validator) | |
223 |
|
229 | |||
224 | repo_group_description = colander.SchemaNode( |
|
230 | repo_group_description = colander.SchemaNode( | |
225 | colander.String(), missing='', widget=deform.widget.TextAreaWidget()) |
|
231 | colander.String(), missing='', widget=deform.widget.TextAreaWidget()) | |
226 |
|
232 | |||
227 | repo_group_copy_permissions = colander.SchemaNode( |
|
233 | repo_group_copy_permissions = colander.SchemaNode( | |
228 | types.StringBooleanType(), |
|
234 | types.StringBooleanType(), | |
229 | missing=False, widget=deform.widget.CheckboxWidget()) |
|
235 | missing=False, widget=deform.widget.CheckboxWidget()) | |
230 |
|
236 | |||
231 | repo_group_enable_locking = colander.SchemaNode( |
|
237 | repo_group_enable_locking = colander.SchemaNode( | |
232 | types.StringBooleanType(), |
|
238 | types.StringBooleanType(), | |
233 | missing=False, widget=deform.widget.CheckboxWidget()) |
|
239 | missing=False, widget=deform.widget.CheckboxWidget()) | |
234 |
|
240 | |||
235 | def deserialize(self, cstruct): |
|
241 | def deserialize(self, cstruct): | |
236 | """ |
|
242 | """ | |
237 | Custom deserialize that allows to chain validation, and verify |
|
243 | Custom deserialize that allows to chain validation, and verify | |
238 | permissions, and as last step uniqueness |
|
244 | permissions, and as last step uniqueness | |
239 | """ |
|
245 | """ | |
240 |
|
246 | |||
241 | appstruct = super(RepoGroupSchema, self).deserialize(cstruct) |
|
247 | appstruct = super(RepoGroupSchema, self).deserialize(cstruct) | |
242 | validated_name = appstruct['repo_group_name'] |
|
248 | validated_name = appstruct['repo_group_name'] | |
243 |
|
249 | |||
244 | # second pass to validate permissions to repo_group |
|
250 | # second pass to validate permissions to repo_group | |
245 | second = RepoGroupAccessSchema().bind(**self.bindings) |
|
251 | second = RepoGroupAccessSchema().bind(**self.bindings) | |
246 | appstruct_second = second.deserialize({'repo_group': validated_name}) |
|
252 | appstruct_second = second.deserialize({'repo_group': validated_name}) | |
247 | # save result |
|
253 | # save result | |
248 | appstruct['repo_group'] = appstruct_second['repo_group'] |
|
254 | appstruct['repo_group'] = appstruct_second['repo_group'] | |
249 |
|
255 | |||
250 | # thirds to validate uniqueness |
|
256 | # thirds to validate uniqueness | |
251 | third = RepoGroupNameUniqueSchema().bind(**self.bindings) |
|
257 | third = RepoGroupNameUniqueSchema().bind(**self.bindings) | |
252 | third.deserialize({'unique_repo_group_name': validated_name}) |
|
258 | third.deserialize({'unique_repo_group_name': validated_name}) | |
253 |
|
259 | |||
254 | return appstruct |
|
260 | return appstruct | |
255 |
|
261 | |||
256 |
|
262 | |||
257 | class RepoGroupSettingsSchema(RepoGroupSchema): |
|
263 | class RepoGroupSettingsSchema(RepoGroupSchema): | |
258 | repo_group = colander.SchemaNode( |
|
264 | repo_group = colander.SchemaNode( | |
259 | colander.Integer(), |
|
265 | colander.Integer(), | |
260 | validator=deferred_repo_group_validator, |
|
266 | validator=deferred_repo_group_validator, | |
261 | widget=deferred_repo_group_widget, |
|
267 | widget=deferred_repo_group_widget, | |
262 | missing='') |
|
268 | missing='') | |
263 |
|
269 | |||
264 | def deserialize(self, cstruct): |
|
270 | def deserialize(self, cstruct): | |
265 | """ |
|
271 | """ | |
266 | Custom deserialize that allows to chain validation, and verify |
|
272 | Custom deserialize that allows to chain validation, and verify | |
267 | permissions, and as last step uniqueness |
|
273 | permissions, and as last step uniqueness | |
268 | """ |
|
274 | """ | |
269 |
|
275 | |||
270 | # first pass, to validate given data |
|
276 | # first pass, to validate given data | |
271 | appstruct = super(RepoGroupSchema, self).deserialize(cstruct) |
|
277 | appstruct = super(RepoGroupSchema, self).deserialize(cstruct) | |
272 | validated_name = appstruct['repo_group_name'] |
|
278 | validated_name = appstruct['repo_group_name'] | |
273 |
|
279 | |||
|
280 | # because of repoSchema adds repo-group as an ID, we inject it as | |||
|
281 | # full name here because validators require it, it's unwrapped later | |||
|
282 | # so it's safe to use and final name is going to be without group anyway | |||
|
283 | ||||
|
284 | group, separator = get_repo_group(appstruct['repo_group']) | |||
|
285 | if group: | |||
|
286 | validated_name = separator.join([group.group_name, validated_name]) | |||
|
287 | ||||
274 | # second pass to validate permissions to repo_group |
|
288 | # second pass to validate permissions to repo_group | |
275 | second = RepoGroupAccessSchema().bind(**self.bindings) |
|
289 | second = RepoGroupAccessSchema().bind(**self.bindings) | |
276 | appstruct_second = second.deserialize({'repo_group': validated_name}) |
|
290 | appstruct_second = second.deserialize({'repo_group': validated_name}) | |
277 | # save result |
|
291 | # save result | |
278 | appstruct['repo_group'] = appstruct_second['repo_group'] |
|
292 | appstruct['repo_group'] = appstruct_second['repo_group'] | |
279 |
|
293 | |||
280 | # thirds to validate uniqueness |
|
294 | # thirds to validate uniqueness | |
281 | third = RepoGroupNameUniqueSchema().bind(**self.bindings) |
|
295 | third = RepoGroupNameUniqueSchema().bind(**self.bindings) | |
282 | third.deserialize({'unique_repo_group_name': validated_name}) |
|
296 | third.deserialize({'unique_repo_group_name': validated_name}) | |
283 |
|
297 | |||
284 | return appstruct |
|
298 | return appstruct |
@@ -1,116 +1,118 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2017 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-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 colander |
|
21 | import colander | |
22 | import pytest |
|
22 | import pytest | |
23 |
|
23 | |||
24 | from rhodecode.model.validation_schema import types |
|
24 | from rhodecode.model.validation_schema import types | |
25 | from rhodecode.model.validation_schema.schemas import repo_group_schema |
|
25 | from rhodecode.model.validation_schema.schemas import repo_group_schema | |
26 |
|
26 | |||
27 |
|
27 | |||
28 | class TestRepoGroupSchema(object): |
|
28 | class TestRepoGroupSchema(object): | |
29 |
|
29 | |||
30 | @pytest.mark.parametrize('given, expected', [ |
|
30 | @pytest.mark.parametrize('given, expected', [ | |
31 | ('my repo', 'my-repo'), |
|
31 | ('my repo', 'my-repo'), | |
32 | (' hello world mike ', 'hello-world-mike'), |
|
32 | (' hello world mike ', 'hello-world-mike'), | |
33 |
|
33 | |||
34 | ('//group1/group2//', 'group1/group2'), |
|
34 | ('//group1/group2//', 'group1/group2'), | |
35 | ('//group1///group2//', 'group1/group2'), |
|
35 | ('//group1///group2//', 'group1/group2'), | |
36 | ('///group1/group2///group3', 'group1/group2/group3'), |
|
36 | ('///group1/group2///group3', 'group1/group2/group3'), | |
37 | ('word g1/group2///group3', 'word-g1/group2/group3'), |
|
37 | ('word g1/group2///group3', 'word-g1/group2/group3'), | |
38 |
|
38 | |||
39 | ('grou p1/gro;,,##up2//.../group3', 'grou-p1/group2/group3'), |
|
39 | ('grou p1/gro;,,##up2//.../group3', 'grou-p1/group2/group3'), | |
40 |
|
40 | |||
41 | ('group,,,/,,,/1/2/3', 'group/1/2/3'), |
|
41 | ('group,,,/,,,/1/2/3', 'group/1/2/3'), | |
42 | ('grou[]p1/gro;up2///gro up3', 'group1/group2/gro-up3'), |
|
42 | ('grou[]p1/gro;up2///gro up3', 'group1/group2/gro-up3'), | |
43 | (u'grou[]p1/gro;up2///gro up3/Δ Δ', u'group1/group2/gro-up3/Δ Δ'), |
|
43 | (u'grou[]p1/gro;up2///gro up3/Δ Δ', u'group1/group2/gro-up3/Δ Δ'), | |
44 | ]) |
|
44 | ]) | |
45 | def test_deserialize_repo_name(self, app, user_admin, given, expected): |
|
45 | def test_deserialize_repo_name(self, app, user_admin, given, expected): | |
46 | schema = repo_group_schema.RepoGroupSchema().bind() |
|
46 | schema = repo_group_schema.RepoGroupSchema().bind() | |
47 | assert schema.get('repo_group_name').deserialize(given) == expected |
|
47 | assert schema.get('repo_group_name').deserialize(given) == expected | |
48 |
|
48 | |||
49 | def test_deserialize(self, app, user_admin): |
|
49 | def test_deserialize(self, app, user_admin): | |
50 | schema = repo_group_schema.RepoGroupSchema().bind( |
|
50 | schema = repo_group_schema.RepoGroupSchema().bind( | |
51 | user=user_admin |
|
51 | user=user_admin | |
52 | ) |
|
52 | ) | |
53 |
|
53 | |||
54 | schema_data = schema.deserialize(dict( |
|
54 | schema_data = schema.deserialize(dict( | |
55 | repo_group_name='my_schema_group', |
|
55 | repo_group_name='my_schema_group', | |
56 | repo_group_owner=user_admin.username |
|
56 | repo_group_owner=user_admin.username | |
57 | )) |
|
57 | )) | |
58 |
|
58 | |||
59 | assert schema_data['repo_group_name'] == u'my_schema_group' |
|
59 | assert schema_data['repo_group_name'] == u'my_schema_group' | |
60 | assert schema_data['repo_group'] == { |
|
60 | assert schema_data['repo_group'] == { | |
61 | 'repo_group_id': None, |
|
61 | 'repo_group_id': None, | |
62 | 'repo_group_name': types.RootLocation, |
|
62 | 'repo_group_name': types.RootLocation, | |
|
63 | 'repo_group_name_with_group': u'my_schema_group', | |||
63 | 'repo_group_name_without_group': u'my_schema_group'} |
|
64 | 'repo_group_name_without_group': u'my_schema_group'} | |
64 |
|
65 | |||
65 | @pytest.mark.parametrize('given, err_key, expected_exc', [ |
|
66 | @pytest.mark.parametrize('given, err_key, expected_exc', [ | |
66 | ('xxx/my_schema_group', 'repo_group', 'Parent repository group `xxx` does not exist'), |
|
67 | ('xxx/my_schema_group', 'repo_group', 'Parent repository group `xxx` does not exist'), | |
67 | ('', 'repo_group_name', 'Name must start with a letter or number. Got ``'), |
|
68 | ('', 'repo_group_name', 'Name must start with a letter or number. Got ``'), | |
68 | ]) |
|
69 | ]) | |
69 | def test_deserialize_with_bad_group_name( |
|
70 | def test_deserialize_with_bad_group_name( | |
70 | self, app, user_admin, given, err_key, expected_exc): |
|
71 | self, app, user_admin, given, err_key, expected_exc): | |
71 | schema = repo_group_schema.RepoGroupSchema().bind( |
|
72 | schema = repo_group_schema.RepoGroupSchema().bind( | |
72 | repo_type_options=['hg'], |
|
73 | repo_type_options=['hg'], | |
73 | user=user_admin |
|
74 | user=user_admin | |
74 | ) |
|
75 | ) | |
75 |
|
76 | |||
76 | with pytest.raises(colander.Invalid) as excinfo: |
|
77 | with pytest.raises(colander.Invalid) as excinfo: | |
77 | schema.deserialize(dict( |
|
78 | schema.deserialize(dict( | |
78 | repo_group_name=given, |
|
79 | repo_group_name=given, | |
79 | repo_group_owner=user_admin.username |
|
80 | repo_group_owner=user_admin.username | |
80 | )) |
|
81 | )) | |
81 |
|
82 | |||
82 | assert excinfo.value.asdict()[err_key] == expected_exc |
|
83 | assert excinfo.value.asdict()[err_key] == expected_exc | |
83 |
|
84 | |||
84 | def test_deserialize_with_group_name(self, app, user_admin, test_repo_group): |
|
85 | def test_deserialize_with_group_name(self, app, user_admin, test_repo_group): | |
85 | schema = repo_group_schema.RepoGroupSchema().bind( |
|
86 | schema = repo_group_schema.RepoGroupSchema().bind( | |
86 | user=user_admin |
|
87 | user=user_admin | |
87 | ) |
|
88 | ) | |
88 |
|
89 | |||
89 | full_name = test_repo_group.group_name + u'/my_schema_group' |
|
90 | full_name = test_repo_group.group_name + u'/my_schema_group' | |
90 | schema_data = schema.deserialize(dict( |
|
91 | schema_data = schema.deserialize(dict( | |
91 | repo_group_name=full_name, |
|
92 | repo_group_name=full_name, | |
92 | repo_group_owner=user_admin.username |
|
93 | repo_group_owner=user_admin.username | |
93 | )) |
|
94 | )) | |
94 |
|
95 | |||
95 | assert schema_data['repo_group_name'] == full_name |
|
96 | assert schema_data['repo_group_name'] == full_name | |
96 | assert schema_data['repo_group'] == { |
|
97 | assert schema_data['repo_group'] == { | |
97 | 'repo_group_id': test_repo_group.group_id, |
|
98 | 'repo_group_id': test_repo_group.group_id, | |
98 | 'repo_group_name': test_repo_group.group_name, |
|
99 | 'repo_group_name': test_repo_group.group_name, | |
|
100 | 'repo_group_name_with_group': full_name, | |||
99 | 'repo_group_name_without_group': u'my_schema_group'} |
|
101 | 'repo_group_name_without_group': u'my_schema_group'} | |
100 |
|
102 | |||
101 | def test_deserialize_with_group_name_regular_user_no_perms( |
|
103 | def test_deserialize_with_group_name_regular_user_no_perms( | |
102 | self, app, user_regular, test_repo_group): |
|
104 | self, app, user_regular, test_repo_group): | |
103 | schema = repo_group_schema.RepoGroupSchema().bind( |
|
105 | schema = repo_group_schema.RepoGroupSchema().bind( | |
104 | user=user_regular |
|
106 | user=user_regular | |
105 | ) |
|
107 | ) | |
106 |
|
108 | |||
107 | full_name = test_repo_group.group_name + u'/my_schema_group' |
|
109 | full_name = test_repo_group.group_name + u'/my_schema_group' | |
108 | with pytest.raises(colander.Invalid) as excinfo: |
|
110 | with pytest.raises(colander.Invalid) as excinfo: | |
109 | schema.deserialize(dict( |
|
111 | schema.deserialize(dict( | |
110 | repo_group_name=full_name, |
|
112 | repo_group_name=full_name, | |
111 | repo_group_owner=user_regular.username |
|
113 | repo_group_owner=user_regular.username | |
112 | )) |
|
114 | )) | |
113 |
|
115 | |||
114 | expected = 'Parent repository group `{}` does not exist'.format( |
|
116 | expected = 'Parent repository group `{}` does not exist'.format( | |
115 | test_repo_group.group_name) |
|
117 | test_repo_group.group_name) | |
116 | assert excinfo.value.asdict()['repo_group'] == expected |
|
118 | assert excinfo.value.asdict()['repo_group'] == expected |
General Comments 0
You need to be logged in to leave comments.
Login now