Show More
@@ -1,138 +1,137 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.controllers.admin.ldap_settings |
|
3 | rhodecode.controllers.admin.ldap_settings | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | ldap controller for RhodeCode |
|
6 | ldap controller for RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Nov 26, 2010 |
|
8 | :created_on: Nov 26, 2010 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 | import logging |
|
25 | import logging | |
26 | import formencode |
|
26 | import formencode | |
27 | import traceback |
|
27 | import traceback | |
28 |
|
28 | |||
29 | from formencode import htmlfill |
|
29 | from formencode import htmlfill | |
30 |
|
30 | |||
31 | from pylons import request, response, session, tmpl_context as c, url |
|
31 | from pylons import request, response, session, tmpl_context as c, url | |
32 | from pylons.controllers.util import abort, redirect |
|
32 | from pylons.controllers.util import abort, redirect | |
33 | from pylons.i18n.translation import _ |
|
33 | from pylons.i18n.translation import _ | |
34 |
|
34 | |||
35 | from sqlalchemy.exc import DatabaseError |
|
35 | from sqlalchemy.exc import DatabaseError | |
36 |
|
36 | |||
37 | from rhodecode.lib.base import BaseController, render |
|
37 | from rhodecode.lib.base import BaseController, render | |
38 | from rhodecode.lib import helpers as h |
|
38 | from rhodecode.lib import helpers as h | |
39 | from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator |
|
39 | from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator | |
40 | from rhodecode.lib.exceptions import LdapImportError |
|
40 | from rhodecode.lib.exceptions import LdapImportError | |
41 | from rhodecode.model.forms import LdapSettingsForm |
|
41 | from rhodecode.model.forms import LdapSettingsForm | |
42 | from rhodecode.model.db import RhodeCodeSettings |
|
42 | from rhodecode.model.db import RhodeCodeSettings | |
43 |
|
43 | |||
44 | log = logging.getLogger(__name__) |
|
44 | log = logging.getLogger(__name__) | |
45 |
|
45 | |||
46 |
|
46 | |||
47 | class LdapSettingsController(BaseController): |
|
47 | class LdapSettingsController(BaseController): | |
48 |
|
48 | |||
49 | search_scope_choices = [('BASE', _('BASE'),), |
|
49 | search_scope_choices = [('BASE', _('BASE'),), | |
50 | ('ONELEVEL', _('ONELEVEL'),), |
|
50 | ('ONELEVEL', _('ONELEVEL'),), | |
51 | ('SUBTREE', _('SUBTREE'),), |
|
51 | ('SUBTREE', _('SUBTREE'),), | |
52 | ] |
|
52 | ] | |
53 | search_scope_default = 'SUBTREE' |
|
53 | search_scope_default = 'SUBTREE' | |
54 |
|
54 | |||
55 | tls_reqcert_choices = [('NEVER', _('NEVER'),), |
|
55 | tls_reqcert_choices = [('NEVER', _('NEVER'),), | |
56 | ('ALLOW', _('ALLOW'),), |
|
56 | ('ALLOW', _('ALLOW'),), | |
57 | ('TRY', _('TRY'),), |
|
57 | ('TRY', _('TRY'),), | |
58 | ('DEMAND', _('DEMAND'),), |
|
58 | ('DEMAND', _('DEMAND'),), | |
59 | ('HARD', _('HARD'),), |
|
59 | ('HARD', _('HARD'),), | |
60 | ] |
|
60 | ] | |
61 | tls_reqcert_default = 'DEMAND' |
|
61 | tls_reqcert_default = 'DEMAND' | |
62 |
|
62 | |||
63 | tls_kind_choices = [('PLAIN', _('No encryption'),), |
|
63 | tls_kind_choices = [('PLAIN', _('No encryption'),), | |
64 | ('LDAPS', _('LDAPS connection'),), |
|
64 | ('LDAPS', _('LDAPS connection'),), | |
65 | ('START_TLS', _('START_TLS on LDAP connection'),) |
|
65 | ('START_TLS', _('START_TLS on LDAP connection'),) | |
66 | ] |
|
66 | ] | |
67 |
|
67 | |||
68 | tls_kind_default = 'PLAIN' |
|
68 | tls_kind_default = 'PLAIN' | |
69 |
|
69 | |||
70 | @LoginRequired() |
|
70 | @LoginRequired() | |
71 | @HasPermissionAllDecorator('hg.admin') |
|
71 | @HasPermissionAllDecorator('hg.admin') | |
72 | def __before__(self): |
|
72 | def __before__(self): | |
73 | c.admin_user = session.get('admin_user') |
|
73 | c.admin_user = session.get('admin_user') | |
74 | c.admin_username = session.get('admin_username') |
|
74 | c.admin_username = session.get('admin_username') | |
75 | c.search_scope_choices = self.search_scope_choices |
|
75 | c.search_scope_choices = self.search_scope_choices | |
76 | c.tls_reqcert_choices = self.tls_reqcert_choices |
|
76 | c.tls_reqcert_choices = self.tls_reqcert_choices | |
77 | c.tls_kind_choices = self.tls_kind_choices |
|
77 | c.tls_kind_choices = self.tls_kind_choices | |
78 |
|
78 | |||
79 | c.search_scope_cur = self.search_scope_default |
|
79 | c.search_scope_cur = self.search_scope_default | |
80 | c.tls_reqcert_cur = self.tls_reqcert_default |
|
80 | c.tls_reqcert_cur = self.tls_reqcert_default | |
81 | c.tls_kind_cur = self.tls_kind_default |
|
81 | c.tls_kind_cur = self.tls_kind_default | |
82 |
|
82 | |||
83 | super(LdapSettingsController, self).__before__() |
|
83 | super(LdapSettingsController, self).__before__() | |
84 |
|
84 | |||
85 | def index(self): |
|
85 | def index(self): | |
86 | defaults = RhodeCodeSettings.get_ldap_settings() |
|
86 | defaults = RhodeCodeSettings.get_ldap_settings() | |
87 | c.search_scope_cur = defaults.get('ldap_search_scope') |
|
87 | c.search_scope_cur = defaults.get('ldap_search_scope') | |
88 | c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert') |
|
88 | c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert') | |
89 | c.tls_kind_cur = defaults.get('ldap_tls_kind') |
|
89 | c.tls_kind_cur = defaults.get('ldap_tls_kind') | |
90 |
|
90 | |||
91 | return htmlfill.render( |
|
91 | return htmlfill.render( | |
92 | render('admin/ldap/ldap.html'), |
|
92 | render('admin/ldap/ldap.html'), | |
93 | defaults=defaults, |
|
93 | defaults=defaults, | |
94 | encoding="UTF-8", |
|
94 | encoding="UTF-8", | |
95 | force_defaults=True,) |
|
95 | force_defaults=True,) | |
96 |
|
96 | |||
97 | def ldap_settings(self): |
|
97 | def ldap_settings(self): | |
98 | """POST ldap create and store ldap settings""" |
|
98 | """POST ldap create and store ldap settings""" | |
99 |
|
99 | |||
100 | _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices], |
|
100 | _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices], | |
101 | [x[0] for x in self.search_scope_choices], |
|
101 | [x[0] for x in self.search_scope_choices], | |
102 | [x[0] for x in self.tls_kind_choices])() |
|
102 | [x[0] for x in self.tls_kind_choices])() | |
103 |
|
103 | |||
104 | try: |
|
104 | try: | |
105 | form_result = _form.to_python(dict(request.POST)) |
|
105 | form_result = _form.to_python(dict(request.POST)) | |
106 | try: |
|
106 | try: | |
107 |
|
107 | |||
108 | for k, v in form_result.items(): |
|
108 | for k, v in form_result.items(): | |
109 | if k.startswith('ldap_'): |
|
109 | if k.startswith('ldap_'): | |
110 | setting = RhodeCodeSettings.get_by_name(k) |
|
110 | setting = RhodeCodeSettings.get_by_name(k) | |
111 | setting.app_settings_value = v |
|
111 | setting.app_settings_value = v | |
112 | self.sa.add(setting) |
|
112 | self.sa.add(setting) | |
113 |
|
113 | |||
114 | self.sa.commit() |
|
114 | self.sa.commit() | |
115 | h.flash(_('Ldap settings updated successfully'), |
|
115 | h.flash(_('Ldap settings updated successfully'), | |
116 | category='success') |
|
116 | category='success') | |
117 | except (DatabaseError,): |
|
117 | except (DatabaseError,): | |
118 | raise |
|
118 | raise | |
119 | except LdapImportError: |
|
119 | except LdapImportError: | |
120 | h.flash(_('Unable to activate ldap. The "python-ldap" library ' |
|
120 | h.flash(_('Unable to activate ldap. The "python-ldap" library ' | |
121 | 'is missing.'), category='warning') |
|
121 | 'is missing.'), category='warning') | |
122 |
|
122 | |||
123 | except formencode.Invalid, errors: |
|
123 | except formencode.Invalid, errors: | |
124 | e = errors.error_dict or {} |
|
124 | e = errors.error_dict or {} | |
125 |
|
125 | |||
126 |
|
||||
127 | return htmlfill.render( |
|
126 | return htmlfill.render( | |
128 | render('admin/ldap/ldap.html'), |
|
127 | render('admin/ldap/ldap.html'), | |
129 | defaults=errors.value, |
|
128 | defaults=errors.value, | |
130 | errors=e, |
|
129 | errors=e, | |
131 | prefix_error=False, |
|
130 | prefix_error=False, | |
132 | encoding="UTF-8") |
|
131 | encoding="UTF-8") | |
133 | except Exception: |
|
132 | except Exception: | |
134 | log.error(traceback.format_exc()) |
|
133 | log.error(traceback.format_exc()) | |
135 | h.flash(_('error occurred during update of ldap settings'), |
|
134 | h.flash(_('error occurred during update of ldap settings'), | |
136 | category='error') |
|
135 | category='error') | |
137 |
|
136 | |||
138 | return redirect(url('ldap_home')) |
|
137 | return redirect(url('ldap_home')) |
@@ -1,405 +1,406 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.controllers.files |
|
3 | rhodecode.controllers.files | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Files controller for RhodeCode |
|
6 | Files controller for RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Apr 21, 2010 |
|
8 | :created_on: Apr 21, 2010 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 | import os |
|
26 | import os | |
27 | import logging |
|
27 | import logging | |
28 | import mimetypes |
|
28 | import mimetypes | |
29 | import traceback |
|
29 | import traceback | |
30 |
|
30 | |||
31 | from pylons import request, response, session, tmpl_context as c, url |
|
31 | from pylons import request, response, session, tmpl_context as c, url | |
32 | from pylons.i18n.translation import _ |
|
32 | from pylons.i18n.translation import _ | |
33 | from pylons.controllers.util import redirect |
|
33 | from pylons.controllers.util import redirect | |
34 |
|
34 | |||
35 | from vcs.backends import ARCHIVE_SPECS |
|
35 | from vcs.backends import ARCHIVE_SPECS | |
36 | from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \ |
|
36 | from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \ | |
37 | EmptyRepositoryError, ImproperArchiveTypeError, VCSError |
|
37 | EmptyRepositoryError, ImproperArchiveTypeError, VCSError | |
38 | from vcs.nodes import FileNode, NodeKind |
|
38 | from vcs.nodes import FileNode, NodeKind | |
39 | from vcs.utils import diffs as differ |
|
39 | from vcs.utils import diffs as differ | |
40 |
|
40 | |||
41 | from rhodecode.lib import convert_line_endings, detect_mode |
|
41 | from rhodecode.lib import convert_line_endings, detect_mode | |
42 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator |
|
42 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |
43 | from rhodecode.lib.base import BaseRepoController, render |
|
43 | from rhodecode.lib.base import BaseRepoController, render | |
44 | from rhodecode.lib.utils import EmptyChangeset |
|
44 | from rhodecode.lib.utils import EmptyChangeset | |
45 | import rhodecode.lib.helpers as h |
|
45 | import rhodecode.lib.helpers as h | |
46 | from rhodecode.model.repo import RepoModel |
|
46 | from rhodecode.model.repo import RepoModel | |
47 |
|
47 | |||
48 | log = logging.getLogger(__name__) |
|
48 | log = logging.getLogger(__name__) | |
49 |
|
49 | |||
50 |
|
50 | |||
51 | class FilesController(BaseRepoController): |
|
51 | class FilesController(BaseRepoController): | |
52 |
|
52 | |||
53 | @LoginRequired() |
|
53 | @LoginRequired() | |
54 | def __before__(self): |
|
54 | def __before__(self): | |
55 | super(FilesController, self).__before__() |
|
55 | super(FilesController, self).__before__() | |
56 | c.cut_off_limit = self.cut_off_limit |
|
56 | c.cut_off_limit = self.cut_off_limit | |
57 |
|
57 | |||
58 | def __get_cs_or_redirect(self, rev, repo_name): |
|
58 | def __get_cs_or_redirect(self, rev, repo_name): | |
59 | """ |
|
59 | """ | |
60 | Safe way to get changeset if error occur it redirects to tip with |
|
60 | Safe way to get changeset if error occur it redirects to tip with | |
61 | proper message |
|
61 | proper message | |
62 |
|
62 | |||
63 | :param rev: revision to fetch |
|
63 | :param rev: revision to fetch | |
64 | :param repo_name: repo name to redirect after |
|
64 | :param repo_name: repo name to redirect after | |
65 | """ |
|
65 | """ | |
66 |
|
66 | |||
67 | try: |
|
67 | try: | |
68 | return c.rhodecode_repo.get_changeset(rev) |
|
68 | return c.rhodecode_repo.get_changeset(rev) | |
69 | except EmptyRepositoryError, e: |
|
69 | except EmptyRepositoryError, e: | |
70 | h.flash(_('There are no files yet'), category='warning') |
|
70 | h.flash(_('There are no files yet'), category='warning') | |
71 | redirect(h.url('summary_home', repo_name=repo_name)) |
|
71 | redirect(h.url('summary_home', repo_name=repo_name)) | |
72 |
|
72 | |||
73 | except RepositoryError, e: |
|
73 | except RepositoryError, e: | |
74 | h.flash(str(e), category='warning') |
|
74 | h.flash(str(e), category='warning') | |
75 | redirect(h.url('files_home', repo_name=repo_name, revision='tip')) |
|
75 | redirect(h.url('files_home', repo_name=repo_name, revision='tip')) | |
76 |
|
76 | |||
77 | def __get_filenode_or_redirect(self, repo_name, cs, path): |
|
77 | def __get_filenode_or_redirect(self, repo_name, cs, path): | |
78 | """ |
|
78 | """ | |
79 | Returns file_node, if error occurs or given path is directory, |
|
79 | Returns file_node, if error occurs or given path is directory, | |
80 | it'll redirect to top level path |
|
80 | it'll redirect to top level path | |
81 |
|
81 | |||
82 | :param repo_name: repo_name |
|
82 | :param repo_name: repo_name | |
83 | :param cs: given changeset |
|
83 | :param cs: given changeset | |
84 | :param path: path to lookup |
|
84 | :param path: path to lookup | |
85 | """ |
|
85 | """ | |
86 |
|
86 | |||
87 | try: |
|
87 | try: | |
88 | file_node = cs.get_node(path) |
|
88 | file_node = cs.get_node(path) | |
89 | if file_node.is_dir(): |
|
89 | if file_node.is_dir(): | |
90 | raise RepositoryError('given path is a directory') |
|
90 | raise RepositoryError('given path is a directory') | |
91 | except RepositoryError, e: |
|
91 | except RepositoryError, e: | |
92 | h.flash(str(e), category='warning') |
|
92 | h.flash(str(e), category='warning') | |
93 | redirect(h.url('files_home', repo_name=repo_name, |
|
93 | redirect(h.url('files_home', repo_name=repo_name, | |
94 | revision=cs.raw_id)) |
|
94 | revision=cs.raw_id)) | |
95 |
|
95 | |||
96 | return file_node |
|
96 | return file_node | |
97 |
|
97 | |||
98 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
98 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
99 | 'repository.admin') |
|
99 | 'repository.admin') | |
100 | def index(self, repo_name, revision, f_path): |
|
100 | def index(self, repo_name, revision, f_path): | |
101 | #reditect to given revision from form if given |
|
101 | #reditect to given revision from form if given | |
102 | post_revision = request.POST.get('at_rev', None) |
|
102 | post_revision = request.POST.get('at_rev', None) | |
103 | if post_revision: |
|
103 | if post_revision: | |
104 | cs = self.__get_cs_or_redirect(post_revision, repo_name) |
|
104 | cs = self.__get_cs_or_redirect(post_revision, repo_name) | |
105 | redirect(url('files_home', repo_name=c.repo_name, |
|
105 | redirect(url('files_home', repo_name=c.repo_name, | |
106 | revision=cs.raw_id, f_path=f_path)) |
|
106 | revision=cs.raw_id, f_path=f_path)) | |
107 |
|
107 | |||
108 | c.changeset = self.__get_cs_or_redirect(revision, repo_name) |
|
108 | c.changeset = self.__get_cs_or_redirect(revision, repo_name) | |
109 | c.branch = request.GET.get('branch', None) |
|
109 | c.branch = request.GET.get('branch', None) | |
110 | c.f_path = f_path |
|
110 | c.f_path = f_path | |
111 |
|
111 | |||
112 | cur_rev = c.changeset.revision |
|
112 | cur_rev = c.changeset.revision | |
113 |
|
113 | |||
114 | #prev link |
|
114 | #prev link | |
115 | try: |
|
115 | try: | |
116 | prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch) |
|
116 | prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch) | |
117 | c.url_prev = url('files_home', repo_name=c.repo_name, |
|
117 | c.url_prev = url('files_home', repo_name=c.repo_name, | |
118 | revision=prev_rev.raw_id, f_path=f_path) |
|
118 | revision=prev_rev.raw_id, f_path=f_path) | |
119 | if c.branch: |
|
119 | if c.branch: | |
120 | c.url_prev += '?branch=%s' % c.branch |
|
120 | c.url_prev += '?branch=%s' % c.branch | |
121 | except (ChangesetDoesNotExistError, VCSError): |
|
121 | except (ChangesetDoesNotExistError, VCSError): | |
122 | c.url_prev = '#' |
|
122 | c.url_prev = '#' | |
123 |
|
123 | |||
124 | #next link |
|
124 | #next link | |
125 | try: |
|
125 | try: | |
126 | next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch) |
|
126 | next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch) | |
127 | c.url_next = url('files_home', repo_name=c.repo_name, |
|
127 | c.url_next = url('files_home', repo_name=c.repo_name, | |
128 | revision=next_rev.raw_id, f_path=f_path) |
|
128 | revision=next_rev.raw_id, f_path=f_path) | |
129 | if c.branch: |
|
129 | if c.branch: | |
130 | c.url_next += '?branch=%s' % c.branch |
|
130 | c.url_next += '?branch=%s' % c.branch | |
131 | except (ChangesetDoesNotExistError, VCSError): |
|
131 | except (ChangesetDoesNotExistError, VCSError): | |
132 | c.url_next = '#' |
|
132 | c.url_next = '#' | |
133 |
|
133 | |||
134 | #files or dirs |
|
134 | #files or dirs | |
135 | try: |
|
135 | try: | |
136 | c.files_list = c.changeset.get_node(f_path) |
|
136 | c.files_list = c.changeset.get_node(f_path) | |
137 |
|
137 | |||
138 | if c.files_list.is_file(): |
|
138 | if c.files_list.is_file(): | |
139 | c.file_history = self._get_node_history(c.changeset, f_path) |
|
139 | c.file_history = self._get_node_history(c.changeset, f_path) | |
140 | else: |
|
140 | else: | |
141 | c.file_history = [] |
|
141 | c.file_history = [] | |
142 | except RepositoryError, e: |
|
142 | except RepositoryError, e: | |
143 | h.flash(str(e), category='warning') |
|
143 | h.flash(str(e), category='warning') | |
144 | redirect(h.url('files_home', repo_name=repo_name, |
|
144 | redirect(h.url('files_home', repo_name=repo_name, | |
145 | revision=revision)) |
|
145 | revision=revision)) | |
146 |
|
146 | |||
147 | return render('files/files.html') |
|
147 | return render('files/files.html') | |
148 |
|
148 | |||
149 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
149 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
150 | 'repository.admin') |
|
150 | 'repository.admin') | |
151 | def rawfile(self, repo_name, revision, f_path): |
|
151 | def rawfile(self, repo_name, revision, f_path): | |
152 | cs = self.__get_cs_or_redirect(revision, repo_name) |
|
152 | cs = self.__get_cs_or_redirect(revision, repo_name) | |
153 | file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path) |
|
153 | file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path) | |
154 |
|
154 | |||
155 | response.content_disposition = 'attachment; filename=%s' % \ |
|
155 | response.content_disposition = 'attachment; filename=%s' % \ | |
156 | f_path.split(os.sep)[-1].encode('utf8', 'replace') |
|
156 | f_path.split(os.sep)[-1].encode('utf8', 'replace') | |
157 |
|
157 | |||
158 | response.content_type = file_node.mimetype |
|
158 | response.content_type = file_node.mimetype | |
159 | return file_node.content |
|
159 | return file_node.content | |
160 |
|
160 | |||
161 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
161 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
162 | 'repository.admin') |
|
162 | 'repository.admin') | |
163 | def raw(self, repo_name, revision, f_path): |
|
163 | def raw(self, repo_name, revision, f_path): | |
164 | cs = self.__get_cs_or_redirect(revision, repo_name) |
|
164 | cs = self.__get_cs_or_redirect(revision, repo_name) | |
165 | file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path) |
|
165 | file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path) | |
166 |
|
166 | |||
167 | raw_mimetype_mapping = { |
|
167 | raw_mimetype_mapping = { | |
168 | # map original mimetype to a mimetype used for "show as raw" |
|
168 | # map original mimetype to a mimetype used for "show as raw" | |
169 | # you can also provide a content-disposition to override the |
|
169 | # you can also provide a content-disposition to override the | |
170 | # default "attachment" disposition. |
|
170 | # default "attachment" disposition. | |
171 | # orig_type: (new_type, new_dispo) |
|
171 | # orig_type: (new_type, new_dispo) | |
172 |
|
172 | |||
173 | # show images inline: |
|
173 | # show images inline: | |
174 | 'image/x-icon': ('image/x-icon', 'inline'), |
|
174 | 'image/x-icon': ('image/x-icon', 'inline'), | |
175 | 'image/png': ('image/png', 'inline'), |
|
175 | 'image/png': ('image/png', 'inline'), | |
176 | 'image/gif': ('image/gif', 'inline'), |
|
176 | 'image/gif': ('image/gif', 'inline'), | |
177 | 'image/jpeg': ('image/jpeg', 'inline'), |
|
177 | 'image/jpeg': ('image/jpeg', 'inline'), | |
178 | 'image/svg+xml': ('image/svg+xml', 'inline'), |
|
178 | 'image/svg+xml': ('image/svg+xml', 'inline'), | |
179 | } |
|
179 | } | |
180 |
|
180 | |||
181 | mimetype = file_node.mimetype |
|
181 | mimetype = file_node.mimetype | |
182 | try: |
|
182 | try: | |
183 | mimetype, dispo = raw_mimetype_mapping[mimetype] |
|
183 | mimetype, dispo = raw_mimetype_mapping[mimetype] | |
184 | except KeyError: |
|
184 | except KeyError: | |
185 | # we don't know anything special about this, handle it safely |
|
185 | # we don't know anything special about this, handle it safely | |
186 | if file_node.is_binary: |
|
186 | if file_node.is_binary: | |
187 | # do same as download raw for binary files |
|
187 | # do same as download raw for binary files | |
188 | mimetype, dispo = 'application/octet-stream', 'attachment' |
|
188 | mimetype, dispo = 'application/octet-stream', 'attachment' | |
189 | else: |
|
189 | else: | |
190 | # do not just use the original mimetype, but force text/plain, |
|
190 | # do not just use the original mimetype, but force text/plain, | |
191 | # otherwise it would serve text/html and that might be unsafe. |
|
191 | # otherwise it would serve text/html and that might be unsafe. | |
192 | # Note: underlying vcs library fakes text/plain mimetype if the |
|
192 | # Note: underlying vcs library fakes text/plain mimetype if the | |
193 | # mimetype can not be determined and it thinks it is not |
|
193 | # mimetype can not be determined and it thinks it is not | |
194 | # binary.This might lead to erroneous text display in some |
|
194 | # binary.This might lead to erroneous text display in some | |
195 | # cases, but helps in other cases, like with text files |
|
195 | # cases, but helps in other cases, like with text files | |
196 | # without extension. |
|
196 | # without extension. | |
197 | mimetype, dispo = 'text/plain', 'inline' |
|
197 | mimetype, dispo = 'text/plain', 'inline' | |
198 |
|
198 | |||
199 | if dispo == 'attachment': |
|
199 | if dispo == 'attachment': | |
200 | dispo = 'attachment; filename=%s' % \ |
|
200 | dispo = 'attachment; filename=%s' % \ | |
201 | f_path.split(os.sep)[-1].encode('utf8', 'replace') |
|
201 | f_path.split(os.sep)[-1].encode('utf8', 'replace') | |
202 |
|
202 | |||
203 | response.content_disposition = dispo |
|
203 | response.content_disposition = dispo | |
204 | response.content_type = mimetype |
|
204 | response.content_type = mimetype | |
205 | return file_node.content |
|
205 | return file_node.content | |
206 |
|
206 | |||
207 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
207 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
208 | 'repository.admin') |
|
208 | 'repository.admin') | |
209 | def annotate(self, repo_name, revision, f_path): |
|
209 | def annotate(self, repo_name, revision, f_path): | |
210 | c.cs = self.__get_cs_or_redirect(revision, repo_name) |
|
210 | c.cs = self.__get_cs_or_redirect(revision, repo_name) | |
211 | c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path) |
|
211 | c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path) | |
212 |
|
212 | |||
213 | c.file_history = self._get_node_history(c.cs, f_path) |
|
213 | c.file_history = self._get_node_history(c.cs, f_path) | |
214 | c.f_path = f_path |
|
214 | c.f_path = f_path | |
215 | return render('files/files_annotate.html') |
|
215 | return render('files/files_annotate.html') | |
216 |
|
216 | |||
217 | @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') |
|
217 | @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') | |
218 | def edit(self, repo_name, revision, f_path): |
|
218 | def edit(self, repo_name, revision, f_path): | |
219 | r_post = request.POST |
|
219 | r_post = request.POST | |
220 |
|
220 | |||
221 | if c.rhodecode_repo.alias == 'hg': |
|
221 | if c.rhodecode_repo.alias == 'hg': | |
222 | from vcs.backends.hg import MercurialInMemoryChangeset as IMC |
|
222 | from vcs.backends.hg import MercurialInMemoryChangeset as IMC | |
223 | elif c.rhodecode_repo.alias == 'git': |
|
223 | elif c.rhodecode_repo.alias == 'git': | |
224 | from vcs.backends.git import GitInMemoryChangeset as IMC |
|
224 | from vcs.backends.git import GitInMemoryChangeset as IMC | |
225 |
|
225 | |||
226 | c.cs = self.__get_cs_or_redirect(revision, repo_name) |
|
226 | c.cs = self.__get_cs_or_redirect(revision, repo_name) | |
227 | c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path) |
|
227 | c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path) | |
228 |
|
228 | |||
229 | c.file_history = self._get_node_history(c.cs, f_path) |
|
229 | c.file_history = self._get_node_history(c.cs, f_path) | |
230 | c.f_path = f_path |
|
230 | c.f_path = f_path | |
231 |
|
231 | |||
232 | if r_post: |
|
232 | if r_post: | |
233 |
|
233 | |||
234 | old_content = c.file.content |
|
234 | old_content = c.file.content | |
235 | sl = old_content.splitlines(1) |
|
235 | sl = old_content.splitlines(1) | |
236 | first_line = sl[0] if sl else '' |
|
236 | first_line = sl[0] if sl else '' | |
237 | # modes: 0 - Unix, 1 - Mac, 2 - DOS |
|
237 | # modes: 0 - Unix, 1 - Mac, 2 - DOS | |
238 | mode = detect_mode(first_line, 0) |
|
238 | mode = detect_mode(first_line, 0) | |
239 | content = convert_line_endings(r_post.get('content'), mode) |
|
239 | content = convert_line_endings(r_post.get('content'), mode) | |
240 |
|
240 | |||
241 | message = r_post.get('message') or (_('Edited %s via RhodeCode') |
|
241 | message = r_post.get('message') or (_('Edited %s via RhodeCode') | |
242 | % (f_path)) |
|
242 | % (f_path)) | |
243 |
|
243 | |||
244 | if content == old_content: |
|
244 | if content == old_content: | |
245 | h.flash(_('No changes'), |
|
245 | h.flash(_('No changes'), | |
246 | category='warning') |
|
246 | category='warning') | |
247 | return redirect(url('changeset_home', repo_name=c.repo_name, |
|
247 | return redirect(url('changeset_home', repo_name=c.repo_name, | |
248 | revision='tip')) |
|
248 | revision='tip')) | |
249 |
|
249 | |||
250 | try: |
|
250 | try: | |
|
251 | # decoding here will force that we have proper encoded values | |||
|
252 | # in any other case this will throw exceptions and deny commit | |||
251 | content = content.encode('utf8') |
|
253 | content = content.encode('utf8') | |
252 | message = message.encode('utf8') |
|
254 | message = message.encode('utf8') | |
253 | path = f_path.encode('utf8') |
|
255 | path = f_path.encode('utf8') | |
254 | author = self.rhodecode_user.full_contact.encode('utf8') |
|
256 | author = self.rhodecode_user.full_contact.encode('utf8') | |
255 | m = IMC(c.rhodecode_repo) |
|
257 | m = IMC(c.rhodecode_repo) | |
256 | m.change(FileNode(path, content)) |
|
258 | m.change(FileNode(path, content)) | |
257 | m.commit(message=message, |
|
259 | m.commit(message=message, | |
258 | author=author, |
|
260 | author=author, | |
259 | parents=[c.cs], branch=c.cs.branch) |
|
261 | parents=[c.cs], branch=c.cs.branch) | |
260 | h.flash(_('Successfully committed to %s' % f_path), |
|
262 | h.flash(_('Successfully committed to %s' % f_path), | |
261 | category='success') |
|
263 | category='success') | |
262 |
|
264 | |||
263 | except Exception, e: |
|
265 | except Exception, e: | |
264 | log.error(traceback.format_exc()) |
|
266 | log.error(traceback.format_exc()) | |
265 | h.flash(_('Error occurred during commit'), category='error') |
|
267 | h.flash(_('Error occurred during commit'), category='error') | |
266 | raise |
|
|||
267 | return redirect(url('changeset_home', |
|
268 | return redirect(url('changeset_home', | |
268 | repo_name=c.repo_name, revision='tip')) |
|
269 | repo_name=c.repo_name, revision='tip')) | |
269 |
|
270 | |||
270 | return render('files/files_edit.html') |
|
271 | return render('files/files_edit.html') | |
271 |
|
272 | |||
272 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
273 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
273 | 'repository.admin') |
|
274 | 'repository.admin') | |
274 | def archivefile(self, repo_name, fname): |
|
275 | def archivefile(self, repo_name, fname): | |
275 |
|
276 | |||
276 | fileformat = None |
|
277 | fileformat = None | |
277 | revision = None |
|
278 | revision = None | |
278 | ext = None |
|
279 | ext = None | |
279 |
|
280 | |||
280 | for a_type, ext_data in ARCHIVE_SPECS.items(): |
|
281 | for a_type, ext_data in ARCHIVE_SPECS.items(): | |
281 | archive_spec = fname.split(ext_data[1]) |
|
282 | archive_spec = fname.split(ext_data[1]) | |
282 | if len(archive_spec) == 2 and archive_spec[1] == '': |
|
283 | if len(archive_spec) == 2 and archive_spec[1] == '': | |
283 | fileformat = a_type or ext_data[1] |
|
284 | fileformat = a_type or ext_data[1] | |
284 | revision = archive_spec[0] |
|
285 | revision = archive_spec[0] | |
285 | ext = ext_data[1] |
|
286 | ext = ext_data[1] | |
286 |
|
287 | |||
287 | try: |
|
288 | try: | |
288 | dbrepo = RepoModel().get_by_repo_name(repo_name) |
|
289 | dbrepo = RepoModel().get_by_repo_name(repo_name) | |
289 | if dbrepo.enable_downloads is False: |
|
290 | if dbrepo.enable_downloads is False: | |
290 | return _('downloads disabled') |
|
291 | return _('downloads disabled') | |
291 |
|
292 | |||
292 | cs = c.rhodecode_repo.get_changeset(revision) |
|
293 | cs = c.rhodecode_repo.get_changeset(revision) | |
293 | content_type = ARCHIVE_SPECS[fileformat][0] |
|
294 | content_type = ARCHIVE_SPECS[fileformat][0] | |
294 | except ChangesetDoesNotExistError: |
|
295 | except ChangesetDoesNotExistError: | |
295 | return _('Unknown revision %s') % revision |
|
296 | return _('Unknown revision %s') % revision | |
296 | except EmptyRepositoryError: |
|
297 | except EmptyRepositoryError: | |
297 | return _('Empty repository') |
|
298 | return _('Empty repository') | |
298 | except (ImproperArchiveTypeError, KeyError): |
|
299 | except (ImproperArchiveTypeError, KeyError): | |
299 | return _('Unknown archive type') |
|
300 | return _('Unknown archive type') | |
300 |
|
301 | |||
301 | response.content_type = content_type |
|
302 | response.content_type = content_type | |
302 | response.content_disposition = 'attachment; filename=%s-%s%s' \ |
|
303 | response.content_disposition = 'attachment; filename=%s-%s%s' \ | |
303 | % (repo_name, revision, ext) |
|
304 | % (repo_name, revision, ext) | |
304 |
|
305 | |||
305 | return cs.get_chunked_archive(stream=None, kind=fileformat) |
|
306 | return cs.get_chunked_archive(stream=None, kind=fileformat) | |
306 |
|
307 | |||
307 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
308 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
308 | 'repository.admin') |
|
309 | 'repository.admin') | |
309 | def diff(self, repo_name, f_path): |
|
310 | def diff(self, repo_name, f_path): | |
310 | diff1 = request.GET.get('diff1') |
|
311 | diff1 = request.GET.get('diff1') | |
311 | diff2 = request.GET.get('diff2') |
|
312 | diff2 = request.GET.get('diff2') | |
312 | c.action = request.GET.get('diff') |
|
313 | c.action = request.GET.get('diff') | |
313 | c.no_changes = diff1 == diff2 |
|
314 | c.no_changes = diff1 == diff2 | |
314 | c.f_path = f_path |
|
315 | c.f_path = f_path | |
315 | c.big_diff = False |
|
316 | c.big_diff = False | |
316 |
|
317 | |||
317 | try: |
|
318 | try: | |
318 | if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]: |
|
319 | if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]: | |
319 | c.changeset_1 = c.rhodecode_repo.get_changeset(diff1) |
|
320 | c.changeset_1 = c.rhodecode_repo.get_changeset(diff1) | |
320 | node1 = c.changeset_1.get_node(f_path) |
|
321 | node1 = c.changeset_1.get_node(f_path) | |
321 | else: |
|
322 | else: | |
322 | c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo) |
|
323 | c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo) | |
323 | node1 = FileNode('.', '', changeset=c.changeset_1) |
|
324 | node1 = FileNode('.', '', changeset=c.changeset_1) | |
324 |
|
325 | |||
325 | if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]: |
|
326 | if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]: | |
326 | c.changeset_2 = c.rhodecode_repo.get_changeset(diff2) |
|
327 | c.changeset_2 = c.rhodecode_repo.get_changeset(diff2) | |
327 | node2 = c.changeset_2.get_node(f_path) |
|
328 | node2 = c.changeset_2.get_node(f_path) | |
328 | else: |
|
329 | else: | |
329 | c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo) |
|
330 | c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo) | |
330 | node2 = FileNode('.', '', changeset=c.changeset_2) |
|
331 | node2 = FileNode('.', '', changeset=c.changeset_2) | |
331 | except RepositoryError: |
|
332 | except RepositoryError: | |
332 | return redirect(url('files_home', |
|
333 | return redirect(url('files_home', | |
333 | repo_name=c.repo_name, f_path=f_path)) |
|
334 | repo_name=c.repo_name, f_path=f_path)) | |
334 |
|
335 | |||
335 | if c.action == 'download': |
|
336 | if c.action == 'download': | |
336 | diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2), |
|
337 | diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2), | |
337 | format='gitdiff') |
|
338 | format='gitdiff') | |
338 |
|
339 | |||
339 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) |
|
340 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) | |
340 | response.content_type = 'text/plain' |
|
341 | response.content_type = 'text/plain' | |
341 | response.content_disposition = 'attachment; filename=%s' \ |
|
342 | response.content_disposition = 'attachment; filename=%s' \ | |
342 | % diff_name |
|
343 | % diff_name | |
343 | return diff.raw_diff() |
|
344 | return diff.raw_diff() | |
344 |
|
345 | |||
345 | elif c.action == 'raw': |
|
346 | elif c.action == 'raw': | |
346 | diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2), |
|
347 | diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2), | |
347 | format='gitdiff') |
|
348 | format='gitdiff') | |
348 | response.content_type = 'text/plain' |
|
349 | response.content_type = 'text/plain' | |
349 | return diff.raw_diff() |
|
350 | return diff.raw_diff() | |
350 |
|
351 | |||
351 | elif c.action == 'diff': |
|
352 | elif c.action == 'diff': | |
352 | if node1.is_binary or node2.is_binary: |
|
353 | if node1.is_binary or node2.is_binary: | |
353 | c.cur_diff = _('Binary file') |
|
354 | c.cur_diff = _('Binary file') | |
354 | elif node1.size > self.cut_off_limit or \ |
|
355 | elif node1.size > self.cut_off_limit or \ | |
355 | node2.size > self.cut_off_limit: |
|
356 | node2.size > self.cut_off_limit: | |
356 | c.cur_diff = '' |
|
357 | c.cur_diff = '' | |
357 | c.big_diff = True |
|
358 | c.big_diff = True | |
358 | else: |
|
359 | else: | |
359 | diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2), |
|
360 | diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2), | |
360 | format='gitdiff') |
|
361 | format='gitdiff') | |
361 | c.cur_diff = diff.as_html() |
|
362 | c.cur_diff = diff.as_html() | |
362 | else: |
|
363 | else: | |
363 |
|
364 | |||
364 | #default option |
|
365 | #default option | |
365 | if node1.is_binary or node2.is_binary: |
|
366 | if node1.is_binary or node2.is_binary: | |
366 | c.cur_diff = _('Binary file') |
|
367 | c.cur_diff = _('Binary file') | |
367 | elif node1.size > self.cut_off_limit or \ |
|
368 | elif node1.size > self.cut_off_limit or \ | |
368 | node2.size > self.cut_off_limit: |
|
369 | node2.size > self.cut_off_limit: | |
369 | c.cur_diff = '' |
|
370 | c.cur_diff = '' | |
370 | c.big_diff = True |
|
371 | c.big_diff = True | |
371 |
|
372 | |||
372 | else: |
|
373 | else: | |
373 | diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2), |
|
374 | diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2), | |
374 | format='gitdiff') |
|
375 | format='gitdiff') | |
375 | c.cur_diff = diff.as_html() |
|
376 | c.cur_diff = diff.as_html() | |
376 |
|
377 | |||
377 | if not c.cur_diff and not c.big_diff: |
|
378 | if not c.cur_diff and not c.big_diff: | |
378 | c.no_changes = True |
|
379 | c.no_changes = True | |
379 | return render('files/file_diff.html') |
|
380 | return render('files/file_diff.html') | |
380 |
|
381 | |||
381 | def _get_node_history(self, cs, f_path): |
|
382 | def _get_node_history(self, cs, f_path): | |
382 | changesets = cs.get_file_history(f_path) |
|
383 | changesets = cs.get_file_history(f_path) | |
383 | hist_l = [] |
|
384 | hist_l = [] | |
384 |
|
385 | |||
385 | changesets_group = ([], _("Changesets")) |
|
386 | changesets_group = ([], _("Changesets")) | |
386 | branches_group = ([], _("Branches")) |
|
387 | branches_group = ([], _("Branches")) | |
387 | tags_group = ([], _("Tags")) |
|
388 | tags_group = ([], _("Tags")) | |
388 |
|
389 | |||
389 | for chs in changesets: |
|
390 | for chs in changesets: | |
390 | n_desc = 'r%s:%s' % (chs.revision, chs.short_id) |
|
391 | n_desc = 'r%s:%s' % (chs.revision, chs.short_id) | |
391 | changesets_group[0].append((chs.raw_id, n_desc,)) |
|
392 | changesets_group[0].append((chs.raw_id, n_desc,)) | |
392 |
|
393 | |||
393 | hist_l.append(changesets_group) |
|
394 | hist_l.append(changesets_group) | |
394 |
|
395 | |||
395 | for name, chs in c.rhodecode_repo.branches.items(): |
|
396 | for name, chs in c.rhodecode_repo.branches.items(): | |
396 | #chs = chs.split(':')[-1] |
|
397 | #chs = chs.split(':')[-1] | |
397 | branches_group[0].append((chs, name),) |
|
398 | branches_group[0].append((chs, name),) | |
398 | hist_l.append(branches_group) |
|
399 | hist_l.append(branches_group) | |
399 |
|
400 | |||
400 | for name, chs in c.rhodecode_repo.tags.items(): |
|
401 | for name, chs in c.rhodecode_repo.tags.items(): | |
401 | #chs = chs.split(':')[-1] |
|
402 | #chs = chs.split(':')[-1] | |
402 | tags_group[0].append((chs, name),) |
|
403 | tags_group[0].append((chs, name),) | |
403 | hist_l.append(tags_group) |
|
404 | hist_l.append(tags_group) | |
404 |
|
405 | |||
405 | return hist_l |
|
406 | return hist_l |
@@ -1,166 +1,175 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.lib.__init__ |
|
3 | rhodecode.lib.__init__ | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Some simple helper functions |
|
6 | Some simple helper functions | |
7 |
|
7 | |||
8 | :created_on: Jan 5, 2011 |
|
8 | :created_on: Jan 5, 2011 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 |
|
26 | |||
27 |
|
||||
28 | def __get_lem(): |
|
27 | def __get_lem(): | |
29 | from pygments import lexers |
|
28 | from pygments import lexers | |
30 | from string import lower |
|
29 | from string import lower | |
31 | from collections import defaultdict |
|
30 | from collections import defaultdict | |
32 |
|
31 | |||
33 | d = defaultdict(lambda: []) |
|
32 | d = defaultdict(lambda: []) | |
34 |
|
33 | |||
35 | def __clean(s): |
|
34 | def __clean(s): | |
36 | s = s.lstrip('*') |
|
35 | s = s.lstrip('*') | |
37 | s = s.lstrip('.') |
|
36 | s = s.lstrip('.') | |
38 |
|
37 | |||
39 | if s.find('[') != -1: |
|
38 | if s.find('[') != -1: | |
40 | exts = [] |
|
39 | exts = [] | |
41 | start, stop = s.find('['), s.find(']') |
|
40 | start, stop = s.find('['), s.find(']') | |
42 |
|
41 | |||
43 | for suffix in s[start + 1:stop]: |
|
42 | for suffix in s[start + 1:stop]: | |
44 | exts.append(s[:s.find('[')] + suffix) |
|
43 | exts.append(s[:s.find('[')] + suffix) | |
45 | return map(lower, exts) |
|
44 | return map(lower, exts) | |
46 | else: |
|
45 | else: | |
47 | return map(lower, [s]) |
|
46 | return map(lower, [s]) | |
48 |
|
47 | |||
49 | for lx, t in sorted(lexers.LEXERS.items()): |
|
48 | for lx, t in sorted(lexers.LEXERS.items()): | |
50 | m = map(__clean, t[-2]) |
|
49 | m = map(__clean, t[-2]) | |
51 | if m: |
|
50 | if m: | |
52 | m = reduce(lambda x, y: x + y, m) |
|
51 | m = reduce(lambda x, y: x + y, m) | |
53 | for ext in m: |
|
52 | for ext in m: | |
54 | desc = lx.replace('Lexer', '') |
|
53 | desc = lx.replace('Lexer', '') | |
55 | d[ext].append(desc) |
|
54 | d[ext].append(desc) | |
56 |
|
55 | |||
57 | return dict(d) |
|
56 | return dict(d) | |
58 |
|
57 | |||
59 | # language map is also used by whoosh indexer, which for those specified |
|
58 | # language map is also used by whoosh indexer, which for those specified | |
60 | # extensions will index it's content |
|
59 | # extensions will index it's content | |
61 | LANGUAGES_EXTENSIONS_MAP = __get_lem() |
|
60 | LANGUAGES_EXTENSIONS_MAP = __get_lem() | |
62 |
|
61 | |||
63 | #Additional mappings that are not present in the pygments lexers |
|
62 | # Additional mappings that are not present in the pygments lexers | |
64 | # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP |
|
63 | # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP | |
65 | ADDITIONAL_MAPPINGS = {'xaml': 'XAML'} |
|
64 | ADDITIONAL_MAPPINGS = {'xaml': 'XAML'} | |
66 |
|
65 | |||
67 | LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS) |
|
66 | LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS) | |
68 |
|
67 | |||
|
68 | ||||
69 | def str2bool(_str): |
|
69 | def str2bool(_str): | |
70 | """ |
|
70 | """ | |
71 | returs True/False value from given string, it tries to translate the |
|
71 | returs True/False value from given string, it tries to translate the | |
72 | string into boolean |
|
72 | string into boolean | |
73 |
|
73 | |||
74 | :param _str: string value to translate into boolean |
|
74 | :param _str: string value to translate into boolean | |
75 | :rtype: boolean |
|
75 | :rtype: boolean | |
76 | :returns: boolean from given string |
|
76 | :returns: boolean from given string | |
77 | """ |
|
77 | """ | |
78 | if _str is None: |
|
78 | if _str is None: | |
79 | return False |
|
79 | return False | |
80 | if _str in (True, False): |
|
80 | if _str in (True, False): | |
81 | return _str |
|
81 | return _str | |
82 | _str = str(_str).strip().lower() |
|
82 | _str = str(_str).strip().lower() | |
83 | return _str in ('t', 'true', 'y', 'yes', 'on', '1') |
|
83 | return _str in ('t', 'true', 'y', 'yes', 'on', '1') | |
84 |
|
84 | |||
|
85 | ||||
85 | def convert_line_endings(temp, mode): |
|
86 | def convert_line_endings(temp, mode): | |
86 | from string import replace |
|
87 | from string import replace | |
87 | #modes: 0 - Unix, 1 - Mac, 2 - DOS |
|
88 | #modes: 0 - Unix, 1 - Mac, 2 - DOS | |
88 | if mode == 0: |
|
89 | if mode == 0: | |
89 | temp = replace(temp, '\r\n', '\n') |
|
90 | temp = replace(temp, '\r\n', '\n') | |
90 | temp = replace(temp, '\r', '\n') |
|
91 | temp = replace(temp, '\r', '\n') | |
91 | elif mode == 1: |
|
92 | elif mode == 1: | |
92 | temp = replace(temp, '\r\n', '\r') |
|
93 | temp = replace(temp, '\r\n', '\r') | |
93 | temp = replace(temp, '\n', '\r') |
|
94 | temp = replace(temp, '\n', '\r') | |
94 | elif mode == 2: |
|
95 | elif mode == 2: | |
95 | import re |
|
96 | import re | |
96 | temp = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", temp) |
|
97 | temp = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", temp) | |
97 | return temp |
|
98 | return temp | |
98 |
|
99 | |||
99 |
|
100 | |||
100 | def detect_mode(line, default): |
|
101 | def detect_mode(line, default): | |
|
102 | """ | |||
|
103 | Detects line break for given line, if line break couldn't be found | |||
|
104 | given default value is returned | |||
|
105 | ||||
|
106 | :param line: str line | |||
|
107 | :param default: default | |||
|
108 | :rtype: int | |||
|
109 | :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS | |||
|
110 | """ | |||
101 | if line.endswith('\r\n'): |
|
111 | if line.endswith('\r\n'): | |
102 | return 2 |
|
112 | return 2 | |
103 | elif line.endswith('\n'): |
|
113 | elif line.endswith('\n'): | |
104 | return 0 |
|
114 | return 0 | |
105 | elif line.endswith('\r'): |
|
115 | elif line.endswith('\r'): | |
106 | return 1 |
|
116 | return 1 | |
107 | else: |
|
117 | else: | |
108 | return default |
|
118 | return default | |
109 |
|
119 | |||
|
120 | ||||
110 | def generate_api_key(username, salt=None): |
|
121 | def generate_api_key(username, salt=None): | |
111 | """ |
|
122 | """ | |
112 | Generates unique API key for given username,if salt is not given |
|
123 | Generates unique API key for given username,if salt is not given | |
113 | it'll be generated from some random string |
|
124 | it'll be generated from some random string | |
114 |
|
125 | |||
115 | :param username: username as string |
|
126 | :param username: username as string | |
116 | :param salt: salt to hash generate KEY |
|
127 | :param salt: salt to hash generate KEY | |
117 | :rtype: str |
|
128 | :rtype: str | |
118 | :returns: sha1 hash from username+salt |
|
129 | :returns: sha1 hash from username+salt | |
119 | """ |
|
130 | """ | |
120 | from tempfile import _RandomNameSequence |
|
131 | from tempfile import _RandomNameSequence | |
121 | import hashlib |
|
132 | import hashlib | |
122 |
|
133 | |||
123 | if salt is None: |
|
134 | if salt is None: | |
124 | salt = _RandomNameSequence().next() |
|
135 | salt = _RandomNameSequence().next() | |
125 |
|
136 | |||
126 | return hashlib.sha1(username + salt).hexdigest() |
|
137 | return hashlib.sha1(username + salt).hexdigest() | |
127 |
|
138 | |||
128 |
|
139 | |||
129 | def safe_unicode(_str, from_encoding='utf8'): |
|
140 | def safe_unicode(_str, from_encoding='utf8'): | |
130 | """ |
|
141 | """ | |
131 | safe unicode function. In case of UnicodeDecode error we try to return |
|
142 | safe unicode function. In case of UnicodeDecode error we try to return | |
132 | unicode with errors replace |
|
143 | unicode with errors replace | |
133 |
|
144 | |||
134 | :param _str: string to decode |
|
145 | :param _str: string to decode | |
135 | :rtype: unicode |
|
146 | :rtype: unicode | |
136 | :returns: unicode object |
|
147 | :returns: unicode object | |
137 | """ |
|
148 | """ | |
138 |
|
149 | |||
139 | if isinstance(_str, unicode): |
|
150 | if isinstance(_str, unicode): | |
140 | return _str |
|
151 | return _str | |
141 |
|
152 | |||
142 | try: |
|
153 | try: | |
143 | u_str = unicode(_str, from_encoding) |
|
154 | u_str = unicode(_str, from_encoding) | |
144 | except UnicodeDecodeError: |
|
155 | except UnicodeDecodeError: | |
145 | u_str = unicode(_str, from_encoding, 'replace') |
|
156 | u_str = unicode(_str, from_encoding, 'replace') | |
146 |
|
157 | |||
147 | return u_str |
|
158 | return u_str | |
148 |
|
159 | |||
149 |
|
160 | |||
150 | def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs): |
|
161 | def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs): | |
151 | """ |
|
162 | """ | |
152 | Custom engine_from_config functions that makes sure we use NullPool for |
|
163 | Custom engine_from_config functions that makes sure we use NullPool for | |
153 | file based sqlite databases. This prevents errors on sqlite. |
|
164 | file based sqlite databases. This prevents errors on sqlite. | |
154 |
|
165 | |||
155 | """ |
|
166 | """ | |
156 | from sqlalchemy import engine_from_config as efc |
|
167 | from sqlalchemy import engine_from_config as efc | |
157 | from sqlalchemy.pool import NullPool |
|
168 | from sqlalchemy.pool import NullPool | |
158 |
|
169 | |||
159 | url = configuration[prefix + 'url'] |
|
170 | url = configuration[prefix + 'url'] | |
160 |
|
171 | |||
161 | if url.startswith('sqlite'): |
|
172 | if url.startswith('sqlite'): | |
162 | kwargs.update({'poolclass':NullPool}) |
|
173 | kwargs.update({'poolclass': NullPool}) | |
163 |
|
174 | |||
164 | return efc(configuration, prefix, **kwargs) |
|
175 | return efc(configuration, prefix, **kwargs) | |
165 |
|
||||
166 |
|
@@ -1,19 +1,20 b'' | |||||
1 | """The application's Globals object""" |
|
1 | """The application's Globals object""" | |
2 |
|
2 | |||
3 | from beaker.cache import CacheManager |
|
3 | from beaker.cache import CacheManager | |
4 | from beaker.util import parse_cache_config_options |
|
4 | from beaker.util import parse_cache_config_options | |
5 |
|
5 | |||
|
6 | ||||
6 | class Globals(object): |
|
7 | class Globals(object): | |
7 | """Globals acts as a container for objects available throughout the |
|
8 | """Globals acts as a container for objects available throughout the | |
8 | life of the application |
|
9 | life of the application | |
9 |
|
10 | |||
10 | """ |
|
11 | """ | |
11 |
|
12 | |||
12 | def __init__(self, config): |
|
13 | def __init__(self, config): | |
13 | """One instance of Globals is created during application |
|
14 | """One instance of Globals is created during application | |
14 | initialization and is available during requests via the |
|
15 | initialization and is available during requests via the | |
15 | 'app_globals' variable |
|
16 | 'app_globals' variable | |
16 |
|
17 | |||
17 | """ |
|
18 | """ | |
18 | self.cache = CacheManager(**parse_cache_config_options(config)) |
|
19 | self.cache = CacheManager(**parse_cache_config_options(config)) | |
19 | self.available_permissions = None # propagated after init_model |
|
20 | self.available_permissions = None # propagated after init_model |
@@ -1,605 +1,605 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.lib.auth |
|
3 | rhodecode.lib.auth | |
4 | ~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | authentication and permission libraries |
|
6 | authentication and permission libraries | |
7 |
|
7 | |||
8 | :created_on: Apr 4, 2010 |
|
8 | :created_on: Apr 4, 2010 | |
9 | :copyright: (c) 2010 by marcink. |
|
9 | :copyright: (c) 2010 by marcink. | |
10 | :license: LICENSE_NAME, see LICENSE_FILE for more details. |
|
10 | :license: LICENSE_NAME, see LICENSE_FILE for more details. | |
11 | """ |
|
11 | """ | |
12 | # This program is free software: you can redistribute it and/or modify |
|
12 | # This program is free software: you can redistribute it and/or modify | |
13 | # it under the terms of the GNU General Public License as published by |
|
13 | # it under the terms of the GNU General Public License as published by | |
14 | # the Free Software Foundation, either version 3 of the License, or |
|
14 | # the Free Software Foundation, either version 3 of the License, or | |
15 | # (at your option) any later version. |
|
15 | # (at your option) any later version. | |
16 | # |
|
16 | # | |
17 | # This program is distributed in the hope that it will be useful, |
|
17 | # This program is distributed in the hope that it will be useful, | |
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | # GNU General Public License for more details. |
|
20 | # GNU General Public License for more details. | |
21 | # |
|
21 | # | |
22 | # You should have received a copy of the GNU General Public License |
|
22 | # You should have received a copy of the GNU General Public License | |
23 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
23 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
24 |
|
24 | |||
25 | import random |
|
25 | import random | |
26 | import logging |
|
26 | import logging | |
27 | import traceback |
|
27 | import traceback | |
28 | import hashlib |
|
28 | import hashlib | |
29 |
|
29 | |||
30 | from tempfile import _RandomNameSequence |
|
30 | from tempfile import _RandomNameSequence | |
31 | from decorator import decorator |
|
31 | from decorator import decorator | |
32 |
|
32 | |||
33 | from pylons import config, session, url, request |
|
33 | from pylons import config, session, url, request | |
34 | from pylons.controllers.util import abort, redirect |
|
34 | from pylons.controllers.util import abort, redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 |
|
36 | |||
37 | from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS |
|
37 | from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS | |
38 |
|
38 | |||
39 | if __platform__ in PLATFORM_WIN: |
|
39 | if __platform__ in PLATFORM_WIN: | |
40 | from hashlib import sha256 |
|
40 | from hashlib import sha256 | |
41 | if __platform__ in PLATFORM_OTHERS: |
|
41 | if __platform__ in PLATFORM_OTHERS: | |
42 | import bcrypt |
|
42 | import bcrypt | |
43 |
|
43 | |||
44 | from rhodecode.lib import str2bool |
|
44 | from rhodecode.lib import str2bool | |
45 | from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError |
|
45 | from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError | |
46 | from rhodecode.lib.utils import get_repo_slug |
|
46 | from rhodecode.lib.utils import get_repo_slug | |
47 | from rhodecode.lib.auth_ldap import AuthLdap |
|
47 | from rhodecode.lib.auth_ldap import AuthLdap | |
48 |
|
48 | |||
49 | from rhodecode.model import meta |
|
49 | from rhodecode.model import meta | |
50 | from rhodecode.model.user import UserModel |
|
50 | from rhodecode.model.user import UserModel | |
51 | from rhodecode.model.db import Permission, RhodeCodeSettings |
|
51 | from rhodecode.model.db import Permission, RhodeCodeSettings | |
52 |
|
52 | |||
53 | log = logging.getLogger(__name__) |
|
53 | log = logging.getLogger(__name__) | |
54 |
|
54 | |||
55 |
|
55 | |||
56 | class PasswordGenerator(object): |
|
56 | class PasswordGenerator(object): | |
57 | """This is a simple class for generating password from |
|
57 | """This is a simple class for generating password from | |
58 | different sets of characters |
|
58 | different sets of characters | |
59 | usage: |
|
59 | usage: | |
60 | passwd_gen = PasswordGenerator() |
|
60 | passwd_gen = PasswordGenerator() | |
61 | #print 8-letter password containing only big and small letters |
|
61 | #print 8-letter password containing only big and small letters | |
62 | of alphabet |
|
62 | of alphabet | |
63 | print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL) |
|
63 | print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL) | |
64 | """ |
|
64 | """ | |
65 | ALPHABETS_NUM = r'''1234567890''' |
|
65 | ALPHABETS_NUM = r'''1234567890''' | |
66 | ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm''' |
|
66 | ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm''' | |
67 | ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM''' |
|
67 | ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM''' | |
68 | ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' |
|
68 | ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' | |
69 | ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \ |
|
69 | ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \ | |
70 | + ALPHABETS_NUM + ALPHABETS_SPECIAL |
|
70 | + ALPHABETS_NUM + ALPHABETS_SPECIAL | |
71 | ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM |
|
71 | ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM | |
72 | ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL |
|
72 | ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL | |
73 | ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM |
|
73 | ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM | |
74 | ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM |
|
74 | ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM | |
75 |
|
75 | |||
76 | def __init__(self, passwd=''): |
|
76 | def __init__(self, passwd=''): | |
77 | self.passwd = passwd |
|
77 | self.passwd = passwd | |
78 |
|
78 | |||
79 | def gen_password(self, len, type): |
|
79 | def gen_password(self, len, type): | |
80 | self.passwd = ''.join([random.choice(type) for _ in xrange(len)]) |
|
80 | self.passwd = ''.join([random.choice(type) for _ in xrange(len)]) | |
81 | return self.passwd |
|
81 | return self.passwd | |
82 |
|
82 | |||
83 |
|
83 | |||
84 | class RhodeCodeCrypto(object): |
|
84 | class RhodeCodeCrypto(object): | |
85 |
|
85 | |||
86 | @classmethod |
|
86 | @classmethod | |
87 | def hash_string(cls, str_): |
|
87 | def hash_string(cls, str_): | |
88 | """ |
|
88 | """ | |
89 | Cryptographic function used for password hashing based on pybcrypt |
|
89 | Cryptographic function used for password hashing based on pybcrypt | |
90 | or pycrypto in windows |
|
90 | or pycrypto in windows | |
91 |
|
91 | |||
92 | :param password: password to hash |
|
92 | :param password: password to hash | |
93 | """ |
|
93 | """ | |
94 | if __platform__ in PLATFORM_WIN: |
|
94 | if __platform__ in PLATFORM_WIN: | |
95 | return sha256(str_).hexdigest() |
|
95 | return sha256(str_).hexdigest() | |
96 | elif __platform__ in PLATFORM_OTHERS: |
|
96 | elif __platform__ in PLATFORM_OTHERS: | |
97 | return bcrypt.hashpw(str_, bcrypt.gensalt(10)) |
|
97 | return bcrypt.hashpw(str_, bcrypt.gensalt(10)) | |
98 | else: |
|
98 | else: | |
99 | raise Exception('Unknown or unsupported platform %s' \ |
|
99 | raise Exception('Unknown or unsupported platform %s' \ | |
100 | % __platform__) |
|
100 | % __platform__) | |
101 |
|
101 | |||
102 | @classmethod |
|
102 | @classmethod | |
103 | def hash_check(cls, password, hashed): |
|
103 | def hash_check(cls, password, hashed): | |
104 | """ |
|
104 | """ | |
105 | Checks matching password with it's hashed value, runs different |
|
105 | Checks matching password with it's hashed value, runs different | |
106 | implementation based on platform it runs on |
|
106 | implementation based on platform it runs on | |
107 |
|
107 | |||
108 | :param password: password |
|
108 | :param password: password | |
109 | :param hashed: password in hashed form |
|
109 | :param hashed: password in hashed form | |
110 | """ |
|
110 | """ | |
111 |
|
111 | |||
112 | if __platform__ in PLATFORM_WIN: |
|
112 | if __platform__ in PLATFORM_WIN: | |
113 | return sha256(password).hexdigest() == hashed |
|
113 | return sha256(password).hexdigest() == hashed | |
114 | elif __platform__ in PLATFORM_OTHERS: |
|
114 | elif __platform__ in PLATFORM_OTHERS: | |
115 | return bcrypt.hashpw(password, hashed) == hashed |
|
115 | return bcrypt.hashpw(password, hashed) == hashed | |
116 | else: |
|
116 | else: | |
117 | raise Exception('Unknown or unsupported platform %s' \ |
|
117 | raise Exception('Unknown or unsupported platform %s' \ | |
118 | % __platform__) |
|
118 | % __platform__) | |
119 |
|
119 | |||
120 |
|
120 | |||
121 | def get_crypt_password(password): |
|
121 | def get_crypt_password(password): | |
122 | return RhodeCodeCrypto.hash_string(password) |
|
122 | return RhodeCodeCrypto.hash_string(password) | |
123 |
|
123 | |||
124 |
|
124 | |||
125 | def check_password(password, hashed): |
|
125 | def check_password(password, hashed): | |
126 | return RhodeCodeCrypto.hash_check(password, hashed) |
|
126 | return RhodeCodeCrypto.hash_check(password, hashed) | |
127 |
|
127 | |||
128 |
|
128 | |||
129 | def generate_api_key(username, salt=None): |
|
129 | def generate_api_key(username, salt=None): | |
130 | if salt is None: |
|
130 | if salt is None: | |
131 | salt = _RandomNameSequence().next() |
|
131 | salt = _RandomNameSequence().next() | |
132 |
|
132 | |||
133 | return hashlib.sha1(username + salt).hexdigest() |
|
133 | return hashlib.sha1(username + salt).hexdigest() | |
134 |
|
134 | |||
135 |
|
135 | |||
136 | def authfunc(environ, username, password): |
|
136 | def authfunc(environ, username, password): | |
137 | """Dummy authentication function used in Mercurial/Git/ and access control, |
|
137 | """Dummy authentication function used in Mercurial/Git/ and access control, | |
138 |
|
138 | |||
139 | :param environ: needed only for using in Basic auth |
|
139 | :param environ: needed only for using in Basic auth | |
140 | """ |
|
140 | """ | |
141 | return authenticate(username, password) |
|
141 | return authenticate(username, password) | |
142 |
|
142 | |||
143 |
|
143 | |||
144 | def authenticate(username, password): |
|
144 | def authenticate(username, password): | |
145 | """Authentication function used for access control, |
|
145 | """Authentication function used for access control, | |
146 | firstly checks for db authentication then if ldap is enabled for ldap |
|
146 | firstly checks for db authentication then if ldap is enabled for ldap | |
147 | authentication, also creates ldap user if not in database |
|
147 | authentication, also creates ldap user if not in database | |
148 |
|
148 | |||
149 | :param username: username |
|
149 | :param username: username | |
150 | :param password: password |
|
150 | :param password: password | |
151 | """ |
|
151 | """ | |
152 |
|
152 | |||
153 | user_model = UserModel() |
|
153 | user_model = UserModel() | |
154 | user = user_model.get_by_username(username, cache=False) |
|
154 | user = user_model.get_by_username(username, cache=False) | |
155 |
|
155 | |||
156 | log.debug('Authenticating user using RhodeCode account') |
|
156 | log.debug('Authenticating user using RhodeCode account') | |
157 | if user is not None and not user.ldap_dn: |
|
157 | if user is not None and not user.ldap_dn: | |
158 | if user.active: |
|
158 | if user.active: | |
159 | if user.username == 'default' and user.active: |
|
159 | if user.username == 'default' and user.active: | |
160 | log.info('user %s authenticated correctly as anonymous user', |
|
160 | log.info('user %s authenticated correctly as anonymous user', | |
161 | username) |
|
161 | username) | |
162 | return True |
|
162 | return True | |
163 |
|
163 | |||
164 | elif user.username == username and check_password(password, |
|
164 | elif user.username == username and check_password(password, | |
165 | user.password): |
|
165 | user.password): | |
166 | log.info('user %s authenticated correctly', username) |
|
166 | log.info('user %s authenticated correctly', username) | |
167 | return True |
|
167 | return True | |
168 | else: |
|
168 | else: | |
169 | log.warning('user %s is disabled', username) |
|
169 | log.warning('user %s is disabled', username) | |
170 |
|
170 | |||
171 | else: |
|
171 | else: | |
172 | log.debug('Regular authentication failed') |
|
172 | log.debug('Regular authentication failed') | |
173 | user_obj = user_model.get_by_username(username, cache=False, |
|
173 | user_obj = user_model.get_by_username(username, cache=False, | |
174 | case_insensitive=True) |
|
174 | case_insensitive=True) | |
175 |
|
175 | |||
176 | if user_obj is not None and not user_obj.ldap_dn: |
|
176 | if user_obj is not None and not user_obj.ldap_dn: | |
177 | log.debug('this user already exists as non ldap') |
|
177 | log.debug('this user already exists as non ldap') | |
178 | return False |
|
178 | return False | |
179 |
|
179 | |||
180 | ldap_settings = RhodeCodeSettings.get_ldap_settings() |
|
180 | ldap_settings = RhodeCodeSettings.get_ldap_settings() | |
181 | #====================================================================== |
|
181 | #====================================================================== | |
182 | # FALLBACK TO LDAP AUTH IF ENABLE |
|
182 | # FALLBACK TO LDAP AUTH IF ENABLE | |
183 | #====================================================================== |
|
183 | #====================================================================== | |
184 | if str2bool(ldap_settings.get('ldap_active')): |
|
184 | if str2bool(ldap_settings.get('ldap_active')): | |
185 | log.debug("Authenticating user using ldap") |
|
185 | log.debug("Authenticating user using ldap") | |
186 | kwargs = { |
|
186 | kwargs = { | |
187 | 'server': ldap_settings.get('ldap_host', ''), |
|
187 | 'server': ldap_settings.get('ldap_host', ''), | |
188 | 'base_dn': ldap_settings.get('ldap_base_dn', ''), |
|
188 | 'base_dn': ldap_settings.get('ldap_base_dn', ''), | |
189 | 'port': ldap_settings.get('ldap_port'), |
|
189 | 'port': ldap_settings.get('ldap_port'), | |
190 | 'bind_dn': ldap_settings.get('ldap_dn_user'), |
|
190 | 'bind_dn': ldap_settings.get('ldap_dn_user'), | |
191 | 'bind_pass': ldap_settings.get('ldap_dn_pass'), |
|
191 | 'bind_pass': ldap_settings.get('ldap_dn_pass'), | |
192 | 'tls_kind': ldap_settings.get('ldap_tls_kind'), |
|
192 | 'tls_kind': ldap_settings.get('ldap_tls_kind'), | |
193 | 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'), |
|
193 | 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'), | |
194 | 'ldap_filter': ldap_settings.get('ldap_filter'), |
|
194 | 'ldap_filter': ldap_settings.get('ldap_filter'), | |
195 | 'search_scope': ldap_settings.get('ldap_search_scope'), |
|
195 | 'search_scope': ldap_settings.get('ldap_search_scope'), | |
196 | 'attr_login': ldap_settings.get('ldap_attr_login'), |
|
196 | 'attr_login': ldap_settings.get('ldap_attr_login'), | |
197 | 'ldap_version': 3, |
|
197 | 'ldap_version': 3, | |
198 | } |
|
198 | } | |
199 | log.debug('Checking for ldap authentication') |
|
199 | log.debug('Checking for ldap authentication') | |
200 | try: |
|
200 | try: | |
201 | aldap = AuthLdap(**kwargs) |
|
201 | aldap = AuthLdap(**kwargs) | |
202 | (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, |
|
202 | (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, | |
203 | password) |
|
203 | password) | |
204 | log.debug('Got ldap DN response %s', user_dn) |
|
204 | log.debug('Got ldap DN response %s', user_dn) | |
205 |
|
205 | |||
206 | get_ldap_attr = lambda k:ldap_attrs.get(ldap_settings\ |
|
206 | get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\ | |
207 | .get(k), [''])[0] |
|
207 | .get(k), [''])[0] | |
208 |
|
208 | |||
209 | user_attrs = { |
|
209 | user_attrs = { | |
210 | 'name': get_ldap_attr('ldap_attr_firstname'), |
|
210 | 'name': get_ldap_attr('ldap_attr_firstname'), | |
211 | 'lastname': get_ldap_attr('ldap_attr_lastname'), |
|
211 | 'lastname': get_ldap_attr('ldap_attr_lastname'), | |
212 | 'email': get_ldap_attr('ldap_attr_email'), |
|
212 | 'email': get_ldap_attr('ldap_attr_email'), | |
213 | } |
|
213 | } | |
214 |
|
214 | |||
215 | if user_model.create_ldap(username, password, user_dn, |
|
215 | if user_model.create_ldap(username, password, user_dn, | |
216 | user_attrs): |
|
216 | user_attrs): | |
217 | log.info('created new ldap user %s', username) |
|
217 | log.info('created new ldap user %s', username) | |
218 |
|
218 | |||
219 | return True |
|
219 | return True | |
220 | except (LdapUsernameError, LdapPasswordError,): |
|
220 | except (LdapUsernameError, LdapPasswordError,): | |
221 | pass |
|
221 | pass | |
222 | except (Exception,): |
|
222 | except (Exception,): | |
223 | log.error(traceback.format_exc()) |
|
223 | log.error(traceback.format_exc()) | |
224 | pass |
|
224 | pass | |
225 | return False |
|
225 | return False | |
226 |
|
226 | |||
227 |
|
227 | |||
228 | class AuthUser(object): |
|
228 | class AuthUser(object): | |
229 | """ |
|
229 | """ | |
230 | A simple object that handles all attributes of user in RhodeCode |
|
230 | A simple object that handles all attributes of user in RhodeCode | |
231 |
|
231 | |||
232 | It does lookup based on API key,given user, or user present in session |
|
232 | It does lookup based on API key,given user, or user present in session | |
233 | Then it fills all required information for such user. It also checks if |
|
233 | Then it fills all required information for such user. It also checks if | |
234 | anonymous access is enabled and if so, it returns default user as logged |
|
234 | anonymous access is enabled and if so, it returns default user as logged | |
235 | in |
|
235 | in | |
236 | """ |
|
236 | """ | |
237 |
|
237 | |||
238 | def __init__(self, user_id=None, api_key=None): |
|
238 | def __init__(self, user_id=None, api_key=None): | |
239 |
|
239 | |||
240 | self.user_id = user_id |
|
240 | self.user_id = user_id | |
241 | self.api_key = None |
|
241 | self.api_key = None | |
242 |
|
242 | |||
243 | self.username = 'None' |
|
243 | self.username = 'None' | |
244 | self.name = '' |
|
244 | self.name = '' | |
245 | self.lastname = '' |
|
245 | self.lastname = '' | |
246 | self.email = '' |
|
246 | self.email = '' | |
247 | self.is_authenticated = False |
|
247 | self.is_authenticated = False | |
248 | self.admin = False |
|
248 | self.admin = False | |
249 | self.permissions = {} |
|
249 | self.permissions = {} | |
250 | self._api_key = api_key |
|
250 | self._api_key = api_key | |
251 | self.propagate_data() |
|
251 | self.propagate_data() | |
252 |
|
252 | |||
253 | def propagate_data(self): |
|
253 | def propagate_data(self): | |
254 | user_model = UserModel() |
|
254 | user_model = UserModel() | |
255 | self.anonymous_user = user_model.get_by_username('default', cache=True) |
|
255 | self.anonymous_user = user_model.get_by_username('default', cache=True) | |
256 | if self._api_key and self._api_key != self.anonymous_user.api_key: |
|
256 | if self._api_key and self._api_key != self.anonymous_user.api_key: | |
257 | #try go get user by api key |
|
257 | #try go get user by api key | |
258 | log.debug('Auth User lookup by API KEY %s', self._api_key) |
|
258 | log.debug('Auth User lookup by API KEY %s', self._api_key) | |
259 | user_model.fill_data(self, api_key=self._api_key) |
|
259 | user_model.fill_data(self, api_key=self._api_key) | |
260 | else: |
|
260 | else: | |
261 | log.debug('Auth User lookup by USER ID %s', self.user_id) |
|
261 | log.debug('Auth User lookup by USER ID %s', self.user_id) | |
262 | if self.user_id is not None \ |
|
262 | if self.user_id is not None \ | |
263 | and self.user_id != self.anonymous_user.user_id: |
|
263 | and self.user_id != self.anonymous_user.user_id: | |
264 | user_model.fill_data(self, user_id=self.user_id) |
|
264 | user_model.fill_data(self, user_id=self.user_id) | |
265 | else: |
|
265 | else: | |
266 | if self.anonymous_user.active is True: |
|
266 | if self.anonymous_user.active is True: | |
267 | user_model.fill_data(self, |
|
267 | user_model.fill_data(self, | |
268 | user_id=self.anonymous_user.user_id) |
|
268 | user_id=self.anonymous_user.user_id) | |
269 | #then we set this user is logged in |
|
269 | #then we set this user is logged in | |
270 | self.is_authenticated = True |
|
270 | self.is_authenticated = True | |
271 | else: |
|
271 | else: | |
272 | self.is_authenticated = False |
|
272 | self.is_authenticated = False | |
273 |
|
273 | |||
274 | log.debug('Auth User is now %s', self) |
|
274 | log.debug('Auth User is now %s', self) | |
275 | user_model.fill_perms(self) |
|
275 | user_model.fill_perms(self) | |
276 |
|
276 | |||
277 | @property |
|
277 | @property | |
278 | def is_admin(self): |
|
278 | def is_admin(self): | |
279 | return self.admin |
|
279 | return self.admin | |
280 |
|
280 | |||
281 | @property |
|
281 | @property | |
282 | def full_contact(self): |
|
282 | def full_contact(self): | |
283 | return '%s %s <%s>' % (self.name, self.lastname, self.email) |
|
283 | return '%s %s <%s>' % (self.name, self.lastname, self.email) | |
284 |
|
284 | |||
285 | def __repr__(self): |
|
285 | def __repr__(self): | |
286 | return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username, |
|
286 | return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username, | |
287 | self.is_authenticated) |
|
287 | self.is_authenticated) | |
288 |
|
288 | |||
289 | def set_authenticated(self, authenticated=True): |
|
289 | def set_authenticated(self, authenticated=True): | |
290 |
|
290 | |||
291 | if self.user_id != self.anonymous_user.user_id: |
|
291 | if self.user_id != self.anonymous_user.user_id: | |
292 | self.is_authenticated = authenticated |
|
292 | self.is_authenticated = authenticated | |
293 |
|
293 | |||
294 |
|
294 | |||
295 | def set_available_permissions(config): |
|
295 | def set_available_permissions(config): | |
296 | """This function will propagate pylons globals with all available defined |
|
296 | """This function will propagate pylons globals with all available defined | |
297 | permission given in db. We don't want to check each time from db for new |
|
297 | permission given in db. We don't want to check each time from db for new | |
298 | permissions since adding a new permission also requires application restart |
|
298 | permissions since adding a new permission also requires application restart | |
299 | ie. to decorate new views with the newly created permission |
|
299 | ie. to decorate new views with the newly created permission | |
300 |
|
300 | |||
301 | :param config: current pylons config instance |
|
301 | :param config: current pylons config instance | |
302 |
|
302 | |||
303 | """ |
|
303 | """ | |
304 | log.info('getting information about all available permissions') |
|
304 | log.info('getting information about all available permissions') | |
305 | try: |
|
305 | try: | |
306 | sa = meta.Session() |
|
306 | sa = meta.Session() | |
307 | all_perms = sa.query(Permission).all() |
|
307 | all_perms = sa.query(Permission).all() | |
308 | except: |
|
308 | except: | |
309 | pass |
|
309 | pass | |
310 | finally: |
|
310 | finally: | |
311 | meta.Session.remove() |
|
311 | meta.Session.remove() | |
312 |
|
312 | |||
313 | config['available_permissions'] = [x.permission_name for x in all_perms] |
|
313 | config['available_permissions'] = [x.permission_name for x in all_perms] | |
314 |
|
314 | |||
315 |
|
315 | |||
316 | #============================================================================== |
|
316 | #============================================================================== | |
317 | # CHECK DECORATORS |
|
317 | # CHECK DECORATORS | |
318 | #============================================================================== |
|
318 | #============================================================================== | |
319 | class LoginRequired(object): |
|
319 | class LoginRequired(object): | |
320 | """ |
|
320 | """ | |
321 | Must be logged in to execute this function else |
|
321 | Must be logged in to execute this function else | |
322 | redirect to login page |
|
322 | redirect to login page | |
323 |
|
323 | |||
324 | :param api_access: if enabled this checks only for valid auth token |
|
324 | :param api_access: if enabled this checks only for valid auth token | |
325 | and grants access based on valid token |
|
325 | and grants access based on valid token | |
326 | """ |
|
326 | """ | |
327 |
|
327 | |||
328 | def __init__(self, api_access=False): |
|
328 | def __init__(self, api_access=False): | |
329 | self.api_access = api_access |
|
329 | self.api_access = api_access | |
330 |
|
330 | |||
331 | def __call__(self, func): |
|
331 | def __call__(self, func): | |
332 | return decorator(self.__wrapper, func) |
|
332 | return decorator(self.__wrapper, func) | |
333 |
|
333 | |||
334 | def __wrapper(self, func, *fargs, **fkwargs): |
|
334 | def __wrapper(self, func, *fargs, **fkwargs): | |
335 | cls = fargs[0] |
|
335 | cls = fargs[0] | |
336 | user = cls.rhodecode_user |
|
336 | user = cls.rhodecode_user | |
337 |
|
337 | |||
338 | api_access_ok = False |
|
338 | api_access_ok = False | |
339 | if self.api_access: |
|
339 | if self.api_access: | |
340 | log.debug('Checking API KEY access for %s', cls) |
|
340 | log.debug('Checking API KEY access for %s', cls) | |
341 | if user.api_key == request.GET.get('api_key'): |
|
341 | if user.api_key == request.GET.get('api_key'): | |
342 | api_access_ok = True |
|
342 | api_access_ok = True | |
343 | else: |
|
343 | else: | |
344 | log.debug("API KEY token not valid") |
|
344 | log.debug("API KEY token not valid") | |
345 |
|
345 | |||
346 | log.debug('Checking if %s is authenticated @ %s', user.username, cls) |
|
346 | log.debug('Checking if %s is authenticated @ %s', user.username, cls) | |
347 | if user.is_authenticated or api_access_ok: |
|
347 | if user.is_authenticated or api_access_ok: | |
348 | log.debug('user %s is authenticated', user.username) |
|
348 | log.debug('user %s is authenticated', user.username) | |
349 | return func(*fargs, **fkwargs) |
|
349 | return func(*fargs, **fkwargs) | |
350 | else: |
|
350 | else: | |
351 | log.warn('user %s NOT authenticated', user.username) |
|
351 | log.warn('user %s NOT authenticated', user.username) | |
352 | p = url.current() |
|
352 | p = url.current() | |
353 |
|
353 | |||
354 | log.debug('redirecting to login page with %s', p) |
|
354 | log.debug('redirecting to login page with %s', p) | |
355 | return redirect(url('login_home', came_from=p)) |
|
355 | return redirect(url('login_home', came_from=p)) | |
356 |
|
356 | |||
357 |
|
357 | |||
358 | class NotAnonymous(object): |
|
358 | class NotAnonymous(object): | |
359 | """Must be logged in to execute this function else |
|
359 | """Must be logged in to execute this function else | |
360 | redirect to login page""" |
|
360 | redirect to login page""" | |
361 |
|
361 | |||
362 | def __call__(self, func): |
|
362 | def __call__(self, func): | |
363 | return decorator(self.__wrapper, func) |
|
363 | return decorator(self.__wrapper, func) | |
364 |
|
364 | |||
365 | def __wrapper(self, func, *fargs, **fkwargs): |
|
365 | def __wrapper(self, func, *fargs, **fkwargs): | |
366 | cls = fargs[0] |
|
366 | cls = fargs[0] | |
367 | self.user = cls.rhodecode_user |
|
367 | self.user = cls.rhodecode_user | |
368 |
|
368 | |||
369 | log.debug('Checking if user is not anonymous @%s', cls) |
|
369 | log.debug('Checking if user is not anonymous @%s', cls) | |
370 |
|
370 | |||
371 | anonymous = self.user.username == 'default' |
|
371 | anonymous = self.user.username == 'default' | |
372 |
|
372 | |||
373 | if anonymous: |
|
373 | if anonymous: | |
374 | p = '' |
|
374 | p = '' | |
375 | if request.environ.get('SCRIPT_NAME') != '/': |
|
375 | if request.environ.get('SCRIPT_NAME') != '/': | |
376 | p += request.environ.get('SCRIPT_NAME') |
|
376 | p += request.environ.get('SCRIPT_NAME') | |
377 |
|
377 | |||
378 | p += request.environ.get('PATH_INFO') |
|
378 | p += request.environ.get('PATH_INFO') | |
379 | if request.environ.get('QUERY_STRING'): |
|
379 | if request.environ.get('QUERY_STRING'): | |
380 | p += '?' + request.environ.get('QUERY_STRING') |
|
380 | p += '?' + request.environ.get('QUERY_STRING') | |
381 |
|
381 | |||
382 | import rhodecode.lib.helpers as h |
|
382 | import rhodecode.lib.helpers as h | |
383 | h.flash(_('You need to be a registered user to ' |
|
383 | h.flash(_('You need to be a registered user to ' | |
384 | 'perform this action'), |
|
384 | 'perform this action'), | |
385 | category='warning') |
|
385 | category='warning') | |
386 | return redirect(url('login_home', came_from=p)) |
|
386 | return redirect(url('login_home', came_from=p)) | |
387 | else: |
|
387 | else: | |
388 | return func(*fargs, **fkwargs) |
|
388 | return func(*fargs, **fkwargs) | |
389 |
|
389 | |||
390 |
|
390 | |||
391 | class PermsDecorator(object): |
|
391 | class PermsDecorator(object): | |
392 | """Base class for controller decorators""" |
|
392 | """Base class for controller decorators""" | |
393 |
|
393 | |||
394 | def __init__(self, *required_perms): |
|
394 | def __init__(self, *required_perms): | |
395 | available_perms = config['available_permissions'] |
|
395 | available_perms = config['available_permissions'] | |
396 | for perm in required_perms: |
|
396 | for perm in required_perms: | |
397 | if perm not in available_perms: |
|
397 | if perm not in available_perms: | |
398 | raise Exception("'%s' permission is not defined" % perm) |
|
398 | raise Exception("'%s' permission is not defined" % perm) | |
399 | self.required_perms = set(required_perms) |
|
399 | self.required_perms = set(required_perms) | |
400 | self.user_perms = None |
|
400 | self.user_perms = None | |
401 |
|
401 | |||
402 | def __call__(self, func): |
|
402 | def __call__(self, func): | |
403 | return decorator(self.__wrapper, func) |
|
403 | return decorator(self.__wrapper, func) | |
404 |
|
404 | |||
405 | def __wrapper(self, func, *fargs, **fkwargs): |
|
405 | def __wrapper(self, func, *fargs, **fkwargs): | |
406 | cls = fargs[0] |
|
406 | cls = fargs[0] | |
407 | self.user = cls.rhodecode_user |
|
407 | self.user = cls.rhodecode_user | |
408 | self.user_perms = self.user.permissions |
|
408 | self.user_perms = self.user.permissions | |
409 | log.debug('checking %s permissions %s for %s %s', |
|
409 | log.debug('checking %s permissions %s for %s %s', | |
410 | self.__class__.__name__, self.required_perms, cls, |
|
410 | self.__class__.__name__, self.required_perms, cls, | |
411 | self.user) |
|
411 | self.user) | |
412 |
|
412 | |||
413 | if self.check_permissions(): |
|
413 | if self.check_permissions(): | |
414 | log.debug('Permission granted for %s %s', cls, self.user) |
|
414 | log.debug('Permission granted for %s %s', cls, self.user) | |
415 | return func(*fargs, **fkwargs) |
|
415 | return func(*fargs, **fkwargs) | |
416 |
|
416 | |||
417 | else: |
|
417 | else: | |
418 | log.warning('Permission denied for %s %s', cls, self.user) |
|
418 | log.warning('Permission denied for %s %s', cls, self.user) | |
419 | #redirect with forbidden ret code |
|
419 | #redirect with forbidden ret code | |
420 | return abort(403) |
|
420 | return abort(403) | |
421 |
|
421 | |||
422 | def check_permissions(self): |
|
422 | def check_permissions(self): | |
423 | """Dummy function for overriding""" |
|
423 | """Dummy function for overriding""" | |
424 | raise Exception('You have to write this function in child class') |
|
424 | raise Exception('You have to write this function in child class') | |
425 |
|
425 | |||
426 |
|
426 | |||
427 | class HasPermissionAllDecorator(PermsDecorator): |
|
427 | class HasPermissionAllDecorator(PermsDecorator): | |
428 | """Checks for access permission for all given predicates. All of them |
|
428 | """Checks for access permission for all given predicates. All of them | |
429 | have to be meet in order to fulfill the request |
|
429 | have to be meet in order to fulfill the request | |
430 | """ |
|
430 | """ | |
431 |
|
431 | |||
432 | def check_permissions(self): |
|
432 | def check_permissions(self): | |
433 | if self.required_perms.issubset(self.user_perms.get('global')): |
|
433 | if self.required_perms.issubset(self.user_perms.get('global')): | |
434 | return True |
|
434 | return True | |
435 | return False |
|
435 | return False | |
436 |
|
436 | |||
437 |
|
437 | |||
438 | class HasPermissionAnyDecorator(PermsDecorator): |
|
438 | class HasPermissionAnyDecorator(PermsDecorator): | |
439 | """Checks for access permission for any of given predicates. In order to |
|
439 | """Checks for access permission for any of given predicates. In order to | |
440 | fulfill the request any of predicates must be meet |
|
440 | fulfill the request any of predicates must be meet | |
441 | """ |
|
441 | """ | |
442 |
|
442 | |||
443 | def check_permissions(self): |
|
443 | def check_permissions(self): | |
444 | if self.required_perms.intersection(self.user_perms.get('global')): |
|
444 | if self.required_perms.intersection(self.user_perms.get('global')): | |
445 | return True |
|
445 | return True | |
446 | return False |
|
446 | return False | |
447 |
|
447 | |||
448 |
|
448 | |||
449 | class HasRepoPermissionAllDecorator(PermsDecorator): |
|
449 | class HasRepoPermissionAllDecorator(PermsDecorator): | |
450 | """Checks for access permission for all given predicates for specific |
|
450 | """Checks for access permission for all given predicates for specific | |
451 | repository. All of them have to be meet in order to fulfill the request |
|
451 | repository. All of them have to be meet in order to fulfill the request | |
452 | """ |
|
452 | """ | |
453 |
|
453 | |||
454 | def check_permissions(self): |
|
454 | def check_permissions(self): | |
455 | repo_name = get_repo_slug(request) |
|
455 | repo_name = get_repo_slug(request) | |
456 | try: |
|
456 | try: | |
457 | user_perms = set([self.user_perms['repositories'][repo_name]]) |
|
457 | user_perms = set([self.user_perms['repositories'][repo_name]]) | |
458 | except KeyError: |
|
458 | except KeyError: | |
459 | return False |
|
459 | return False | |
460 | if self.required_perms.issubset(user_perms): |
|
460 | if self.required_perms.issubset(user_perms): | |
461 | return True |
|
461 | return True | |
462 | return False |
|
462 | return False | |
463 |
|
463 | |||
464 |
|
464 | |||
465 | class HasRepoPermissionAnyDecorator(PermsDecorator): |
|
465 | class HasRepoPermissionAnyDecorator(PermsDecorator): | |
466 | """Checks for access permission for any of given predicates for specific |
|
466 | """Checks for access permission for any of given predicates for specific | |
467 | repository. In order to fulfill the request any of predicates must be meet |
|
467 | repository. In order to fulfill the request any of predicates must be meet | |
468 | """ |
|
468 | """ | |
469 |
|
469 | |||
470 | def check_permissions(self): |
|
470 | def check_permissions(self): | |
471 | repo_name = get_repo_slug(request) |
|
471 | repo_name = get_repo_slug(request) | |
472 |
|
472 | |||
473 | try: |
|
473 | try: | |
474 | user_perms = set([self.user_perms['repositories'][repo_name]]) |
|
474 | user_perms = set([self.user_perms['repositories'][repo_name]]) | |
475 | except KeyError: |
|
475 | except KeyError: | |
476 | return False |
|
476 | return False | |
477 | if self.required_perms.intersection(user_perms): |
|
477 | if self.required_perms.intersection(user_perms): | |
478 | return True |
|
478 | return True | |
479 | return False |
|
479 | return False | |
480 |
|
480 | |||
481 |
|
481 | |||
482 | #============================================================================== |
|
482 | #============================================================================== | |
483 | # CHECK FUNCTIONS |
|
483 | # CHECK FUNCTIONS | |
484 | #============================================================================== |
|
484 | #============================================================================== | |
485 | class PermsFunction(object): |
|
485 | class PermsFunction(object): | |
486 | """Base function for other check functions""" |
|
486 | """Base function for other check functions""" | |
487 |
|
487 | |||
488 | def __init__(self, *perms): |
|
488 | def __init__(self, *perms): | |
489 | available_perms = config['available_permissions'] |
|
489 | available_perms = config['available_permissions'] | |
490 |
|
490 | |||
491 | for perm in perms: |
|
491 | for perm in perms: | |
492 | if perm not in available_perms: |
|
492 | if perm not in available_perms: | |
493 | raise Exception("'%s' permission in not defined" % perm) |
|
493 | raise Exception("'%s' permission in not defined" % perm) | |
494 | self.required_perms = set(perms) |
|
494 | self.required_perms = set(perms) | |
495 | self.user_perms = None |
|
495 | self.user_perms = None | |
496 | self.granted_for = '' |
|
496 | self.granted_for = '' | |
497 | self.repo_name = None |
|
497 | self.repo_name = None | |
498 |
|
498 | |||
499 | def __call__(self, check_Location=''): |
|
499 | def __call__(self, check_Location=''): | |
500 | user = session.get('rhodecode_user', False) |
|
500 | user = session.get('rhodecode_user', False) | |
501 | if not user: |
|
501 | if not user: | |
502 | return False |
|
502 | return False | |
503 | self.user_perms = user.permissions |
|
503 | self.user_perms = user.permissions | |
504 | self.granted_for = user |
|
504 | self.granted_for = user | |
505 | log.debug('checking %s %s %s', self.__class__.__name__, |
|
505 | log.debug('checking %s %s %s', self.__class__.__name__, | |
506 | self.required_perms, user) |
|
506 | self.required_perms, user) | |
507 |
|
507 | |||
508 | if self.check_permissions(): |
|
508 | if self.check_permissions(): | |
509 | log.debug('Permission granted %s @ %s', self.granted_for, |
|
509 | log.debug('Permission granted %s @ %s', self.granted_for, | |
510 | check_Location or 'unspecified location') |
|
510 | check_Location or 'unspecified location') | |
511 | return True |
|
511 | return True | |
512 |
|
512 | |||
513 | else: |
|
513 | else: | |
514 | log.warning('Permission denied for %s @ %s', self.granted_for, |
|
514 | log.warning('Permission denied for %s @ %s', self.granted_for, | |
515 | check_Location or 'unspecified location') |
|
515 | check_Location or 'unspecified location') | |
516 | return False |
|
516 | return False | |
517 |
|
517 | |||
518 | def check_permissions(self): |
|
518 | def check_permissions(self): | |
519 | """Dummy function for overriding""" |
|
519 | """Dummy function for overriding""" | |
520 | raise Exception('You have to write this function in child class') |
|
520 | raise Exception('You have to write this function in child class') | |
521 |
|
521 | |||
522 |
|
522 | |||
523 | class HasPermissionAll(PermsFunction): |
|
523 | class HasPermissionAll(PermsFunction): | |
524 | def check_permissions(self): |
|
524 | def check_permissions(self): | |
525 | if self.required_perms.issubset(self.user_perms.get('global')): |
|
525 | if self.required_perms.issubset(self.user_perms.get('global')): | |
526 | return True |
|
526 | return True | |
527 | return False |
|
527 | return False | |
528 |
|
528 | |||
529 |
|
529 | |||
530 | class HasPermissionAny(PermsFunction): |
|
530 | class HasPermissionAny(PermsFunction): | |
531 | def check_permissions(self): |
|
531 | def check_permissions(self): | |
532 | if self.required_perms.intersection(self.user_perms.get('global')): |
|
532 | if self.required_perms.intersection(self.user_perms.get('global')): | |
533 | return True |
|
533 | return True | |
534 | return False |
|
534 | return False | |
535 |
|
535 | |||
536 |
|
536 | |||
537 | class HasRepoPermissionAll(PermsFunction): |
|
537 | class HasRepoPermissionAll(PermsFunction): | |
538 |
|
538 | |||
539 | def __call__(self, repo_name=None, check_Location=''): |
|
539 | def __call__(self, repo_name=None, check_Location=''): | |
540 | self.repo_name = repo_name |
|
540 | self.repo_name = repo_name | |
541 | return super(HasRepoPermissionAll, self).__call__(check_Location) |
|
541 | return super(HasRepoPermissionAll, self).__call__(check_Location) | |
542 |
|
542 | |||
543 | def check_permissions(self): |
|
543 | def check_permissions(self): | |
544 | if not self.repo_name: |
|
544 | if not self.repo_name: | |
545 | self.repo_name = get_repo_slug(request) |
|
545 | self.repo_name = get_repo_slug(request) | |
546 |
|
546 | |||
547 | try: |
|
547 | try: | |
548 | self.user_perms = set([self.user_perms['reposit' |
|
548 | self.user_perms = set([self.user_perms['reposit' | |
549 | 'ories'][self.repo_name]]) |
|
549 | 'ories'][self.repo_name]]) | |
550 | except KeyError: |
|
550 | except KeyError: | |
551 | return False |
|
551 | return False | |
552 | self.granted_for = self.repo_name |
|
552 | self.granted_for = self.repo_name | |
553 | if self.required_perms.issubset(self.user_perms): |
|
553 | if self.required_perms.issubset(self.user_perms): | |
554 | return True |
|
554 | return True | |
555 | return False |
|
555 | return False | |
556 |
|
556 | |||
557 |
|
557 | |||
558 | class HasRepoPermissionAny(PermsFunction): |
|
558 | class HasRepoPermissionAny(PermsFunction): | |
559 |
|
559 | |||
560 | def __call__(self, repo_name=None, check_Location=''): |
|
560 | def __call__(self, repo_name=None, check_Location=''): | |
561 | self.repo_name = repo_name |
|
561 | self.repo_name = repo_name | |
562 | return super(HasRepoPermissionAny, self).__call__(check_Location) |
|
562 | return super(HasRepoPermissionAny, self).__call__(check_Location) | |
563 |
|
563 | |||
564 | def check_permissions(self): |
|
564 | def check_permissions(self): | |
565 | if not self.repo_name: |
|
565 | if not self.repo_name: | |
566 | self.repo_name = get_repo_slug(request) |
|
566 | self.repo_name = get_repo_slug(request) | |
567 |
|
567 | |||
568 | try: |
|
568 | try: | |
569 | self.user_perms = set([self.user_perms['reposi' |
|
569 | self.user_perms = set([self.user_perms['reposi' | |
570 | 'tories'][self.repo_name]]) |
|
570 | 'tories'][self.repo_name]]) | |
571 | except KeyError: |
|
571 | except KeyError: | |
572 | return False |
|
572 | return False | |
573 | self.granted_for = self.repo_name |
|
573 | self.granted_for = self.repo_name | |
574 | if self.required_perms.intersection(self.user_perms): |
|
574 | if self.required_perms.intersection(self.user_perms): | |
575 | return True |
|
575 | return True | |
576 | return False |
|
576 | return False | |
577 |
|
577 | |||
578 |
|
578 | |||
579 | #============================================================================== |
|
579 | #============================================================================== | |
580 | # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH |
|
580 | # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH | |
581 | #============================================================================== |
|
581 | #============================================================================== | |
582 | class HasPermissionAnyMiddleware(object): |
|
582 | class HasPermissionAnyMiddleware(object): | |
583 | def __init__(self, *perms): |
|
583 | def __init__(self, *perms): | |
584 | self.required_perms = set(perms) |
|
584 | self.required_perms = set(perms) | |
585 |
|
585 | |||
586 | def __call__(self, user, repo_name): |
|
586 | def __call__(self, user, repo_name): | |
587 | usr = AuthUser(user.user_id) |
|
587 | usr = AuthUser(user.user_id) | |
588 | try: |
|
588 | try: | |
589 | self.user_perms = set([usr.permissions['repositories'][repo_name]]) |
|
589 | self.user_perms = set([usr.permissions['repositories'][repo_name]]) | |
590 | except: |
|
590 | except: | |
591 | self.user_perms = set() |
|
591 | self.user_perms = set() | |
592 | self.granted_for = '' |
|
592 | self.granted_for = '' | |
593 | self.username = user.username |
|
593 | self.username = user.username | |
594 | self.repo_name = repo_name |
|
594 | self.repo_name = repo_name | |
595 | return self.check_permissions() |
|
595 | return self.check_permissions() | |
596 |
|
596 | |||
597 | def check_permissions(self): |
|
597 | def check_permissions(self): | |
598 | log.debug('checking mercurial protocol ' |
|
598 | log.debug('checking mercurial protocol ' | |
599 | 'permissions %s for user:%s repository:%s', self.user_perms, |
|
599 | 'permissions %s for user:%s repository:%s', self.user_perms, | |
600 | self.username, self.repo_name) |
|
600 | self.username, self.repo_name) | |
601 | if self.required_perms.intersection(self.user_perms): |
|
601 | if self.required_perms.intersection(self.user_perms): | |
602 | log.debug('permission granted') |
|
602 | log.debug('permission granted') | |
603 | return True |
|
603 | return True | |
604 | log.debug('permission denied') |
|
604 | log.debug('permission denied') | |
605 | return False |
|
605 | return False |
@@ -1,104 +1,101 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | # -*- coding: utf-8 -*- | |
2 | # encoding: utf-8 |
|
2 | """ | |
3 | # mercurial repository backup manager |
|
3 | rhodecode.lib.backup_manager | |
4 | # Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
|
5 | ||
|
6 | Mercurial repositories backup manager, it allows to backups all | |||
|
7 | repositories and send it to backup server using RSA key via ssh. | |||
|
8 | ||||
|
9 | :created_on: Feb 28, 2010 | |||
|
10 | :copyright: (c) 2010 by marcink. | |||
|
11 | :license: LICENSE_NAME, see LICENSE_FILE for more details. | |||
|
12 | """ | |||
6 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
7 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
8 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
9 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
10 | # |
|
17 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
15 | # |
|
22 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 |
|
25 | |||
19 | """ |
|
26 | import os | |
20 | Created on Feb 28, 2010 |
|
27 | import sys | |
21 | Mercurial repositories backup manager |
|
|||
22 | @author: marcink |
|
|||
23 | """ |
|
|||
24 |
|
||||
25 |
|
28 | |||
26 | import logging |
|
29 | import logging | |
27 | import tarfile |
|
30 | import tarfile | |
28 | import os |
|
|||
29 | import datetime |
|
31 | import datetime | |
30 | import sys |
|
|||
31 | import subprocess |
|
32 | import subprocess | |
|
33 | ||||
32 | logging.basicConfig(level=logging.DEBUG, |
|
34 | logging.basicConfig(level=logging.DEBUG, | |
33 | format="%(asctime)s %(levelname)-5.5s %(message)s") |
|
35 | format="%(asctime)s %(levelname)-5.5s %(message)s") | |
34 |
|
36 | |||
|
37 | ||||
35 | class BackupManager(object): |
|
38 | class BackupManager(object): | |
36 | def __init__(self, repos_location, rsa_key, backup_server): |
|
39 | def __init__(self, repos_location, rsa_key, backup_server): | |
37 | today = datetime.datetime.now().weekday() + 1 |
|
40 | today = datetime.datetime.now().weekday() + 1 | |
38 | self.backup_file_name = "mercurial_repos.%s.tar.gz" % today |
|
41 | self.backup_file_name = "mercurial_repos.%s.tar.gz" % today | |
39 |
|
42 | |||
40 | self.id_rsa_path = self.get_id_rsa(rsa_key) |
|
43 | self.id_rsa_path = self.get_id_rsa(rsa_key) | |
41 | self.repos_path = self.get_repos_path(repos_location) |
|
44 | self.repos_path = self.get_repos_path(repos_location) | |
42 | self.backup_server = backup_server |
|
45 | self.backup_server = backup_server | |
43 |
|
46 | |||
44 | self.backup_file_path = '/tmp' |
|
47 | self.backup_file_path = '/tmp' | |
45 |
|
48 | |||
46 | logging.info('starting backup for %s', self.repos_path) |
|
49 | logging.info('starting backup for %s', self.repos_path) | |
47 | logging.info('backup target %s', self.backup_file_path) |
|
50 | logging.info('backup target %s', self.backup_file_path) | |
48 |
|
51 | |||
49 |
|
||||
50 | def get_id_rsa(self, rsa_key): |
|
52 | def get_id_rsa(self, rsa_key): | |
51 | if not os.path.isfile(rsa_key): |
|
53 | if not os.path.isfile(rsa_key): | |
52 | logging.error('Could not load id_rsa key file in %s', rsa_key) |
|
54 | logging.error('Could not load id_rsa key file in %s', rsa_key) | |
53 | sys.exit() |
|
55 | sys.exit() | |
54 | return rsa_key |
|
56 | return rsa_key | |
55 |
|
57 | |||
56 | def get_repos_path(self, path): |
|
58 | def get_repos_path(self, path): | |
57 | if not os.path.isdir(path): |
|
59 | if not os.path.isdir(path): | |
58 | logging.error('Wrong location for repositories in %s', path) |
|
60 | logging.error('Wrong location for repositories in %s', path) | |
59 | sys.exit() |
|
61 | sys.exit() | |
60 | return path |
|
62 | return path | |
61 |
|
63 | |||
62 | def backup_repos(self): |
|
64 | def backup_repos(self): | |
63 | bckp_file = os.path.join(self.backup_file_path, self.backup_file_name) |
|
65 | bckp_file = os.path.join(self.backup_file_path, self.backup_file_name) | |
64 | tar = tarfile.open(bckp_file, "w:gz") |
|
66 | tar = tarfile.open(bckp_file, "w:gz") | |
65 |
|
67 | |||
66 | for dir_name in os.listdir(self.repos_path): |
|
68 | for dir_name in os.listdir(self.repos_path): | |
67 | logging.info('backing up %s', dir_name) |
|
69 | logging.info('backing up %s', dir_name) | |
68 | tar.add(os.path.join(self.repos_path, dir_name), dir_name) |
|
70 | tar.add(os.path.join(self.repos_path, dir_name), dir_name) | |
69 | tar.close() |
|
71 | tar.close() | |
70 | logging.info('finished backup of mercurial repositories') |
|
72 | logging.info('finished backup of mercurial repositories') | |
71 |
|
73 | |||
72 |
|
||||
73 |
|
||||
74 | def transfer_files(self): |
|
74 | def transfer_files(self): | |
75 | params = { |
|
75 | params = { | |
76 | 'id_rsa_key': self.id_rsa_path, |
|
76 | 'id_rsa_key': self.id_rsa_path, | |
77 | 'backup_file':os.path.join(self.backup_file_path, |
|
77 | 'backup_file': os.path.join(self.backup_file_path, | |
78 | self.backup_file_name), |
|
78 | self.backup_file_name), | |
79 | 'backup_server':self.backup_server |
|
79 | 'backup_server': self.backup_server | |
80 | } |
|
80 | } | |
81 | cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params, |
|
81 | cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params, | |
82 | '%(backup_file)s' % params, |
|
82 | '%(backup_file)s' % params, | |
83 | '%(backup_server)s' % params] |
|
83 | '%(backup_server)s' % params] | |
84 |
|
84 | |||
85 | subprocess.call(cmd) |
|
85 | subprocess.call(cmd) | |
86 | logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4]) |
|
86 | logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4]) | |
87 |
|
87 | |||
88 |
|
||||
89 | def rm_file(self): |
|
88 | def rm_file(self): | |
90 | logging.info('Removing file %s', self.backup_file_name) |
|
89 | logging.info('Removing file %s', self.backup_file_name) | |
91 | os.remove(os.path.join(self.backup_file_path, self.backup_file_name)) |
|
90 | os.remove(os.path.join(self.backup_file_path, self.backup_file_name)) | |
92 |
|
91 | |||
93 |
|
||||
94 |
|
||||
95 | if __name__ == "__main__": |
|
92 | if __name__ == "__main__": | |
96 |
|
93 | |||
97 | repo_location = '/home/repo_path' |
|
94 | repo_location = '/home/repo_path' | |
98 | backup_server = 'root@192.168.1.100:/backups/mercurial' |
|
95 | backup_server = 'root@192.168.1.100:/backups/mercurial' | |
99 | rsa_key = '/home/id_rsa' |
|
96 | rsa_key = '/home/id_rsa' | |
100 |
|
97 | |||
101 | B_MANAGER = BackupManager(repo_location, rsa_key, backup_server) |
|
98 | B_MANAGER = BackupManager(repo_location, rsa_key, backup_server) | |
102 | B_MANAGER.backup_repos() |
|
99 | B_MANAGER.backup_repos() | |
103 | B_MANAGER.transfer_files() |
|
100 | B_MANAGER.transfer_files() | |
104 | B_MANAGER.rm_file() |
|
101 | B_MANAGER.rm_file() |
@@ -1,79 +1,81 b'' | |||||
1 | """The base Controller API |
|
1 | """The base Controller API | |
2 |
|
2 | |||
3 | Provides the BaseController class for subclassing. |
|
3 | Provides the BaseController class for subclassing. | |
4 | """ |
|
4 | """ | |
5 | import copy |
|
5 | import copy | |
6 |
|
6 | |||
7 | from pylons import config, tmpl_context as c, request, session |
|
7 | from pylons import config, tmpl_context as c, request, session | |
8 | from pylons.controllers import WSGIController |
|
8 | from pylons.controllers import WSGIController | |
9 | from pylons.templating import render_mako as render |
|
9 | from pylons.templating import render_mako as render | |
10 |
|
10 | |||
11 | from rhodecode import __version__ |
|
11 | from rhodecode import __version__ | |
12 | from rhodecode.lib.auth import AuthUser |
|
12 | from rhodecode.lib.auth import AuthUser | |
13 | from rhodecode.lib.utils import get_repo_slug |
|
13 | from rhodecode.lib.utils import get_repo_slug | |
14 | from rhodecode.model import meta |
|
14 | from rhodecode.model import meta | |
15 | from rhodecode.model.scm import ScmModel |
|
15 | from rhodecode.model.scm import ScmModel | |
16 | from rhodecode import BACKENDS |
|
16 | from rhodecode import BACKENDS | |
17 |
|
17 | |||
|
18 | ||||
18 | class BaseController(WSGIController): |
|
19 | class BaseController(WSGIController): | |
19 |
|
20 | |||
20 | def __before__(self): |
|
21 | def __before__(self): | |
21 | c.rhodecode_version = __version__ |
|
22 | c.rhodecode_version = __version__ | |
22 | c.rhodecode_name = config.get('rhodecode_title') |
|
23 | c.rhodecode_name = config.get('rhodecode_title') | |
23 | c.ga_code = config.get('rhodecode_ga_code') |
|
24 | c.ga_code = config.get('rhodecode_ga_code') | |
24 | c.repo_name = get_repo_slug(request) |
|
25 | c.repo_name = get_repo_slug(request) | |
25 | c.backends = BACKENDS.keys() |
|
26 | c.backends = BACKENDS.keys() | |
26 | self.cut_off_limit = int(config.get('cut_off_limit')) |
|
27 | self.cut_off_limit = int(config.get('cut_off_limit')) | |
27 |
|
28 | |||
28 | self.sa = meta.Session() |
|
29 | self.sa = meta.Session() | |
29 | self.scm_model = ScmModel(self.sa) |
|
30 | self.scm_model = ScmModel(self.sa) | |
30 | c.cached_repo_list = self.scm_model.get_repos() |
|
31 | c.cached_repo_list = self.scm_model.get_repos() | |
31 | #c.unread_journal = scm_model.get_unread_journal() |
|
32 | #c.unread_journal = scm_model.get_unread_journal() | |
32 |
|
33 | |||
33 | def __call__(self, environ, start_response): |
|
34 | def __call__(self, environ, start_response): | |
34 | """Invoke the Controller""" |
|
35 | """Invoke the Controller""" | |
35 | # WSGIController.__call__ dispatches to the Controller method |
|
36 | # WSGIController.__call__ dispatches to the Controller method | |
36 | # the request is routed to. This routing information is |
|
37 | # the request is routed to. This routing information is | |
37 | # available in environ['pylons.routes_dict'] |
|
38 | # available in environ['pylons.routes_dict'] | |
38 | try: |
|
39 | try: | |
39 |
#putting this here makes sure that we update permissions e |
|
40 | # putting this here makes sure that we update permissions each time | |
40 | api_key = request.GET.get('api_key') |
|
41 | api_key = request.GET.get('api_key') | |
41 | user_id = getattr(session.get('rhodecode_user'), 'user_id', None) |
|
42 | user_id = getattr(session.get('rhodecode_user'), 'user_id', None) | |
42 | self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key) |
|
43 | self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key) | |
43 | self.rhodecode_user.set_authenticated( |
|
44 | self.rhodecode_user.set_authenticated( | |
44 | getattr(session.get('rhodecode_user'), |
|
45 | getattr(session.get('rhodecode_user'), | |
45 | 'is_authenticated', False)) |
|
46 | 'is_authenticated', False)) | |
46 | session['rhodecode_user'] = self.rhodecode_user |
|
47 | session['rhodecode_user'] = self.rhodecode_user | |
47 | session.save() |
|
48 | session.save() | |
48 | return WSGIController.__call__(self, environ, start_response) |
|
49 | return WSGIController.__call__(self, environ, start_response) | |
49 | finally: |
|
50 | finally: | |
50 | meta.Session.remove() |
|
51 | meta.Session.remove() | |
51 |
|
52 | |||
52 |
|
53 | |||
53 | class BaseRepoController(BaseController): |
|
54 | class BaseRepoController(BaseController): | |
54 | """ |
|
55 | """ | |
55 | Base class for controllers responsible for loading all needed data |
|
56 | Base class for controllers responsible for loading all needed data | |
56 | for those controllers, loaded items are |
|
57 | for those controllers, loaded items are | |
57 |
|
58 | |||
58 | c.rhodecode_repo: instance of scm repository (taken from cache) |
|
59 | c.rhodecode_repo: instance of scm repository (taken from cache) | |
59 |
|
60 | |||
60 | """ |
|
61 | """ | |
61 |
|
62 | |||
62 | def __before__(self): |
|
63 | def __before__(self): | |
63 | super(BaseRepoController, self).__before__() |
|
64 | super(BaseRepoController, self).__before__() | |
64 | if c.repo_name: |
|
65 | if c.repo_name: | |
65 |
|
66 | |||
66 | r, dbrepo = self.scm_model.get(c.repo_name, retval='repo') |
|
67 | r, dbrepo = self.scm_model.get(c.repo_name, retval='repo') | |
67 |
|
68 | |||
68 | if r is not None: |
|
69 | if r is not None: | |
69 |
c.repository_followers = |
|
70 | c.repository_followers = \ | |
|
71 | self.scm_model.get_followers(c.repo_name) | |||
70 | c.repository_forks = self.scm_model.get_forks(c.repo_name) |
|
72 | c.repository_forks = self.scm_model.get_forks(c.repo_name) | |
71 | else: |
|
73 | else: | |
72 | c.repository_followers = 0 |
|
74 | c.repository_followers = 0 | |
73 | c.repository_forks = 0 |
|
75 | c.repository_forks = 0 | |
74 |
|
76 | |||
75 | # Since RhodeCode uses heavy memory caching we make a deepcopy |
|
77 | # Since RhodeCode uses heavy memory caching we make a deepcopy | |
76 | # of object taken from cache. This way we lose reference to cached |
|
78 | # of object taken from cache. This way we lose reference to cached | |
77 |
# instance in memory and keep it relatively small even for |
|
79 | # instance in memory and keep it relatively small even for | |
78 | # very large number of changesets |
|
80 | # very large number of changesets | |
79 | c.rhodecode_repo = copy.copy(r) |
|
81 | c.rhodecode_repo = copy.copy(r) |
@@ -1,82 +1,85 b'' | |||||
1 |
|
1 | |||
2 | import logging |
|
2 | import logging | |
3 |
|
3 | |||
4 | BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38) |
|
4 | BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38) | |
5 |
|
5 | |||
6 | # Sequences |
|
6 | # Sequences | |
7 | RESET_SEQ = "\033[0m" |
|
7 | RESET_SEQ = "\033[0m" | |
8 | COLOR_SEQ = "\033[1;%dm" |
|
8 | COLOR_SEQ = "\033[1;%dm" | |
9 | BOLD_SEQ = "\033[1m" |
|
9 | BOLD_SEQ = "\033[1m" | |
10 |
|
10 | |||
11 | COLORS = { |
|
11 | COLORS = { | |
12 |
'CRITICAL': MAGENTA, |
|
12 | 'CRITICAL': MAGENTA, | |
13 |
'ERROR': RED, |
|
13 | 'ERROR': RED, | |
14 |
'WARNING': CYAN, |
|
14 | 'WARNING': CYAN, | |
15 |
'INFO': GREEN, |
|
15 | 'INFO': GREEN, | |
16 |
'DEBUG': BLUE, |
|
16 | 'DEBUG': BLUE, | |
17 |
'SQL' |
|
17 | 'SQL': YELLOW | |
18 | } |
|
18 | } | |
19 |
|
19 | |||
|
20 | ||||
20 | def one_space_trim(s): |
|
21 | def one_space_trim(s): | |
21 | if s.find(" ") == -1: |
|
22 | if s.find(" ") == -1: | |
22 | return s |
|
23 | return s | |
23 | else: |
|
24 | else: | |
24 | s = s.replace(' ', ' ') |
|
25 | s = s.replace(' ', ' ') | |
25 | return one_space_trim(s) |
|
26 | return one_space_trim(s) | |
26 |
|
27 | |||
|
28 | ||||
27 | def format_sql(sql): |
|
29 | def format_sql(sql): | |
28 | sql = sql.replace('\n', '') |
|
30 | sql = sql.replace('\n', '') | |
29 | sql = one_space_trim(sql) |
|
31 | sql = one_space_trim(sql) | |
30 | sql = sql\ |
|
32 | sql = sql\ | |
31 | .replace(',', ',\n\t')\ |
|
33 | .replace(',', ',\n\t')\ | |
32 | .replace('SELECT', '\n\tSELECT \n\t')\ |
|
34 | .replace('SELECT', '\n\tSELECT \n\t')\ | |
33 | .replace('UPDATE', '\n\tUPDATE \n\t')\ |
|
35 | .replace('UPDATE', '\n\tUPDATE \n\t')\ | |
34 | .replace('DELETE', '\n\tDELETE \n\t')\ |
|
36 | .replace('DELETE', '\n\tDELETE \n\t')\ | |
35 | .replace('FROM', '\n\tFROM')\ |
|
37 | .replace('FROM', '\n\tFROM')\ | |
36 | .replace('ORDER BY', '\n\tORDER BY')\ |
|
38 | .replace('ORDER BY', '\n\tORDER BY')\ | |
37 | .replace('LIMIT', '\n\tLIMIT')\ |
|
39 | .replace('LIMIT', '\n\tLIMIT')\ | |
38 | .replace('WHERE', '\n\tWHERE')\ |
|
40 | .replace('WHERE', '\n\tWHERE')\ | |
39 | .replace('AND', '\n\tAND')\ |
|
41 | .replace('AND', '\n\tAND')\ | |
40 | .replace('LEFT', '\n\tLEFT')\ |
|
42 | .replace('LEFT', '\n\tLEFT')\ | |
41 | .replace('INNER', '\n\tINNER')\ |
|
43 | .replace('INNER', '\n\tINNER')\ | |
42 | .replace('INSERT', '\n\tINSERT')\ |
|
44 | .replace('INSERT', '\n\tINSERT')\ | |
43 | .replace('DELETE', '\n\tDELETE') |
|
45 | .replace('DELETE', '\n\tDELETE') | |
44 | return sql |
|
46 | return sql | |
45 |
|
47 | |||
|
48 | ||||
46 | class ColorFormatter(logging.Formatter): |
|
49 | class ColorFormatter(logging.Formatter): | |
47 |
|
50 | |||
48 | def __init__(self, *args, **kwargs): |
|
51 | def __init__(self, *args, **kwargs): | |
49 | # can't do super(...) here because Formatter is an old school class |
|
52 | # can't do super(...) here because Formatter is an old school class | |
50 | logging.Formatter.__init__(self, *args, **kwargs) |
|
53 | logging.Formatter.__init__(self, *args, **kwargs) | |
51 |
|
54 | |||
52 | def format(self, record): |
|
55 | def format(self, record): | |
53 | """ |
|
56 | """ | |
54 | Changes record's levelname to use with COLORS enum |
|
57 | Changes record's levelname to use with COLORS enum | |
55 | """ |
|
58 | """ | |
56 |
|
59 | |||
57 | levelname = record.levelname |
|
60 | levelname = record.levelname | |
58 | start = COLOR_SEQ % (COLORS[levelname]) |
|
61 | start = COLOR_SEQ % (COLORS[levelname]) | |
59 | def_record = logging.Formatter.format(self, record) |
|
62 | def_record = logging.Formatter.format(self, record) | |
60 | end = RESET_SEQ |
|
63 | end = RESET_SEQ | |
61 |
|
64 | |||
62 | colored_record = start + def_record + end |
|
65 | colored_record = start + def_record + end | |
63 | return colored_record |
|
66 | return colored_record | |
64 |
|
67 | |||
65 |
|
68 | |||
66 | class ColorFormatterSql(logging.Formatter): |
|
69 | class ColorFormatterSql(logging.Formatter): | |
67 |
|
70 | |||
68 | def __init__(self, *args, **kwargs): |
|
71 | def __init__(self, *args, **kwargs): | |
69 | # can't do super(...) here because Formatter is an old school class |
|
72 | # can't do super(...) here because Formatter is an old school class | |
70 | logging.Formatter.__init__(self, *args, **kwargs) |
|
73 | logging.Formatter.__init__(self, *args, **kwargs) | |
71 |
|
74 | |||
72 | def format(self, record): |
|
75 | def format(self, record): | |
73 | """ |
|
76 | """ | |
74 | Changes record's levelname to use with COLORS enum |
|
77 | Changes record's levelname to use with COLORS enum | |
75 | """ |
|
78 | """ | |
76 |
|
79 | |||
77 | start = COLOR_SEQ % (COLORS['SQL']) |
|
80 | start = COLOR_SEQ % (COLORS['SQL']) | |
78 | def_record = format_sql(logging.Formatter.format(self, record)) |
|
81 | def_record = format_sql(logging.Formatter.format(self, record)) | |
79 | end = RESET_SEQ |
|
82 | end = RESET_SEQ | |
80 |
|
83 | |||
81 | colored_record = start + def_record + end |
|
84 | colored_record = start + def_record + end | |
82 | return colored_record |
|
85 | return colored_record |
@@ -1,30 +1,47 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | # -*- coding: utf-8 -*- | |
2 | # encoding: utf-8 |
|
2 | """ | |
3 | # Custom Exceptions modules |
|
3 | rhodecode.lib.exceptions | |
4 | # Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 | # |
|
5 | ||
|
6 | Set of custom exceptions used in RhodeCode | |||
|
7 | ||||
|
8 | :created_on: Nov 17, 2010 | |||
|
9 | :copyright: (c) 2010 by marcink. | |||
|
10 | :license: LICENSE_NAME, see LICENSE_FILE for more details. | |||
|
11 | """ | |||
6 | # This program is free software: you can redistribute it and/or modify |
|
12 | # This program is free software: you can redistribute it and/or modify | |
7 | # it under the terms of the GNU General Public License as published by |
|
13 | # it under the terms of the GNU General Public License as published by | |
8 | # the Free Software Foundation, either version 3 of the License, or |
|
14 | # the Free Software Foundation, either version 3 of the License, or | |
9 | # (at your option) any later version. |
|
15 | # (at your option) any later version. | |
10 | # |
|
16 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
17 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
20 | # GNU General Public License for more details. | |
15 | # |
|
21 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
22 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
23 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | """ |
|
24 | ||
19 | Created on Nov 17, 2010 |
|
25 | ||
20 | Custom Exceptions modules |
|
26 | class LdapUsernameError(Exception): | |
21 | @author: marcink |
|
27 | pass | |
22 | """ |
|
28 | ||
|
29 | ||||
|
30 | class LdapPasswordError(Exception): | |||
|
31 | pass | |||
|
32 | ||||
23 |
|
33 | |||
24 |
class Ldap |
|
34 | class LdapConnectionError(Exception): | |
25 | class LdapPasswordError(Exception):pass |
|
35 | pass | |
26 | class LdapConnectionError(Exception):pass |
|
36 | ||
27 | class LdapImportError(Exception):pass |
|
37 | ||
|
38 | class LdapImportError(Exception): | |||
|
39 | pass | |||
28 |
|
40 | |||
29 | class DefaultUserException(Exception):pass |
|
41 | ||
30 |
class User |
|
42 | class DefaultUserException(Exception): | |
|
43 | pass | |||
|
44 | ||||
|
45 | ||||
|
46 | class UserOwnsReposException(Exception): | |||
|
47 | pass |
@@ -1,113 +1,116 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.lib.hooks |
|
3 | rhodecode.lib.hooks | |
4 | ~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Hooks runned by rhodecode |
|
6 | Hooks runned by rhodecode | |
7 |
|
7 | |||
8 | :created_on: Aug 6, 2010 |
|
8 | :created_on: Aug 6, 2010 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 | import os |
|
25 | import os | |
26 | import sys |
|
26 | import sys | |
27 | import getpass |
|
27 | import getpass | |
28 |
|
28 | |||
29 | from mercurial.cmdutil import revrange |
|
29 | from mercurial.cmdutil import revrange | |
30 | from mercurial.node import nullrev |
|
30 | from mercurial.node import nullrev | |
31 |
|
31 | |||
32 | from rhodecode.lib import helpers as h |
|
32 | from rhodecode.lib import helpers as h | |
33 | from rhodecode.lib.utils import action_logger |
|
33 | from rhodecode.lib.utils import action_logger | |
34 |
|
34 | |||
|
35 | ||||
35 | def repo_size(ui, repo, hooktype=None, **kwargs): |
|
36 | def repo_size(ui, repo, hooktype=None, **kwargs): | |
36 | """Presents size of repository after push |
|
37 | """Presents size of repository after push | |
37 |
|
38 | |||
38 | :param ui: |
|
39 | :param ui: | |
39 | :param repo: |
|
40 | :param repo: | |
40 | :param hooktype: |
|
41 | :param hooktype: | |
41 | """ |
|
42 | """ | |
42 |
|
43 | |||
43 | if hooktype != 'changegroup': |
|
44 | if hooktype != 'changegroup': | |
44 | return False |
|
45 | return False | |
45 | size_hg, size_root = 0, 0 |
|
46 | size_hg, size_root = 0, 0 | |
46 | for path, dirs, files in os.walk(repo.root): |
|
47 | for path, dirs, files in os.walk(repo.root): | |
47 | if path.find('.hg') != -1: |
|
48 | if path.find('.hg') != -1: | |
48 | for f in files: |
|
49 | for f in files: | |
49 | try: |
|
50 | try: | |
50 | size_hg += os.path.getsize(os.path.join(path, f)) |
|
51 | size_hg += os.path.getsize(os.path.join(path, f)) | |
51 | except OSError: |
|
52 | except OSError: | |
52 | pass |
|
53 | pass | |
53 | else: |
|
54 | else: | |
54 | for f in files: |
|
55 | for f in files: | |
55 | try: |
|
56 | try: | |
56 | size_root += os.path.getsize(os.path.join(path, f)) |
|
57 | size_root += os.path.getsize(os.path.join(path, f)) | |
57 | except OSError: |
|
58 | except OSError: | |
58 | pass |
|
59 | pass | |
59 |
|
60 | |||
60 | size_hg_f = h.format_byte_size(size_hg) |
|
61 | size_hg_f = h.format_byte_size(size_hg) | |
61 | size_root_f = h.format_byte_size(size_root) |
|
62 | size_root_f = h.format_byte_size(size_root) | |
62 | size_total_f = h.format_byte_size(size_root + size_hg) |
|
63 | size_total_f = h.format_byte_size(size_root + size_hg) | |
63 | sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \ |
|
64 | sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \ | |
64 | % (size_hg_f, size_root_f, size_total_f)) |
|
65 | % (size_hg_f, size_root_f, size_total_f)) | |
65 |
|
66 | |||
|
67 | ||||
66 | def log_pull_action(ui, repo, **kwargs): |
|
68 | def log_pull_action(ui, repo, **kwargs): | |
67 | """Logs user last pull action |
|
69 | """Logs user last pull action | |
68 |
|
70 | |||
69 | :param ui: |
|
71 | :param ui: | |
70 | :param repo: |
|
72 | :param repo: | |
71 | """ |
|
73 | """ | |
72 |
|
74 | |||
73 | extra_params = dict(repo.ui.configitems('rhodecode_extras')) |
|
75 | extra_params = dict(repo.ui.configitems('rhodecode_extras')) | |
74 | username = extra_params['username'] |
|
76 | username = extra_params['username'] | |
75 | repository = extra_params['repository'] |
|
77 | repository = extra_params['repository'] | |
76 | action = 'pull' |
|
78 | action = 'pull' | |
77 |
|
79 | |||
78 | action_logger(username, action, repository, extra_params['ip']) |
|
80 | action_logger(username, action, repository, extra_params['ip']) | |
79 |
|
81 | |||
80 | return 0 |
|
82 | return 0 | |
81 |
|
83 | |||
|
84 | ||||
82 | def log_push_action(ui, repo, **kwargs): |
|
85 | def log_push_action(ui, repo, **kwargs): | |
83 | """Maps user last push action to new changeset id, from mercurial |
|
86 | """Maps user last push action to new changeset id, from mercurial | |
84 |
|
87 | |||
85 | :param ui: |
|
88 | :param ui: | |
86 | :param repo: |
|
89 | :param repo: | |
87 | """ |
|
90 | """ | |
88 |
|
91 | |||
89 | extra_params = dict(repo.ui.configitems('rhodecode_extras')) |
|
92 | extra_params = dict(repo.ui.configitems('rhodecode_extras')) | |
90 | username = extra_params['username'] |
|
93 | username = extra_params['username'] | |
91 | repository = extra_params['repository'] |
|
94 | repository = extra_params['repository'] | |
92 | action = extra_params['action'] + ':%s' |
|
95 | action = extra_params['action'] + ':%s' | |
93 | node = kwargs['node'] |
|
96 | node = kwargs['node'] | |
94 |
|
97 | |||
95 | def get_revs(repo, rev_opt): |
|
98 | def get_revs(repo, rev_opt): | |
96 | if rev_opt: |
|
99 | if rev_opt: | |
97 | revs = revrange(repo, rev_opt) |
|
100 | revs = revrange(repo, rev_opt) | |
98 |
|
101 | |||
99 | if len(revs) == 0: |
|
102 | if len(revs) == 0: | |
100 | return (nullrev, nullrev) |
|
103 | return (nullrev, nullrev) | |
101 | return (max(revs), min(revs)) |
|
104 | return (max(revs), min(revs)) | |
102 | else: |
|
105 | else: | |
103 | return (len(repo) - 1, 0) |
|
106 | return (len(repo) - 1, 0) | |
104 |
|
107 | |||
105 | stop, start = get_revs(repo, [node + ':']) |
|
108 | stop, start = get_revs(repo, [node + ':']) | |
106 |
|
109 | |||
107 | revs = (str(repo[r]) for r in xrange(start, stop + 1)) |
|
110 | revs = (str(repo[r]) for r in xrange(start, stop + 1)) | |
108 |
|
111 | |||
109 | action = action % ','.join(revs) |
|
112 | action = action % ','.join(revs) | |
110 |
|
113 | |||
111 | action_logger(username, action, repository, extra_params['ip']) |
|
114 | action_logger(username, action, repository, extra_params['ip']) | |
112 |
|
115 | |||
113 | return 0 |
|
116 | return 0 |
@@ -1,133 +1,142 b'' | |||||
1 |
import os |
|
1 | import os | |
2 | import sys |
|
2 | import sys | |
|
3 | import time | |||
|
4 | import errno | |||
|
5 | ||||
3 | from warnings import warn |
|
6 | from warnings import warn | |
4 | from multiprocessing.util import Finalize |
|
7 | from multiprocessing.util import Finalize | |
5 | import errno |
|
|||
6 |
|
8 | |||
7 | from rhodecode import __platform__, PLATFORM_WIN |
|
9 | from rhodecode import __platform__, PLATFORM_WIN | |
8 |
|
10 | |||
9 | if __platform__ in PLATFORM_WIN: |
|
11 | if __platform__ in PLATFORM_WIN: | |
10 | import ctypes |
|
12 | import ctypes | |
|
13 | ||||
11 | def kill(pid): |
|
14 | def kill(pid): | |
12 | """kill function for Win32""" |
|
15 | """kill function for Win32""" | |
13 | kernel32 = ctypes.windll.kernel32 |
|
16 | kernel32 = ctypes.windll.kernel32 | |
14 | handle = kernel32.OpenProcess(1, 0, pid) |
|
17 | handle = kernel32.OpenProcess(1, 0, pid) | |
15 | return (0 != kernel32.TerminateProcess(handle, 0)) |
|
18 | return (0 != kernel32.TerminateProcess(handle, 0)) | |
16 |
|
19 | |||
17 | else: |
|
20 | else: | |
18 | kill = os.kill |
|
21 | kill = os.kill | |
19 |
|
22 | |||
20 | class LockHeld(Exception):pass |
|
23 | ||
|
24 | class LockHeld(Exception): | |||
|
25 | pass | |||
21 |
|
26 | |||
22 |
|
27 | |||
23 | class DaemonLock(object): |
|
28 | class DaemonLock(object): | |
24 | """daemon locking |
|
29 | """daemon locking | |
25 | USAGE: |
|
30 | USAGE: | |
26 | try: |
|
31 | try: | |
27 | l = DaemonLock(desc='test lock') |
|
32 | l = DaemonLock(desc='test lock') | |
28 | main() |
|
33 | main() | |
29 | l.release() |
|
34 | l.release() | |
30 | except LockHeld: |
|
35 | except LockHeld: | |
31 | sys.exit(1) |
|
36 | sys.exit(1) | |
32 | """ |
|
37 | """ | |
33 |
|
38 | |||
34 | def __init__(self, file=None, callbackfn=None, |
|
39 | def __init__(self, file=None, callbackfn=None, | |
35 | desc='daemon lock', debug=False): |
|
40 | desc='daemon lock', debug=False): | |
36 |
|
41 | |||
37 |
self.pidfile = file if file else os.path.join( |
|
42 | self.pidfile = file if file else os.path.join( | |
38 |
|
|
43 | os.path.dirname(__file__), | |
|
44 | 'running.lock') | |||
39 | self.callbackfn = callbackfn |
|
45 | self.callbackfn = callbackfn | |
40 | self.desc = desc |
|
46 | self.desc = desc | |
41 | self.debug = debug |
|
47 | self.debug = debug | |
42 | self.held = False |
|
48 | self.held = False | |
43 | #run the lock automatically ! |
|
49 | #run the lock automatically ! | |
44 | self.lock() |
|
50 | self.lock() | |
45 | self._finalize = Finalize(self, DaemonLock._on_finalize, |
|
51 | self._finalize = Finalize(self, DaemonLock._on_finalize, | |
46 | args=(self, debug), exitpriority=10) |
|
52 | args=(self, debug), exitpriority=10) | |
47 |
|
53 | |||
48 | @staticmethod |
|
54 | @staticmethod | |
49 | def _on_finalize(lock, debug): |
|
55 | def _on_finalize(lock, debug): | |
50 | if lock.held: |
|
56 | if lock.held: | |
51 | if debug: |
|
57 | if debug: | |
52 | print 'leck held finilazing and running lock.release()' |
|
58 | print 'leck held finilazing and running lock.release()' | |
53 | lock.release() |
|
59 | lock.release() | |
54 |
|
60 | |||
55 |
|
||||
56 | def lock(self): |
|
61 | def lock(self): | |
57 | """locking function, if lock is present it will raise LockHeld exception |
|
62 | """ | |
|
63 | locking function, if lock is present it | |||
|
64 | will raise LockHeld exception | |||
58 | """ |
|
65 | """ | |
59 | lockname = '%s' % (os.getpid()) |
|
66 | lockname = '%s' % (os.getpid()) | |
60 | if self.debug: |
|
67 | if self.debug: | |
61 | print 'running lock' |
|
68 | print 'running lock' | |
62 | self.trylock() |
|
69 | self.trylock() | |
63 | self.makelock(lockname, self.pidfile) |
|
70 | self.makelock(lockname, self.pidfile) | |
64 | return True |
|
71 | return True | |
65 |
|
72 | |||
66 | def trylock(self): |
|
73 | def trylock(self): | |
67 | running_pid = False |
|
74 | running_pid = False | |
68 | if self.debug: |
|
75 | if self.debug: | |
69 | print 'checking for already running process' |
|
76 | print 'checking for already running process' | |
70 | try: |
|
77 | try: | |
71 | pidfile = open(self.pidfile, "r") |
|
78 | pidfile = open(self.pidfile, "r") | |
72 | pidfile.seek(0) |
|
79 | pidfile.seek(0) | |
73 | running_pid = int(pidfile.readline()) |
|
80 | running_pid = int(pidfile.readline()) | |
74 |
|
81 | |||
75 | pidfile.close() |
|
82 | pidfile.close() | |
76 |
|
83 | |||
77 | if self.debug: |
|
84 | if self.debug: | |
78 |
print 'lock file present running_pid: %s, |
|
85 | print ('lock file present running_pid: %s, ' | |
79 | % running_pid |
|
86 | 'checking for execution') % running_pid | |
80 | # Now we check the PID from lock file matches to the current |
|
87 | # Now we check the PID from lock file matches to the current | |
81 | # process PID |
|
88 | # process PID | |
82 | if running_pid: |
|
89 | if running_pid: | |
83 | try: |
|
90 | try: | |
84 | kill(running_pid, 0) |
|
91 | kill(running_pid, 0) | |
85 | except OSError, exc: |
|
92 | except OSError, exc: | |
86 | if exc.errno in (errno.ESRCH, errno.EPERM): |
|
93 | if exc.errno in (errno.ESRCH, errno.EPERM): | |
87 |
print "Lock File is there but |
|
94 | print ("Lock File is there but" | |
|
95 | " the program is not running") | |||
88 | print "Removing lock file for the: %s" % running_pid |
|
96 | print "Removing lock file for the: %s" % running_pid | |
89 | self.release() |
|
97 | self.release() | |
90 | else: |
|
98 | else: | |
91 | raise |
|
99 | raise | |
92 | else: |
|
100 | else: | |
93 | print "You already have an instance of the program running" |
|
101 | print "You already have an instance of the program running" | |
94 | print "It is running as process %s" % running_pid |
|
102 | print "It is running as process %s" % running_pid | |
95 | raise LockHeld() |
|
103 | raise LockHeld() | |
96 |
|
104 | |||
97 | except IOError, e: |
|
105 | except IOError, e: | |
98 | if e.errno != 2: |
|
106 | if e.errno != 2: | |
99 | raise |
|
107 | raise | |
100 |
|
108 | |||
101 | def release(self): |
|
109 | def release(self): | |
102 | """releases the pid by removing the pidfile |
|
110 | """releases the pid by removing the pidfile | |
103 | """ |
|
111 | """ | |
104 | if self.debug: |
|
112 | if self.debug: | |
105 | print 'trying to release the pidlock' |
|
113 | print 'trying to release the pidlock' | |
106 |
|
114 | |||
107 | if self.callbackfn: |
|
115 | if self.callbackfn: | |
108 | #execute callback function on release |
|
116 | #execute callback function on release | |
109 | if self.debug: |
|
117 | if self.debug: | |
110 | print 'executing callback function %s' % self.callbackfn |
|
118 | print 'executing callback function %s' % self.callbackfn | |
111 | self.callbackfn() |
|
119 | self.callbackfn() | |
112 | try: |
|
120 | try: | |
113 | if self.debug: |
|
121 | if self.debug: | |
114 | print 'removing pidfile %s' % self.pidfile |
|
122 | print 'removing pidfile %s' % self.pidfile | |
115 | os.remove(self.pidfile) |
|
123 | os.remove(self.pidfile) | |
116 | self.held = False |
|
124 | self.held = False | |
117 | except OSError, e: |
|
125 | except OSError, e: | |
118 | if self.debug: |
|
126 | if self.debug: | |
119 | print 'removing pidfile failed %s' % e |
|
127 | print 'removing pidfile failed %s' % e | |
120 | pass |
|
128 | pass | |
121 |
|
129 | |||
122 | def makelock(self, lockname, pidfile): |
|
130 | def makelock(self, lockname, pidfile): | |
123 | """ |
|
131 | """ | |
124 | this function will make an actual lock |
|
132 | this function will make an actual lock | |
|
133 | ||||
125 | :param lockname: acctual pid of file |
|
134 | :param lockname: acctual pid of file | |
126 | :param pidfile: the file to write the pid in |
|
135 | :param pidfile: the file to write the pid in | |
127 | """ |
|
136 | """ | |
128 | if self.debug: |
|
137 | if self.debug: | |
129 | print 'creating a file %s and pid: %s' % (pidfile, lockname) |
|
138 | print 'creating a file %s and pid: %s' % (pidfile, lockname) | |
130 | pidfile = open(self.pidfile, "wb") |
|
139 | pidfile = open(self.pidfile, "wb") | |
131 | pidfile.write(lockname) |
|
140 | pidfile.write(lockname) | |
132 | pidfile.close |
|
141 | pidfile.close | |
133 | self.held = True |
|
142 | self.held = True |
@@ -1,51 +1,53 b'' | |||||
1 | from __future__ import with_statement |
|
1 | from __future__ import with_statement | |
2 |
|
2 | |||
3 | import cProfile |
|
3 | import cProfile | |
4 | import pstats |
|
4 | import pstats | |
5 | import cgi |
|
5 | import cgi | |
6 | import pprint |
|
6 | import pprint | |
7 | import threading |
|
7 | import threading | |
8 |
|
8 | |||
9 | from StringIO import StringIO |
|
9 | from StringIO import StringIO | |
10 |
|
10 | |||
|
11 | ||||
11 | class ProfilingMiddleware(object): |
|
12 | class ProfilingMiddleware(object): | |
12 | def __init__(self, app): |
|
13 | def __init__(self, app): | |
13 | self.lock = threading.Lock() |
|
14 | self.lock = threading.Lock() | |
14 | self.app = app |
|
15 | self.app = app | |
15 |
|
16 | |||
16 |
|
||||
17 | def __call__(self, environ, start_response): |
|
17 | def __call__(self, environ, start_response): | |
18 | with self.lock: |
|
18 | with self.lock: | |
19 | profiler = cProfile.Profile() |
|
19 | profiler = cProfile.Profile() | |
|
20 | ||||
20 | def run_app(*a, **kw): |
|
21 | def run_app(*a, **kw): | |
21 | self.response = self.app(environ, start_response) |
|
22 | self.response = self.app(environ, start_response) | |
22 |
|
23 | |||
23 | profiler.runcall(run_app, environ, start_response) |
|
24 | profiler.runcall(run_app, environ, start_response) | |
24 |
|
25 | |||
25 | profiler.snapshot_stats() |
|
26 | profiler.snapshot_stats() | |
26 |
|
27 | |||
27 | stats = pstats.Stats(profiler) |
|
28 | stats = pstats.Stats(profiler) | |
28 | stats.sort_stats('cumulative') |
|
29 | stats.sort_stats('cumulative') | |
29 |
|
30 | |||
30 | # Redirect output |
|
31 | # Redirect output | |
31 | out = StringIO() |
|
32 | out = StringIO() | |
32 | stats.stream = out |
|
33 | stats.stream = out | |
33 |
|
34 | |||
34 | stats.print_stats() |
|
35 | stats.print_stats() | |
35 |
|
36 | |||
36 | resp = ''.join(self.response) |
|
37 | resp = ''.join(self.response) | |
37 |
|
38 | |||
38 | # Lets at least only put this on html-like responses. |
|
39 | # Lets at least only put this on html-like responses. | |
39 | if resp.strip().startswith('<'): |
|
40 | if resp.strip().startswith('<'): | |
40 | ## The profiling info is just appended to the response. |
|
41 | ## The profiling info is just appended to the response. | |
41 | ## Browsers don't mind this. |
|
42 | ## Browsers don't mind this. | |
42 |
resp += '<pre style="text-align:left; |
|
43 | resp += ('<pre style="text-align:left; ' | |
|
44 | 'border-top: 4px dashed red; padding: 1em;">') | |||
43 | resp += cgi.escape(out.getvalue(), True) |
|
45 | resp += cgi.escape(out.getvalue(), True) | |
44 |
|
46 | |||
45 | output = StringIO() |
|
47 | output = StringIO() | |
46 | pprint.pprint(environ, output, depth=3) |
|
48 | pprint.pprint(environ, output, depth=3) | |
47 |
|
49 | |||
48 | resp += cgi.escape(output.getvalue(), True) |
|
50 | resp += cgi.escape(output.getvalue(), True) | |
49 | resp += '</pre>' |
|
51 | resp += '</pre>' | |
50 |
|
52 | |||
51 | return resp |
|
53 | return resp |
@@ -1,143 +1,143 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.lib.smtp_mailer |
|
3 | rhodecode.lib.smtp_mailer | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Simple smtp mailer used in RhodeCode |
|
6 | Simple smtp mailer used in RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Sep 13, 2010 |
|
8 | :created_on: Sep 13, 2010 | |
9 | :copyright: (c) 2011 by marcink. |
|
9 | :copyright: (c) 2011 by marcink. | |
10 | :license: LICENSE_NAME, see LICENSE_FILE for more details. |
|
10 | :license: LICENSE_NAME, see LICENSE_FILE for more details. | |
11 | """ |
|
11 | """ | |
12 |
|
12 | |||
13 | import logging |
|
13 | import logging | |
14 | import smtplib |
|
14 | import smtplib | |
15 | import mimetypes |
|
15 | import mimetypes | |
16 | from socket import sslerror |
|
16 | from socket import sslerror | |
17 |
|
17 | |||
18 | from email.mime.multipart import MIMEMultipart |
|
18 | from email.mime.multipart import MIMEMultipart | |
19 | from email.mime.image import MIMEImage |
|
19 | from email.mime.image import MIMEImage | |
20 | from email.mime.audio import MIMEAudio |
|
20 | from email.mime.audio import MIMEAudio | |
21 | from email.mime.base import MIMEBase |
|
21 | from email.mime.base import MIMEBase | |
22 | from email.mime.text import MIMEText |
|
22 | from email.mime.text import MIMEText | |
23 | from email.utils import formatdate |
|
23 | from email.utils import formatdate | |
24 | from email import encoders |
|
24 | from email import encoders | |
25 |
|
25 | |||
|
26 | ||||
26 | class SmtpMailer(object): |
|
27 | class SmtpMailer(object): | |
27 | """SMTP mailer class |
|
28 | """SMTP mailer class | |
28 |
|
29 | |||
29 |
mailer = SmtpMailer(mail_from, user, passwd, mail_server, |
|
30 | mailer = SmtpMailer(mail_from, user, passwd, mail_server, | |
|
31 | mail_port, ssl, tls) | |||
30 | mailer.send(recipients, subject, body, attachment_files) |
|
32 | mailer.send(recipients, subject, body, attachment_files) | |
31 |
|
33 | |||
32 | :param recipients might be a list of string or single string |
|
34 | :param recipients might be a list of string or single string | |
33 | :param attachment_files is a dict of {filename:location} |
|
35 | :param attachment_files is a dict of {filename:location} | |
34 | it tries to guess the mimetype and attach the file |
|
36 | it tries to guess the mimetype and attach the file | |
35 |
|
37 | |||
36 | """ |
|
38 | """ | |
37 |
|
39 | |||
38 | def __init__(self, mail_from, user, passwd, mail_server, |
|
40 | def __init__(self, mail_from, user, passwd, mail_server, | |
39 | mail_port=None, ssl=False, tls=False, debug=False): |
|
41 | mail_port=None, ssl=False, tls=False, debug=False): | |
40 |
|
42 | |||
41 | self.mail_from = mail_from |
|
43 | self.mail_from = mail_from | |
42 | self.mail_server = mail_server |
|
44 | self.mail_server = mail_server | |
43 | self.mail_port = mail_port |
|
45 | self.mail_port = mail_port | |
44 | self.user = user |
|
46 | self.user = user | |
45 | self.passwd = passwd |
|
47 | self.passwd = passwd | |
46 | self.ssl = ssl |
|
48 | self.ssl = ssl | |
47 | self.tls = tls |
|
49 | self.tls = tls | |
48 | self.debug = debug |
|
50 | self.debug = debug | |
49 |
|
51 | |||
50 | def send(self, recipients=[], subject='', body='', attachment_files=None): |
|
52 | def send(self, recipients=[], subject='', body='', attachment_files=None): | |
51 |
|
53 | |||
52 | if isinstance(recipients, basestring): |
|
54 | if isinstance(recipients, basestring): | |
53 | recipients = [recipients] |
|
55 | recipients = [recipients] | |
54 | if self.ssl: |
|
56 | if self.ssl: | |
55 | smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port) |
|
57 | smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port) | |
56 | else: |
|
58 | else: | |
57 | smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port) |
|
59 | smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port) | |
58 |
|
60 | |||
59 | if self.tls: |
|
61 | if self.tls: | |
60 | smtp_serv.ehlo() |
|
62 | smtp_serv.ehlo() | |
61 | smtp_serv.starttls() |
|
63 | smtp_serv.starttls() | |
62 |
|
64 | |||
63 | if self.debug: |
|
65 | if self.debug: | |
64 | smtp_serv.set_debuglevel(1) |
|
66 | smtp_serv.set_debuglevel(1) | |
65 |
|
67 | |||
66 | smtp_serv.ehlo() |
|
68 | smtp_serv.ehlo() | |
67 |
|
69 | |||
68 | #if server requires authorization you must provide login and password |
|
70 | #if server requires authorization you must provide login and password | |
69 | #but only if we have them |
|
71 | #but only if we have them | |
70 | if self.user and self.passwd: |
|
72 | if self.user and self.passwd: | |
71 | smtp_serv.login(self.user, self.passwd) |
|
73 | smtp_serv.login(self.user, self.passwd) | |
72 |
|
74 | |||
73 |
|
||||
74 | date_ = formatdate(localtime=True) |
|
75 | date_ = formatdate(localtime=True) | |
75 | msg = MIMEMultipart() |
|
76 | msg = MIMEMultipart() | |
76 | msg['From'] = self.mail_from |
|
77 | msg['From'] = self.mail_from | |
77 | msg['To'] = ','.join(recipients) |
|
78 | msg['To'] = ','.join(recipients) | |
78 | msg['Date'] = date_ |
|
79 | msg['Date'] = date_ | |
79 | msg['Subject'] = subject |
|
80 | msg['Subject'] = subject | |
80 | msg.preamble = 'You will not see this in a MIME-aware mail reader.\n' |
|
81 | msg.preamble = 'You will not see this in a MIME-aware mail reader.\n' | |
81 |
|
82 | |||
82 | msg.attach(MIMEText(body)) |
|
83 | msg.attach(MIMEText(body)) | |
83 |
|
84 | |||
84 | if attachment_files: |
|
85 | if attachment_files: | |
85 | self.__atach_files(msg, attachment_files) |
|
86 | self.__atach_files(msg, attachment_files) | |
86 |
|
87 | |||
87 | smtp_serv.sendmail(self.mail_from, recipients, msg.as_string()) |
|
88 | smtp_serv.sendmail(self.mail_from, recipients, msg.as_string()) | |
88 | logging.info('MAIL SEND TO: %s' % recipients) |
|
89 | logging.info('MAIL SEND TO: %s' % recipients) | |
89 |
|
90 | |||
90 | try: |
|
91 | try: | |
91 | smtp_serv.quit() |
|
92 | smtp_serv.quit() | |
92 | except sslerror: |
|
93 | except sslerror: | |
93 | # sslerror is raised in tls connections on closing sometimes |
|
94 | # sslerror is raised in tls connections on closing sometimes | |
94 | pass |
|
95 | pass | |
95 |
|
96 | |||
96 |
|
||||
97 |
|
||||
98 | def __atach_files(self, msg, attachment_files): |
|
97 | def __atach_files(self, msg, attachment_files): | |
99 | if isinstance(attachment_files, dict): |
|
98 | if isinstance(attachment_files, dict): | |
100 | for f_name, msg_file in attachment_files.items(): |
|
99 | for f_name, msg_file in attachment_files.items(): | |
101 | ctype, encoding = mimetypes.guess_type(f_name) |
|
100 | ctype, encoding = mimetypes.guess_type(f_name) | |
102 |
logging.info("guessing file %s type based on %s" |
|
101 | logging.info("guessing file %s type based on %s", ctype, | |
|
102 | f_name) | |||
103 | if ctype is None or encoding is not None: |
|
103 | if ctype is None or encoding is not None: | |
104 |
# No guess could be made, or the file is encoded |
|
104 | # No guess could be made, or the file is encoded | |
105 | # use a generic bag-of-bits type. |
|
105 | # (compressed), so use a generic bag-of-bits type. | |
106 | ctype = 'application/octet-stream' |
|
106 | ctype = 'application/octet-stream' | |
107 | maintype, subtype = ctype.split('/', 1) |
|
107 | maintype, subtype = ctype.split('/', 1) | |
108 | if maintype == 'text': |
|
108 | if maintype == 'text': | |
109 | # Note: we should handle calculating the charset |
|
109 | # Note: we should handle calculating the charset | |
110 | file_part = MIMEText(self.get_content(msg_file), |
|
110 | file_part = MIMEText(self.get_content(msg_file), | |
111 | _subtype=subtype) |
|
111 | _subtype=subtype) | |
112 | elif maintype == 'image': |
|
112 | elif maintype == 'image': | |
113 | file_part = MIMEImage(self.get_content(msg_file), |
|
113 | file_part = MIMEImage(self.get_content(msg_file), | |
114 | _subtype=subtype) |
|
114 | _subtype=subtype) | |
115 | elif maintype == 'audio': |
|
115 | elif maintype == 'audio': | |
116 | file_part = MIMEAudio(self.get_content(msg_file), |
|
116 | file_part = MIMEAudio(self.get_content(msg_file), | |
117 | _subtype=subtype) |
|
117 | _subtype=subtype) | |
118 | else: |
|
118 | else: | |
119 | file_part = MIMEBase(maintype, subtype) |
|
119 | file_part = MIMEBase(maintype, subtype) | |
120 | file_part.set_payload(self.get_content(msg_file)) |
|
120 | file_part.set_payload(self.get_content(msg_file)) | |
121 | # Encode the payload using Base64 |
|
121 | # Encode the payload using Base64 | |
122 | encoders.encode_base64(msg) |
|
122 | encoders.encode_base64(msg) | |
123 | # Set the filename parameter |
|
123 | # Set the filename parameter | |
124 | file_part.add_header('Content-Disposition', 'attachment', |
|
124 | file_part.add_header('Content-Disposition', 'attachment', | |
125 | filename=f_name) |
|
125 | filename=f_name) | |
126 | file_part.add_header('Content-Type', ctype, name=f_name) |
|
126 | file_part.add_header('Content-Type', ctype, name=f_name) | |
127 | msg.attach(file_part) |
|
127 | msg.attach(file_part) | |
128 | else: |
|
128 | else: | |
129 | raise Exception('Attachment files should be' |
|
129 | raise Exception('Attachment files should be' | |
130 | 'a dict in format {"filename":"filepath"}') |
|
130 | 'a dict in format {"filename":"filepath"}') | |
131 |
|
131 | |||
132 | def get_content(self, msg_file): |
|
132 | def get_content(self, msg_file): | |
133 | """Get content based on type, if content is a string do open first |
|
133 | """Get content based on type, if content is a string do open first | |
134 | else just read because it's a probably open file object |
|
134 | else just read because it's a probably open file object | |
135 |
|
135 | |||
136 | :param msg_file: |
|
136 | :param msg_file: | |
137 | """ |
|
137 | """ | |
138 | if isinstance(msg_file, str): |
|
138 | if isinstance(msg_file, str): | |
139 | return open(msg_file, "rb").read() |
|
139 | return open(msg_file, "rb").read() | |
140 | else: |
|
140 | else: | |
141 | #just for safe seek to 0 |
|
141 | #just for safe seek to 0 | |
142 | msg_file.seek(0) |
|
142 | msg_file.seek(0) | |
143 | return msg_file.read() |
|
143 | return msg_file.read() |
@@ -1,28 +1,30 b'' | |||||
1 | from sqlalchemy.interfaces import ConnectionProxy |
|
1 | from sqlalchemy.interfaces import ConnectionProxy | |
2 | import time |
|
2 | import time | |
3 | import logging |
|
3 | import logging | |
4 | log = logging.getLogger('timerproxy') |
|
4 | log = logging.getLogger('timerproxy') | |
5 |
|
5 | |||
6 | BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38) |
|
6 | BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38) | |
7 |
|
7 | |||
|
8 | ||||
8 | def color_sql(sql): |
|
9 | def color_sql(sql): | |
9 | COLOR_SEQ = "\033[1;%dm" |
|
10 | COLOR_SEQ = "\033[1;%dm" | |
10 | COLOR_SQL = YELLOW |
|
11 | COLOR_SQL = YELLOW | |
11 | normal = '\x1b[0m' |
|
12 | normal = '\x1b[0m' | |
12 | return COLOR_SEQ % COLOR_SQL + sql + normal |
|
13 | return COLOR_SEQ % COLOR_SQL + sql + normal | |
13 |
|
14 | |||
|
15 | ||||
14 | class TimerProxy(ConnectionProxy): |
|
16 | class TimerProxy(ConnectionProxy): | |
15 |
|
17 | |||
16 | def __init__(self): |
|
18 | def __init__(self): | |
17 | super(TimerProxy, self).__init__() |
|
19 | super(TimerProxy, self).__init__() | |
18 |
|
20 | |||
19 | def cursor_execute(self, execute, cursor, statement, parameters, |
|
21 | def cursor_execute(self, execute, cursor, statement, parameters, | |
20 | context, executemany): |
|
22 | context, executemany): | |
21 |
|
23 | |||
22 | now = time.time() |
|
24 | now = time.time() | |
23 | try: |
|
25 | try: | |
24 | log.info(color_sql(">>>>> STARTING QUERY >>>>>")) |
|
26 | log.info(color_sql(">>>>> STARTING QUERY >>>>>")) | |
25 | return execute(cursor, statement, parameters, context) |
|
27 | return execute(cursor, statement, parameters, context) | |
26 | finally: |
|
28 | finally: | |
27 | total = time.time() - now |
|
29 | total = time.time() - now | |
28 | log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total)) |
|
30 | log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total)) |
General Comments 0
You need to be logged in to leave comments.
Login now