##// END OF EJS Templates
pep8ify
marcink -
r1307:c1516b35 beta
parent child Browse files
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 every time
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 = self.scm_model.get_followers(c.repo_name)
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, # level 50
12 'CRITICAL': MAGENTA,
13 'ERROR': RED, # level 40
13 'ERROR': RED,
14 'WARNING': CYAN, # level 30
14 'WARNING': CYAN,
15 'INFO': GREEN, # level 20
15 'INFO': GREEN,
16 'DEBUG': BLUE, # level 10
16 'DEBUG': BLUE,
17 'SQL' : YELLOW
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 LdapUsernameError(Exception):pass
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 UserOwnsReposException(Exception):pass
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, time
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(os.path.dirname(__file__),
42 self.pidfile = file if file else os.path.join(
38 'running.lock')
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, checking for execution'\
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 the program is not running"
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; border-top: 4px dashed red; padding: 1em;">'
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, mail_port, ssl, tls)
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" , ctype, f_name)
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 (compressed), so
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