##// END OF EJS Templates
admin: cleanup imports.
marcink -
r2079:9c79ce85 default
parent child Browse files
Show More
@@ -1,73 +1,72 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 logging
21 import logging
22
22
23 from pyramid.view import view_config
23 from pyramid.view import view_config
24 from sqlalchemy.orm import joinedload
25
24
26 from rhodecode.apps._base import BaseAppView
25 from rhodecode.apps._base import BaseAppView
27 from rhodecode.model.db import UserLog
26 from rhodecode.model.db import joinedload, UserLog
28 from rhodecode.lib.user_log_filter import user_log_filter
27 from rhodecode.lib.user_log_filter import user_log_filter
29 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
28 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
30 from rhodecode.lib.utils2 import safe_int
29 from rhodecode.lib.utils2 import safe_int
31 from rhodecode.lib.helpers import Page
30 from rhodecode.lib.helpers import Page
32
31
33 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
34
33
35
34
36 class AdminAuditLogsView(BaseAppView):
35 class AdminAuditLogsView(BaseAppView):
37 def load_default_context(self):
36 def load_default_context(self):
38 c = self._get_local_tmpl_context()
37 c = self._get_local_tmpl_context()
39 self._register_global_c(c)
38 self._register_global_c(c)
40 return c
39 return c
41
40
42 @LoginRequired()
41 @LoginRequired()
43 @HasPermissionAllDecorator('hg.admin')
42 @HasPermissionAllDecorator('hg.admin')
44 @view_config(
43 @view_config(
45 route_name='admin_audit_logs', request_method='GET',
44 route_name='admin_audit_logs', request_method='GET',
46 renderer='rhodecode:templates/admin/admin_audit_logs.mako')
45 renderer='rhodecode:templates/admin/admin_audit_logs.mako')
47 def admin_audit_logs(self):
46 def admin_audit_logs(self):
48 c = self.load_default_context()
47 c = self.load_default_context()
49
48
50 users_log = UserLog.query()\
49 users_log = UserLog.query()\
51 .options(joinedload(UserLog.user))\
50 .options(joinedload(UserLog.user))\
52 .options(joinedload(UserLog.repository))
51 .options(joinedload(UserLog.repository))
53
52
54 # FILTERING
53 # FILTERING
55 c.search_term = self.request.GET.get('filter')
54 c.search_term = self.request.GET.get('filter')
56 try:
55 try:
57 users_log = user_log_filter(users_log, c.search_term)
56 users_log = user_log_filter(users_log, c.search_term)
58 except Exception:
57 except Exception:
59 # we want this to crash for now
58 # we want this to crash for now
60 raise
59 raise
61
60
62 users_log = users_log.order_by(UserLog.action_date.desc())
61 users_log = users_log.order_by(UserLog.action_date.desc())
63
62
64 p = safe_int(self.request.GET.get('page', 1), 1)
63 p = safe_int(self.request.GET.get('page', 1), 1)
65
64
66 def url_generator(**kw):
65 def url_generator(**kw):
67 if c.search_term:
66 if c.search_term:
68 kw['filter'] = c.search_term
67 kw['filter'] = c.search_term
69 return self.request.current_route_path(_query=kw)
68 return self.request.current_route_path(_query=kw)
70
69
71 c.audit_logs = Page(users_log, page=p, items_per_page=10,
70 c.audit_logs = Page(users_log, page=p, items_per_page=10,
72 url=url_generator)
71 url=url_generator)
73 return self._get_template_context(c)
72 return self._get_template_context(c)
@@ -1,65 +1,64 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 logging
21 import logging
22
22
23
24 from pyramid.httpexceptions import HTTPFound
23 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
24 from pyramid.view import view_config
26
25
27 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
28 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
30 from rhodecode.model.db import PullRequest
29 from rhodecode.model.db import PullRequest
31
30
32
31
33 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
34
33
35
34
36 class AdminMainView(BaseAppView):
35 class AdminMainView(BaseAppView):
37
36
38 @LoginRequired()
37 @LoginRequired()
39 @HasPermissionAllDecorator('hg.admin')
38 @HasPermissionAllDecorator('hg.admin')
40 @view_config(
39 @view_config(
41 route_name='admin_home', request_method='GET')
40 route_name='admin_home', request_method='GET')
42 def admin_main(self):
41 def admin_main(self):
43 # redirect _admin to audit logs...
42 # redirect _admin to audit logs...
44 raise HTTPFound(h.route_path('admin_audit_logs'))
43 raise HTTPFound(h.route_path('admin_audit_logs'))
45
44
46 @LoginRequired()
45 @LoginRequired()
47 @view_config(route_name='pull_requests_global_0', request_method='GET')
46 @view_config(route_name='pull_requests_global_0', request_method='GET')
48 @view_config(route_name='pull_requests_global_1', request_method='GET')
47 @view_config(route_name='pull_requests_global_1', request_method='GET')
49 @view_config(route_name='pull_requests_global', request_method='GET')
48 @view_config(route_name='pull_requests_global', request_method='GET')
50 def pull_requests(self):
49 def pull_requests(self):
51 """
50 """
52 Global redirect for Pull Requests
51 Global redirect for Pull Requests
53
52
54 :param pull_request_id: id of pull requests in the system
53 :param pull_request_id: id of pull requests in the system
55 """
54 """
56
55
57 pull_request = PullRequest.get_or_404(
56 pull_request = PullRequest.get_or_404(
58 self.request.matchdict['pull_request_id'])
57 self.request.matchdict['pull_request_id'])
59 pull_request_id = pull_request.pull_request_id
58 pull_request_id = pull_request.pull_request_id
60
59
61 repo_name = pull_request.target_repo.repo_name
60 repo_name = pull_request.target_repo.repo_name
62
61
63 raise HTTPFound(
62 raise HTTPFound(
64 h.route_path('pullrequest_show', repo_name=repo_name,
63 h.route_path('pullrequest_show', repo_name=repo_name,
65 pull_request_id=pull_request_id))
64 pull_request_id=pull_request_id))
@@ -1,54 +1,53 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 collections
21 import collections
22 import logging
22 import logging
23
23
24
25 from pyramid.view import view_config
24 from pyramid.view import view_config
26
25
27 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
28 from rhodecode.apps.admin.navigation import navigation_list
27 from rhodecode.apps.admin.navigation import navigation_list
29 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
28 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
30 from rhodecode.lib.utils import read_opensource_licenses
29 from rhodecode.lib.utils import read_opensource_licenses
31
30
32 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
33
32
34
33
35 class OpenSourceLicensesAdminSettingsView(BaseAppView):
34 class OpenSourceLicensesAdminSettingsView(BaseAppView):
36
35
37 def load_default_context(self):
36 def load_default_context(self):
38 c = self._get_local_tmpl_context()
37 c = self._get_local_tmpl_context()
39 self._register_global_c(c)
38 self._register_global_c(c)
40 return c
39 return c
41
40
42 @LoginRequired()
41 @LoginRequired()
43 @HasPermissionAllDecorator('hg.admin')
42 @HasPermissionAllDecorator('hg.admin')
44 @view_config(
43 @view_config(
45 route_name='admin_settings_open_source', request_method='GET',
44 route_name='admin_settings_open_source', request_method='GET',
46 renderer='rhodecode:templates/admin/settings/settings.mako')
45 renderer='rhodecode:templates/admin/settings/settings.mako')
47 def open_source_licenses(self):
46 def open_source_licenses(self):
48 c = self.load_default_context()
47 c = self.load_default_context()
49 c.active = 'open_source'
48 c.active = 'open_source'
50 c.navlist = navigation_list(self.request)
49 c.navlist = navigation_list(self.request)
51 items = sorted(read_opensource_licenses().items(), key=lambda t: t[0])
50 items = sorted(read_opensource_licenses().items(), key=lambda t: t[0])
52 c.opensource_licenses = collections.OrderedDict(items)
51 c.opensource_licenses = collections.OrderedDict(items)
53
52
54 return self._get_template_context(c)
53 return self._get_template_context(c)
@@ -1,479 +1,481 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 re
21 import re
22 import logging
22 import logging
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import datetime
25 import datetime
25 from pyramid.interfaces import IRoutesMapper
26 from pyramid.interfaces import IRoutesMapper
26
27
27 from pyramid.view import view_config
28 from pyramid.view import view_config
28 from pyramid.httpexceptions import HTTPFound
29 from pyramid.httpexceptions import HTTPFound
29 from pyramid.renderers import render
30 from pyramid.renderers import render
30 from pyramid.response import Response
31 from pyramid.response import Response
31
32
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
34 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
34 from rhodecode.events import trigger
35 from rhodecode.events import trigger
35
36
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
39 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
39 from rhodecode.lib.utils2 import aslist, safe_unicode
40 from rhodecode.lib.utils2 import aslist, safe_unicode
40 from rhodecode.model.db import or_, joinedload, coalesce, User, UserIpMap, UserSshKeys
41 from rhodecode.model.db import (
42 or_, coalesce, User, UserIpMap, UserSshKeys)
41 from rhodecode.model.forms import (
43 from rhodecode.model.forms import (
42 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
44 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
43 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
44 from rhodecode.model.permission import PermissionModel
46 from rhodecode.model.permission import PermissionModel
45 from rhodecode.model.settings import SettingsModel
47 from rhodecode.model.settings import SettingsModel
46
48
47
49
48 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
49
51
50
52
51 class AdminPermissionsView(BaseAppView, DataGridAppView):
53 class AdminPermissionsView(BaseAppView, DataGridAppView):
52 def load_default_context(self):
54 def load_default_context(self):
53 c = self._get_local_tmpl_context()
55 c = self._get_local_tmpl_context()
54
56
55 self._register_global_c(c)
57 self._register_global_c(c)
56 PermissionModel().set_global_permission_choices(
58 PermissionModel().set_global_permission_choices(
57 c, gettext_translator=self.request.translate)
59 c, gettext_translator=self.request.translate)
58 return c
60 return c
59
61
60 @LoginRequired()
62 @LoginRequired()
61 @HasPermissionAllDecorator('hg.admin')
63 @HasPermissionAllDecorator('hg.admin')
62 @view_config(
64 @view_config(
63 route_name='admin_permissions_application', request_method='GET',
65 route_name='admin_permissions_application', request_method='GET',
64 renderer='rhodecode:templates/admin/permissions/permissions.mako')
66 renderer='rhodecode:templates/admin/permissions/permissions.mako')
65 def permissions_application(self):
67 def permissions_application(self):
66 c = self.load_default_context()
68 c = self.load_default_context()
67 c.active = 'application'
69 c.active = 'application'
68
70
69 c.user = User.get_default_user(refresh=True)
71 c.user = User.get_default_user(refresh=True)
70
72
71 app_settings = SettingsModel().get_all_settings()
73 app_settings = SettingsModel().get_all_settings()
72 defaults = {
74 defaults = {
73 'anonymous': c.user.active,
75 'anonymous': c.user.active,
74 'default_register_message': app_settings.get(
76 'default_register_message': app_settings.get(
75 'rhodecode_register_message')
77 'rhodecode_register_message')
76 }
78 }
77 defaults.update(c.user.get_default_perms())
79 defaults.update(c.user.get_default_perms())
78
80
79 data = render('rhodecode:templates/admin/permissions/permissions.mako',
81 data = render('rhodecode:templates/admin/permissions/permissions.mako',
80 self._get_template_context(c), self.request)
82 self._get_template_context(c), self.request)
81 html = formencode.htmlfill.render(
83 html = formencode.htmlfill.render(
82 data,
84 data,
83 defaults=defaults,
85 defaults=defaults,
84 encoding="UTF-8",
86 encoding="UTF-8",
85 force_defaults=False
87 force_defaults=False
86 )
88 )
87 return Response(html)
89 return Response(html)
88
90
89 @LoginRequired()
91 @LoginRequired()
90 @HasPermissionAllDecorator('hg.admin')
92 @HasPermissionAllDecorator('hg.admin')
91 @CSRFRequired()
93 @CSRFRequired()
92 @view_config(
94 @view_config(
93 route_name='admin_permissions_application_update', request_method='POST',
95 route_name='admin_permissions_application_update', request_method='POST',
94 renderer='rhodecode:templates/admin/permissions/permissions.mako')
96 renderer='rhodecode:templates/admin/permissions/permissions.mako')
95 def permissions_application_update(self):
97 def permissions_application_update(self):
96 _ = self.request.translate
98 _ = self.request.translate
97 c = self.load_default_context()
99 c = self.load_default_context()
98 c.active = 'application'
100 c.active = 'application'
99
101
100 _form = ApplicationPermissionsForm(
102 _form = ApplicationPermissionsForm(
101 [x[0] for x in c.register_choices],
103 [x[0] for x in c.register_choices],
102 [x[0] for x in c.password_reset_choices],
104 [x[0] for x in c.password_reset_choices],
103 [x[0] for x in c.extern_activate_choices])()
105 [x[0] for x in c.extern_activate_choices])()
104
106
105 try:
107 try:
106 form_result = _form.to_python(dict(self.request.POST))
108 form_result = _form.to_python(dict(self.request.POST))
107 form_result.update({'perm_user_name': User.DEFAULT_USER})
109 form_result.update({'perm_user_name': User.DEFAULT_USER})
108 PermissionModel().update_application_permissions(form_result)
110 PermissionModel().update_application_permissions(form_result)
109
111
110 settings = [
112 settings = [
111 ('register_message', 'default_register_message'),
113 ('register_message', 'default_register_message'),
112 ]
114 ]
113 for setting, form_key in settings:
115 for setting, form_key in settings:
114 sett = SettingsModel().create_or_update_setting(
116 sett = SettingsModel().create_or_update_setting(
115 setting, form_result[form_key])
117 setting, form_result[form_key])
116 Session().add(sett)
118 Session().add(sett)
117
119
118 Session().commit()
120 Session().commit()
119 h.flash(_('Application permissions updated successfully'),
121 h.flash(_('Application permissions updated successfully'),
120 category='success')
122 category='success')
121
123
122 except formencode.Invalid as errors:
124 except formencode.Invalid as errors:
123 defaults = errors.value
125 defaults = errors.value
124
126
125 data = render(
127 data = render(
126 'rhodecode:templates/admin/permissions/permissions.mako',
128 'rhodecode:templates/admin/permissions/permissions.mako',
127 self._get_template_context(c), self.request)
129 self._get_template_context(c), self.request)
128 html = formencode.htmlfill.render(
130 html = formencode.htmlfill.render(
129 data,
131 data,
130 defaults=defaults,
132 defaults=defaults,
131 errors=errors.error_dict or {},
133 errors=errors.error_dict or {},
132 prefix_error=False,
134 prefix_error=False,
133 encoding="UTF-8",
135 encoding="UTF-8",
134 force_defaults=False
136 force_defaults=False
135 )
137 )
136 return Response(html)
138 return Response(html)
137
139
138 except Exception:
140 except Exception:
139 log.exception("Exception during update of permissions")
141 log.exception("Exception during update of permissions")
140 h.flash(_('Error occurred during update of permissions'),
142 h.flash(_('Error occurred during update of permissions'),
141 category='error')
143 category='error')
142
144
143 raise HTTPFound(h.route_path('admin_permissions_application'))
145 raise HTTPFound(h.route_path('admin_permissions_application'))
144
146
145 @LoginRequired()
147 @LoginRequired()
146 @HasPermissionAllDecorator('hg.admin')
148 @HasPermissionAllDecorator('hg.admin')
147 @view_config(
149 @view_config(
148 route_name='admin_permissions_object', request_method='GET',
150 route_name='admin_permissions_object', request_method='GET',
149 renderer='rhodecode:templates/admin/permissions/permissions.mako')
151 renderer='rhodecode:templates/admin/permissions/permissions.mako')
150 def permissions_objects(self):
152 def permissions_objects(self):
151 c = self.load_default_context()
153 c = self.load_default_context()
152 c.active = 'objects'
154 c.active = 'objects'
153
155
154 c.user = User.get_default_user(refresh=True)
156 c.user = User.get_default_user(refresh=True)
155 defaults = {}
157 defaults = {}
156 defaults.update(c.user.get_default_perms())
158 defaults.update(c.user.get_default_perms())
157
159
158 data = render(
160 data = render(
159 'rhodecode:templates/admin/permissions/permissions.mako',
161 'rhodecode:templates/admin/permissions/permissions.mako',
160 self._get_template_context(c), self.request)
162 self._get_template_context(c), self.request)
161 html = formencode.htmlfill.render(
163 html = formencode.htmlfill.render(
162 data,
164 data,
163 defaults=defaults,
165 defaults=defaults,
164 encoding="UTF-8",
166 encoding="UTF-8",
165 force_defaults=False
167 force_defaults=False
166 )
168 )
167 return Response(html)
169 return Response(html)
168
170
169 @LoginRequired()
171 @LoginRequired()
170 @HasPermissionAllDecorator('hg.admin')
172 @HasPermissionAllDecorator('hg.admin')
171 @CSRFRequired()
173 @CSRFRequired()
172 @view_config(
174 @view_config(
173 route_name='admin_permissions_object_update', request_method='POST',
175 route_name='admin_permissions_object_update', request_method='POST',
174 renderer='rhodecode:templates/admin/permissions/permissions.mako')
176 renderer='rhodecode:templates/admin/permissions/permissions.mako')
175 def permissions_objects_update(self):
177 def permissions_objects_update(self):
176 _ = self.request.translate
178 _ = self.request.translate
177 c = self.load_default_context()
179 c = self.load_default_context()
178 c.active = 'objects'
180 c.active = 'objects'
179
181
180 _form = ObjectPermissionsForm(
182 _form = ObjectPermissionsForm(
181 [x[0] for x in c.repo_perms_choices],
183 [x[0] for x in c.repo_perms_choices],
182 [x[0] for x in c.group_perms_choices],
184 [x[0] for x in c.group_perms_choices],
183 [x[0] for x in c.user_group_perms_choices])()
185 [x[0] for x in c.user_group_perms_choices])()
184
186
185 try:
187 try:
186 form_result = _form.to_python(dict(self.request.POST))
188 form_result = _form.to_python(dict(self.request.POST))
187 form_result.update({'perm_user_name': User.DEFAULT_USER})
189 form_result.update({'perm_user_name': User.DEFAULT_USER})
188 PermissionModel().update_object_permissions(form_result)
190 PermissionModel().update_object_permissions(form_result)
189
191
190 Session().commit()
192 Session().commit()
191 h.flash(_('Object permissions updated successfully'),
193 h.flash(_('Object permissions updated successfully'),
192 category='success')
194 category='success')
193
195
194 except formencode.Invalid as errors:
196 except formencode.Invalid as errors:
195 defaults = errors.value
197 defaults = errors.value
196
198
197 data = render(
199 data = render(
198 'rhodecode:templates/admin/permissions/permissions.mako',
200 'rhodecode:templates/admin/permissions/permissions.mako',
199 self._get_template_context(c), self.request)
201 self._get_template_context(c), self.request)
200 html = formencode.htmlfill.render(
202 html = formencode.htmlfill.render(
201 data,
203 data,
202 defaults=defaults,
204 defaults=defaults,
203 errors=errors.error_dict or {},
205 errors=errors.error_dict or {},
204 prefix_error=False,
206 prefix_error=False,
205 encoding="UTF-8",
207 encoding="UTF-8",
206 force_defaults=False
208 force_defaults=False
207 )
209 )
208 return Response(html)
210 return Response(html)
209 except Exception:
211 except Exception:
210 log.exception("Exception during update of permissions")
212 log.exception("Exception during update of permissions")
211 h.flash(_('Error occurred during update of permissions'),
213 h.flash(_('Error occurred during update of permissions'),
212 category='error')
214 category='error')
213
215
214 raise HTTPFound(h.route_path('admin_permissions_object'))
216 raise HTTPFound(h.route_path('admin_permissions_object'))
215
217
216 @LoginRequired()
218 @LoginRequired()
217 @HasPermissionAllDecorator('hg.admin')
219 @HasPermissionAllDecorator('hg.admin')
218 @view_config(
220 @view_config(
219 route_name='admin_permissions_global', request_method='GET',
221 route_name='admin_permissions_global', request_method='GET',
220 renderer='rhodecode:templates/admin/permissions/permissions.mako')
222 renderer='rhodecode:templates/admin/permissions/permissions.mako')
221 def permissions_global(self):
223 def permissions_global(self):
222 c = self.load_default_context()
224 c = self.load_default_context()
223 c.active = 'global'
225 c.active = 'global'
224
226
225 c.user = User.get_default_user(refresh=True)
227 c.user = User.get_default_user(refresh=True)
226 defaults = {}
228 defaults = {}
227 defaults.update(c.user.get_default_perms())
229 defaults.update(c.user.get_default_perms())
228
230
229 data = render(
231 data = render(
230 'rhodecode:templates/admin/permissions/permissions.mako',
232 'rhodecode:templates/admin/permissions/permissions.mako',
231 self._get_template_context(c), self.request)
233 self._get_template_context(c), self.request)
232 html = formencode.htmlfill.render(
234 html = formencode.htmlfill.render(
233 data,
235 data,
234 defaults=defaults,
236 defaults=defaults,
235 encoding="UTF-8",
237 encoding="UTF-8",
236 force_defaults=False
238 force_defaults=False
237 )
239 )
238 return Response(html)
240 return Response(html)
239
241
240 @LoginRequired()
242 @LoginRequired()
241 @HasPermissionAllDecorator('hg.admin')
243 @HasPermissionAllDecorator('hg.admin')
242 @CSRFRequired()
244 @CSRFRequired()
243 @view_config(
245 @view_config(
244 route_name='admin_permissions_global_update', request_method='POST',
246 route_name='admin_permissions_global_update', request_method='POST',
245 renderer='rhodecode:templates/admin/permissions/permissions.mako')
247 renderer='rhodecode:templates/admin/permissions/permissions.mako')
246 def permissions_global_update(self):
248 def permissions_global_update(self):
247 _ = self.request.translate
249 _ = self.request.translate
248 c = self.load_default_context()
250 c = self.load_default_context()
249 c.active = 'global'
251 c.active = 'global'
250
252
251 _form = UserPermissionsForm(
253 _form = UserPermissionsForm(
252 [x[0] for x in c.repo_create_choices],
254 [x[0] for x in c.repo_create_choices],
253 [x[0] for x in c.repo_create_on_write_choices],
255 [x[0] for x in c.repo_create_on_write_choices],
254 [x[0] for x in c.repo_group_create_choices],
256 [x[0] for x in c.repo_group_create_choices],
255 [x[0] for x in c.user_group_create_choices],
257 [x[0] for x in c.user_group_create_choices],
256 [x[0] for x in c.fork_choices],
258 [x[0] for x in c.fork_choices],
257 [x[0] for x in c.inherit_default_permission_choices])()
259 [x[0] for x in c.inherit_default_permission_choices])()
258
260
259 try:
261 try:
260 form_result = _form.to_python(dict(self.request.POST))
262 form_result = _form.to_python(dict(self.request.POST))
261 form_result.update({'perm_user_name': User.DEFAULT_USER})
263 form_result.update({'perm_user_name': User.DEFAULT_USER})
262 PermissionModel().update_user_permissions(form_result)
264 PermissionModel().update_user_permissions(form_result)
263
265
264 Session().commit()
266 Session().commit()
265 h.flash(_('Global permissions updated successfully'),
267 h.flash(_('Global permissions updated successfully'),
266 category='success')
268 category='success')
267
269
268 except formencode.Invalid as errors:
270 except formencode.Invalid as errors:
269 defaults = errors.value
271 defaults = errors.value
270
272
271 data = render(
273 data = render(
272 'rhodecode:templates/admin/permissions/permissions.mako',
274 'rhodecode:templates/admin/permissions/permissions.mako',
273 self._get_template_context(c), self.request)
275 self._get_template_context(c), self.request)
274 html = formencode.htmlfill.render(
276 html = formencode.htmlfill.render(
275 data,
277 data,
276 defaults=defaults,
278 defaults=defaults,
277 errors=errors.error_dict or {},
279 errors=errors.error_dict or {},
278 prefix_error=False,
280 prefix_error=False,
279 encoding="UTF-8",
281 encoding="UTF-8",
280 force_defaults=False
282 force_defaults=False
281 )
283 )
282 return Response(html)
284 return Response(html)
283 except Exception:
285 except Exception:
284 log.exception("Exception during update of permissions")
286 log.exception("Exception during update of permissions")
285 h.flash(_('Error occurred during update of permissions'),
287 h.flash(_('Error occurred during update of permissions'),
286 category='error')
288 category='error')
287
289
288 raise HTTPFound(h.route_path('admin_permissions_global'))
290 raise HTTPFound(h.route_path('admin_permissions_global'))
289
291
290 @LoginRequired()
292 @LoginRequired()
291 @HasPermissionAllDecorator('hg.admin')
293 @HasPermissionAllDecorator('hg.admin')
292 @view_config(
294 @view_config(
293 route_name='admin_permissions_ips', request_method='GET',
295 route_name='admin_permissions_ips', request_method='GET',
294 renderer='rhodecode:templates/admin/permissions/permissions.mako')
296 renderer='rhodecode:templates/admin/permissions/permissions.mako')
295 def permissions_ips(self):
297 def permissions_ips(self):
296 c = self.load_default_context()
298 c = self.load_default_context()
297 c.active = 'ips'
299 c.active = 'ips'
298
300
299 c.user = User.get_default_user(refresh=True)
301 c.user = User.get_default_user(refresh=True)
300 c.user_ip_map = (
302 c.user_ip_map = (
301 UserIpMap.query().filter(UserIpMap.user == c.user).all())
303 UserIpMap.query().filter(UserIpMap.user == c.user).all())
302
304
303 return self._get_template_context(c)
305 return self._get_template_context(c)
304
306
305 @LoginRequired()
307 @LoginRequired()
306 @HasPermissionAllDecorator('hg.admin')
308 @HasPermissionAllDecorator('hg.admin')
307 @view_config(
309 @view_config(
308 route_name='admin_permissions_overview', request_method='GET',
310 route_name='admin_permissions_overview', request_method='GET',
309 renderer='rhodecode:templates/admin/permissions/permissions.mako')
311 renderer='rhodecode:templates/admin/permissions/permissions.mako')
310 def permissions_overview(self):
312 def permissions_overview(self):
311 c = self.load_default_context()
313 c = self.load_default_context()
312 c.active = 'perms'
314 c.active = 'perms'
313
315
314 c.user = User.get_default_user(refresh=True)
316 c.user = User.get_default_user(refresh=True)
315 c.perm_user = c.user.AuthUser()
317 c.perm_user = c.user.AuthUser()
316 return self._get_template_context(c)
318 return self._get_template_context(c)
317
319
318 @LoginRequired()
320 @LoginRequired()
319 @HasPermissionAllDecorator('hg.admin')
321 @HasPermissionAllDecorator('hg.admin')
320 @view_config(
322 @view_config(
321 route_name='admin_permissions_auth_token_access', request_method='GET',
323 route_name='admin_permissions_auth_token_access', request_method='GET',
322 renderer='rhodecode:templates/admin/permissions/permissions.mako')
324 renderer='rhodecode:templates/admin/permissions/permissions.mako')
323 def auth_token_access(self):
325 def auth_token_access(self):
324 from rhodecode import CONFIG
326 from rhodecode import CONFIG
325
327
326 c = self.load_default_context()
328 c = self.load_default_context()
327 c.active = 'auth_token_access'
329 c.active = 'auth_token_access'
328
330
329 c.user = User.get_default_user(refresh=True)
331 c.user = User.get_default_user(refresh=True)
330 c.perm_user = c.user.AuthUser()
332 c.perm_user = c.user.AuthUser()
331
333
332 mapper = self.request.registry.queryUtility(IRoutesMapper)
334 mapper = self.request.registry.queryUtility(IRoutesMapper)
333 c.view_data = []
335 c.view_data = []
334
336
335 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
337 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
336 introspector = self.request.registry.introspector
338 introspector = self.request.registry.introspector
337
339
338 view_intr = {}
340 view_intr = {}
339 for view_data in introspector.get_category('views'):
341 for view_data in introspector.get_category('views'):
340 intr = view_data['introspectable']
342 intr = view_data['introspectable']
341
343
342 if 'route_name' in intr and intr['attr']:
344 if 'route_name' in intr and intr['attr']:
343 view_intr[intr['route_name']] = '{}:{}'.format(
345 view_intr[intr['route_name']] = '{}:{}'.format(
344 str(intr['derived_callable'].func_name), intr['attr']
346 str(intr['derived_callable'].func_name), intr['attr']
345 )
347 )
346
348
347 c.whitelist_key = 'api_access_controllers_whitelist'
349 c.whitelist_key = 'api_access_controllers_whitelist'
348 c.whitelist_file = CONFIG.get('__file__')
350 c.whitelist_file = CONFIG.get('__file__')
349 whitelist_views = aslist(
351 whitelist_views = aslist(
350 CONFIG.get(c.whitelist_key), sep=',')
352 CONFIG.get(c.whitelist_key), sep=',')
351
353
352 for route_info in mapper.get_routes():
354 for route_info in mapper.get_routes():
353 if not route_info.name.startswith('__'):
355 if not route_info.name.startswith('__'):
354 routepath = route_info.pattern
356 routepath = route_info.pattern
355
357
356 def replace(matchobj):
358 def replace(matchobj):
357 if matchobj.group(1):
359 if matchobj.group(1):
358 return "{%s}" % matchobj.group(1).split(':')[0]
360 return "{%s}" % matchobj.group(1).split(':')[0]
359 else:
361 else:
360 return "{%s}" % matchobj.group(2)
362 return "{%s}" % matchobj.group(2)
361
363
362 routepath = _argument_prog.sub(replace, routepath)
364 routepath = _argument_prog.sub(replace, routepath)
363
365
364 if not routepath.startswith('/'):
366 if not routepath.startswith('/'):
365 routepath = '/' + routepath
367 routepath = '/' + routepath
366
368
367 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
369 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
368 active = view_fqn in whitelist_views
370 active = view_fqn in whitelist_views
369 c.view_data.append((route_info.name, view_fqn, routepath, active))
371 c.view_data.append((route_info.name, view_fqn, routepath, active))
370
372
371 c.whitelist_views = whitelist_views
373 c.whitelist_views = whitelist_views
372 return self._get_template_context(c)
374 return self._get_template_context(c)
373
375
374 def ssh_enabled(self):
376 def ssh_enabled(self):
375 return self.request.registry.settings.get(
377 return self.request.registry.settings.get(
376 'ssh.generate_authorized_keyfile')
378 'ssh.generate_authorized_keyfile')
377
379
378 @LoginRequired()
380 @LoginRequired()
379 @HasPermissionAllDecorator('hg.admin')
381 @HasPermissionAllDecorator('hg.admin')
380 @view_config(
382 @view_config(
381 route_name='admin_permissions_ssh_keys', request_method='GET',
383 route_name='admin_permissions_ssh_keys', request_method='GET',
382 renderer='rhodecode:templates/admin/permissions/permissions.mako')
384 renderer='rhodecode:templates/admin/permissions/permissions.mako')
383 def ssh_keys(self):
385 def ssh_keys(self):
384 c = self.load_default_context()
386 c = self.load_default_context()
385 c.active = 'ssh_keys'
387 c.active = 'ssh_keys'
386 c.ssh_enabled = self.ssh_enabled()
388 c.ssh_enabled = self.ssh_enabled()
387 return self._get_template_context(c)
389 return self._get_template_context(c)
388
390
389 @LoginRequired()
391 @LoginRequired()
390 @HasPermissionAllDecorator('hg.admin')
392 @HasPermissionAllDecorator('hg.admin')
391 @view_config(
393 @view_config(
392 route_name='admin_permissions_ssh_keys_data', request_method='GET',
394 route_name='admin_permissions_ssh_keys_data', request_method='GET',
393 renderer='json_ext', xhr=True)
395 renderer='json_ext', xhr=True)
394 def ssh_keys_data(self):
396 def ssh_keys_data(self):
395 _ = self.request.translate
397 _ = self.request.translate
396 column_map = {
398 column_map = {
397 'fingerprint': 'ssh_key_fingerprint',
399 'fingerprint': 'ssh_key_fingerprint',
398 'username': User.username
400 'username': User.username
399 }
401 }
400 draw, start, limit = self._extract_chunk(self.request)
402 draw, start, limit = self._extract_chunk(self.request)
401 search_q, order_by, order_dir = self._extract_ordering(
403 search_q, order_by, order_dir = self._extract_ordering(
402 self.request, column_map=column_map)
404 self.request, column_map=column_map)
403
405
404 ssh_keys_data_total_count = UserSshKeys.query()\
406 ssh_keys_data_total_count = UserSshKeys.query()\
405 .count()
407 .count()
406
408
407 # json generate
409 # json generate
408 base_q = UserSshKeys.query().join(UserSshKeys.user)
410 base_q = UserSshKeys.query().join(UserSshKeys.user)
409
411
410 if search_q:
412 if search_q:
411 like_expression = u'%{}%'.format(safe_unicode(search_q))
413 like_expression = u'%{}%'.format(safe_unicode(search_q))
412 base_q = base_q.filter(or_(
414 base_q = base_q.filter(or_(
413 User.username.ilike(like_expression),
415 User.username.ilike(like_expression),
414 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
416 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
415 ))
417 ))
416
418
417 users_data_total_filtered_count = base_q.count()
419 users_data_total_filtered_count = base_q.count()
418
420
419 sort_col = self._get_order_col(order_by, UserSshKeys)
421 sort_col = self._get_order_col(order_by, UserSshKeys)
420 if sort_col:
422 if sort_col:
421 if order_dir == 'asc':
423 if order_dir == 'asc':
422 # handle null values properly to order by NULL last
424 # handle null values properly to order by NULL last
423 if order_by in ['created_on']:
425 if order_by in ['created_on']:
424 sort_col = coalesce(sort_col, datetime.date.max)
426 sort_col = coalesce(sort_col, datetime.date.max)
425 sort_col = sort_col.asc()
427 sort_col = sort_col.asc()
426 else:
428 else:
427 # handle null values properly to order by NULL last
429 # handle null values properly to order by NULL last
428 if order_by in ['created_on']:
430 if order_by in ['created_on']:
429 sort_col = coalesce(sort_col, datetime.date.min)
431 sort_col = coalesce(sort_col, datetime.date.min)
430 sort_col = sort_col.desc()
432 sort_col = sort_col.desc()
431
433
432 base_q = base_q.order_by(sort_col)
434 base_q = base_q.order_by(sort_col)
433 base_q = base_q.offset(start).limit(limit)
435 base_q = base_q.offset(start).limit(limit)
434
436
435 ssh_keys = base_q.all()
437 ssh_keys = base_q.all()
436
438
437 ssh_keys_data = []
439 ssh_keys_data = []
438 for ssh_key in ssh_keys:
440 for ssh_key in ssh_keys:
439 ssh_keys_data.append({
441 ssh_keys_data.append({
440 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
442 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
441 "fingerprint": ssh_key.ssh_key_fingerprint,
443 "fingerprint": ssh_key.ssh_key_fingerprint,
442 "description": ssh_key.description,
444 "description": ssh_key.description,
443 "created_on": h.format_date(ssh_key.created_on),
445 "created_on": h.format_date(ssh_key.created_on),
444 "action": h.link_to(
446 "action": h.link_to(
445 _('Edit'), h.route_path('edit_user_ssh_keys',
447 _('Edit'), h.route_path('edit_user_ssh_keys',
446 user_id=ssh_key.user.user_id))
448 user_id=ssh_key.user.user_id))
447 })
449 })
448
450
449 data = ({
451 data = ({
450 'draw': draw,
452 'draw': draw,
451 'data': ssh_keys_data,
453 'data': ssh_keys_data,
452 'recordsTotal': ssh_keys_data_total_count,
454 'recordsTotal': ssh_keys_data_total_count,
453 'recordsFiltered': users_data_total_filtered_count,
455 'recordsFiltered': users_data_total_filtered_count,
454 })
456 })
455
457
456 return data
458 return data
457
459
458 @LoginRequired()
460 @LoginRequired()
459 @HasPermissionAllDecorator('hg.admin')
461 @HasPermissionAllDecorator('hg.admin')
460 @CSRFRequired()
462 @CSRFRequired()
461 @view_config(
463 @view_config(
462 route_name='admin_permissions_ssh_keys_update', request_method='POST',
464 route_name='admin_permissions_ssh_keys_update', request_method='POST',
463 renderer='rhodecode:templates/admin/permissions/permissions.mako')
465 renderer='rhodecode:templates/admin/permissions/permissions.mako')
464 def ssh_keys_update(self):
466 def ssh_keys_update(self):
465 _ = self.request.translate
467 _ = self.request.translate
466 self.load_default_context()
468 self.load_default_context()
467
469
468 ssh_enabled = self.ssh_enabled()
470 ssh_enabled = self.ssh_enabled()
469 key_file = self.request.registry.settings.get(
471 key_file = self.request.registry.settings.get(
470 'ssh.authorized_keys_file_path')
472 'ssh.authorized_keys_file_path')
471 if ssh_enabled:
473 if ssh_enabled:
472 trigger(SshKeyFileChangeEvent(), self.request.registry)
474 trigger(SshKeyFileChangeEvent(), self.request.registry)
473 h.flash(_('Updated SSH keys file: {}').format(key_file),
475 h.flash(_('Updated SSH keys file: {}').format(key_file),
474 category='success')
476 category='success')
475 else:
477 else:
476 h.flash(_('SSH key support is disabled in .ini file'),
478 h.flash(_('SSH key support is disabled in .ini file'),
477 category='warning')
479 category='warning')
478
480
479 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
481 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
@@ -1,92 +1,91 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 logging
21 import logging
22
22
23 import psutil
23 import psutil
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps.admin.navigation import navigation_list
27 from rhodecode.apps.admin.navigation import navigation_list
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (
28 from rhodecode.lib.auth import (
30 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
31 from rhodecode.lib.utils2 import safe_int
30 from rhodecode.lib.utils2 import safe_int
32
31
33 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
34
33
35
34
36 class AdminProcessManagementView(BaseAppView):
35 class AdminProcessManagementView(BaseAppView):
37 def load_default_context(self):
36 def load_default_context(self):
38 c = self._get_local_tmpl_context()
37 c = self._get_local_tmpl_context()
39 self._register_global_c(c)
38 self._register_global_c(c)
40 return c
39 return c
41
40
42 @LoginRequired()
41 @LoginRequired()
43 @HasPermissionAllDecorator('hg.admin')
42 @HasPermissionAllDecorator('hg.admin')
44 @view_config(
43 @view_config(
45 route_name='admin_settings_process_management', request_method='GET',
44 route_name='admin_settings_process_management', request_method='GET',
46 renderer='rhodecode:templates/admin/settings/settings.mako')
45 renderer='rhodecode:templates/admin/settings/settings.mako')
47 def process_management(self):
46 def process_management(self):
48 _ = self.request.translate
47 _ = self.request.translate
49 c = self.load_default_context()
48 c = self.load_default_context()
50
49
51 c.active = 'process_management'
50 c.active = 'process_management'
52 c.navlist = navigation_list(self.request)
51 c.navlist = navigation_list(self.request)
53 c.gunicorn_processes = (
52 c.gunicorn_processes = (
54 p for p in psutil.process_iter() if 'gunicorn' in p.name())
53 p for p in psutil.process_iter() if 'gunicorn' in p.name())
55 return self._get_template_context(c)
54 return self._get_template_context(c)
56
55
57 @LoginRequired()
56 @LoginRequired()
58 @HasPermissionAllDecorator('hg.admin')
57 @HasPermissionAllDecorator('hg.admin')
59 @CSRFRequired()
58 @CSRFRequired()
60 @view_config(
59 @view_config(
61 route_name='admin_settings_process_management_signal',
60 route_name='admin_settings_process_management_signal',
62 request_method='POST', renderer='json_ext')
61 request_method='POST', renderer='json_ext')
63 def process_management_signal(self):
62 def process_management_signal(self):
64 pids = self.request.json.get('pids', [])
63 pids = self.request.json.get('pids', [])
65 result = []
64 result = []
66 def on_terminate(proc):
65 def on_terminate(proc):
67 msg = "process `PID:{}` terminated with exit code {}".format(
66 msg = "process `PID:{}` terminated with exit code {}".format(
68 proc.pid, proc.returncode)
67 proc.pid, proc.returncode)
69 result.append(msg)
68 result.append(msg)
70
69
71 procs = []
70 procs = []
72 for pid in pids:
71 for pid in pids:
73 pid = safe_int(pid)
72 pid = safe_int(pid)
74 if pid:
73 if pid:
75 try:
74 try:
76 proc = psutil.Process(pid)
75 proc = psutil.Process(pid)
77 except psutil.NoSuchProcess:
76 except psutil.NoSuchProcess:
78 continue
77 continue
79
78
80 children = proc.children(recursive=True)
79 children = proc.children(recursive=True)
81 if children:
80 if children:
82 print('Wont kill Master Process')
81 print('Wont kill Master Process')
83 else:
82 else:
84 procs.append(proc)
83 procs.append(proc)
85
84
86 for p in procs:
85 for p in procs:
87 p.terminate()
86 p.terminate()
88 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
87 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
89 for p in alive:
88 for p in alive:
90 p.kill()
89 p.kill()
91
90
92 return {'result': result}
91 return {'result': result}
@@ -1,180 +1,181 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 logging
21 import logging
22 import formencode
22 import formencode
23 import formencode.htmlfill
23
24
24 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
25 from pyramid.view import view_config
26 from pyramid.view import view_config
26 from pyramid.renderers import render
27 from pyramid.renderers import render
27 from pyramid.response import Response
28 from pyramid.response import Response
28
29
29 from rhodecode.apps._base import BaseAppView, DataGridAppView
30 from rhodecode.apps._base import BaseAppView, DataGridAppView
30
31
31 from rhodecode.lib.ext_json import json
32 from rhodecode.lib.ext_json import json
32 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
33 LoginRequired, CSRFRequired, NotAnonymous,
34 LoginRequired, CSRFRequired, NotAnonymous,
34 HasPermissionAny, HasRepoGroupPermissionAny)
35 HasPermissionAny, HasRepoGroupPermissionAny)
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib.utils import repo_name_slug
37 from rhodecode.lib.utils import repo_name_slug
37 from rhodecode.lib.utils2 import safe_int, safe_unicode
38 from rhodecode.lib.utils2 import safe_int, safe_unicode
38 from rhodecode.model.forms import RepoForm
39 from rhodecode.model.forms import RepoForm
39 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
41 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
41 from rhodecode.model.settings import SettingsModel
42 from rhodecode.model.settings import SettingsModel
42 from rhodecode.model.db import Repository, RepoGroup
43 from rhodecode.model.db import Repository, RepoGroup
43
44
44 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
45
46
46
47
47 class AdminReposView(BaseAppView, DataGridAppView):
48 class AdminReposView(BaseAppView, DataGridAppView):
48
49
49 def load_default_context(self):
50 def load_default_context(self):
50 c = self._get_local_tmpl_context()
51 c = self._get_local_tmpl_context()
51 self._register_global_c(c)
52 self._register_global_c(c)
52 return c
53 return c
53
54
54 def _load_form_data(self, c):
55 def _load_form_data(self, c):
55 acl_groups = RepoGroupList(RepoGroup.query().all(),
56 acl_groups = RepoGroupList(RepoGroup.query().all(),
56 perm_set=['group.write', 'group.admin'])
57 perm_set=['group.write', 'group.admin'])
57 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
58 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
58 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
59 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
59 c.landing_revs_choices, c.landing_revs = \
60 c.landing_revs_choices, c.landing_revs = \
60 ScmModel().get_repo_landing_revs()
61 ScmModel().get_repo_landing_revs()
61 c.personal_repo_group = self._rhodecode_user.personal_repo_group
62 c.personal_repo_group = self._rhodecode_user.personal_repo_group
62
63
63 @LoginRequired()
64 @LoginRequired()
64 @NotAnonymous()
65 @NotAnonymous()
65 @view_config(
66 @view_config(
66 route_name='repos', request_method='GET',
67 route_name='repos', request_method='GET',
67 renderer='rhodecode:templates/admin/repos/repos.mako')
68 renderer='rhodecode:templates/admin/repos/repos.mako')
68 def repository_list(self):
69 def repository_list(self):
69 c = self.load_default_context()
70 c = self.load_default_context()
70
71
71 repo_list = Repository.get_all_repos()
72 repo_list = Repository.get_all_repos()
72 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
73 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
73 repos_data = RepoModel().get_repos_as_dict(
74 repos_data = RepoModel().get_repos_as_dict(
74 repo_list=c.repo_list, admin=True, super_user_actions=True)
75 repo_list=c.repo_list, admin=True, super_user_actions=True)
75 # json used to render the grid
76 # json used to render the grid
76 c.data = json.dumps(repos_data)
77 c.data = json.dumps(repos_data)
77
78
78 return self._get_template_context(c)
79 return self._get_template_context(c)
79
80
80 @LoginRequired()
81 @LoginRequired()
81 @NotAnonymous()
82 @NotAnonymous()
82 # perms check inside
83 # perms check inside
83 @view_config(
84 @view_config(
84 route_name='repo_new', request_method='GET',
85 route_name='repo_new', request_method='GET',
85 renderer='rhodecode:templates/admin/repos/repo_add.mako')
86 renderer='rhodecode:templates/admin/repos/repo_add.mako')
86 def repository_new(self):
87 def repository_new(self):
87 c = self.load_default_context()
88 c = self.load_default_context()
88
89
89 new_repo = self.request.GET.get('repo', '')
90 new_repo = self.request.GET.get('repo', '')
90 parent_group = safe_int(self.request.GET.get('parent_group'))
91 parent_group = safe_int(self.request.GET.get('parent_group'))
91 _gr = RepoGroup.get(parent_group)
92 _gr = RepoGroup.get(parent_group)
92
93
93 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
94 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
94 # you're not super admin nor have global create permissions,
95 # you're not super admin nor have global create permissions,
95 # but maybe you have at least write permission to a parent group ?
96 # but maybe you have at least write permission to a parent group ?
96
97
97 gr_name = _gr.group_name if _gr else None
98 gr_name = _gr.group_name if _gr else None
98 # create repositories with write permission on group is set to true
99 # create repositories with write permission on group is set to true
99 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
100 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
100 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
101 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
101 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
102 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
102 if not (group_admin or (group_write and create_on_write)):
103 if not (group_admin or (group_write and create_on_write)):
103 raise HTTPForbidden()
104 raise HTTPForbidden()
104
105
105 self._load_form_data(c)
106 self._load_form_data(c)
106 c.new_repo = repo_name_slug(new_repo)
107 c.new_repo = repo_name_slug(new_repo)
107
108
108 # apply the defaults from defaults page
109 # apply the defaults from defaults page
109 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
110 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
110 # set checkbox to autochecked
111 # set checkbox to autochecked
111 defaults['repo_copy_permissions'] = True
112 defaults['repo_copy_permissions'] = True
112
113
113 parent_group_choice = '-1'
114 parent_group_choice = '-1'
114 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
115 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
115 parent_group_choice = self._rhodecode_user.personal_repo_group
116 parent_group_choice = self._rhodecode_user.personal_repo_group
116
117
117 if parent_group and _gr:
118 if parent_group and _gr:
118 if parent_group in [x[0] for x in c.repo_groups]:
119 if parent_group in [x[0] for x in c.repo_groups]:
119 parent_group_choice = safe_unicode(parent_group)
120 parent_group_choice = safe_unicode(parent_group)
120
121
121 defaults.update({'repo_group': parent_group_choice})
122 defaults.update({'repo_group': parent_group_choice})
122
123
123 data = render('rhodecode:templates/admin/repos/repo_add.mako',
124 data = render('rhodecode:templates/admin/repos/repo_add.mako',
124 self._get_template_context(c), self.request)
125 self._get_template_context(c), self.request)
125 html = formencode.htmlfill.render(
126 html = formencode.htmlfill.render(
126 data,
127 data,
127 defaults=defaults,
128 defaults=defaults,
128 encoding="UTF-8",
129 encoding="UTF-8",
129 force_defaults=False
130 force_defaults=False
130 )
131 )
131 return Response(html)
132 return Response(html)
132
133
133 @LoginRequired()
134 @LoginRequired()
134 @NotAnonymous()
135 @NotAnonymous()
135 @CSRFRequired()
136 @CSRFRequired()
136 # perms check inside
137 # perms check inside
137 @view_config(
138 @view_config(
138 route_name='repo_create', request_method='POST',
139 route_name='repo_create', request_method='POST',
139 renderer='rhodecode:templates/admin/repos/repos.mako')
140 renderer='rhodecode:templates/admin/repos/repos.mako')
140 def repository_create(self):
141 def repository_create(self):
141 c = self.load_default_context()
142 c = self.load_default_context()
142
143
143 form_result = {}
144 form_result = {}
144 task_id = None
145 task_id = None
145 self._load_form_data(c)
146 self._load_form_data(c)
146
147
147 try:
148 try:
148 # CanWriteToGroup validators checks permissions of this POST
149 # CanWriteToGroup validators checks permissions of this POST
149 form_result = RepoForm(repo_groups=c.repo_groups_choices,
150 form_result = RepoForm(repo_groups=c.repo_groups_choices,
150 landing_revs=c.landing_revs_choices)()\
151 landing_revs=c.landing_revs_choices)()\
151 .to_python(dict(self.request.POST))
152 .to_python(dict(self.request.POST))
152
153
153 # create is done sometimes async on celery, db transaction
154 # create is done sometimes async on celery, db transaction
154 # management is handled there.
155 # management is handled there.
155 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
156 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
156 from celery.result import BaseAsyncResult
157 from celery.result import BaseAsyncResult
157 if isinstance(task, BaseAsyncResult):
158 if isinstance(task, BaseAsyncResult):
158 task_id = task.task_id
159 task_id = task.task_id
159 except formencode.Invalid as errors:
160 except formencode.Invalid as errors:
160 data = render('rhodecode:templates/admin/repos/repo_add.mako',
161 data = render('rhodecode:templates/admin/repos/repo_add.mako',
161 self._get_template_context(c), self.request)
162 self._get_template_context(c), self.request)
162 html = formencode.htmlfill.render(
163 html = formencode.htmlfill.render(
163 data,
164 data,
164 defaults=errors.value,
165 defaults=errors.value,
165 errors=errors.error_dict or {},
166 errors=errors.error_dict or {},
166 prefix_error=False,
167 prefix_error=False,
167 encoding="UTF-8",
168 encoding="UTF-8",
168 force_defaults=False
169 force_defaults=False
169 )
170 )
170 return Response(html)
171 return Response(html)
171
172
172 except Exception as e:
173 except Exception as e:
173 msg = self._log_creation_exception(e, form_result.get('repo_name'))
174 msg = self._log_creation_exception(e, form_result.get('repo_name'))
174 h.flash(msg, category='error')
175 h.flash(msg, category='error')
175 raise HTTPFound(h.route_path('home'))
176 raise HTTPFound(h.route_path('home'))
176
177
177 raise HTTPFound(
178 raise HTTPFound(
178 h.route_path('repo_creating',
179 h.route_path('repo_creating',
179 repo_name=form_result['repo_name_full'],
180 repo_name=form_result['repo_name_full'],
180 _query=dict(task_id=task_id)))
181 _query=dict(task_id=task_id)))
@@ -1,673 +1,674 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 logging
21 import logging
22 import datetime
22 import datetime
23 import formencode
23 import formencode
24 import formencode.htmlfill
24
25
25 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.view import view_config
27 from pyramid.view import view_config
27 from sqlalchemy.sql.functions import coalesce
28 from sqlalchemy.sql.functions import coalesce
28 from sqlalchemy.exc import IntegrityError
29 from sqlalchemy.exc import IntegrityError
29
30
30 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
32 from rhodecode.events import trigger
33 from rhodecode.events import trigger
33
34
34 from rhodecode.lib import audit_logger
35 from rhodecode.lib import audit_logger
35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.auth import (
37 from rhodecode.lib.auth import (
37 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib.utils2 import safe_int, safe_unicode
40 from rhodecode.lib.utils2 import safe_int, safe_unicode
40 from rhodecode.model.auth_token import AuthTokenModel
41 from rhodecode.model.auth_token import AuthTokenModel
41 from rhodecode.model.ssh_key import SshKeyModel
42 from rhodecode.model.ssh_key import SshKeyModel
42 from rhodecode.model.user import UserModel
43 from rhodecode.model.user import UserModel
43 from rhodecode.model.user_group import UserGroupModel
44 from rhodecode.model.user_group import UserGroupModel
44 from rhodecode.model.db import (
45 from rhodecode.model.db import (
45 or_, User, UserIpMap, UserEmailMap, UserApiKeys, UserSshKeys)
46 or_, User, UserIpMap, UserEmailMap, UserApiKeys, UserSshKeys)
46 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
47
48
48 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
49
50
50
51
51 class AdminUsersView(BaseAppView, DataGridAppView):
52 class AdminUsersView(BaseAppView, DataGridAppView):
52 ALLOW_SCOPED_TOKENS = False
53 ALLOW_SCOPED_TOKENS = False
53 """
54 """
54 This view has alternative version inside EE, if modified please take a look
55 This view has alternative version inside EE, if modified please take a look
55 in there as well.
56 in there as well.
56 """
57 """
57
58
58 def load_default_context(self):
59 def load_default_context(self):
59 c = self._get_local_tmpl_context()
60 c = self._get_local_tmpl_context()
60 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
61 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
61 self._register_global_c(c)
62 self._register_global_c(c)
62 return c
63 return c
63
64
64 def _redirect_for_default_user(self, username):
65 def _redirect_for_default_user(self, username):
65 _ = self.request.translate
66 _ = self.request.translate
66 if username == User.DEFAULT_USER:
67 if username == User.DEFAULT_USER:
67 h.flash(_("You can't edit this user"), category='warning')
68 h.flash(_("You can't edit this user"), category='warning')
68 # TODO(marcink): redirect to 'users' admin panel once this
69 # TODO(marcink): redirect to 'users' admin panel once this
69 # is a pyramid view
70 # is a pyramid view
70 raise HTTPFound('/')
71 raise HTTPFound('/')
71
72
72 @LoginRequired()
73 @LoginRequired()
73 @HasPermissionAllDecorator('hg.admin')
74 @HasPermissionAllDecorator('hg.admin')
74 @view_config(
75 @view_config(
75 route_name='users', request_method='GET',
76 route_name='users', request_method='GET',
76 renderer='rhodecode:templates/admin/users/users.mako')
77 renderer='rhodecode:templates/admin/users/users.mako')
77 def users_list(self):
78 def users_list(self):
78 c = self.load_default_context()
79 c = self.load_default_context()
79 return self._get_template_context(c)
80 return self._get_template_context(c)
80
81
81 @LoginRequired()
82 @LoginRequired()
82 @HasPermissionAllDecorator('hg.admin')
83 @HasPermissionAllDecorator('hg.admin')
83 @view_config(
84 @view_config(
84 # renderer defined below
85 # renderer defined below
85 route_name='users_data', request_method='GET',
86 route_name='users_data', request_method='GET',
86 renderer='json_ext', xhr=True)
87 renderer='json_ext', xhr=True)
87 def users_list_data(self):
88 def users_list_data(self):
88 column_map = {
89 column_map = {
89 'first_name': 'name',
90 'first_name': 'name',
90 'last_name': 'lastname',
91 'last_name': 'lastname',
91 }
92 }
92 draw, start, limit = self._extract_chunk(self.request)
93 draw, start, limit = self._extract_chunk(self.request)
93 search_q, order_by, order_dir = self._extract_ordering(
94 search_q, order_by, order_dir = self._extract_ordering(
94 self.request, column_map=column_map)
95 self.request, column_map=column_map)
95
96
96 _render = self.request.get_partial_renderer(
97 _render = self.request.get_partial_renderer(
97 'data_table/_dt_elements.mako')
98 'data_table/_dt_elements.mako')
98
99
99 def user_actions(user_id, username):
100 def user_actions(user_id, username):
100 return _render("user_actions", user_id, username)
101 return _render("user_actions", user_id, username)
101
102
102 users_data_total_count = User.query()\
103 users_data_total_count = User.query()\
103 .filter(User.username != User.DEFAULT_USER) \
104 .filter(User.username != User.DEFAULT_USER) \
104 .count()
105 .count()
105
106
106 # json generate
107 # json generate
107 base_q = User.query().filter(User.username != User.DEFAULT_USER)
108 base_q = User.query().filter(User.username != User.DEFAULT_USER)
108
109
109 if search_q:
110 if search_q:
110 like_expression = u'%{}%'.format(safe_unicode(search_q))
111 like_expression = u'%{}%'.format(safe_unicode(search_q))
111 base_q = base_q.filter(or_(
112 base_q = base_q.filter(or_(
112 User.username.ilike(like_expression),
113 User.username.ilike(like_expression),
113 User._email.ilike(like_expression),
114 User._email.ilike(like_expression),
114 User.name.ilike(like_expression),
115 User.name.ilike(like_expression),
115 User.lastname.ilike(like_expression),
116 User.lastname.ilike(like_expression),
116 ))
117 ))
117
118
118 users_data_total_filtered_count = base_q.count()
119 users_data_total_filtered_count = base_q.count()
119
120
120 sort_col = getattr(User, order_by, None)
121 sort_col = getattr(User, order_by, None)
121 if sort_col:
122 if sort_col:
122 if order_dir == 'asc':
123 if order_dir == 'asc':
123 # handle null values properly to order by NULL last
124 # handle null values properly to order by NULL last
124 if order_by in ['last_activity']:
125 if order_by in ['last_activity']:
125 sort_col = coalesce(sort_col, datetime.date.max)
126 sort_col = coalesce(sort_col, datetime.date.max)
126 sort_col = sort_col.asc()
127 sort_col = sort_col.asc()
127 else:
128 else:
128 # handle null values properly to order by NULL last
129 # handle null values properly to order by NULL last
129 if order_by in ['last_activity']:
130 if order_by in ['last_activity']:
130 sort_col = coalesce(sort_col, datetime.date.min)
131 sort_col = coalesce(sort_col, datetime.date.min)
131 sort_col = sort_col.desc()
132 sort_col = sort_col.desc()
132
133
133 base_q = base_q.order_by(sort_col)
134 base_q = base_q.order_by(sort_col)
134 base_q = base_q.offset(start).limit(limit)
135 base_q = base_q.offset(start).limit(limit)
135
136
136 users_list = base_q.all()
137 users_list = base_q.all()
137
138
138 users_data = []
139 users_data = []
139 for user in users_list:
140 for user in users_list:
140 users_data.append({
141 users_data.append({
141 "username": h.gravatar_with_user(self.request, user.username),
142 "username": h.gravatar_with_user(self.request, user.username),
142 "email": user.email,
143 "email": user.email,
143 "first_name": user.first_name,
144 "first_name": user.first_name,
144 "last_name": user.last_name,
145 "last_name": user.last_name,
145 "last_login": h.format_date(user.last_login),
146 "last_login": h.format_date(user.last_login),
146 "last_activity": h.format_date(user.last_activity),
147 "last_activity": h.format_date(user.last_activity),
147 "active": h.bool2icon(user.active),
148 "active": h.bool2icon(user.active),
148 "active_raw": user.active,
149 "active_raw": user.active,
149 "admin": h.bool2icon(user.admin),
150 "admin": h.bool2icon(user.admin),
150 "extern_type": user.extern_type,
151 "extern_type": user.extern_type,
151 "extern_name": user.extern_name,
152 "extern_name": user.extern_name,
152 "action": user_actions(user.user_id, user.username),
153 "action": user_actions(user.user_id, user.username),
153 })
154 })
154
155
155 data = ({
156 data = ({
156 'draw': draw,
157 'draw': draw,
157 'data': users_data,
158 'data': users_data,
158 'recordsTotal': users_data_total_count,
159 'recordsTotal': users_data_total_count,
159 'recordsFiltered': users_data_total_filtered_count,
160 'recordsFiltered': users_data_total_filtered_count,
160 })
161 })
161
162
162 return data
163 return data
163
164
164 @LoginRequired()
165 @LoginRequired()
165 @HasPermissionAllDecorator('hg.admin')
166 @HasPermissionAllDecorator('hg.admin')
166 @view_config(
167 @view_config(
167 route_name='edit_user_auth_tokens', request_method='GET',
168 route_name='edit_user_auth_tokens', request_method='GET',
168 renderer='rhodecode:templates/admin/users/user_edit.mako')
169 renderer='rhodecode:templates/admin/users/user_edit.mako')
169 def auth_tokens(self):
170 def auth_tokens(self):
170 _ = self.request.translate
171 _ = self.request.translate
171 c = self.load_default_context()
172 c = self.load_default_context()
172
173
173 user_id = self.request.matchdict.get('user_id')
174 user_id = self.request.matchdict.get('user_id')
174 c.user = User.get_or_404(user_id)
175 c.user = User.get_or_404(user_id)
175 self._redirect_for_default_user(c.user.username)
176 self._redirect_for_default_user(c.user.username)
176
177
177 c.active = 'auth_tokens'
178 c.active = 'auth_tokens'
178
179
179 c.lifetime_values = [
180 c.lifetime_values = [
180 (str(-1), _('forever')),
181 (str(-1), _('forever')),
181 (str(5), _('5 minutes')),
182 (str(5), _('5 minutes')),
182 (str(60), _('1 hour')),
183 (str(60), _('1 hour')),
183 (str(60 * 24), _('1 day')),
184 (str(60 * 24), _('1 day')),
184 (str(60 * 24 * 30), _('1 month')),
185 (str(60 * 24 * 30), _('1 month')),
185 ]
186 ]
186 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
187 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
187 c.role_values = [
188 c.role_values = [
188 (x, AuthTokenModel.cls._get_role_name(x))
189 (x, AuthTokenModel.cls._get_role_name(x))
189 for x in AuthTokenModel.cls.ROLES]
190 for x in AuthTokenModel.cls.ROLES]
190 c.role_options = [(c.role_values, _("Role"))]
191 c.role_options = [(c.role_values, _("Role"))]
191 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
192 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
192 c.user.user_id, show_expired=True)
193 c.user.user_id, show_expired=True)
193 return self._get_template_context(c)
194 return self._get_template_context(c)
194
195
195 def maybe_attach_token_scope(self, token):
196 def maybe_attach_token_scope(self, token):
196 # implemented in EE edition
197 # implemented in EE edition
197 pass
198 pass
198
199
199 @LoginRequired()
200 @LoginRequired()
200 @HasPermissionAllDecorator('hg.admin')
201 @HasPermissionAllDecorator('hg.admin')
201 @CSRFRequired()
202 @CSRFRequired()
202 @view_config(
203 @view_config(
203 route_name='edit_user_auth_tokens_add', request_method='POST')
204 route_name='edit_user_auth_tokens_add', request_method='POST')
204 def auth_tokens_add(self):
205 def auth_tokens_add(self):
205 _ = self.request.translate
206 _ = self.request.translate
206 c = self.load_default_context()
207 c = self.load_default_context()
207
208
208 user_id = self.request.matchdict.get('user_id')
209 user_id = self.request.matchdict.get('user_id')
209 c.user = User.get_or_404(user_id)
210 c.user = User.get_or_404(user_id)
210
211
211 self._redirect_for_default_user(c.user.username)
212 self._redirect_for_default_user(c.user.username)
212
213
213 user_data = c.user.get_api_data()
214 user_data = c.user.get_api_data()
214 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
215 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
215 description = self.request.POST.get('description')
216 description = self.request.POST.get('description')
216 role = self.request.POST.get('role')
217 role = self.request.POST.get('role')
217
218
218 token = AuthTokenModel().create(
219 token = AuthTokenModel().create(
219 c.user.user_id, description, lifetime, role)
220 c.user.user_id, description, lifetime, role)
220 token_data = token.get_api_data()
221 token_data = token.get_api_data()
221
222
222 self.maybe_attach_token_scope(token)
223 self.maybe_attach_token_scope(token)
223 audit_logger.store_web(
224 audit_logger.store_web(
224 'user.edit.token.add', action_data={
225 'user.edit.token.add', action_data={
225 'data': {'token': token_data, 'user': user_data}},
226 'data': {'token': token_data, 'user': user_data}},
226 user=self._rhodecode_user, )
227 user=self._rhodecode_user, )
227 Session().commit()
228 Session().commit()
228
229
229 h.flash(_("Auth token successfully created"), category='success')
230 h.flash(_("Auth token successfully created"), category='success')
230 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
231 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
231
232
232 @LoginRequired()
233 @LoginRequired()
233 @HasPermissionAllDecorator('hg.admin')
234 @HasPermissionAllDecorator('hg.admin')
234 @CSRFRequired()
235 @CSRFRequired()
235 @view_config(
236 @view_config(
236 route_name='edit_user_auth_tokens_delete', request_method='POST')
237 route_name='edit_user_auth_tokens_delete', request_method='POST')
237 def auth_tokens_delete(self):
238 def auth_tokens_delete(self):
238 _ = self.request.translate
239 _ = self.request.translate
239 c = self.load_default_context()
240 c = self.load_default_context()
240
241
241 user_id = self.request.matchdict.get('user_id')
242 user_id = self.request.matchdict.get('user_id')
242 c.user = User.get_or_404(user_id)
243 c.user = User.get_or_404(user_id)
243 self._redirect_for_default_user(c.user.username)
244 self._redirect_for_default_user(c.user.username)
244 user_data = c.user.get_api_data()
245 user_data = c.user.get_api_data()
245
246
246 del_auth_token = self.request.POST.get('del_auth_token')
247 del_auth_token = self.request.POST.get('del_auth_token')
247
248
248 if del_auth_token:
249 if del_auth_token:
249 token = UserApiKeys.get_or_404(del_auth_token)
250 token = UserApiKeys.get_or_404(del_auth_token)
250 token_data = token.get_api_data()
251 token_data = token.get_api_data()
251
252
252 AuthTokenModel().delete(del_auth_token, c.user.user_id)
253 AuthTokenModel().delete(del_auth_token, c.user.user_id)
253 audit_logger.store_web(
254 audit_logger.store_web(
254 'user.edit.token.delete', action_data={
255 'user.edit.token.delete', action_data={
255 'data': {'token': token_data, 'user': user_data}},
256 'data': {'token': token_data, 'user': user_data}},
256 user=self._rhodecode_user,)
257 user=self._rhodecode_user,)
257 Session().commit()
258 Session().commit()
258 h.flash(_("Auth token successfully deleted"), category='success')
259 h.flash(_("Auth token successfully deleted"), category='success')
259
260
260 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
261 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
261
262
262 @LoginRequired()
263 @LoginRequired()
263 @HasPermissionAllDecorator('hg.admin')
264 @HasPermissionAllDecorator('hg.admin')
264 @view_config(
265 @view_config(
265 route_name='edit_user_ssh_keys', request_method='GET',
266 route_name='edit_user_ssh_keys', request_method='GET',
266 renderer='rhodecode:templates/admin/users/user_edit.mako')
267 renderer='rhodecode:templates/admin/users/user_edit.mako')
267 def ssh_keys(self):
268 def ssh_keys(self):
268 _ = self.request.translate
269 _ = self.request.translate
269 c = self.load_default_context()
270 c = self.load_default_context()
270
271
271 user_id = self.request.matchdict.get('user_id')
272 user_id = self.request.matchdict.get('user_id')
272 c.user = User.get_or_404(user_id)
273 c.user = User.get_or_404(user_id)
273 self._redirect_for_default_user(c.user.username)
274 self._redirect_for_default_user(c.user.username)
274
275
275 c.active = 'ssh_keys'
276 c.active = 'ssh_keys'
276 c.default_key = self.request.GET.get('default_key')
277 c.default_key = self.request.GET.get('default_key')
277 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
278 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
278 return self._get_template_context(c)
279 return self._get_template_context(c)
279
280
280 @LoginRequired()
281 @LoginRequired()
281 @HasPermissionAllDecorator('hg.admin')
282 @HasPermissionAllDecorator('hg.admin')
282 @view_config(
283 @view_config(
283 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
284 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
284 renderer='rhodecode:templates/admin/users/user_edit.mako')
285 renderer='rhodecode:templates/admin/users/user_edit.mako')
285 def ssh_keys_generate_keypair(self):
286 def ssh_keys_generate_keypair(self):
286 _ = self.request.translate
287 _ = self.request.translate
287 c = self.load_default_context()
288 c = self.load_default_context()
288
289
289 user_id = self.request.matchdict.get('user_id')
290 user_id = self.request.matchdict.get('user_id')
290 c.user = User.get_or_404(user_id)
291 c.user = User.get_or_404(user_id)
291 self._redirect_for_default_user(c.user.username)
292 self._redirect_for_default_user(c.user.username)
292
293
293 c.active = 'ssh_keys_generate'
294 c.active = 'ssh_keys_generate'
294 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
295 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
295 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
296 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
296
297
297 return self._get_template_context(c)
298 return self._get_template_context(c)
298
299
299 @LoginRequired()
300 @LoginRequired()
300 @HasPermissionAllDecorator('hg.admin')
301 @HasPermissionAllDecorator('hg.admin')
301 @CSRFRequired()
302 @CSRFRequired()
302 @view_config(
303 @view_config(
303 route_name='edit_user_ssh_keys_add', request_method='POST')
304 route_name='edit_user_ssh_keys_add', request_method='POST')
304 def ssh_keys_add(self):
305 def ssh_keys_add(self):
305 _ = self.request.translate
306 _ = self.request.translate
306 c = self.load_default_context()
307 c = self.load_default_context()
307
308
308 user_id = self.request.matchdict.get('user_id')
309 user_id = self.request.matchdict.get('user_id')
309 c.user = User.get_or_404(user_id)
310 c.user = User.get_or_404(user_id)
310
311
311 self._redirect_for_default_user(c.user.username)
312 self._redirect_for_default_user(c.user.username)
312
313
313 user_data = c.user.get_api_data()
314 user_data = c.user.get_api_data()
314 key_data = self.request.POST.get('key_data')
315 key_data = self.request.POST.get('key_data')
315 description = self.request.POST.get('description')
316 description = self.request.POST.get('description')
316
317
317 try:
318 try:
318 if not key_data:
319 if not key_data:
319 raise ValueError('Please add a valid public key')
320 raise ValueError('Please add a valid public key')
320
321
321 key = SshKeyModel().parse_key(key_data.strip())
322 key = SshKeyModel().parse_key(key_data.strip())
322 fingerprint = key.hash_md5()
323 fingerprint = key.hash_md5()
323
324
324 ssh_key = SshKeyModel().create(
325 ssh_key = SshKeyModel().create(
325 c.user.user_id, fingerprint, key_data, description)
326 c.user.user_id, fingerprint, key_data, description)
326 ssh_key_data = ssh_key.get_api_data()
327 ssh_key_data = ssh_key.get_api_data()
327
328
328 audit_logger.store_web(
329 audit_logger.store_web(
329 'user.edit.ssh_key.add', action_data={
330 'user.edit.ssh_key.add', action_data={
330 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
331 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
331 user=self._rhodecode_user, )
332 user=self._rhodecode_user, )
332 Session().commit()
333 Session().commit()
333
334
334 # Trigger an event on change of keys.
335 # Trigger an event on change of keys.
335 trigger(SshKeyFileChangeEvent(), self.request.registry)
336 trigger(SshKeyFileChangeEvent(), self.request.registry)
336
337
337 h.flash(_("Ssh Key successfully created"), category='success')
338 h.flash(_("Ssh Key successfully created"), category='success')
338
339
339 except IntegrityError:
340 except IntegrityError:
340 log.exception("Exception during ssh key saving")
341 log.exception("Exception during ssh key saving")
341 h.flash(_('An error occurred during ssh key saving: {}').format(
342 h.flash(_('An error occurred during ssh key saving: {}').format(
342 'Such key already exists, please use a different one'),
343 'Such key already exists, please use a different one'),
343 category='error')
344 category='error')
344 except Exception as e:
345 except Exception as e:
345 log.exception("Exception during ssh key saving")
346 log.exception("Exception during ssh key saving")
346 h.flash(_('An error occurred during ssh key saving: {}').format(e),
347 h.flash(_('An error occurred during ssh key saving: {}').format(e),
347 category='error')
348 category='error')
348
349
349 return HTTPFound(
350 return HTTPFound(
350 h.route_path('edit_user_ssh_keys', user_id=user_id))
351 h.route_path('edit_user_ssh_keys', user_id=user_id))
351
352
352 @LoginRequired()
353 @LoginRequired()
353 @HasPermissionAllDecorator('hg.admin')
354 @HasPermissionAllDecorator('hg.admin')
354 @CSRFRequired()
355 @CSRFRequired()
355 @view_config(
356 @view_config(
356 route_name='edit_user_ssh_keys_delete', request_method='POST')
357 route_name='edit_user_ssh_keys_delete', request_method='POST')
357 def ssh_keys_delete(self):
358 def ssh_keys_delete(self):
358 _ = self.request.translate
359 _ = self.request.translate
359 c = self.load_default_context()
360 c = self.load_default_context()
360
361
361 user_id = self.request.matchdict.get('user_id')
362 user_id = self.request.matchdict.get('user_id')
362 c.user = User.get_or_404(user_id)
363 c.user = User.get_or_404(user_id)
363 self._redirect_for_default_user(c.user.username)
364 self._redirect_for_default_user(c.user.username)
364 user_data = c.user.get_api_data()
365 user_data = c.user.get_api_data()
365
366
366 del_ssh_key = self.request.POST.get('del_ssh_key')
367 del_ssh_key = self.request.POST.get('del_ssh_key')
367
368
368 if del_ssh_key:
369 if del_ssh_key:
369 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
370 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
370 ssh_key_data = ssh_key.get_api_data()
371 ssh_key_data = ssh_key.get_api_data()
371
372
372 SshKeyModel().delete(del_ssh_key, c.user.user_id)
373 SshKeyModel().delete(del_ssh_key, c.user.user_id)
373 audit_logger.store_web(
374 audit_logger.store_web(
374 'user.edit.ssh_key.delete', action_data={
375 'user.edit.ssh_key.delete', action_data={
375 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
376 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
376 user=self._rhodecode_user,)
377 user=self._rhodecode_user,)
377 Session().commit()
378 Session().commit()
378 # Trigger an event on change of keys.
379 # Trigger an event on change of keys.
379 trigger(SshKeyFileChangeEvent(), self.request.registry)
380 trigger(SshKeyFileChangeEvent(), self.request.registry)
380 h.flash(_("Ssh key successfully deleted"), category='success')
381 h.flash(_("Ssh key successfully deleted"), category='success')
381
382
382 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
383 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
383
384
384 @LoginRequired()
385 @LoginRequired()
385 @HasPermissionAllDecorator('hg.admin')
386 @HasPermissionAllDecorator('hg.admin')
386 @view_config(
387 @view_config(
387 route_name='edit_user_emails', request_method='GET',
388 route_name='edit_user_emails', request_method='GET',
388 renderer='rhodecode:templates/admin/users/user_edit.mako')
389 renderer='rhodecode:templates/admin/users/user_edit.mako')
389 def emails(self):
390 def emails(self):
390 _ = self.request.translate
391 _ = self.request.translate
391 c = self.load_default_context()
392 c = self.load_default_context()
392
393
393 user_id = self.request.matchdict.get('user_id')
394 user_id = self.request.matchdict.get('user_id')
394 c.user = User.get_or_404(user_id)
395 c.user = User.get_or_404(user_id)
395 self._redirect_for_default_user(c.user.username)
396 self._redirect_for_default_user(c.user.username)
396
397
397 c.active = 'emails'
398 c.active = 'emails'
398 c.user_email_map = UserEmailMap.query() \
399 c.user_email_map = UserEmailMap.query() \
399 .filter(UserEmailMap.user == c.user).all()
400 .filter(UserEmailMap.user == c.user).all()
400
401
401 return self._get_template_context(c)
402 return self._get_template_context(c)
402
403
403 @LoginRequired()
404 @LoginRequired()
404 @HasPermissionAllDecorator('hg.admin')
405 @HasPermissionAllDecorator('hg.admin')
405 @CSRFRequired()
406 @CSRFRequired()
406 @view_config(
407 @view_config(
407 route_name='edit_user_emails_add', request_method='POST')
408 route_name='edit_user_emails_add', request_method='POST')
408 def emails_add(self):
409 def emails_add(self):
409 _ = self.request.translate
410 _ = self.request.translate
410 c = self.load_default_context()
411 c = self.load_default_context()
411
412
412 user_id = self.request.matchdict.get('user_id')
413 user_id = self.request.matchdict.get('user_id')
413 c.user = User.get_or_404(user_id)
414 c.user = User.get_or_404(user_id)
414 self._redirect_for_default_user(c.user.username)
415 self._redirect_for_default_user(c.user.username)
415
416
416 email = self.request.POST.get('new_email')
417 email = self.request.POST.get('new_email')
417 user_data = c.user.get_api_data()
418 user_data = c.user.get_api_data()
418 try:
419 try:
419 UserModel().add_extra_email(c.user.user_id, email)
420 UserModel().add_extra_email(c.user.user_id, email)
420 audit_logger.store_web(
421 audit_logger.store_web(
421 'user.edit.email.add', action_data={'email': email, 'user': user_data},
422 'user.edit.email.add', action_data={'email': email, 'user': user_data},
422 user=self._rhodecode_user)
423 user=self._rhodecode_user)
423 Session().commit()
424 Session().commit()
424 h.flash(_("Added new email address `%s` for user account") % email,
425 h.flash(_("Added new email address `%s` for user account") % email,
425 category='success')
426 category='success')
426 except formencode.Invalid as error:
427 except formencode.Invalid as error:
427 h.flash(h.escape(error.error_dict['email']), category='error')
428 h.flash(h.escape(error.error_dict['email']), category='error')
428 except Exception:
429 except Exception:
429 log.exception("Exception during email saving")
430 log.exception("Exception during email saving")
430 h.flash(_('An error occurred during email saving'),
431 h.flash(_('An error occurred during email saving'),
431 category='error')
432 category='error')
432 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
433 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
433
434
434 @LoginRequired()
435 @LoginRequired()
435 @HasPermissionAllDecorator('hg.admin')
436 @HasPermissionAllDecorator('hg.admin')
436 @CSRFRequired()
437 @CSRFRequired()
437 @view_config(
438 @view_config(
438 route_name='edit_user_emails_delete', request_method='POST')
439 route_name='edit_user_emails_delete', request_method='POST')
439 def emails_delete(self):
440 def emails_delete(self):
440 _ = self.request.translate
441 _ = self.request.translate
441 c = self.load_default_context()
442 c = self.load_default_context()
442
443
443 user_id = self.request.matchdict.get('user_id')
444 user_id = self.request.matchdict.get('user_id')
444 c.user = User.get_or_404(user_id)
445 c.user = User.get_or_404(user_id)
445 self._redirect_for_default_user(c.user.username)
446 self._redirect_for_default_user(c.user.username)
446
447
447 email_id = self.request.POST.get('del_email_id')
448 email_id = self.request.POST.get('del_email_id')
448 user_model = UserModel()
449 user_model = UserModel()
449
450
450 email = UserEmailMap.query().get(email_id).email
451 email = UserEmailMap.query().get(email_id).email
451 user_data = c.user.get_api_data()
452 user_data = c.user.get_api_data()
452 user_model.delete_extra_email(c.user.user_id, email_id)
453 user_model.delete_extra_email(c.user.user_id, email_id)
453 audit_logger.store_web(
454 audit_logger.store_web(
454 'user.edit.email.delete', action_data={'email': email, 'user': user_data},
455 'user.edit.email.delete', action_data={'email': email, 'user': user_data},
455 user=self._rhodecode_user)
456 user=self._rhodecode_user)
456 Session().commit()
457 Session().commit()
457 h.flash(_("Removed email address from user account"),
458 h.flash(_("Removed email address from user account"),
458 category='success')
459 category='success')
459 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
460 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
460
461
461 @LoginRequired()
462 @LoginRequired()
462 @HasPermissionAllDecorator('hg.admin')
463 @HasPermissionAllDecorator('hg.admin')
463 @view_config(
464 @view_config(
464 route_name='edit_user_ips', request_method='GET',
465 route_name='edit_user_ips', request_method='GET',
465 renderer='rhodecode:templates/admin/users/user_edit.mako')
466 renderer='rhodecode:templates/admin/users/user_edit.mako')
466 def ips(self):
467 def ips(self):
467 _ = self.request.translate
468 _ = self.request.translate
468 c = self.load_default_context()
469 c = self.load_default_context()
469
470
470 user_id = self.request.matchdict.get('user_id')
471 user_id = self.request.matchdict.get('user_id')
471 c.user = User.get_or_404(user_id)
472 c.user = User.get_or_404(user_id)
472 self._redirect_for_default_user(c.user.username)
473 self._redirect_for_default_user(c.user.username)
473
474
474 c.active = 'ips'
475 c.active = 'ips'
475 c.user_ip_map = UserIpMap.query() \
476 c.user_ip_map = UserIpMap.query() \
476 .filter(UserIpMap.user == c.user).all()
477 .filter(UserIpMap.user == c.user).all()
477
478
478 c.inherit_default_ips = c.user.inherit_default_permissions
479 c.inherit_default_ips = c.user.inherit_default_permissions
479 c.default_user_ip_map = UserIpMap.query() \
480 c.default_user_ip_map = UserIpMap.query() \
480 .filter(UserIpMap.user == User.get_default_user()).all()
481 .filter(UserIpMap.user == User.get_default_user()).all()
481
482
482 return self._get_template_context(c)
483 return self._get_template_context(c)
483
484
484 @LoginRequired()
485 @LoginRequired()
485 @HasPermissionAllDecorator('hg.admin')
486 @HasPermissionAllDecorator('hg.admin')
486 @CSRFRequired()
487 @CSRFRequired()
487 @view_config(
488 @view_config(
488 route_name='edit_user_ips_add', request_method='POST')
489 route_name='edit_user_ips_add', request_method='POST')
489 def ips_add(self):
490 def ips_add(self):
490 _ = self.request.translate
491 _ = self.request.translate
491 c = self.load_default_context()
492 c = self.load_default_context()
492
493
493 user_id = self.request.matchdict.get('user_id')
494 user_id = self.request.matchdict.get('user_id')
494 c.user = User.get_or_404(user_id)
495 c.user = User.get_or_404(user_id)
495 # NOTE(marcink): this view is allowed for default users, as we can
496 # NOTE(marcink): this view is allowed for default users, as we can
496 # edit their IP white list
497 # edit their IP white list
497
498
498 user_model = UserModel()
499 user_model = UserModel()
499 desc = self.request.POST.get('description')
500 desc = self.request.POST.get('description')
500 try:
501 try:
501 ip_list = user_model.parse_ip_range(
502 ip_list = user_model.parse_ip_range(
502 self.request.POST.get('new_ip'))
503 self.request.POST.get('new_ip'))
503 except Exception as e:
504 except Exception as e:
504 ip_list = []
505 ip_list = []
505 log.exception("Exception during ip saving")
506 log.exception("Exception during ip saving")
506 h.flash(_('An error occurred during ip saving:%s' % (e,)),
507 h.flash(_('An error occurred during ip saving:%s' % (e,)),
507 category='error')
508 category='error')
508 added = []
509 added = []
509 user_data = c.user.get_api_data()
510 user_data = c.user.get_api_data()
510 for ip in ip_list:
511 for ip in ip_list:
511 try:
512 try:
512 user_model.add_extra_ip(c.user.user_id, ip, desc)
513 user_model.add_extra_ip(c.user.user_id, ip, desc)
513 audit_logger.store_web(
514 audit_logger.store_web(
514 'user.edit.ip.add', action_data={'ip': ip, 'user': user_data},
515 'user.edit.ip.add', action_data={'ip': ip, 'user': user_data},
515 user=self._rhodecode_user)
516 user=self._rhodecode_user)
516 Session().commit()
517 Session().commit()
517 added.append(ip)
518 added.append(ip)
518 except formencode.Invalid as error:
519 except formencode.Invalid as error:
519 msg = error.error_dict['ip']
520 msg = error.error_dict['ip']
520 h.flash(msg, category='error')
521 h.flash(msg, category='error')
521 except Exception:
522 except Exception:
522 log.exception("Exception during ip saving")
523 log.exception("Exception during ip saving")
523 h.flash(_('An error occurred during ip saving'),
524 h.flash(_('An error occurred during ip saving'),
524 category='error')
525 category='error')
525 if added:
526 if added:
526 h.flash(
527 h.flash(
527 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
528 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
528 category='success')
529 category='success')
529 if 'default_user' in self.request.POST:
530 if 'default_user' in self.request.POST:
530 # case for editing global IP list we do it for 'DEFAULT' user
531 # case for editing global IP list we do it for 'DEFAULT' user
531 raise HTTPFound(h.route_path('admin_permissions_ips'))
532 raise HTTPFound(h.route_path('admin_permissions_ips'))
532 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
533 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
533
534
534 @LoginRequired()
535 @LoginRequired()
535 @HasPermissionAllDecorator('hg.admin')
536 @HasPermissionAllDecorator('hg.admin')
536 @CSRFRequired()
537 @CSRFRequired()
537 @view_config(
538 @view_config(
538 route_name='edit_user_ips_delete', request_method='POST')
539 route_name='edit_user_ips_delete', request_method='POST')
539 def ips_delete(self):
540 def ips_delete(self):
540 _ = self.request.translate
541 _ = self.request.translate
541 c = self.load_default_context()
542 c = self.load_default_context()
542
543
543 user_id = self.request.matchdict.get('user_id')
544 user_id = self.request.matchdict.get('user_id')
544 c.user = User.get_or_404(user_id)
545 c.user = User.get_or_404(user_id)
545 # NOTE(marcink): this view is allowed for default users, as we can
546 # NOTE(marcink): this view is allowed for default users, as we can
546 # edit their IP white list
547 # edit their IP white list
547
548
548 ip_id = self.request.POST.get('del_ip_id')
549 ip_id = self.request.POST.get('del_ip_id')
549 user_model = UserModel()
550 user_model = UserModel()
550 user_data = c.user.get_api_data()
551 user_data = c.user.get_api_data()
551 ip = UserIpMap.query().get(ip_id).ip_addr
552 ip = UserIpMap.query().get(ip_id).ip_addr
552 user_model.delete_extra_ip(c.user.user_id, ip_id)
553 user_model.delete_extra_ip(c.user.user_id, ip_id)
553 audit_logger.store_web(
554 audit_logger.store_web(
554 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
555 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
555 user=self._rhodecode_user)
556 user=self._rhodecode_user)
556 Session().commit()
557 Session().commit()
557 h.flash(_("Removed ip address from user whitelist"), category='success')
558 h.flash(_("Removed ip address from user whitelist"), category='success')
558
559
559 if 'default_user' in self.request.POST:
560 if 'default_user' in self.request.POST:
560 # case for editing global IP list we do it for 'DEFAULT' user
561 # case for editing global IP list we do it for 'DEFAULT' user
561 raise HTTPFound(h.route_path('admin_permissions_ips'))
562 raise HTTPFound(h.route_path('admin_permissions_ips'))
562 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
563 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
563
564
564 @LoginRequired()
565 @LoginRequired()
565 @HasPermissionAllDecorator('hg.admin')
566 @HasPermissionAllDecorator('hg.admin')
566 @view_config(
567 @view_config(
567 route_name='edit_user_groups_management', request_method='GET',
568 route_name='edit_user_groups_management', request_method='GET',
568 renderer='rhodecode:templates/admin/users/user_edit.mako')
569 renderer='rhodecode:templates/admin/users/user_edit.mako')
569 def groups_management(self):
570 def groups_management(self):
570 c = self.load_default_context()
571 c = self.load_default_context()
571
572
572 user_id = self.request.matchdict.get('user_id')
573 user_id = self.request.matchdict.get('user_id')
573 c.user = User.get_or_404(user_id)
574 c.user = User.get_or_404(user_id)
574 c.data = c.user.group_member
575 c.data = c.user.group_member
575 self._redirect_for_default_user(c.user.username)
576 self._redirect_for_default_user(c.user.username)
576 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
577 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
577 for group in c.user.group_member]
578 for group in c.user.group_member]
578 c.groups = json.dumps(groups)
579 c.groups = json.dumps(groups)
579 c.active = 'groups'
580 c.active = 'groups'
580
581
581 return self._get_template_context(c)
582 return self._get_template_context(c)
582
583
583 @LoginRequired()
584 @LoginRequired()
584 @HasPermissionAllDecorator('hg.admin')
585 @HasPermissionAllDecorator('hg.admin')
585 @CSRFRequired()
586 @CSRFRequired()
586 @view_config(
587 @view_config(
587 route_name='edit_user_groups_management_updates', request_method='POST')
588 route_name='edit_user_groups_management_updates', request_method='POST')
588 def groups_management_updates(self):
589 def groups_management_updates(self):
589 _ = self.request.translate
590 _ = self.request.translate
590 c = self.load_default_context()
591 c = self.load_default_context()
591
592
592 user_id = self.request.matchdict.get('user_id')
593 user_id = self.request.matchdict.get('user_id')
593 c.user = User.get_or_404(user_id)
594 c.user = User.get_or_404(user_id)
594 self._redirect_for_default_user(c.user.username)
595 self._redirect_for_default_user(c.user.username)
595
596
596 user_groups = set(self.request.POST.getall('users_group_id'))
597 user_groups = set(self.request.POST.getall('users_group_id'))
597 user_groups_objects = []
598 user_groups_objects = []
598
599
599 for ugid in user_groups:
600 for ugid in user_groups:
600 user_groups_objects.append(
601 user_groups_objects.append(
601 UserGroupModel().get_group(safe_int(ugid)))
602 UserGroupModel().get_group(safe_int(ugid)))
602 user_group_model = UserGroupModel()
603 user_group_model = UserGroupModel()
603 user_group_model.change_groups(c.user, user_groups_objects)
604 user_group_model.change_groups(c.user, user_groups_objects)
604
605
605 Session().commit()
606 Session().commit()
606 c.active = 'user_groups_management'
607 c.active = 'user_groups_management'
607 h.flash(_("Groups successfully changed"), category='success')
608 h.flash(_("Groups successfully changed"), category='success')
608
609
609 return HTTPFound(h.route_path(
610 return HTTPFound(h.route_path(
610 'edit_user_groups_management', user_id=user_id))
611 'edit_user_groups_management', user_id=user_id))
611
612
612 @LoginRequired()
613 @LoginRequired()
613 @HasPermissionAllDecorator('hg.admin')
614 @HasPermissionAllDecorator('hg.admin')
614 @view_config(
615 @view_config(
615 route_name='edit_user_audit_logs', request_method='GET',
616 route_name='edit_user_audit_logs', request_method='GET',
616 renderer='rhodecode:templates/admin/users/user_edit.mako')
617 renderer='rhodecode:templates/admin/users/user_edit.mako')
617 def user_audit_logs(self):
618 def user_audit_logs(self):
618 _ = self.request.translate
619 _ = self.request.translate
619 c = self.load_default_context()
620 c = self.load_default_context()
620
621
621 user_id = self.request.matchdict.get('user_id')
622 user_id = self.request.matchdict.get('user_id')
622 c.user = User.get_or_404(user_id)
623 c.user = User.get_or_404(user_id)
623 self._redirect_for_default_user(c.user.username)
624 self._redirect_for_default_user(c.user.username)
624 c.active = 'audit'
625 c.active = 'audit'
625
626
626 p = safe_int(self.request.GET.get('page', 1), 1)
627 p = safe_int(self.request.GET.get('page', 1), 1)
627
628
628 filter_term = self.request.GET.get('filter')
629 filter_term = self.request.GET.get('filter')
629 user_log = UserModel().get_user_log(c.user, filter_term)
630 user_log = UserModel().get_user_log(c.user, filter_term)
630
631
631 def url_generator(**kw):
632 def url_generator(**kw):
632 if filter_term:
633 if filter_term:
633 kw['filter'] = filter_term
634 kw['filter'] = filter_term
634 return self.request.current_route_path(_query=kw)
635 return self.request.current_route_path(_query=kw)
635
636
636 c.audit_logs = h.Page(
637 c.audit_logs = h.Page(
637 user_log, page=p, items_per_page=10, url=url_generator)
638 user_log, page=p, items_per_page=10, url=url_generator)
638 c.filter_term = filter_term
639 c.filter_term = filter_term
639 return self._get_template_context(c)
640 return self._get_template_context(c)
640
641
641 @LoginRequired()
642 @LoginRequired()
642 @HasPermissionAllDecorator('hg.admin')
643 @HasPermissionAllDecorator('hg.admin')
643 @view_config(
644 @view_config(
644 route_name='edit_user_perms_summary', request_method='GET',
645 route_name='edit_user_perms_summary', request_method='GET',
645 renderer='rhodecode:templates/admin/users/user_edit.mako')
646 renderer='rhodecode:templates/admin/users/user_edit.mako')
646 def user_perms_summary(self):
647 def user_perms_summary(self):
647 _ = self.request.translate
648 _ = self.request.translate
648 c = self.load_default_context()
649 c = self.load_default_context()
649
650
650 user_id = self.request.matchdict.get('user_id')
651 user_id = self.request.matchdict.get('user_id')
651 c.user = User.get_or_404(user_id)
652 c.user = User.get_or_404(user_id)
652 self._redirect_for_default_user(c.user.username)
653 self._redirect_for_default_user(c.user.username)
653
654
654 c.active = 'perms_summary'
655 c.active = 'perms_summary'
655 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
656 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
656
657
657 return self._get_template_context(c)
658 return self._get_template_context(c)
658
659
659 @LoginRequired()
660 @LoginRequired()
660 @HasPermissionAllDecorator('hg.admin')
661 @HasPermissionAllDecorator('hg.admin')
661 @view_config(
662 @view_config(
662 route_name='edit_user_perms_summary_json', request_method='GET',
663 route_name='edit_user_perms_summary_json', request_method='GET',
663 renderer='json_ext')
664 renderer='json_ext')
664 def user_perms_summary_json(self):
665 def user_perms_summary_json(self):
665 self.load_default_context()
666 self.load_default_context()
666
667
667 user_id = self.request.matchdict.get('user_id')
668 user_id = self.request.matchdict.get('user_id')
668 user = User.get_or_404(user_id)
669 user = User.get_or_404(user_id)
669 self._redirect_for_default_user(user.username)
670 self._redirect_for_default_user(user.username)
670
671
671 perm_user = user.AuthUser(ip_addr=self.request.remote_addr)
672 perm_user = user.AuthUser(ip_addr=self.request.remote_addr)
672
673
673 return perm_user.permissions
674 return perm_user.permissions
@@ -1,412 +1,413 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2017 RhodeCode GmbH
3 # Copyright (C) 2013-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 time
21 import time
22 import logging
22 import logging
23
23
24 import formencode
24 import formencode
25 import formencode.htmlfill
25 import peppercorn
26 import peppercorn
26
27
27 from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden, HTTPFound
28 from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden, HTTPFound
28 from pyramid.view import view_config
29 from pyramid.view import view_config
29 from pyramid.renderers import render
30 from pyramid.renderers import render
30 from pyramid.response import Response
31 from pyramid.response import Response
31
32
32 from rhodecode.apps._base import BaseAppView
33 from rhodecode.apps._base import BaseAppView
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
35 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
35 from rhodecode.lib.utils2 import time_to_datetime
36 from rhodecode.lib.utils2 import time_to_datetime
36 from rhodecode.lib.ext_json import json
37 from rhodecode.lib.ext_json import json
37 from rhodecode.lib.vcs.exceptions import VCSError, NodeNotChangedError
38 from rhodecode.lib.vcs.exceptions import VCSError, NodeNotChangedError
38 from rhodecode.model.gist import GistModel
39 from rhodecode.model.gist import GistModel
39 from rhodecode.model.meta import Session
40 from rhodecode.model.meta import Session
40 from rhodecode.model.db import Gist, User, or_
41 from rhodecode.model.db import Gist, User, or_
41 from rhodecode.model import validation_schema
42 from rhodecode.model import validation_schema
42 from rhodecode.model.validation_schema.schemas import gist_schema
43 from rhodecode.model.validation_schema.schemas import gist_schema
43
44
44
45
45 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
46
47
47
48
48 class GistView(BaseAppView):
49 class GistView(BaseAppView):
49
50
50 def load_default_context(self):
51 def load_default_context(self):
51 _ = self.request.translate
52 _ = self.request.translate
52 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
53 c.user = c.auth_user.get_instance()
54 c.user = c.auth_user.get_instance()
54
55
55 c.lifetime_values = [
56 c.lifetime_values = [
56 (-1, _('forever')),
57 (-1, _('forever')),
57 (5, _('5 minutes')),
58 (5, _('5 minutes')),
58 (60, _('1 hour')),
59 (60, _('1 hour')),
59 (60 * 24, _('1 day')),
60 (60 * 24, _('1 day')),
60 (60 * 24 * 30, _('1 month')),
61 (60 * 24 * 30, _('1 month')),
61 ]
62 ]
62
63
63 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
64 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
64 c.acl_options = [
65 c.acl_options = [
65 (Gist.ACL_LEVEL_PRIVATE, _("Requires registered account")),
66 (Gist.ACL_LEVEL_PRIVATE, _("Requires registered account")),
66 (Gist.ACL_LEVEL_PUBLIC, _("Can be accessed by anonymous users"))
67 (Gist.ACL_LEVEL_PUBLIC, _("Can be accessed by anonymous users"))
67 ]
68 ]
68
69
69 self._register_global_c(c)
70 self._register_global_c(c)
70 return c
71 return c
71
72
72 @LoginRequired()
73 @LoginRequired()
73 @view_config(
74 @view_config(
74 route_name='gists_show', request_method='GET',
75 route_name='gists_show', request_method='GET',
75 renderer='rhodecode:templates/admin/gists/index.mako')
76 renderer='rhodecode:templates/admin/gists/index.mako')
76 def gist_show_all(self):
77 def gist_show_all(self):
77 c = self.load_default_context()
78 c = self.load_default_context()
78
79
79 not_default_user = self._rhodecode_user.username != User.DEFAULT_USER
80 not_default_user = self._rhodecode_user.username != User.DEFAULT_USER
80 c.show_private = self.request.GET.get('private') and not_default_user
81 c.show_private = self.request.GET.get('private') and not_default_user
81 c.show_public = self.request.GET.get('public') and not_default_user
82 c.show_public = self.request.GET.get('public') and not_default_user
82 c.show_all = self.request.GET.get('all') and self._rhodecode_user.admin
83 c.show_all = self.request.GET.get('all') and self._rhodecode_user.admin
83
84
84 gists = _gists = Gist().query()\
85 gists = _gists = Gist().query()\
85 .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
86 .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
86 .order_by(Gist.created_on.desc())
87 .order_by(Gist.created_on.desc())
87
88
88 c.active = 'public'
89 c.active = 'public'
89 # MY private
90 # MY private
90 if c.show_private and not c.show_public:
91 if c.show_private and not c.show_public:
91 gists = _gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\
92 gists = _gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\
92 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
93 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
93 c.active = 'my_private'
94 c.active = 'my_private'
94 # MY public
95 # MY public
95 elif c.show_public and not c.show_private:
96 elif c.show_public and not c.show_private:
96 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\
97 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\
97 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
98 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
98 c.active = 'my_public'
99 c.active = 'my_public'
99 # MY public+private
100 # MY public+private
100 elif c.show_private and c.show_public:
101 elif c.show_private and c.show_public:
101 gists = _gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC,
102 gists = _gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC,
102 Gist.gist_type == Gist.GIST_PRIVATE))\
103 Gist.gist_type == Gist.GIST_PRIVATE))\
103 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
104 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
104 c.active = 'my_all'
105 c.active = 'my_all'
105 # Show all by super-admin
106 # Show all by super-admin
106 elif c.show_all:
107 elif c.show_all:
107 c.active = 'all'
108 c.active = 'all'
108 gists = _gists
109 gists = _gists
109
110
110 # default show ALL public gists
111 # default show ALL public gists
111 if not c.show_public and not c.show_private and not c.show_all:
112 if not c.show_public and not c.show_private and not c.show_all:
112 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)
113 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)
113 c.active = 'public'
114 c.active = 'public'
114
115
115 _render = self.request.get_partial_renderer(
116 _render = self.request.get_partial_renderer(
116 'data_table/_dt_elements.mako')
117 'data_table/_dt_elements.mako')
117
118
118 data = []
119 data = []
119
120
120 for gist in gists:
121 for gist in gists:
121 data.append({
122 data.append({
122 'created_on': _render('gist_created', gist.created_on),
123 'created_on': _render('gist_created', gist.created_on),
123 'created_on_raw': gist.created_on,
124 'created_on_raw': gist.created_on,
124 'type': _render('gist_type', gist.gist_type),
125 'type': _render('gist_type', gist.gist_type),
125 'access_id': _render('gist_access_id', gist.gist_access_id, gist.owner.full_contact),
126 'access_id': _render('gist_access_id', gist.gist_access_id, gist.owner.full_contact),
126 'author': _render('gist_author', gist.owner.full_contact, gist.created_on, gist.gist_expires),
127 'author': _render('gist_author', gist.owner.full_contact, gist.created_on, gist.gist_expires),
127 'author_raw': h.escape(gist.owner.full_contact),
128 'author_raw': h.escape(gist.owner.full_contact),
128 'expires': _render('gist_expires', gist.gist_expires),
129 'expires': _render('gist_expires', gist.gist_expires),
129 'description': _render('gist_description', gist.gist_description)
130 'description': _render('gist_description', gist.gist_description)
130 })
131 })
131 c.data = json.dumps(data)
132 c.data = json.dumps(data)
132
133
133 return self._get_template_context(c)
134 return self._get_template_context(c)
134
135
135 @LoginRequired()
136 @LoginRequired()
136 @NotAnonymous()
137 @NotAnonymous()
137 @view_config(
138 @view_config(
138 route_name='gists_new', request_method='GET',
139 route_name='gists_new', request_method='GET',
139 renderer='rhodecode:templates/admin/gists/new.mako')
140 renderer='rhodecode:templates/admin/gists/new.mako')
140 def gist_new(self):
141 def gist_new(self):
141 c = self.load_default_context()
142 c = self.load_default_context()
142 return self._get_template_context(c)
143 return self._get_template_context(c)
143
144
144 @LoginRequired()
145 @LoginRequired()
145 @NotAnonymous()
146 @NotAnonymous()
146 @CSRFRequired()
147 @CSRFRequired()
147 @view_config(
148 @view_config(
148 route_name='gists_create', request_method='POST',
149 route_name='gists_create', request_method='POST',
149 renderer='rhodecode:templates/admin/gists/new.mako')
150 renderer='rhodecode:templates/admin/gists/new.mako')
150 def gist_create(self):
151 def gist_create(self):
151 _ = self.request.translate
152 _ = self.request.translate
152 c = self.load_default_context()
153 c = self.load_default_context()
153
154
154 data = dict(self.request.POST)
155 data = dict(self.request.POST)
155 data['filename'] = data.get('filename') or Gist.DEFAULT_FILENAME
156 data['filename'] = data.get('filename') or Gist.DEFAULT_FILENAME
156 data['nodes'] = [{
157 data['nodes'] = [{
157 'filename': data['filename'],
158 'filename': data['filename'],
158 'content': data.get('content'),
159 'content': data.get('content'),
159 'mimetype': data.get('mimetype') # None is autodetect
160 'mimetype': data.get('mimetype') # None is autodetect
160 }]
161 }]
161
162
162 data['gist_type'] = (
163 data['gist_type'] = (
163 Gist.GIST_PUBLIC if data.get('public') else Gist.GIST_PRIVATE)
164 Gist.GIST_PUBLIC if data.get('public') else Gist.GIST_PRIVATE)
164 data['gist_acl_level'] = (
165 data['gist_acl_level'] = (
165 data.get('gist_acl_level') or Gist.ACL_LEVEL_PRIVATE)
166 data.get('gist_acl_level') or Gist.ACL_LEVEL_PRIVATE)
166
167
167 schema = gist_schema.GistSchema().bind(
168 schema = gist_schema.GistSchema().bind(
168 lifetime_options=[x[0] for x in c.lifetime_values])
169 lifetime_options=[x[0] for x in c.lifetime_values])
169
170
170 try:
171 try:
171
172
172 schema_data = schema.deserialize(data)
173 schema_data = schema.deserialize(data)
173 # convert to safer format with just KEYs so we sure no duplicates
174 # convert to safer format with just KEYs so we sure no duplicates
174 schema_data['nodes'] = gist_schema.sequence_to_nodes(
175 schema_data['nodes'] = gist_schema.sequence_to_nodes(
175 schema_data['nodes'])
176 schema_data['nodes'])
176
177
177 gist = GistModel().create(
178 gist = GistModel().create(
178 gist_id=schema_data['gistid'], # custom access id not real ID
179 gist_id=schema_data['gistid'], # custom access id not real ID
179 description=schema_data['description'],
180 description=schema_data['description'],
180 owner=self._rhodecode_user.user_id,
181 owner=self._rhodecode_user.user_id,
181 gist_mapping=schema_data['nodes'],
182 gist_mapping=schema_data['nodes'],
182 gist_type=schema_data['gist_type'],
183 gist_type=schema_data['gist_type'],
183 lifetime=schema_data['lifetime'],
184 lifetime=schema_data['lifetime'],
184 gist_acl_level=schema_data['gist_acl_level']
185 gist_acl_level=schema_data['gist_acl_level']
185 )
186 )
186 Session().commit()
187 Session().commit()
187 new_gist_id = gist.gist_access_id
188 new_gist_id = gist.gist_access_id
188 except validation_schema.Invalid as errors:
189 except validation_schema.Invalid as errors:
189 defaults = data
190 defaults = data
190 errors = errors.asdict()
191 errors = errors.asdict()
191
192
192 if 'nodes.0.content' in errors:
193 if 'nodes.0.content' in errors:
193 errors['content'] = errors['nodes.0.content']
194 errors['content'] = errors['nodes.0.content']
194 del errors['nodes.0.content']
195 del errors['nodes.0.content']
195 if 'nodes.0.filename' in errors:
196 if 'nodes.0.filename' in errors:
196 errors['filename'] = errors['nodes.0.filename']
197 errors['filename'] = errors['nodes.0.filename']
197 del errors['nodes.0.filename']
198 del errors['nodes.0.filename']
198
199
199 data = render('rhodecode:templates/admin/gists/new.mako',
200 data = render('rhodecode:templates/admin/gists/new.mako',
200 self._get_template_context(c), self.request)
201 self._get_template_context(c), self.request)
201 html = formencode.htmlfill.render(
202 html = formencode.htmlfill.render(
202 data,
203 data,
203 defaults=defaults,
204 defaults=defaults,
204 errors=errors,
205 errors=errors,
205 prefix_error=False,
206 prefix_error=False,
206 encoding="UTF-8",
207 encoding="UTF-8",
207 force_defaults=False
208 force_defaults=False
208 )
209 )
209 return Response(html)
210 return Response(html)
210
211
211 except Exception:
212 except Exception:
212 log.exception("Exception while trying to create a gist")
213 log.exception("Exception while trying to create a gist")
213 h.flash(_('Error occurred during gist creation'), category='error')
214 h.flash(_('Error occurred during gist creation'), category='error')
214 raise HTTPFound(h.route_url('gists_new'))
215 raise HTTPFound(h.route_url('gists_new'))
215 raise HTTPFound(h.route_url('gist_show', gist_id=new_gist_id))
216 raise HTTPFound(h.route_url('gist_show', gist_id=new_gist_id))
216
217
217 @LoginRequired()
218 @LoginRequired()
218 @NotAnonymous()
219 @NotAnonymous()
219 @CSRFRequired()
220 @CSRFRequired()
220 @view_config(
221 @view_config(
221 route_name='gist_delete', request_method='POST')
222 route_name='gist_delete', request_method='POST')
222 def gist_delete(self):
223 def gist_delete(self):
223 _ = self.request.translate
224 _ = self.request.translate
224 gist_id = self.request.matchdict['gist_id']
225 gist_id = self.request.matchdict['gist_id']
225
226
226 c = self.load_default_context()
227 c = self.load_default_context()
227 c.gist = Gist.get_or_404(gist_id)
228 c.gist = Gist.get_or_404(gist_id)
228
229
229 owner = c.gist.gist_owner == self._rhodecode_user.user_id
230 owner = c.gist.gist_owner == self._rhodecode_user.user_id
230 if not (h.HasPermissionAny('hg.admin')() or owner):
231 if not (h.HasPermissionAny('hg.admin')() or owner):
231 log.warning('Deletion of Gist was forbidden '
232 log.warning('Deletion of Gist was forbidden '
232 'by unauthorized user: `%s`', self._rhodecode_user)
233 'by unauthorized user: `%s`', self._rhodecode_user)
233 raise HTTPNotFound()
234 raise HTTPNotFound()
234
235
235 GistModel().delete(c.gist)
236 GistModel().delete(c.gist)
236 Session().commit()
237 Session().commit()
237 h.flash(_('Deleted gist %s') % c.gist.gist_access_id, category='success')
238 h.flash(_('Deleted gist %s') % c.gist.gist_access_id, category='success')
238
239
239 raise HTTPFound(h.route_url('gists_show'))
240 raise HTTPFound(h.route_url('gists_show'))
240
241
241 def _get_gist(self, gist_id):
242 def _get_gist(self, gist_id):
242
243
243 gist = Gist.get_or_404(gist_id)
244 gist = Gist.get_or_404(gist_id)
244
245
245 # Check if this gist is expired
246 # Check if this gist is expired
246 if gist.gist_expires != -1:
247 if gist.gist_expires != -1:
247 if time.time() > gist.gist_expires:
248 if time.time() > gist.gist_expires:
248 log.error(
249 log.error(
249 'Gist expired at %s', time_to_datetime(gist.gist_expires))
250 'Gist expired at %s', time_to_datetime(gist.gist_expires))
250 raise HTTPNotFound()
251 raise HTTPNotFound()
251
252
252 # check if this gist requires a login
253 # check if this gist requires a login
253 is_default_user = self._rhodecode_user.username == User.DEFAULT_USER
254 is_default_user = self._rhodecode_user.username == User.DEFAULT_USER
254 if gist.acl_level == Gist.ACL_LEVEL_PRIVATE and is_default_user:
255 if gist.acl_level == Gist.ACL_LEVEL_PRIVATE and is_default_user:
255 log.error("Anonymous user %s tried to access protected gist `%s`",
256 log.error("Anonymous user %s tried to access protected gist `%s`",
256 self._rhodecode_user, gist_id)
257 self._rhodecode_user, gist_id)
257 raise HTTPNotFound()
258 raise HTTPNotFound()
258 return gist
259 return gist
259
260
260 @LoginRequired()
261 @LoginRequired()
261 @view_config(
262 @view_config(
262 route_name='gist_show', request_method='GET',
263 route_name='gist_show', request_method='GET',
263 renderer='rhodecode:templates/admin/gists/show.mako')
264 renderer='rhodecode:templates/admin/gists/show.mako')
264 @view_config(
265 @view_config(
265 route_name='gist_show_rev', request_method='GET',
266 route_name='gist_show_rev', request_method='GET',
266 renderer='rhodecode:templates/admin/gists/show.mako')
267 renderer='rhodecode:templates/admin/gists/show.mako')
267 @view_config(
268 @view_config(
268 route_name='gist_show_formatted', request_method='GET',
269 route_name='gist_show_formatted', request_method='GET',
269 renderer=None)
270 renderer=None)
270 @view_config(
271 @view_config(
271 route_name='gist_show_formatted_path', request_method='GET',
272 route_name='gist_show_formatted_path', request_method='GET',
272 renderer=None)
273 renderer=None)
273 def gist_show(self):
274 def gist_show(self):
274 gist_id = self.request.matchdict['gist_id']
275 gist_id = self.request.matchdict['gist_id']
275
276
276 # TODO(marcink): expose those via matching dict
277 # TODO(marcink): expose those via matching dict
277 revision = self.request.matchdict.get('revision', 'tip')
278 revision = self.request.matchdict.get('revision', 'tip')
278 f_path = self.request.matchdict.get('f_path', None)
279 f_path = self.request.matchdict.get('f_path', None)
279 return_format = self.request.matchdict.get('format')
280 return_format = self.request.matchdict.get('format')
280
281
281 c = self.load_default_context()
282 c = self.load_default_context()
282 c.gist = self._get_gist(gist_id)
283 c.gist = self._get_gist(gist_id)
283 c.render = not self.request.GET.get('no-render', False)
284 c.render = not self.request.GET.get('no-render', False)
284
285
285 try:
286 try:
286 c.file_last_commit, c.files = GistModel().get_gist_files(
287 c.file_last_commit, c.files = GistModel().get_gist_files(
287 gist_id, revision=revision)
288 gist_id, revision=revision)
288 except VCSError:
289 except VCSError:
289 log.exception("Exception in gist show")
290 log.exception("Exception in gist show")
290 raise HTTPNotFound()
291 raise HTTPNotFound()
291
292
292 if return_format == 'raw':
293 if return_format == 'raw':
293 content = '\n\n'.join([f.content for f in c.files
294 content = '\n\n'.join([f.content for f in c.files
294 if (f_path is None or f.path == f_path)])
295 if (f_path is None or f.path == f_path)])
295 response = Response(content)
296 response = Response(content)
296 response.content_type = 'text/plain'
297 response.content_type = 'text/plain'
297 return response
298 return response
298
299
299 return self._get_template_context(c)
300 return self._get_template_context(c)
300
301
301 @LoginRequired()
302 @LoginRequired()
302 @NotAnonymous()
303 @NotAnonymous()
303 @view_config(
304 @view_config(
304 route_name='gist_edit', request_method='GET',
305 route_name='gist_edit', request_method='GET',
305 renderer='rhodecode:templates/admin/gists/edit.mako')
306 renderer='rhodecode:templates/admin/gists/edit.mako')
306 def gist_edit(self):
307 def gist_edit(self):
307 _ = self.request.translate
308 _ = self.request.translate
308 gist_id = self.request.matchdict['gist_id']
309 gist_id = self.request.matchdict['gist_id']
309 c = self.load_default_context()
310 c = self.load_default_context()
310 c.gist = self._get_gist(gist_id)
311 c.gist = self._get_gist(gist_id)
311
312
312 owner = c.gist.gist_owner == self._rhodecode_user.user_id
313 owner = c.gist.gist_owner == self._rhodecode_user.user_id
313 if not (h.HasPermissionAny('hg.admin')() or owner):
314 if not (h.HasPermissionAny('hg.admin')() or owner):
314 raise HTTPNotFound()
315 raise HTTPNotFound()
315
316
316 try:
317 try:
317 c.file_last_commit, c.files = GistModel().get_gist_files(gist_id)
318 c.file_last_commit, c.files = GistModel().get_gist_files(gist_id)
318 except VCSError:
319 except VCSError:
319 log.exception("Exception in gist edit")
320 log.exception("Exception in gist edit")
320 raise HTTPNotFound()
321 raise HTTPNotFound()
321
322
322 if c.gist.gist_expires == -1:
323 if c.gist.gist_expires == -1:
323 expiry = _('never')
324 expiry = _('never')
324 else:
325 else:
325 # this cannot use timeago, since it's used in select2 as a value
326 # this cannot use timeago, since it's used in select2 as a value
326 expiry = h.age(h.time_to_datetime(c.gist.gist_expires))
327 expiry = h.age(h.time_to_datetime(c.gist.gist_expires))
327
328
328 c.lifetime_values.append(
329 c.lifetime_values.append(
329 (0, _('%(expiry)s - current value') % {'expiry': _(expiry)})
330 (0, _('%(expiry)s - current value') % {'expiry': _(expiry)})
330 )
331 )
331
332
332 return self._get_template_context(c)
333 return self._get_template_context(c)
333
334
334 @LoginRequired()
335 @LoginRequired()
335 @NotAnonymous()
336 @NotAnonymous()
336 @CSRFRequired()
337 @CSRFRequired()
337 @view_config(
338 @view_config(
338 route_name='gist_update', request_method='POST',
339 route_name='gist_update', request_method='POST',
339 renderer='rhodecode:templates/admin/gists/edit.mako')
340 renderer='rhodecode:templates/admin/gists/edit.mako')
340 def gist_update(self):
341 def gist_update(self):
341 _ = self.request.translate
342 _ = self.request.translate
342 gist_id = self.request.matchdict['gist_id']
343 gist_id = self.request.matchdict['gist_id']
343 c = self.load_default_context()
344 c = self.load_default_context()
344 c.gist = self._get_gist(gist_id)
345 c.gist = self._get_gist(gist_id)
345
346
346 owner = c.gist.gist_owner == self._rhodecode_user.user_id
347 owner = c.gist.gist_owner == self._rhodecode_user.user_id
347 if not (h.HasPermissionAny('hg.admin')() or owner):
348 if not (h.HasPermissionAny('hg.admin')() or owner):
348 raise HTTPNotFound()
349 raise HTTPNotFound()
349
350
350 data = peppercorn.parse(self.request.POST.items())
351 data = peppercorn.parse(self.request.POST.items())
351
352
352 schema = gist_schema.GistSchema()
353 schema = gist_schema.GistSchema()
353 schema = schema.bind(
354 schema = schema.bind(
354 # '0' is special value to leave lifetime untouched
355 # '0' is special value to leave lifetime untouched
355 lifetime_options=[x[0] for x in c.lifetime_values] + [0],
356 lifetime_options=[x[0] for x in c.lifetime_values] + [0],
356 )
357 )
357
358
358 try:
359 try:
359 schema_data = schema.deserialize(data)
360 schema_data = schema.deserialize(data)
360 # convert to safer format with just KEYs so we sure no duplicates
361 # convert to safer format with just KEYs so we sure no duplicates
361 schema_data['nodes'] = gist_schema.sequence_to_nodes(
362 schema_data['nodes'] = gist_schema.sequence_to_nodes(
362 schema_data['nodes'])
363 schema_data['nodes'])
363
364
364 GistModel().update(
365 GistModel().update(
365 gist=c.gist,
366 gist=c.gist,
366 description=schema_data['description'],
367 description=schema_data['description'],
367 owner=c.gist.owner,
368 owner=c.gist.owner,
368 gist_mapping=schema_data['nodes'],
369 gist_mapping=schema_data['nodes'],
369 lifetime=schema_data['lifetime'],
370 lifetime=schema_data['lifetime'],
370 gist_acl_level=schema_data['gist_acl_level']
371 gist_acl_level=schema_data['gist_acl_level']
371 )
372 )
372
373
373 Session().commit()
374 Session().commit()
374 h.flash(_('Successfully updated gist content'), category='success')
375 h.flash(_('Successfully updated gist content'), category='success')
375 except NodeNotChangedError:
376 except NodeNotChangedError:
376 # raised if nothing was changed in repo itself. We anyway then
377 # raised if nothing was changed in repo itself. We anyway then
377 # store only DB stuff for gist
378 # store only DB stuff for gist
378 Session().commit()
379 Session().commit()
379 h.flash(_('Successfully updated gist data'), category='success')
380 h.flash(_('Successfully updated gist data'), category='success')
380 except validation_schema.Invalid as errors:
381 except validation_schema.Invalid as errors:
381 errors = h.escape(errors.asdict())
382 errors = h.escape(errors.asdict())
382 h.flash(_('Error occurred during update of gist {}: {}').format(
383 h.flash(_('Error occurred during update of gist {}: {}').format(
383 gist_id, errors), category='error')
384 gist_id, errors), category='error')
384 except Exception:
385 except Exception:
385 log.exception("Exception in gist edit")
386 log.exception("Exception in gist edit")
386 h.flash(_('Error occurred during update of gist %s') % gist_id,
387 h.flash(_('Error occurred during update of gist %s') % gist_id,
387 category='error')
388 category='error')
388
389
389 raise HTTPFound(h.route_url('gist_show', gist_id=gist_id))
390 raise HTTPFound(h.route_url('gist_show', gist_id=gist_id))
390
391
391 @LoginRequired()
392 @LoginRequired()
392 @NotAnonymous()
393 @NotAnonymous()
393 @view_config(
394 @view_config(
394 route_name='gist_edit_check_revision', request_method='GET',
395 route_name='gist_edit_check_revision', request_method='GET',
395 renderer='json_ext')
396 renderer='json_ext')
396 def gist_edit_check_revision(self):
397 def gist_edit_check_revision(self):
397 _ = self.request.translate
398 _ = self.request.translate
398 gist_id = self.request.matchdict['gist_id']
399 gist_id = self.request.matchdict['gist_id']
399 c = self.load_default_context()
400 c = self.load_default_context()
400 c.gist = self._get_gist(gist_id)
401 c.gist = self._get_gist(gist_id)
401
402
402 last_rev = c.gist.scm_instance().get_commit()
403 last_rev = c.gist.scm_instance().get_commit()
403 success = True
404 success = True
404 revision = self.request.GET.get('revision')
405 revision = self.request.GET.get('revision')
405
406
406 if revision != last_rev.raw_id:
407 if revision != last_rev.raw_id:
407 log.error('Last revision %s is different then submitted %s'
408 log.error('Last revision %s is different then submitted %s'
408 % (revision, last_rev))
409 % (revision, last_rev))
409 # our gist has newer version than we
410 # our gist has newer version than we
410 success = False
411 success = False
411
412
412 return {'success': success}
413 return {'success': success}
General Comments 0
You need to be logged in to leave comments. Login now