##// END OF EJS Templates
merges for stable
marcink -
r1228:73434499 default
parent child Browse files
Show More
@@ -1,254 +1,257 b''
1 .. _changelog:
1 .. _changelog:
2
2
3 Changelog
3 Changelog
4 =========
4 =========
5
5
6
6
7 1.1.8 (**2011-04-XX**)
7 1.1.8 (**2011-04-XX**)
8 ======================
8 ======================
9
9
10 news
10 news
11 ----
11 ----
12
12
13 fixes
13 fixes
14 -----
14 -----
15
15
16 - fixed #140 freeze of python dateutil library, since new version is python2.x
16 - fixed #140 freeze of python dateutil library, since new version is python2.x
17 incompatible
17 incompatible
18 - setup-app will check for write permission in given path
18 - setup-app will check for write permission in given path
19 - cleaned up license info issue #149
19 - cleaned up license info issue #149
20 - fixes for issues #137 and #116
20 - fixes for issues #137 and #116
21 - fixes crashes on gravatar, when passed in email as unicode
21 - fixes crashes on gravatar, when passed in email as unicode
22 - fixed tooltip flickering problems
22 - fixed tooltip flickering problems
23 - fixed came_from redirection on windows
24 - fixed logging modules,and sql formatters
25 - windows fixes for os.kill and path spliting, issues #148 and #133
23
26
24 1.1.7 (**2011-03-23**)
27 1.1.7 (**2011-03-23**)
25 ======================
28 ======================
26
29
27 news
30 news
28 ----
31 ----
29
32
30 fixes
33 fixes
31 -----
34 -----
32
35
33 - fixed (again) #136 installation support for FreeBSD
36 - fixed (again) #136 installation support for FreeBSD
34
37
35
38
36 1.1.6 (**2011-03-21**)
39 1.1.6 (**2011-03-21**)
37 ======================
40 ======================
38
41
39 news
42 news
40 ----
43 ----
41
44
42 fixes
45 fixes
43 -----
46 -----
44
47
45 - fixed #136 installation support for FreeBSD
48 - fixed #136 installation support for FreeBSD
46 - RhodeCode will check for python version during installation
49 - RhodeCode will check for python version during installation
47
50
48 1.1.5 (**2011-03-17**)
51 1.1.5 (**2011-03-17**)
49 ======================
52 ======================
50
53
51 news
54 news
52 ----
55 ----
53
56
54 - basic windows support, by exchanging pybcrypt into sha256 for windows only
57 - basic windows support, by exchanging pybcrypt into sha256 for windows only
55 highly inspired by idea of mantis406
58 highly inspired by idea of mantis406
56
59
57 fixes
60 fixes
58 -----
61 -----
59
62
60 - fixed sorting by author in main page
63 - fixed sorting by author in main page
61 - fixed crashes with diffs on binary files
64 - fixed crashes with diffs on binary files
62 - fixed #131 problem with boolean values for LDAP
65 - fixed #131 problem with boolean values for LDAP
63 - fixed #122 mysql problems thanks to striker69
66 - fixed #122 mysql problems thanks to striker69
64 - fixed problem with errors on calling raw/raw_files/annotate functions
67 - fixed problem with errors on calling raw/raw_files/annotate functions
65 with unknown revisions
68 with unknown revisions
66 - fixed returned rawfiles attachment names with international character
69 - fixed returned rawfiles attachment names with international character
67 - cleaned out docs, big thanks to Jason Harris
70 - cleaned out docs, big thanks to Jason Harris
68
71
69 1.1.4 (**2011-02-19**)
72 1.1.4 (**2011-02-19**)
70 ======================
73 ======================
71
74
72 news
75 news
73 ----
76 ----
74
77
75 fixes
78 fixes
76 -----
79 -----
77
80
78 - fixed formencode import problem on settings page, that caused server crash
81 - fixed formencode import problem on settings page, that caused server crash
79 when that page was accessed as first after server start
82 when that page was accessed as first after server start
80 - journal fixes
83 - journal fixes
81 - fixed option to access repository just by entering http://server/<repo_name>
84 - fixed option to access repository just by entering http://server/<repo_name>
82
85
83
86
84 1.1.3 (**2011-02-16**)
87 1.1.3 (**2011-02-16**)
85 ======================
88 ======================
86
89
87 news
90 news
88 ----
91 ----
89
92
90 - implemented #102 allowing the '.' character in username
93 - implemented #102 allowing the '.' character in username
91 - added option to access repository just by entering http://server/<repo_name>
94 - added option to access repository just by entering http://server/<repo_name>
92 - celery task ignores result for better performance
95 - celery task ignores result for better performance
93
96
94 fixes
97 fixes
95 -----
98 -----
96
99
97 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
100 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
98 apollo13 and Johan Walles
101 apollo13 and Johan Walles
99 - small fixes in journal
102 - small fixes in journal
100 - fixed problems with getting setting for celery from .ini files
103 - fixed problems with getting setting for celery from .ini files
101 - registration, password reset and login boxes share the same title as main
104 - registration, password reset and login boxes share the same title as main
102 application now
105 application now
103 - fixed #113: to high permissions to fork repository
106 - fixed #113: to high permissions to fork repository
104 - fixed problem with '[' chars in commit messages in journal
107 - fixed problem with '[' chars in commit messages in journal
105 - removed issue with space inside renamed repository after deletion
108 - removed issue with space inside renamed repository after deletion
106 - db transaction fixes when filesystem repository creation failed
109 - db transaction fixes when filesystem repository creation failed
107 - fixed #106 relation issues on databases different than sqlite
110 - fixed #106 relation issues on databases different than sqlite
108 - fixed static files paths links to use of url() method
111 - fixed static files paths links to use of url() method
109
112
110 1.1.2 (**2011-01-12**)
113 1.1.2 (**2011-01-12**)
111 ======================
114 ======================
112
115
113 news
116 news
114 ----
117 ----
115
118
116
119
117 fixes
120 fixes
118 -----
121 -----
119
122
120 - fixes #98 protection against float division of percentage stats
123 - fixes #98 protection against float division of percentage stats
121 - fixed graph bug
124 - fixed graph bug
122 - forced webhelpers version since it was making troubles during installation
125 - forced webhelpers version since it was making troubles during installation
123
126
124 1.1.1 (**2011-01-06**)
127 1.1.1 (**2011-01-06**)
125 ======================
128 ======================
126
129
127 news
130 news
128 ----
131 ----
129
132
130 - added force https option into ini files for easier https usage (no need to
133 - added force https option into ini files for easier https usage (no need to
131 set server headers with this options)
134 set server headers with this options)
132 - small css updates
135 - small css updates
133
136
134 fixes
137 fixes
135 -----
138 -----
136
139
137 - fixed #96 redirect loop on files view on repositories without changesets
140 - fixed #96 redirect loop on files view on repositories without changesets
138 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
141 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
139 and server crashed with errors
142 and server crashed with errors
140 - fixed large tooltips problems on main page
143 - fixed large tooltips problems on main page
141 - fixed #92 whoosh indexer is more error proof
144 - fixed #92 whoosh indexer is more error proof
142
145
143 1.1.0 (**2010-12-18**)
146 1.1.0 (**2010-12-18**)
144 ======================
147 ======================
145
148
146 news
149 news
147 ----
150 ----
148
151
149 - rewrite of internals for vcs >=0.1.10
152 - rewrite of internals for vcs >=0.1.10
150 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
153 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
151 with older clients
154 with older clients
152 - anonymous access, authentication via ldap
155 - anonymous access, authentication via ldap
153 - performance upgrade for cached repos list - each repository has it's own
156 - performance upgrade for cached repos list - each repository has it's own
154 cache that's invalidated when needed.
157 cache that's invalidated when needed.
155 - performance upgrades on repositories with large amount of commits (20K+)
158 - performance upgrades on repositories with large amount of commits (20K+)
156 - main page quick filter for filtering repositories
159 - main page quick filter for filtering repositories
157 - user dashboards with ability to follow chosen repositories actions
160 - user dashboards with ability to follow chosen repositories actions
158 - sends email to admin on new user registration
161 - sends email to admin on new user registration
159 - added cache/statistics reset options into repository settings
162 - added cache/statistics reset options into repository settings
160 - more detailed action logger (based on hooks) with pushed changesets lists
163 - more detailed action logger (based on hooks) with pushed changesets lists
161 and options to disable those hooks from admin panel
164 and options to disable those hooks from admin panel
162 - introduced new enhanced changelog for merges that shows more accurate results
165 - introduced new enhanced changelog for merges that shows more accurate results
163 - new improved and faster code stats (based on pygments lexers mapping tables,
166 - new improved and faster code stats (based on pygments lexers mapping tables,
164 showing up to 10 trending sources for each repository. Additionally stats
167 showing up to 10 trending sources for each repository. Additionally stats
165 can be disabled in repository settings.
168 can be disabled in repository settings.
166 - gui optimizations, fixed application width to 1024px
169 - gui optimizations, fixed application width to 1024px
167 - added cut off (for large files/changesets) limit into config files
170 - added cut off (for large files/changesets) limit into config files
168 - whoosh, celeryd, upgrade moved to paster command
171 - whoosh, celeryd, upgrade moved to paster command
169 - other than sqlite database backends can be used
172 - other than sqlite database backends can be used
170
173
171 fixes
174 fixes
172 -----
175 -----
173
176
174 - fixes #61 forked repo was showing only after cache expired
177 - fixes #61 forked repo was showing only after cache expired
175 - fixes #76 no confirmation on user deletes
178 - fixes #76 no confirmation on user deletes
176 - fixes #66 Name field misspelled
179 - fixes #66 Name field misspelled
177 - fixes #72 block user removal when he owns repositories
180 - fixes #72 block user removal when he owns repositories
178 - fixes #69 added password confirmation fields
181 - fixes #69 added password confirmation fields
179 - fixes #87 RhodeCode crashes occasionally on updating repository owner
182 - fixes #87 RhodeCode crashes occasionally on updating repository owner
180 - fixes #82 broken annotations on files with more than 1 blank line at the end
183 - fixes #82 broken annotations on files with more than 1 blank line at the end
181 - a lot of fixes and tweaks for file browser
184 - a lot of fixes and tweaks for file browser
182 - fixed detached session issues
185 - fixed detached session issues
183 - fixed when user had no repos he would see all repos listed in my account
186 - fixed when user had no repos he would see all repos listed in my account
184 - fixed ui() instance bug when global hgrc settings was loaded for server
187 - fixed ui() instance bug when global hgrc settings was loaded for server
185 instance and all hgrc options were merged with our db ui() object
188 instance and all hgrc options were merged with our db ui() object
186 - numerous small bugfixes
189 - numerous small bugfixes
187
190
188 (special thanks for TkSoh for detailed feedback)
191 (special thanks for TkSoh for detailed feedback)
189
192
190
193
191 1.0.2 (**2010-11-12**)
194 1.0.2 (**2010-11-12**)
192 ======================
195 ======================
193
196
194 news
197 news
195 ----
198 ----
196
199
197 - tested under python2.7
200 - tested under python2.7
198 - bumped sqlalchemy and celery versions
201 - bumped sqlalchemy and celery versions
199
202
200 fixes
203 fixes
201 -----
204 -----
202
205
203 - fixed #59 missing graph.js
206 - fixed #59 missing graph.js
204 - fixed repo_size crash when repository had broken symlinks
207 - fixed repo_size crash when repository had broken symlinks
205 - fixed python2.5 crashes.
208 - fixed python2.5 crashes.
206
209
207
210
208 1.0.1 (**2010-11-10**)
211 1.0.1 (**2010-11-10**)
209 ======================
212 ======================
210
213
211 news
214 news
212 ----
215 ----
213
216
214 - small css updated
217 - small css updated
215
218
216 fixes
219 fixes
217 -----
220 -----
218
221
219 - fixed #53 python2.5 incompatible enumerate calls
222 - fixed #53 python2.5 incompatible enumerate calls
220 - fixed #52 disable mercurial extension for web
223 - fixed #52 disable mercurial extension for web
221 - fixed #51 deleting repositories don't delete it's dependent objects
224 - fixed #51 deleting repositories don't delete it's dependent objects
222
225
223
226
224 1.0.0 (**2010-11-02**)
227 1.0.0 (**2010-11-02**)
225 ======================
228 ======================
226
229
227 - security bugfix simplehg wasn't checking for permissions on commands
230 - security bugfix simplehg wasn't checking for permissions on commands
228 other than pull or push.
231 other than pull or push.
229 - fixed doubled messages after push or pull in admin journal
232 - fixed doubled messages after push or pull in admin journal
230 - templating and css corrections, fixed repo switcher on chrome, updated titles
233 - templating and css corrections, fixed repo switcher on chrome, updated titles
231 - admin menu accessible from options menu on repository view
234 - admin menu accessible from options menu on repository view
232 - permissions cached queries
235 - permissions cached queries
233
236
234 1.0.0rc4 (**2010-10-12**)
237 1.0.0rc4 (**2010-10-12**)
235 ==========================
238 ==========================
236
239
237 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
240 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
238 - removed cache_manager settings from sqlalchemy meta
241 - removed cache_manager settings from sqlalchemy meta
239 - added sqlalchemy cache settings to ini files
242 - added sqlalchemy cache settings to ini files
240 - validated password length and added second try of failure on paster setup-app
243 - validated password length and added second try of failure on paster setup-app
241 - fixed setup database destroy prompt even when there was no db
244 - fixed setup database destroy prompt even when there was no db
242
245
243
246
244 1.0.0rc3 (**2010-10-11**)
247 1.0.0rc3 (**2010-10-11**)
245 =========================
248 =========================
246
249
247 - fixed i18n during installation.
250 - fixed i18n during installation.
248
251
249 1.0.0rc2 (**2010-10-11**)
252 1.0.0rc2 (**2010-10-11**)
250 =========================
253 =========================
251
254
252 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
255 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
253 occure. After vcs is fixed it'll be put back again.
256 occure. After vcs is fixed it'll be put back again.
254 - templating/css rewrites, optimized css. No newline at end of file
257 - templating/css rewrites, optimized css.
@@ -1,56 +1,56 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.__init__
3 rhodecode.__init__
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode, a web based repository management based on pylons
6 RhodeCode, a web based repository management based on pylons
7 versioning implementation: http://semver.org/
7 versioning implementation: http://semver.org/
8
8
9 :created_on: Apr 9, 2010
9 :created_on: Apr 9, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import platform
26 import platform
27
27
28 VERSION = (1, 1, 8)
28 VERSION = (1, 1, 8)
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30 __dbversion__ = 2 # defines current db version for migrations
30 __dbversion__ = 2 # defines current db version for migrations
31 __platform__ = platform.system()
31 __platform__ = platform.system()
32 __license__ = 'GPLv3'
32 __license__ = 'GPLv3'
33
33
34 PLATFORM_WIN = ('Windows',)
34 PLATFORM_WIN = ('Windows')
35 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD',)
35 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD')
36
36
37 try:
37 try:
38 from rhodecode.lib.utils import get_current_revision
38 from rhodecode.lib.utils import get_current_revision
39 _rev = get_current_revision()
39 _rev = get_current_revision()
40 except ImportError:
40 except ImportError:
41 #this is needed when doing some setup.py operations
41 #this is needed when doing some setup.py operations
42 _rev = False
42 _rev = False
43
43
44 if len(VERSION) > 3 and _rev:
44 if len(VERSION) > 3 and _rev:
45 __version__ += ' [rev:%s]' % _rev[0]
45 __version__ += ' [rev:%s]' % _rev[0]
46
46
47
47
48 def get_version():
48 def get_version():
49 """Returns shorter version (digit parts only) as string."""
49 """Returns shorter version (digit parts only) as string."""
50
50
51 return '.'.join((str(each) for each in VERSION[:3]))
51 return '.'.join((str(each) for each in VERSION[:3]))
52
52
53 BACKENDS = {
53 BACKENDS = {
54 'hg': 'Mercurial repository',
54 'hg': 'Mercurial repository',
55 #'git': 'Git repository',
55 #'git': 'Git repository',
56 }
56 }
@@ -1,68 +1,81 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 def str2bool(s):
26
27 if s is None:
27 def str2bool(_str):
28 """
29 returs True/False value from given string, it tries to translate the
30 string into boolean
31
32 :param _str: string value to translate into boolean
33 :rtype: boolean
34 :returns: boolean from given string
35 """
36 if _str is None:
28 return False
37 return False
29 if s in (True, False):
38 if _str in (True, False):
30 return s
39 return _str
31 s = str(s).strip().lower()
40 _str = str(_str).strip().lower()
32 return s in ('t', 'true', 'y', 'yes', 'on', '1')
41 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
42
33
43
34 def generate_api_key(username, salt=None):
44 def generate_api_key(username, salt=None):
35 """
45 """
36 Generates uniq API key for given username
46 Generates unique API key for given username,if salt is not given
47 it'll be generated from some random string
37
48
38 :param username: username as string
49 :param username: username as string
39 :param salt: salt to hash generate KEY
50 :param salt: salt to hash generate KEY
51 :rtype: str
52 :returns: sha1 hash from username+salt
40 """
53 """
41 from tempfile import _RandomNameSequence
54 from tempfile import _RandomNameSequence
42 import hashlib
55 import hashlib
43
56
44 if salt is None:
57 if salt is None:
45 salt = _RandomNameSequence().next()
58 salt = _RandomNameSequence().next()
46
59
47 return hashlib.sha1(username + salt).hexdigest()
60 return hashlib.sha1(username + salt).hexdigest()
48
61
49 def safe_unicode(_str):
62
63 def safe_unicode(_str, from_encoding='utf8'):
50 """
64 """
51 safe unicode function. In case of UnicodeDecode error we try to return
65 safe unicode function. In case of UnicodeDecode error we try to return
52 unicode with errors replace, if this fails we return unicode with
66 unicode with errors replace
53 string_escape decoding
67
68 :param _str: string to decode
69 :rtype: unicode
70 :returns: unicode object
54 """
71 """
55
72
56 if isinstance(_str, unicode):
73 if isinstance(_str, unicode):
57 return _str
74 return _str
58
75
59 try:
76 try:
60 u_str = unicode(_str)
77 u_str = unicode(_str, from_encoding)
61 except UnicodeDecodeError:
78 except UnicodeDecodeError:
62 try:
79 u_str = unicode(_str, from_encoding, 'replace')
63 u_str = _str.decode('utf-8', 'replace')
64 except UnicodeDecodeError:
65 #incase we have a decode error just represent as byte string
66 u_str = unicode(_str.encode('string_escape'))
67
80
68 return u_str No newline at end of file
81 return u_str
@@ -1,612 +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
28
29 from decorator import decorator
29 from decorator import decorator
30
30
31 from pylons import config, session, url, request
31 from pylons import config, session, url, request
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 rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
35 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
36
36
37 if __platform__ in PLATFORM_WIN:
37 if __platform__ in PLATFORM_WIN:
38 from hashlib import sha256
38 from hashlib import sha256
39 if __platform__ in PLATFORM_OTHERS:
39 if __platform__ in PLATFORM_OTHERS:
40 import bcrypt
40 import bcrypt
41
41
42 from rhodecode.lib import str2bool
42 from rhodecode.lib import str2bool
43 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
43 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
44 from rhodecode.lib.utils import get_repo_slug
44 from rhodecode.lib.utils import get_repo_slug
45 from rhodecode.lib.auth_ldap import AuthLdap
45 from rhodecode.lib.auth_ldap import AuthLdap
46
46
47 from rhodecode.model import meta
47 from rhodecode.model import meta
48 from rhodecode.model.user import UserModel
48 from rhodecode.model.user import UserModel
49 from rhodecode.model.db import Permission, RepoToPerm, Repository, \
49 from rhodecode.model.db import Permission, RepoToPerm, Repository, \
50 User, UserToPerm
50 User, UserToPerm
51
51
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55 class PasswordGenerator(object):
55 class PasswordGenerator(object):
56 """This is a simple class for generating password from
56 """This is a simple class for generating password from
57 different sets of characters
57 different sets of characters
58 usage:
58 usage:
59 passwd_gen = PasswordGenerator()
59 passwd_gen = PasswordGenerator()
60 #print 8-letter password containing only big and small letters of alphabet
60 #print 8-letter password containing only big and small letters of alphabet
61 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
61 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
62 """
62 """
63 ALPHABETS_NUM = r'''1234567890'''#[0]
63 ALPHABETS_NUM = r'''1234567890'''#[0]
64 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
64 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
65 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
65 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
66 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
66 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
67 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
67 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
68 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
68 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
69 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
69 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
70 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
70 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
71 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
71 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
72
72
73 def __init__(self, passwd=''):
73 def __init__(self, passwd=''):
74 self.passwd = passwd
74 self.passwd = passwd
75
75
76 def gen_password(self, len, type):
76 def gen_password(self, len, type):
77 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
77 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
78 return self.passwd
78 return self.passwd
79
79
80 class RhodeCodeCrypto(object):
80 class RhodeCodeCrypto(object):
81
81
82 @classmethod
82 @classmethod
83 def hash_string(cls, str_):
83 def hash_string(cls, str_):
84 """
84 """
85 Cryptographic function used for password hashing based on pybcrypt
85 Cryptographic function used for password hashing based on pybcrypt
86 or pycrypto in windows
86 or pycrypto in windows
87
87
88 :param password: password to hash
88 :param password: password to hash
89 """
89 """
90 if __platform__ in PLATFORM_WIN:
90 if __platform__ in PLATFORM_WIN:
91 return sha256(str_).hexdigest()
91 return sha256(str_).hexdigest()
92 elif __platform__ in PLATFORM_OTHERS:
92 elif __platform__ in PLATFORM_OTHERS:
93 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
93 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
94 else:
94 else:
95 raise Exception('Unknown or unsupported platform %s' % __platform__)
95 raise Exception('Unknown or unsupported platform %s' % __platform__)
96
96
97 @classmethod
97 @classmethod
98 def hash_check(cls, password, hashed):
98 def hash_check(cls, password, hashed):
99 """
99 """
100 Checks matching password with it's hashed value, runs different
100 Checks matching password with it's hashed value, runs different
101 implementation based on platform it runs on
101 implementation based on platform it runs on
102
102
103 :param password: password
103 :param password: password
104 :param hashed: password in hashed form
104 :param hashed: password in hashed form
105 """
105 """
106
106
107 if __platform__ in PLATFORM_WIN:
107 if __platform__ in PLATFORM_WIN:
108 return sha256(password).hexdigest() == hashed
108 return sha256(password).hexdigest() == hashed
109 elif __platform__ in PLATFORM_OTHERS:
109 elif __platform__ in PLATFORM_OTHERS:
110 return bcrypt.hashpw(password, hashed) == hashed
110 return bcrypt.hashpw(password, hashed) == hashed
111 else:
111 else:
112 raise Exception('Unknown or unsupported platform %s' % __platform__)
112 raise Exception('Unknown or unsupported platform %s' % __platform__)
113
113
114
114
115 def get_crypt_password(password):
115 def get_crypt_password(password):
116 return RhodeCodeCrypto.hash_string(password)
116 return RhodeCodeCrypto.hash_string(password)
117
117
118 def check_password(password, hashed):
118 def check_password(password, hashed):
119 return RhodeCodeCrypto.hash_check(password, hashed)
119 return RhodeCodeCrypto.hash_check(password, hashed)
120
120
121 def authfunc(environ, username, password):
121 def authfunc(environ, username, password):
122 """
122 """
123 Dummy authentication function used in Mercurial/Git/ and access control,
123 Dummy authentication function used in Mercurial/Git/ and access control,
124
124
125 :param environ: needed only for using in Basic auth
125 :param environ: needed only for using in Basic auth
126 """
126 """
127 return authenticate(username, password)
127 return authenticate(username, password)
128
128
129
129
130 def authenticate(username, password):
130 def authenticate(username, password):
131 """
131 """
132 Authentication function used for access control,
132 Authentication function used for access control,
133 firstly checks for db authentication then if ldap is enabled for ldap
133 firstly checks for db authentication then if ldap is enabled for ldap
134 authentication, also creates ldap user if not in database
134 authentication, also creates ldap user if not in database
135
135
136 :param username: username
136 :param username: username
137 :param password: password
137 :param password: password
138 """
138 """
139 user_model = UserModel()
139 user_model = UserModel()
140 user = user_model.get_by_username(username, cache=False)
140 user = user_model.get_by_username(username, cache=False)
141
141
142 log.debug('Authenticating user using RhodeCode account')
142 log.debug('Authenticating user using RhodeCode account')
143 if user is not None and user.is_ldap is False:
143 if user is not None and user.is_ldap is False:
144 if user.active:
144 if user.active:
145
145
146 if user.username == 'default' and user.active:
146 if user.username == 'default' and user.active:
147 log.info('user %s authenticated correctly as anonymous user',
147 log.info('user %s authenticated correctly as anonymous user',
148 username)
148 username)
149 return True
149 return True
150
150
151 elif user.username == username and check_password(password, user.password):
151 elif user.username == username and check_password(password, user.password):
152 log.info('user %s authenticated correctly', username)
152 log.info('user %s authenticated correctly', username)
153 return True
153 return True
154 else:
154 else:
155 log.warning('user %s is disabled', username)
155 log.warning('user %s is disabled', username)
156
156
157 else:
157 else:
158 log.debug('Regular authentication failed')
158 log.debug('Regular authentication failed')
159 user_obj = user_model.get_by_username(username, cache=False,
159 user_obj = user_model.get_by_username(username, cache=False,
160 case_insensitive=True)
160 case_insensitive=True)
161
161
162 if user_obj is not None and user_obj.is_ldap is False:
162 if user_obj is not None and user_obj.is_ldap is False:
163 log.debug('this user already exists as non ldap')
163 log.debug('this user already exists as non ldap')
164 return False
164 return False
165
165
166 from rhodecode.model.settings import SettingsModel
166 from rhodecode.model.settings import SettingsModel
167 ldap_settings = SettingsModel().get_ldap_settings()
167 ldap_settings = SettingsModel().get_ldap_settings()
168
168
169 #======================================================================
169 #======================================================================
170 # FALLBACK TO LDAP AUTH IN ENABLE
170 # FALLBACK TO LDAP AUTH IN ENABLE
171 #======================================================================
171 #======================================================================
172 if str2bool(ldap_settings.get('ldap_active')):
172 if str2bool(ldap_settings.get('ldap_active')):
173 log.debug("Authenticating user using ldap")
173 log.debug("Authenticating user using ldap")
174 kwargs = {
174 kwargs = {
175 'server':ldap_settings.get('ldap_host', ''),
175 'server':ldap_settings.get('ldap_host', ''),
176 'base_dn':ldap_settings.get('ldap_base_dn', ''),
176 'base_dn':ldap_settings.get('ldap_base_dn', ''),
177 'port':ldap_settings.get('ldap_port'),
177 'port':ldap_settings.get('ldap_port'),
178 'bind_dn':ldap_settings.get('ldap_dn_user'),
178 'bind_dn':ldap_settings.get('ldap_dn_user'),
179 'bind_pass':ldap_settings.get('ldap_dn_pass'),
179 'bind_pass':ldap_settings.get('ldap_dn_pass'),
180 'use_ldaps':str2bool(ldap_settings.get('ldap_ldaps')),
180 'use_ldaps':str2bool(ldap_settings.get('ldap_ldaps')),
181 'ldap_version':3,
181 'ldap_version':3,
182 }
182 }
183 log.debug('Checking for ldap authentication')
183 log.debug('Checking for ldap authentication')
184 try:
184 try:
185 aldap = AuthLdap(**kwargs)
185 aldap = AuthLdap(**kwargs)
186 res = aldap.authenticate_ldap(username, password)
186 res = aldap.authenticate_ldap(username, password)
187 log.debug('Got ldap response %s', res)
187 log.debug('Got ldap response %s', res)
188
188
189 if user_model.create_ldap(username, password):
189 if user_model.create_ldap(username, password):
190 log.info('created new ldap user')
190 log.info('created new ldap user')
191
191
192 return True
192 return True
193 except (LdapUsernameError, LdapPasswordError,):
193 except (LdapUsernameError, LdapPasswordError,):
194 pass
194 pass
195 except (Exception,):
195 except (Exception,):
196 log.error(traceback.format_exc())
196 log.error(traceback.format_exc())
197 pass
197 pass
198 return False
198 return False
199
199
200 class AuthUser(object):
200 class AuthUser(object):
201 """
201 """
202 A simple object that handles a mercurial username for authentication
202 A simple object that handles a mercurial username for authentication
203 """
203 """
204 def __init__(self):
204 def __init__(self):
205 self.username = 'None'
205 self.username = 'None'
206 self.name = ''
206 self.name = ''
207 self.lastname = ''
207 self.lastname = ''
208 self.email = ''
208 self.email = ''
209 self.user_id = None
209 self.user_id = None
210 self.is_authenticated = False
210 self.is_authenticated = False
211 self.is_admin = False
211 self.is_admin = False
212 self.permissions = {}
212 self.permissions = {}
213
213
214 def __repr__(self):
214 def __repr__(self):
215 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
215 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
216
216
217 def set_available_permissions(config):
217 def set_available_permissions(config):
218 """
218 """
219 This function will propagate pylons globals with all available defined
219 This function will propagate pylons globals with all available defined
220 permission given in db. We don't wannt to check each time from db for new
220 permission given in db. We don't wannt to check each time from db for new
221 permissions since adding a new permission also requires application restart
221 permissions since adding a new permission also requires application restart
222 ie. to decorate new views with the newly created permission
222 ie. to decorate new views with the newly created permission
223 :param config:
223 :param config:
224 """
224 """
225 log.info('getting information about all available permissions')
225 log.info('getting information about all available permissions')
226 try:
226 try:
227 sa = meta.Session()
227 sa = meta.Session()
228 all_perms = sa.query(Permission).all()
228 all_perms = sa.query(Permission).all()
229 except:
229 except:
230 pass
230 pass
231 finally:
231 finally:
232 meta.Session.remove()
232 meta.Session.remove()
233
233
234 config['available_permissions'] = [x.permission_name for x in all_perms]
234 config['available_permissions'] = [x.permission_name for x in all_perms]
235
235
236 def set_base_path(config):
236 def set_base_path(config):
237 config['base_path'] = config['pylons.app_globals'].base_path
237 config['base_path'] = config['pylons.app_globals'].base_path
238
238
239
239
240 def fill_perms(user):
240 def fill_perms(user):
241 """
241 """
242 Fills user permission attribute with permissions taken from database
242 Fills user permission attribute with permissions taken from database
243 :param user:
243 :param user:
244 """
244 """
245
245
246 sa = meta.Session()
246 sa = meta.Session()
247 user.permissions['repositories'] = {}
247 user.permissions['repositories'] = {}
248 user.permissions['global'] = set()
248 user.permissions['global'] = set()
249
249
250 #===========================================================================
250 #===========================================================================
251 # fetch default permissions
251 # fetch default permissions
252 #===========================================================================
252 #===========================================================================
253 default_user = UserModel().get_by_username('default', cache=True)
253 default_user = UserModel().get_by_username('default', cache=True)
254
254
255 default_perms = sa.query(RepoToPerm, Repository, Permission)\
255 default_perms = sa.query(RepoToPerm, Repository, Permission)\
256 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
256 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
257 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
257 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
258 .filter(RepoToPerm.user == default_user).all()
258 .filter(RepoToPerm.user == default_user).all()
259
259
260 if user.is_admin:
260 if user.is_admin:
261 #=======================================================================
261 #=======================================================================
262 # #admin have all default rights set to admin
262 # #admin have all default rights set to admin
263 #=======================================================================
263 #=======================================================================
264 user.permissions['global'].add('hg.admin')
264 user.permissions['global'].add('hg.admin')
265
265
266 for perm in default_perms:
266 for perm in default_perms:
267 p = 'repository.admin'
267 p = 'repository.admin'
268 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
268 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
269
269
270 else:
270 else:
271 #=======================================================================
271 #=======================================================================
272 # set default permissions
272 # set default permissions
273 #=======================================================================
273 #=======================================================================
274
274
275 #default global
275 #default global
276 default_global_perms = sa.query(UserToPerm)\
276 default_global_perms = sa.query(UserToPerm)\
277 .filter(UserToPerm.user == sa.query(User)\
277 .filter(UserToPerm.user == sa.query(User)\
278 .filter(User.username == 'default').one())
278 .filter(User.username == 'default').one())
279
279
280 for perm in default_global_perms:
280 for perm in default_global_perms:
281 user.permissions['global'].add(perm.permission.permission_name)
281 user.permissions['global'].add(perm.permission.permission_name)
282
282
283 #default repositories
283 #default repositories
284 for perm in default_perms:
284 for perm in default_perms:
285 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
285 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
286 #disable defaults for private repos,
286 #disable defaults for private repos,
287 p = 'repository.none'
287 p = 'repository.none'
288 elif perm.Repository.user_id == user.user_id:
288 elif perm.Repository.user_id == user.user_id:
289 #set admin if owner
289 #set admin if owner
290 p = 'repository.admin'
290 p = 'repository.admin'
291 else:
291 else:
292 p = perm.Permission.permission_name
292 p = perm.Permission.permission_name
293
293
294 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
294 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
295
295
296 #=======================================================================
296 #=======================================================================
297 # #overwrite default with user permissions if any
297 # #overwrite default with user permissions if any
298 #=======================================================================
298 #=======================================================================
299 user_perms = sa.query(RepoToPerm, Permission, Repository)\
299 user_perms = sa.query(RepoToPerm, Permission, Repository)\
300 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
300 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
301 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
301 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
302 .filter(RepoToPerm.user_id == user.user_id).all()
302 .filter(RepoToPerm.user_id == user.user_id).all()
303
303
304 for perm in user_perms:
304 for perm in user_perms:
305 if perm.Repository.user_id == user.user_id:#set admin if owner
305 if perm.Repository.user_id == user.user_id:#set admin if owner
306 p = 'repository.admin'
306 p = 'repository.admin'
307 else:
307 else:
308 p = perm.Permission.permission_name
308 p = perm.Permission.permission_name
309 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
309 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
310 meta.Session.remove()
310 meta.Session.remove()
311 return user
311 return user
312
312
313 def get_user(session):
313 def get_user(session):
314 """
314 """
315 Gets user from session, and wraps permissions into user
315 Gets user from session, and wraps permissions into user
316 :param session:
316 :param session:
317 """
317 """
318 user = session.get('rhodecode_user', AuthUser())
318 user = session.get('rhodecode_user', AuthUser())
319 #if the user is not logged in we check for anonymous access
319 #if the user is not logged in we check for anonymous access
320 #if user is logged and it's a default user check if we still have anonymous
320 #if user is logged and it's a default user check if we still have anonymous
321 #access enabled
321 #access enabled
322 if user.user_id is None or user.username == 'default':
322 if user.user_id is None or user.username == 'default':
323 anonymous_user = UserModel().get_by_username('default', cache=True)
323 anonymous_user = UserModel().get_by_username('default', cache=True)
324 if anonymous_user.active is True:
324 if anonymous_user.active is True:
325 #then we set this user is logged in
325 #then we set this user is logged in
326 user.is_authenticated = True
326 user.is_authenticated = True
327 user.user_id = anonymous_user.user_id
327 user.user_id = anonymous_user.user_id
328 else:
328 else:
329 user.is_authenticated = False
329 user.is_authenticated = False
330
330
331 if user.is_authenticated:
331 if user.is_authenticated:
332 user = UserModel().fill_data(user)
332 user = UserModel().fill_data(user)
333
333
334 user = fill_perms(user)
334 user = fill_perms(user)
335 session['rhodecode_user'] = user
335 session['rhodecode_user'] = user
336 session.save()
336 session.save()
337 return user
337 return user
338
338
339 #===============================================================================
339 #===============================================================================
340 # CHECK DECORATORS
340 # CHECK DECORATORS
341 #===============================================================================
341 #===============================================================================
342 class LoginRequired(object):
342 class LoginRequired(object):
343 """Must be logged in to execute this function else
343 """Must be logged in to execute this function else
344 redirect to login page"""
344 redirect to login page"""
345
345
346 def __call__(self, func):
346 def __call__(self, func):
347 return decorator(self.__wrapper, func)
347 return decorator(self.__wrapper, func)
348
348
349 def __wrapper(self, func, *fargs, **fkwargs):
349 def __wrapper(self, func, *fargs, **fkwargs):
350 user = session.get('rhodecode_user', AuthUser())
350 user = session.get('rhodecode_user', AuthUser())
351 log.debug('Checking login required for user:%s', user.username)
351 log.debug('Checking login required for user:%s', user.username)
352 if user.is_authenticated:
352 if user.is_authenticated:
353 log.debug('user %s is authenticated', user.username)
353 log.debug('user %s is authenticated', user.username)
354 return func(*fargs, **fkwargs)
354 return func(*fargs, **fkwargs)
355 else:
355 else:
356 log.warn('user %s not authenticated', user.username)
356 log.warn('user %s not authenticated', user.username)
357
357 p = url.current()
358 p = ''
359 if request.environ.get('SCRIPT_NAME') != '/':
360 p += request.environ.get('SCRIPT_NAME')
361
362 p += request.environ.get('PATH_INFO')
363 if request.environ.get('QUERY_STRING'):
364 p += '?' + request.environ.get('QUERY_STRING')
365
358
366 log.debug('redirecting to login page with %s', p)
359 log.debug('redirecting to login page with %s', p)
367 return redirect(url('login_home', came_from=p))
360 return redirect(url('login_home', came_from=p))
368
361
369 class NotAnonymous(object):
362 class NotAnonymous(object):
370 """Must be logged in to execute this function else
363 """Must be logged in to execute this function else
371 redirect to login page"""
364 redirect to login page"""
372
365
373 def __call__(self, func):
366 def __call__(self, func):
374 return decorator(self.__wrapper, func)
367 return decorator(self.__wrapper, func)
375
368
376 def __wrapper(self, func, *fargs, **fkwargs):
369 def __wrapper(self, func, *fargs, **fkwargs):
377 user = session.get('rhodecode_user', AuthUser())
370 user = session.get('rhodecode_user', AuthUser())
378 log.debug('Checking if user is not anonymous')
371 log.debug('Checking if user is not anonymous')
379
372
380 anonymous = user.username == 'default'
373 anonymous = user.username == 'default'
381
374
382 if anonymous:
375 if anonymous:
383 p = ''
376 p = ''
384 if request.environ.get('SCRIPT_NAME') != '/':
377 if request.environ.get('SCRIPT_NAME') != '/':
385 p += request.environ.get('SCRIPT_NAME')
378 p += request.environ.get('SCRIPT_NAME')
386
379
387 p += request.environ.get('PATH_INFO')
380 p += request.environ.get('PATH_INFO')
388 if request.environ.get('QUERY_STRING'):
381 if request.environ.get('QUERY_STRING'):
389 p += '?' + request.environ.get('QUERY_STRING')
382 p += '?' + request.environ.get('QUERY_STRING')
390 return redirect(url('login_home', came_from=p))
383 return redirect(url('login_home', came_from=p))
391 else:
384 else:
392 return func(*fargs, **fkwargs)
385 return func(*fargs, **fkwargs)
393
386
394 class PermsDecorator(object):
387 class PermsDecorator(object):
395 """Base class for decorators"""
388 """Base class for decorators"""
396
389
397 def __init__(self, *required_perms):
390 def __init__(self, *required_perms):
398 available_perms = config['available_permissions']
391 available_perms = config['available_permissions']
399 for perm in required_perms:
392 for perm in required_perms:
400 if perm not in available_perms:
393 if perm not in available_perms:
401 raise Exception("'%s' permission is not defined" % perm)
394 raise Exception("'%s' permission is not defined" % perm)
402 self.required_perms = set(required_perms)
395 self.required_perms = set(required_perms)
403 self.user_perms = None
396 self.user_perms = None
404
397
405 def __call__(self, func):
398 def __call__(self, func):
406 return decorator(self.__wrapper, func)
399 return decorator(self.__wrapper, func)
407
400
408
401
409 def __wrapper(self, func, *fargs, **fkwargs):
402 def __wrapper(self, func, *fargs, **fkwargs):
410 # _wrapper.__name__ = func.__name__
403 # _wrapper.__name__ = func.__name__
411 # _wrapper.__dict__.update(func.__dict__)
404 # _wrapper.__dict__.update(func.__dict__)
412 # _wrapper.__doc__ = func.__doc__
405 # _wrapper.__doc__ = func.__doc__
413 self.user = session.get('rhodecode_user', AuthUser())
406 self.user = session.get('rhodecode_user', AuthUser())
414 self.user_perms = self.user.permissions
407 self.user_perms = self.user.permissions
415 log.debug('checking %s permissions %s for %s %s',
408 log.debug('checking %s permissions %s for %s %s',
416 self.__class__.__name__, self.required_perms, func.__name__,
409 self.__class__.__name__, self.required_perms, func.__name__,
417 self.user)
410 self.user)
418
411
419 if self.check_permissions():
412 if self.check_permissions():
420 log.debug('Permission granted for %s %s', func.__name__, self.user)
413 log.debug('Permission granted for %s %s', func.__name__, self.user)
421
414
422 return func(*fargs, **fkwargs)
415 return func(*fargs, **fkwargs)
423
416
424 else:
417 else:
425 log.warning('Permission denied for %s %s', func.__name__, self.user)
418 log.warning('Permission denied for %s %s', func.__name__, self.user)
426 #redirect with forbidden ret code
419 #redirect with forbidden ret code
427 return abort(403)
420 return abort(403)
428
421
429
422
430
423
431 def check_permissions(self):
424 def check_permissions(self):
432 """Dummy function for overriding"""
425 """Dummy function for overriding"""
433 raise Exception('You have to write this function in child class')
426 raise Exception('You have to write this function in child class')
434
427
435 class HasPermissionAllDecorator(PermsDecorator):
428 class HasPermissionAllDecorator(PermsDecorator):
436 """Checks for access permission for all given predicates. All of them
429 """Checks for access permission for all given predicates. All of them
437 have to be meet in order to fulfill the request
430 have to be meet in order to fulfill the request
438 """
431 """
439
432
440 def check_permissions(self):
433 def check_permissions(self):
441 if self.required_perms.issubset(self.user_perms.get('global')):
434 if self.required_perms.issubset(self.user_perms.get('global')):
442 return True
435 return True
443 return False
436 return False
444
437
445
438
446 class HasPermissionAnyDecorator(PermsDecorator):
439 class HasPermissionAnyDecorator(PermsDecorator):
447 """Checks for access permission for any of given predicates. In order to
440 """Checks for access permission for any of given predicates. In order to
448 fulfill the request any of predicates must be meet
441 fulfill the request any of predicates must be meet
449 """
442 """
450
443
451 def check_permissions(self):
444 def check_permissions(self):
452 if self.required_perms.intersection(self.user_perms.get('global')):
445 if self.required_perms.intersection(self.user_perms.get('global')):
453 return True
446 return True
454 return False
447 return False
455
448
456 class HasRepoPermissionAllDecorator(PermsDecorator):
449 class HasRepoPermissionAllDecorator(PermsDecorator):
457 """Checks for access permission for all given predicates for specific
450 """Checks for access permission for all given predicates for specific
458 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
459 """
452 """
460
453
461 def check_permissions(self):
454 def check_permissions(self):
462 repo_name = get_repo_slug(request)
455 repo_name = get_repo_slug(request)
463 try:
456 try:
464 user_perms = set([self.user_perms['repositories'][repo_name]])
457 user_perms = set([self.user_perms['repositories'][repo_name]])
465 except KeyError:
458 except KeyError:
466 return False
459 return False
467 if self.required_perms.issubset(user_perms):
460 if self.required_perms.issubset(user_perms):
468 return True
461 return True
469 return False
462 return False
470
463
471
464
472 class HasRepoPermissionAnyDecorator(PermsDecorator):
465 class HasRepoPermissionAnyDecorator(PermsDecorator):
473 """Checks for access permission for any of given predicates for specific
466 """Checks for access permission for any of given predicates for specific
474 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
475 """
468 """
476
469
477 def check_permissions(self):
470 def check_permissions(self):
478 repo_name = get_repo_slug(request)
471 repo_name = get_repo_slug(request)
479
472
480 try:
473 try:
481 user_perms = set([self.user_perms['repositories'][repo_name]])
474 user_perms = set([self.user_perms['repositories'][repo_name]])
482 except KeyError:
475 except KeyError:
483 return False
476 return False
484 if self.required_perms.intersection(user_perms):
477 if self.required_perms.intersection(user_perms):
485 return True
478 return True
486 return False
479 return False
487 #===============================================================================
480 #===============================================================================
488 # CHECK FUNCTIONS
481 # CHECK FUNCTIONS
489 #===============================================================================
482 #===============================================================================
490
483
491 class PermsFunction(object):
484 class PermsFunction(object):
492 """Base function for other check functions"""
485 """Base function for other check functions"""
493
486
494 def __init__(self, *perms):
487 def __init__(self, *perms):
495 available_perms = config['available_permissions']
488 available_perms = config['available_permissions']
496
489
497 for perm in perms:
490 for perm in perms:
498 if perm not in available_perms:
491 if perm not in available_perms:
499 raise Exception("'%s' permission in not defined" % perm)
492 raise Exception("'%s' permission in not defined" % perm)
500 self.required_perms = set(perms)
493 self.required_perms = set(perms)
501 self.user_perms = None
494 self.user_perms = None
502 self.granted_for = ''
495 self.granted_for = ''
503 self.repo_name = None
496 self.repo_name = None
504
497
505 def __call__(self, check_Location=''):
498 def __call__(self, check_Location=''):
506 user = session.get('rhodecode_user', False)
499 user = session.get('rhodecode_user', False)
507 if not user:
500 if not user:
508 return False
501 return False
509 self.user_perms = user.permissions
502 self.user_perms = user.permissions
510 self.granted_for = user.username
503 self.granted_for = user.username
511 log.debug('checking %s %s %s', self.__class__.__name__,
504 log.debug('checking %s %s %s', self.__class__.__name__,
512 self.required_perms, user)
505 self.required_perms, user)
513
506
514 if self.check_permissions():
507 if self.check_permissions():
515 log.debug('Permission granted for %s @ %s %s', self.granted_for,
508 log.debug('Permission granted for %s @ %s %s', self.granted_for,
516 check_Location, user)
509 check_Location, user)
517 return True
510 return True
518
511
519 else:
512 else:
520 log.warning('Permission denied for %s @ %s %s', self.granted_for,
513 log.warning('Permission denied for %s @ %s %s', self.granted_for,
521 check_Location, user)
514 check_Location, user)
522 return False
515 return False
523
516
524 def check_permissions(self):
517 def check_permissions(self):
525 """Dummy function for overriding"""
518 """Dummy function for overriding"""
526 raise Exception('You have to write this function in child class')
519 raise Exception('You have to write this function in child class')
527
520
528 class HasPermissionAll(PermsFunction):
521 class HasPermissionAll(PermsFunction):
529 def check_permissions(self):
522 def check_permissions(self):
530 if self.required_perms.issubset(self.user_perms.get('global')):
523 if self.required_perms.issubset(self.user_perms.get('global')):
531 return True
524 return True
532 return False
525 return False
533
526
534 class HasPermissionAny(PermsFunction):
527 class HasPermissionAny(PermsFunction):
535 def check_permissions(self):
528 def check_permissions(self):
536 if self.required_perms.intersection(self.user_perms.get('global')):
529 if self.required_perms.intersection(self.user_perms.get('global')):
537 return True
530 return True
538 return False
531 return False
539
532
540 class HasRepoPermissionAll(PermsFunction):
533 class HasRepoPermissionAll(PermsFunction):
541
534
542 def __call__(self, repo_name=None, check_Location=''):
535 def __call__(self, repo_name=None, check_Location=''):
543 self.repo_name = repo_name
536 self.repo_name = repo_name
544 return super(HasRepoPermissionAll, self).__call__(check_Location)
537 return super(HasRepoPermissionAll, self).__call__(check_Location)
545
538
546 def check_permissions(self):
539 def check_permissions(self):
547 if not self.repo_name:
540 if not self.repo_name:
548 self.repo_name = get_repo_slug(request)
541 self.repo_name = get_repo_slug(request)
549
542
550 try:
543 try:
551 self.user_perms = set([self.user_perms['repositories']\
544 self.user_perms = set([self.user_perms['repositories']\
552 [self.repo_name]])
545 [self.repo_name]])
553 except KeyError:
546 except KeyError:
554 return False
547 return False
555 self.granted_for = self.repo_name
548 self.granted_for = self.repo_name
556 if self.required_perms.issubset(self.user_perms):
549 if self.required_perms.issubset(self.user_perms):
557 return True
550 return True
558 return False
551 return False
559
552
560 class HasRepoPermissionAny(PermsFunction):
553 class HasRepoPermissionAny(PermsFunction):
561
554
562 def __call__(self, repo_name=None, check_Location=''):
555 def __call__(self, repo_name=None, check_Location=''):
563 self.repo_name = repo_name
556 self.repo_name = repo_name
564 return super(HasRepoPermissionAny, self).__call__(check_Location)
557 return super(HasRepoPermissionAny, self).__call__(check_Location)
565
558
566 def check_permissions(self):
559 def check_permissions(self):
567 if not self.repo_name:
560 if not self.repo_name:
568 self.repo_name = get_repo_slug(request)
561 self.repo_name = get_repo_slug(request)
569
562
570 try:
563 try:
571 self.user_perms = set([self.user_perms['repositories']\
564 self.user_perms = set([self.user_perms['repositories']\
572 [self.repo_name]])
565 [self.repo_name]])
573 except KeyError:
566 except KeyError:
574 return False
567 return False
575 self.granted_for = self.repo_name
568 self.granted_for = self.repo_name
576 if self.required_perms.intersection(self.user_perms):
569 if self.required_perms.intersection(self.user_perms):
577 return True
570 return True
578 return False
571 return False
579
572
580 #===============================================================================
573 #===============================================================================
581 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
574 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
582 #===============================================================================
575 #===============================================================================
583
576
584 class HasPermissionAnyMiddleware(object):
577 class HasPermissionAnyMiddleware(object):
585 def __init__(self, *perms):
578 def __init__(self, *perms):
586 self.required_perms = set(perms)
579 self.required_perms = set(perms)
587
580
588 def __call__(self, user, repo_name):
581 def __call__(self, user, repo_name):
589 usr = AuthUser()
582 usr = AuthUser()
590 usr.user_id = user.user_id
583 usr.user_id = user.user_id
591 usr.username = user.username
584 usr.username = user.username
592 usr.is_admin = user.admin
585 usr.is_admin = user.admin
593
586
594 try:
587 try:
595 self.user_perms = set([fill_perms(usr)\
588 self.user_perms = set([fill_perms(usr)\
596 .permissions['repositories'][repo_name]])
589 .permissions['repositories'][repo_name]])
597 except:
590 except:
598 self.user_perms = set()
591 self.user_perms = set()
599 self.granted_for = ''
592 self.granted_for = ''
600 self.username = user.username
593 self.username = user.username
601 self.repo_name = repo_name
594 self.repo_name = repo_name
602 return self.check_permissions()
595 return self.check_permissions()
603
596
604 def check_permissions(self):
597 def check_permissions(self):
605 log.debug('checking mercurial protocol '
598 log.debug('checking mercurial protocol '
606 'permissions for user:%s repository:%s',
599 'permissions for user:%s repository:%s',
607 self.username, self.repo_name)
600 self.username, self.repo_name)
608 if self.required_perms.intersection(self.user_perms):
601 if self.required_perms.intersection(self.user_perms):
609 log.debug('permission granted')
602 log.debug('permission granted')
610 return True
603 return True
611 log.debug('permission denied')
604 log.debug('permission denied')
612 return False
605 return False
@@ -1,38 +1,82 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, # level 50
13 'ERROR': RED, # level 40
13 'ERROR': RED, # level 40
14 'WARNING': CYAN, # level 30
14 'WARNING': CYAN, # level 30
15 'INFO': GREEN, # level 20
15 'INFO': GREEN, # level 20
16 'DEBUG': BLUE, # level 10
16 'DEBUG': BLUE, # level 10
17 'SQL' : YELLOW
17 }
18 }
18
19
20 def one_space_trim(s):
21 if s.find(" ") == -1:
22 return s
23 else:
24 s = s.replace(' ', ' ')
25 return one_space_trim(s)
26
27 def format_sql(sql):
28 sql = sql.replace('\n', '')
29 sql = one_space_trim(sql)
30 sql = sql\
31 .replace(',', ',\n\t')\
32 .replace('SELECT', '\n\tSELECT \n\t')\
33 .replace('UPDATE', '\n\tUPDATE \n\t')\
34 .replace('DELETE', '\n\tDELETE \n\t')\
35 .replace('FROM', '\n\tFROM')\
36 .replace('ORDER BY', '\n\tORDER BY')\
37 .replace('LIMIT', '\n\tLIMIT')\
38 .replace('WHERE', '\n\tWHERE')\
39 .replace('AND', '\n\tAND')\
40 .replace('LEFT', '\n\tLEFT')\
41 .replace('INNER', '\n\tINNER')\
42 .replace('INSERT', '\n\tINSERT')\
43 .replace('DELETE', '\n\tDELETE')
44 return sql
45
19 class ColorFormatter(logging.Formatter):
46 class ColorFormatter(logging.Formatter):
20
47
21 def __init__(self, *args, **kwargs):
48 def __init__(self, *args, **kwargs):
22 # can't do super(...) here because Formatter is an old school class
49 # can't do super(...) here because Formatter is an old school class
23 logging.Formatter.__init__(self, *args, **kwargs)
50 logging.Formatter.__init__(self, *args, **kwargs)
24
51
25 def format(self, record):
52 def format(self, record):
26 """
53 """
27 Changes record's levelname to use with COLORS enum
54 Changes record's levelname to use with COLORS enum
28 """
55 """
29
56
30 levelname = record.levelname
57 levelname = record.levelname
31 start = COLOR_SEQ % (COLORS[levelname])
58 start = COLOR_SEQ % (COLORS[levelname])
32 def_record = logging.Formatter.format(self, record)
59 def_record = logging.Formatter.format(self, record)
33 end = RESET_SEQ
60 end = RESET_SEQ
34
61
35 colored_record = start + def_record + end
62 colored_record = start + def_record + end
36 return colored_record
63 return colored_record
37
64
38 logging.ColorFormatter = ColorFormatter
65
66 class ColorFormatterSql(logging.Formatter):
67
68 def __init__(self, *args, **kwargs):
69 # can't do super(...) here because Formatter is an old school class
70 logging.Formatter.__init__(self, *args, **kwargs)
71
72 def format(self, record):
73 """
74 Changes record's levelname to use with COLORS enum
75 """
76
77 start = COLOR_SEQ % (COLORS['SQL'])
78 def_record = format_sql(logging.Formatter.format(self, record))
79 end = RESET_SEQ
80
81 colored_record = start + def_record + end
82 return colored_record
@@ -1,30 +1,48 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-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 Custom Exceptions modules
7
8 :created_on: Apr 10, 2010
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING 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 Created on Nov 17, 2010
26
20 Custom Exceptions modules
27 class LdapUsernameError(Exception):
21 @author: marcink
28 pass
22 """
29
30
31 class LdapPasswordError(Exception):
32 pass
33
23
34
24 class LdapUsernameError(Exception):pass
35 class LdapConnectionError(Exception):
25 class LdapPasswordError(Exception):pass
36 pass
26 class LdapConnectionError(Exception):pass
37
27 class LdapImportError(Exception):pass
38
39 class LdapImportError(Exception):
40 pass
28
41
29 class DefaultUserException(Exception):pass
42
30 class UserOwnsReposException(Exception):pass
43 class DefaultUserException(Exception):
44 pass
45
46
47 class UserOwnsReposException(Exception):
48 pass
@@ -1,120 +1,133 b''
1 import os, time
1 import os, time
2 import sys
2 import sys
3 from warnings import warn
3 from warnings import warn
4 from multiprocessing.util import Finalize
4 from multiprocessing.util import Finalize
5 import errno
5 import errno
6
6
7 from rhodecode import __platform__, PLATFORM_WIN
8
9 if __platform__ in PLATFORM_WIN:
10 import ctypes
11 def kill(pid):
12 """kill function for Win32"""
13 kernel32 = ctypes.windll.kernel32
14 handle = kernel32.OpenProcess(1, 0, pid)
15 return (0 != kernel32.TerminateProcess(handle, 0))
16
17 else:
18 kill = os.kill
19
7 class LockHeld(Exception):pass
20 class LockHeld(Exception):pass
8
21
9
22
10 class DaemonLock(object):
23 class DaemonLock(object):
11 """daemon locking
24 """daemon locking
12 USAGE:
25 USAGE:
13 try:
26 try:
14 l = DaemonLock(desc='test lock')
27 l = DaemonLock(desc='test lock')
15 main()
28 main()
16 l.release()
29 l.release()
17 except LockHeld:
30 except LockHeld:
18 sys.exit(1)
31 sys.exit(1)
19 """
32 """
20
33
21 def __init__(self, file=None, callbackfn=None,
34 def __init__(self, file=None, callbackfn=None,
22 desc='daemon lock', debug=False):
35 desc='daemon lock', debug=False):
23
36
24 self.pidfile = file if file else os.path.join(os.path.dirname(__file__),
37 self.pidfile = file if file else os.path.join(os.path.dirname(__file__),
25 'running.lock')
38 'running.lock')
26 self.callbackfn = callbackfn
39 self.callbackfn = callbackfn
27 self.desc = desc
40 self.desc = desc
28 self.debug = debug
41 self.debug = debug
29 self.held = False
42 self.held = False
30 #run the lock automatically !
43 #run the lock automatically !
31 self.lock()
44 self.lock()
32 self._finalize = Finalize(self, DaemonLock._on_finalize,
45 self._finalize = Finalize(self, DaemonLock._on_finalize,
33 args=(self, debug), exitpriority=10)
46 args=(self, debug), exitpriority=10)
34
47
35 @staticmethod
48 @staticmethod
36 def _on_finalize(lock, debug):
49 def _on_finalize(lock, debug):
37 if lock.held:
50 if lock.held:
38 if debug:
51 if debug:
39 print 'leck held finilazing and running lock.release()'
52 print 'leck held finilazing and running lock.release()'
40 lock.release()
53 lock.release()
41
54
42
55
43 def lock(self):
56 def lock(self):
44 """locking function, if lock is present it will raise LockHeld exception
57 """locking function, if lock is present it will raise LockHeld exception
45 """
58 """
46 lockname = '%s' % (os.getpid())
59 lockname = '%s' % (os.getpid())
47 if self.debug:
60 if self.debug:
48 print 'running lock'
61 print 'running lock'
49 self.trylock()
62 self.trylock()
50 self.makelock(lockname, self.pidfile)
63 self.makelock(lockname, self.pidfile)
51 return True
64 return True
52
65
53 def trylock(self):
66 def trylock(self):
54 running_pid = False
67 running_pid = False
55 if self.debug:
68 if self.debug:
56 print 'checking for already running process'
69 print 'checking for already running process'
57 try:
70 try:
58 pidfile = open(self.pidfile, "r")
71 pidfile = open(self.pidfile, "r")
59 pidfile.seek(0)
72 pidfile.seek(0)
60 running_pid = int(pidfile.readline())
73 running_pid = int(pidfile.readline())
61
74
62 pidfile.close()
75 pidfile.close()
63
76
64 if self.debug:
77 if self.debug:
65 print 'lock file present running_pid: %s, checking for execution'\
78 print 'lock file present running_pid: %s, checking for execution'\
66 % running_pid
79 % running_pid
67 # Now we check the PID from lock file matches to the current
80 # Now we check the PID from lock file matches to the current
68 # process PID
81 # process PID
69 if running_pid:
82 if running_pid:
70 try:
83 try:
71 os.kill(running_pid, 0)
84 kill(running_pid, 0)
72 except OSError, exc:
85 except OSError, exc:
73 if exc.errno in (errno.ESRCH, errno.EPERM):
86 if exc.errno in (errno.ESRCH, errno.EPERM):
74 print "Lock File is there but the program is not running"
87 print "Lock File is there but the program is not running"
75 print "Removing lock file for the: %s" % running_pid
88 print "Removing lock file for the: %s" % running_pid
76 self.release()
89 self.release()
77 else:
90 else:
78 raise
91 raise
79 else:
92 else:
80 print "You already have an instance of the program running"
93 print "You already have an instance of the program running"
81 print "It is running as process %s" % running_pid
94 print "It is running as process %s" % running_pid
82 raise LockHeld()
95 raise LockHeld()
83
96
84 except IOError, e:
97 except IOError, e:
85 if e.errno != 2:
98 if e.errno != 2:
86 raise
99 raise
87
100
88 def release(self):
101 def release(self):
89 """releases the pid by removing the pidfile
102 """releases the pid by removing the pidfile
90 """
103 """
91 if self.debug:
104 if self.debug:
92 print 'trying to release the pidlock'
105 print 'trying to release the pidlock'
93
106
94 if self.callbackfn:
107 if self.callbackfn:
95 #execute callback function on release
108 #execute callback function on release
96 if self.debug:
109 if self.debug:
97 print 'executing callback function %s' % self.callbackfn
110 print 'executing callback function %s' % self.callbackfn
98 self.callbackfn()
111 self.callbackfn()
99 try:
112 try:
100 if self.debug:
113 if self.debug:
101 print 'removing pidfile %s' % self.pidfile
114 print 'removing pidfile %s' % self.pidfile
102 os.remove(self.pidfile)
115 os.remove(self.pidfile)
103 self.held = False
116 self.held = False
104 except OSError, e:
117 except OSError, e:
105 if self.debug:
118 if self.debug:
106 print 'removing pidfile failed %s' % e
119 print 'removing pidfile failed %s' % e
107 pass
120 pass
108
121
109 def makelock(self, lockname, pidfile):
122 def makelock(self, lockname, pidfile):
110 """
123 """
111 this function will make an actual lock
124 this function will make an actual lock
112 :param lockname: acctual pid of file
125 :param lockname: acctual pid of file
113 :param pidfile: the file to write the pid in
126 :param pidfile: the file to write the pid in
114 """
127 """
115 if self.debug:
128 if self.debug:
116 print 'creating a file %s and pid: %s' % (pidfile, lockname)
129 print 'creating a file %s and pid: %s' % (pidfile, lockname)
117 pidfile = open(self.pidfile, "wb")
130 pidfile = open(self.pidfile, "wb")
118 pidfile.write(lockname)
131 pidfile.write(lockname)
119 pidfile.close
132 pidfile.close
120 self.held = True
133 self.held = True
@@ -1,59 +1,28 b''
1 from sqlalchemy.interfaces import ConnectionProxy
1 from sqlalchemy.interfaces import ConnectionProxy
2 import time
2 import time
3 from sqlalchemy import log
3 import logging
4 log = logging.getLogger('timerproxy')
5
4 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
6 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
5
7
6 def color_sql(sql):
8 def color_sql(sql):
7 COLOR_SEQ = "\033[1;%dm"
9 COLOR_SEQ = "\033[1;%dm"
8 COLOR_SQL = YELLOW
10 COLOR_SQL = YELLOW
9 normal = '\x1b[0m'
11 normal = '\x1b[0m'
10 return COLOR_SEQ % COLOR_SQL + sql + normal
12 return COLOR_SEQ % COLOR_SQL + sql + normal
11
12 def one_space_trim(s):
13 if s.find(" ") == -1:
14 return s
15 else:
16 s = s.replace(' ', ' ')
17 return one_space_trim(s)
18
19 def format_sql(sql):
20 sql = color_sql(sql)
21 sql = sql.replace('\n', '')
22 sql = one_space_trim(sql)
23 sql = sql\
24 .replace(',', ',\n\t')\
25 .replace('SELECT', '\n\tSELECT \n\t')\
26 .replace('UPDATE', '\n\tUPDATE \n\t')\
27 .replace('DELETE', '\n\tDELETE \n\t')\
28 .replace('FROM', '\n\tFROM')\
29 .replace('ORDER BY', '\n\tORDER BY')\
30 .replace('LIMIT', '\n\tLIMIT')\
31 .replace('WHERE', '\n\tWHERE')\
32 .replace('AND', '\n\tAND')\
33 .replace('LEFT', '\n\tLEFT')\
34 .replace('INNER', '\n\tINNER')\
35 .replace('INSERT', '\n\tINSERT')\
36 .replace('DELETE', '\n\tDELETE')
37 return sql
38
39
13
40 class TimerProxy(ConnectionProxy):
14 class TimerProxy(ConnectionProxy):
41
15
42 def __init__(self):
16 def __init__(self):
43 super(TimerProxy, self).__init__()
17 super(TimerProxy, self).__init__()
44 self.logging_name = 'timerProxy'
18
45 self.log = log.instance_logger(self, True)
19 def cursor_execute(self, execute, cursor, statement, parameters,
46
20 context, executemany):
47 def cursor_execute(self, execute, cursor, statement, parameters, context, executemany):
21
48
49 now = time.time()
22 now = time.time()
50 try:
23 try:
51 self.log.info(">>>>> STARTING QUERY >>>>>")
24 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
52 return execute(cursor, statement, parameters, context)
25 return execute(cursor, statement, parameters, context)
53 finally:
26 finally:
54 total = time.time() - now
27 total = time.time() - now
55 try:
28 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
56 self.log.info(format_sql("Query: %s" % statement % parameters))
57 except TypeError:
58 self.log.info(format_sql("Query: %s %s" % (statement, parameters)))
59 self.log.info("<<<<< TOTAL TIME: %f <<<<<" % total)
General Comments 0
You need to be logged in to leave comments. Login now