##// END OF EJS Templates
encryption: use common method to fetch encryption key for encrypted fields.
marcink -
r261:c66c7ec2 default
parent child Browse files
Show More
@@ -1,103 +1,103 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import shlex
22 import shlex
23 import Pyro4
23 import Pyro4
24 import platform
24 import platform
25
25
26 from rhodecode.model import init_model
26 from rhodecode.model import init_model
27
27
28
28
29 def configure_pyro4(config):
29 def configure_pyro4(config):
30 """
30 """
31 Configure Pyro4 based on `config`.
31 Configure Pyro4 based on `config`.
32
32
33 This will mainly set the different configuration parameters of the Pyro4
33 This will mainly set the different configuration parameters of the Pyro4
34 library based on the settings in our INI files. The Pyro4 documentation
34 library based on the settings in our INI files. The Pyro4 documentation
35 lists more details about the specific settings and their meaning.
35 lists more details about the specific settings and their meaning.
36 """
36 """
37 Pyro4.config.COMMTIMEOUT = float(config['vcs.connection_timeout'])
37 Pyro4.config.COMMTIMEOUT = float(config['vcs.connection_timeout'])
38 Pyro4.config.SERIALIZER = 'pickle'
38 Pyro4.config.SERIALIZER = 'pickle'
39 Pyro4.config.SERIALIZERS_ACCEPTED.add('pickle')
39 Pyro4.config.SERIALIZERS_ACCEPTED.add('pickle')
40
40
41 # Note: We need server configuration in the WSGI processes
41 # Note: We need server configuration in the WSGI processes
42 # because we provide a callback server in certain vcs operations.
42 # because we provide a callback server in certain vcs operations.
43 Pyro4.config.SERVERTYPE = "multiplex"
43 Pyro4.config.SERVERTYPE = "multiplex"
44 Pyro4.config.POLLTIMEOUT = 0.01
44 Pyro4.config.POLLTIMEOUT = 0.01
45
45
46
46
47 def configure_vcs(config):
47 def configure_vcs(config):
48 """
48 """
49 Patch VCS config with some RhodeCode specific stuff
49 Patch VCS config with some RhodeCode specific stuff
50 """
50 """
51 from rhodecode.lib.vcs import conf
51 from rhodecode.lib.vcs import conf
52 from rhodecode.lib.utils2 import aslist
52 from rhodecode.lib.utils2 import aslist
53 conf.settings.BACKENDS = {
53 conf.settings.BACKENDS = {
54 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository',
54 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository',
55 'git': 'rhodecode.lib.vcs.backends.git.GitRepository',
55 'git': 'rhodecode.lib.vcs.backends.git.GitRepository',
56 'svn': 'rhodecode.lib.vcs.backends.svn.SubversionRepository',
56 'svn': 'rhodecode.lib.vcs.backends.svn.SubversionRepository',
57 }
57 }
58
58
59 conf.settings.HG_USE_REBASE_FOR_MERGING = config.get(
59 conf.settings.HG_USE_REBASE_FOR_MERGING = config.get(
60 'rhodecode_hg_use_rebase_for_merging', False)
60 'rhodecode_hg_use_rebase_for_merging', False)
61 conf.settings.GIT_REV_FILTER = shlex.split(
61 conf.settings.GIT_REV_FILTER = shlex.split(
62 config.get('git_rev_filter', '--all').strip())
62 config.get('git_rev_filter', '--all').strip())
63 conf.settings.DEFAULT_ENCODINGS = aslist(config.get('default_encoding',
63 conf.settings.DEFAULT_ENCODINGS = aslist(config.get('default_encoding',
64 'UTF-8'), sep=',')
64 'UTF-8'), sep=',')
65 conf.settings.ALIASES[:] = config.get('vcs.backends')
65 conf.settings.ALIASES[:] = config.get('vcs.backends')
66 conf.settings.SVN_COMPATIBLE_VERSION = config.get(
66 conf.settings.SVN_COMPATIBLE_VERSION = config.get(
67 'vcs.svn.compatible_version')
67 'vcs.svn.compatible_version')
68
68
69
69
70 def initialize_database(config):
70 def initialize_database(config):
71 from rhodecode.lib.utils2 import engine_from_config
71 from rhodecode.lib.utils2 import engine_from_config, get_encryption_key
72 engine = engine_from_config(config, 'sqlalchemy.db1.')
72 engine = engine_from_config(config, 'sqlalchemy.db1.')
73 init_model(engine, encryption_key=config['beaker.session.secret'])
73 init_model(engine, encryption_key=get_encryption_key(config))
74
74
75
75
76 def initialize_test_environment(settings, test_env=None):
76 def initialize_test_environment(settings, test_env=None):
77 if test_env is None:
77 if test_env is None:
78 test_env = not int(os.environ.get('RC_NO_TMP_PATH', 0))
78 test_env = not int(os.environ.get('RC_NO_TMP_PATH', 0))
79
79
80 from rhodecode.lib.utils import (
80 from rhodecode.lib.utils import (
81 create_test_directory, create_test_database, create_test_repositories,
81 create_test_directory, create_test_database, create_test_repositories,
82 create_test_index)
82 create_test_index)
83 from rhodecode.tests import TESTS_TMP_PATH
83 from rhodecode.tests import TESTS_TMP_PATH
84 # test repos
84 # test repos
85 if test_env:
85 if test_env:
86 create_test_directory(TESTS_TMP_PATH)
86 create_test_directory(TESTS_TMP_PATH)
87 create_test_database(TESTS_TMP_PATH, settings)
87 create_test_database(TESTS_TMP_PATH, settings)
88 create_test_repositories(TESTS_TMP_PATH, settings)
88 create_test_repositories(TESTS_TMP_PATH, settings)
89 create_test_index(TESTS_TMP_PATH, settings)
89 create_test_index(TESTS_TMP_PATH, settings)
90
90
91
91
92 def get_vcs_server_protocol(config):
92 def get_vcs_server_protocol(config):
93 protocol = config.get('vcs.server.protocol', 'pyro4')
93 protocol = config.get('vcs.server.protocol', 'pyro4')
94 return protocol
94 return protocol
95
95
96
96
97 def set_instance_id(config):
97 def set_instance_id(config):
98 """ Sets a dynamic generated config['instance_id'] if missing or '*' """
98 """ Sets a dynamic generated config['instance_id'] if missing or '*' """
99
99
100 config['instance_id'] = config.get('instance_id') or ''
100 config['instance_id'] = config.get('instance_id') or ''
101 if config['instance_id'] == '*' or not config['instance_id']:
101 if config['instance_id'] == '*' or not config['instance_id']:
102 _platform_id = platform.uname()[1] or 'instance'
102 _platform_id = platform.uname()[1] or 'instance'
103 config['instance_id'] = '%s-%s' % (_platform_id, os.getpid())
103 config['instance_id'] = '%s-%s' % (_platform_id, os.getpid())
@@ -1,854 +1,858 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2016 RhodeCode GmbH
3 # Copyright (C) 2011-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 Some simple helper functions
23 Some simple helper functions
24 """
24 """
25
25
26
26
27 import collections
27 import collections
28 import datetime
28 import datetime
29 import dateutil.relativedelta
29 import dateutil.relativedelta
30 import hashlib
30 import hashlib
31 import logging
31 import logging
32 import re
32 import re
33 import sys
33 import sys
34 import time
34 import time
35 import threading
35 import threading
36 import urllib
36 import urllib
37 import urlobject
37 import urlobject
38 import uuid
38 import uuid
39
39
40 import pygments.lexers
40 import pygments.lexers
41 import sqlalchemy
41 import sqlalchemy
42 import sqlalchemy.engine.url
42 import sqlalchemy.engine.url
43 import webob
43 import webob
44
44
45 import rhodecode
45 import rhodecode
46
46
47
47
48 def md5(s):
48 def md5(s):
49 return hashlib.md5(s).hexdigest()
49 return hashlib.md5(s).hexdigest()
50
50
51
51
52 def md5_safe(s):
52 def md5_safe(s):
53 return md5(safe_str(s))
53 return md5(safe_str(s))
54
54
55
55
56 def __get_lem():
56 def __get_lem():
57 """
57 """
58 Get language extension map based on what's inside pygments lexers
58 Get language extension map based on what's inside pygments lexers
59 """
59 """
60 d = collections.defaultdict(lambda: [])
60 d = collections.defaultdict(lambda: [])
61
61
62 def __clean(s):
62 def __clean(s):
63 s = s.lstrip('*')
63 s = s.lstrip('*')
64 s = s.lstrip('.')
64 s = s.lstrip('.')
65
65
66 if s.find('[') != -1:
66 if s.find('[') != -1:
67 exts = []
67 exts = []
68 start, stop = s.find('['), s.find(']')
68 start, stop = s.find('['), s.find(']')
69
69
70 for suffix in s[start + 1:stop]:
70 for suffix in s[start + 1:stop]:
71 exts.append(s[:s.find('[')] + suffix)
71 exts.append(s[:s.find('[')] + suffix)
72 return [e.lower() for e in exts]
72 return [e.lower() for e in exts]
73 else:
73 else:
74 return [s.lower()]
74 return [s.lower()]
75
75
76 for lx, t in sorted(pygments.lexers.LEXERS.items()):
76 for lx, t in sorted(pygments.lexers.LEXERS.items()):
77 m = map(__clean, t[-2])
77 m = map(__clean, t[-2])
78 if m:
78 if m:
79 m = reduce(lambda x, y: x + y, m)
79 m = reduce(lambda x, y: x + y, m)
80 for ext in m:
80 for ext in m:
81 desc = lx.replace('Lexer', '')
81 desc = lx.replace('Lexer', '')
82 d[ext].append(desc)
82 d[ext].append(desc)
83
83
84 return dict(d)
84 return dict(d)
85
85
86
86
87 def str2bool(_str):
87 def str2bool(_str):
88 """
88 """
89 returs True/False value from given string, it tries to translate the
89 returs True/False value from given string, it tries to translate the
90 string into boolean
90 string into boolean
91
91
92 :param _str: string value to translate into boolean
92 :param _str: string value to translate into boolean
93 :rtype: boolean
93 :rtype: boolean
94 :returns: boolean from given string
94 :returns: boolean from given string
95 """
95 """
96 if _str is None:
96 if _str is None:
97 return False
97 return False
98 if _str in (True, False):
98 if _str in (True, False):
99 return _str
99 return _str
100 _str = str(_str).strip().lower()
100 _str = str(_str).strip().lower()
101 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
101 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
102
102
103
103
104 def aslist(obj, sep=None, strip=True):
104 def aslist(obj, sep=None, strip=True):
105 """
105 """
106 Returns given string separated by sep as list
106 Returns given string separated by sep as list
107
107
108 :param obj:
108 :param obj:
109 :param sep:
109 :param sep:
110 :param strip:
110 :param strip:
111 """
111 """
112 if isinstance(obj, (basestring)):
112 if isinstance(obj, (basestring)):
113 lst = obj.split(sep)
113 lst = obj.split(sep)
114 if strip:
114 if strip:
115 lst = [v.strip() for v in lst]
115 lst = [v.strip() for v in lst]
116 return lst
116 return lst
117 elif isinstance(obj, (list, tuple)):
117 elif isinstance(obj, (list, tuple)):
118 return obj
118 return obj
119 elif obj is None:
119 elif obj is None:
120 return []
120 return []
121 else:
121 else:
122 return [obj]
122 return [obj]
123
123
124
124
125 def convert_line_endings(line, mode):
125 def convert_line_endings(line, mode):
126 """
126 """
127 Converts a given line "line end" accordingly to given mode
127 Converts a given line "line end" accordingly to given mode
128
128
129 Available modes are::
129 Available modes are::
130 0 - Unix
130 0 - Unix
131 1 - Mac
131 1 - Mac
132 2 - DOS
132 2 - DOS
133
133
134 :param line: given line to convert
134 :param line: given line to convert
135 :param mode: mode to convert to
135 :param mode: mode to convert to
136 :rtype: str
136 :rtype: str
137 :return: converted line according to mode
137 :return: converted line according to mode
138 """
138 """
139 if mode == 0:
139 if mode == 0:
140 line = line.replace('\r\n', '\n')
140 line = line.replace('\r\n', '\n')
141 line = line.replace('\r', '\n')
141 line = line.replace('\r', '\n')
142 elif mode == 1:
142 elif mode == 1:
143 line = line.replace('\r\n', '\r')
143 line = line.replace('\r\n', '\r')
144 line = line.replace('\n', '\r')
144 line = line.replace('\n', '\r')
145 elif mode == 2:
145 elif mode == 2:
146 line = re.sub('\r(?!\n)|(?<!\r)\n', '\r\n', line)
146 line = re.sub('\r(?!\n)|(?<!\r)\n', '\r\n', line)
147 return line
147 return line
148
148
149
149
150 def detect_mode(line, default):
150 def detect_mode(line, default):
151 """
151 """
152 Detects line break for given line, if line break couldn't be found
152 Detects line break for given line, if line break couldn't be found
153 given default value is returned
153 given default value is returned
154
154
155 :param line: str line
155 :param line: str line
156 :param default: default
156 :param default: default
157 :rtype: int
157 :rtype: int
158 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
158 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
159 """
159 """
160 if line.endswith('\r\n'):
160 if line.endswith('\r\n'):
161 return 2
161 return 2
162 elif line.endswith('\n'):
162 elif line.endswith('\n'):
163 return 0
163 return 0
164 elif line.endswith('\r'):
164 elif line.endswith('\r'):
165 return 1
165 return 1
166 else:
166 else:
167 return default
167 return default
168
168
169
169
170 def safe_int(val, default=None):
170 def safe_int(val, default=None):
171 """
171 """
172 Returns int() of val if val is not convertable to int use default
172 Returns int() of val if val is not convertable to int use default
173 instead
173 instead
174
174
175 :param val:
175 :param val:
176 :param default:
176 :param default:
177 """
177 """
178
178
179 try:
179 try:
180 val = int(val)
180 val = int(val)
181 except (ValueError, TypeError):
181 except (ValueError, TypeError):
182 val = default
182 val = default
183
183
184 return val
184 return val
185
185
186
186
187 def safe_unicode(str_, from_encoding=None):
187 def safe_unicode(str_, from_encoding=None):
188 """
188 """
189 safe unicode function. Does few trick to turn str_ into unicode
189 safe unicode function. Does few trick to turn str_ into unicode
190
190
191 In case of UnicodeDecode error, we try to return it with encoding detected
191 In case of UnicodeDecode error, we try to return it with encoding detected
192 by chardet library if it fails fallback to unicode with errors replaced
192 by chardet library if it fails fallback to unicode with errors replaced
193
193
194 :param str_: string to decode
194 :param str_: string to decode
195 :rtype: unicode
195 :rtype: unicode
196 :returns: unicode object
196 :returns: unicode object
197 """
197 """
198 if isinstance(str_, unicode):
198 if isinstance(str_, unicode):
199 return str_
199 return str_
200
200
201 if not from_encoding:
201 if not from_encoding:
202 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
202 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
203 'utf8'), sep=',')
203 'utf8'), sep=',')
204 from_encoding = DEFAULT_ENCODINGS
204 from_encoding = DEFAULT_ENCODINGS
205
205
206 if not isinstance(from_encoding, (list, tuple)):
206 if not isinstance(from_encoding, (list, tuple)):
207 from_encoding = [from_encoding]
207 from_encoding = [from_encoding]
208
208
209 try:
209 try:
210 return unicode(str_)
210 return unicode(str_)
211 except UnicodeDecodeError:
211 except UnicodeDecodeError:
212 pass
212 pass
213
213
214 for enc in from_encoding:
214 for enc in from_encoding:
215 try:
215 try:
216 return unicode(str_, enc)
216 return unicode(str_, enc)
217 except UnicodeDecodeError:
217 except UnicodeDecodeError:
218 pass
218 pass
219
219
220 try:
220 try:
221 import chardet
221 import chardet
222 encoding = chardet.detect(str_)['encoding']
222 encoding = chardet.detect(str_)['encoding']
223 if encoding is None:
223 if encoding is None:
224 raise Exception()
224 raise Exception()
225 return str_.decode(encoding)
225 return str_.decode(encoding)
226 except (ImportError, UnicodeDecodeError, Exception):
226 except (ImportError, UnicodeDecodeError, Exception):
227 return unicode(str_, from_encoding[0], 'replace')
227 return unicode(str_, from_encoding[0], 'replace')
228
228
229
229
230 def safe_str(unicode_, to_encoding=None):
230 def safe_str(unicode_, to_encoding=None):
231 """
231 """
232 safe str function. Does few trick to turn unicode_ into string
232 safe str function. Does few trick to turn unicode_ into string
233
233
234 In case of UnicodeEncodeError, we try to return it with encoding detected
234 In case of UnicodeEncodeError, we try to return it with encoding detected
235 by chardet library if it fails fallback to string with errors replaced
235 by chardet library if it fails fallback to string with errors replaced
236
236
237 :param unicode_: unicode to encode
237 :param unicode_: unicode to encode
238 :rtype: str
238 :rtype: str
239 :returns: str object
239 :returns: str object
240 """
240 """
241
241
242 # if it's not basestr cast to str
242 # if it's not basestr cast to str
243 if not isinstance(unicode_, basestring):
243 if not isinstance(unicode_, basestring):
244 return str(unicode_)
244 return str(unicode_)
245
245
246 if isinstance(unicode_, str):
246 if isinstance(unicode_, str):
247 return unicode_
247 return unicode_
248
248
249 if not to_encoding:
249 if not to_encoding:
250 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
250 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
251 'utf8'), sep=',')
251 'utf8'), sep=',')
252 to_encoding = DEFAULT_ENCODINGS
252 to_encoding = DEFAULT_ENCODINGS
253
253
254 if not isinstance(to_encoding, (list, tuple)):
254 if not isinstance(to_encoding, (list, tuple)):
255 to_encoding = [to_encoding]
255 to_encoding = [to_encoding]
256
256
257 for enc in to_encoding:
257 for enc in to_encoding:
258 try:
258 try:
259 return unicode_.encode(enc)
259 return unicode_.encode(enc)
260 except UnicodeEncodeError:
260 except UnicodeEncodeError:
261 pass
261 pass
262
262
263 try:
263 try:
264 import chardet
264 import chardet
265 encoding = chardet.detect(unicode_)['encoding']
265 encoding = chardet.detect(unicode_)['encoding']
266 if encoding is None:
266 if encoding is None:
267 raise UnicodeEncodeError()
267 raise UnicodeEncodeError()
268
268
269 return unicode_.encode(encoding)
269 return unicode_.encode(encoding)
270 except (ImportError, UnicodeEncodeError):
270 except (ImportError, UnicodeEncodeError):
271 return unicode_.encode(to_encoding[0], 'replace')
271 return unicode_.encode(to_encoding[0], 'replace')
272
272
273
273
274 def remove_suffix(s, suffix):
274 def remove_suffix(s, suffix):
275 if s.endswith(suffix):
275 if s.endswith(suffix):
276 s = s[:-1 * len(suffix)]
276 s = s[:-1 * len(suffix)]
277 return s
277 return s
278
278
279
279
280 def remove_prefix(s, prefix):
280 def remove_prefix(s, prefix):
281 if s.startswith(prefix):
281 if s.startswith(prefix):
282 s = s[len(prefix):]
282 s = s[len(prefix):]
283 return s
283 return s
284
284
285
285
286 def find_calling_context(ignore_modules=None):
286 def find_calling_context(ignore_modules=None):
287 """
287 """
288 Look through the calling stack and return the frame which called
288 Look through the calling stack and return the frame which called
289 this function and is part of core module ( ie. rhodecode.* )
289 this function and is part of core module ( ie. rhodecode.* )
290
290
291 :param ignore_modules: list of modules to ignore eg. ['rhodecode.lib']
291 :param ignore_modules: list of modules to ignore eg. ['rhodecode.lib']
292 """
292 """
293
293
294 ignore_modules = ignore_modules or []
294 ignore_modules = ignore_modules or []
295
295
296 f = sys._getframe(2)
296 f = sys._getframe(2)
297 while f.f_back is not None:
297 while f.f_back is not None:
298 name = f.f_globals.get('__name__')
298 name = f.f_globals.get('__name__')
299 if name and name.startswith(__name__.split('.')[0]):
299 if name and name.startswith(__name__.split('.')[0]):
300 if name not in ignore_modules:
300 if name not in ignore_modules:
301 return f
301 return f
302 f = f.f_back
302 f = f.f_back
303 return None
303 return None
304
304
305
305
306 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
306 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
307 """Custom engine_from_config functions."""
307 """Custom engine_from_config functions."""
308 log = logging.getLogger('sqlalchemy.engine')
308 log = logging.getLogger('sqlalchemy.engine')
309 engine = sqlalchemy.engine_from_config(configuration, prefix, **kwargs)
309 engine = sqlalchemy.engine_from_config(configuration, prefix, **kwargs)
310
310
311 def color_sql(sql):
311 def color_sql(sql):
312 color_seq = '\033[1;33m' # This is yellow: code 33
312 color_seq = '\033[1;33m' # This is yellow: code 33
313 normal = '\x1b[0m'
313 normal = '\x1b[0m'
314 return ''.join([color_seq, sql, normal])
314 return ''.join([color_seq, sql, normal])
315
315
316 if configuration['debug']:
316 if configuration['debug']:
317 # attach events only for debug configuration
317 # attach events only for debug configuration
318
318
319 def before_cursor_execute(conn, cursor, statement,
319 def before_cursor_execute(conn, cursor, statement,
320 parameters, context, executemany):
320 parameters, context, executemany):
321 setattr(conn, 'query_start_time', time.time())
321 setattr(conn, 'query_start_time', time.time())
322 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
322 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
323 calling_context = find_calling_context(ignore_modules=[
323 calling_context = find_calling_context(ignore_modules=[
324 'rhodecode.lib.caching_query',
324 'rhodecode.lib.caching_query',
325 'rhodecode.model.settings',
325 'rhodecode.model.settings',
326 ])
326 ])
327 if calling_context:
327 if calling_context:
328 log.info(color_sql('call context %s:%s' % (
328 log.info(color_sql('call context %s:%s' % (
329 calling_context.f_code.co_filename,
329 calling_context.f_code.co_filename,
330 calling_context.f_lineno,
330 calling_context.f_lineno,
331 )))
331 )))
332
332
333 def after_cursor_execute(conn, cursor, statement,
333 def after_cursor_execute(conn, cursor, statement,
334 parameters, context, executemany):
334 parameters, context, executemany):
335 delattr(conn, 'query_start_time')
335 delattr(conn, 'query_start_time')
336
336
337 sqlalchemy.event.listen(engine, "before_cursor_execute",
337 sqlalchemy.event.listen(engine, "before_cursor_execute",
338 before_cursor_execute)
338 before_cursor_execute)
339 sqlalchemy.event.listen(engine, "after_cursor_execute",
339 sqlalchemy.event.listen(engine, "after_cursor_execute",
340 after_cursor_execute)
340 after_cursor_execute)
341
341
342 return engine
342 return engine
343
343
344
344
345 def get_encryption_key(config):
346 return config['beaker.session.secret']
347
348
345 def age(prevdate, now=None, show_short_version=False, show_suffix=True,
349 def age(prevdate, now=None, show_short_version=False, show_suffix=True,
346 short_format=False):
350 short_format=False):
347 """
351 """
348 Turns a datetime into an age string.
352 Turns a datetime into an age string.
349 If show_short_version is True, this generates a shorter string with
353 If show_short_version is True, this generates a shorter string with
350 an approximate age; ex. '1 day ago', rather than '1 day and 23 hours ago'.
354 an approximate age; ex. '1 day ago', rather than '1 day and 23 hours ago'.
351
355
352 * IMPORTANT*
356 * IMPORTANT*
353 Code of this function is written in special way so it's easier to
357 Code of this function is written in special way so it's easier to
354 backport it to javascript. If you mean to update it, please also update
358 backport it to javascript. If you mean to update it, please also update
355 `jquery.timeago-extension.js` file
359 `jquery.timeago-extension.js` file
356
360
357 :param prevdate: datetime object
361 :param prevdate: datetime object
358 :param now: get current time, if not define we use
362 :param now: get current time, if not define we use
359 `datetime.datetime.now()`
363 `datetime.datetime.now()`
360 :param show_short_version: if it should approximate the date and
364 :param show_short_version: if it should approximate the date and
361 return a shorter string
365 return a shorter string
362 :param show_suffix:
366 :param show_suffix:
363 :param short_format: show short format, eg 2D instead of 2 days
367 :param short_format: show short format, eg 2D instead of 2 days
364 :rtype: unicode
368 :rtype: unicode
365 :returns: unicode words describing age
369 :returns: unicode words describing age
366 """
370 """
367 from pylons.i18n.translation import _, ungettext
371 from pylons.i18n.translation import _, ungettext
368
372
369 def _get_relative_delta(now, prevdate):
373 def _get_relative_delta(now, prevdate):
370 base = dateutil.relativedelta.relativedelta(now, prevdate)
374 base = dateutil.relativedelta.relativedelta(now, prevdate)
371 return {
375 return {
372 'year': base.years,
376 'year': base.years,
373 'month': base.months,
377 'month': base.months,
374 'day': base.days,
378 'day': base.days,
375 'hour': base.hours,
379 'hour': base.hours,
376 'minute': base.minutes,
380 'minute': base.minutes,
377 'second': base.seconds,
381 'second': base.seconds,
378 }
382 }
379
383
380 def _is_leap_year(year):
384 def _is_leap_year(year):
381 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
385 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
382
386
383 def get_month(prevdate):
387 def get_month(prevdate):
384 return prevdate.month
388 return prevdate.month
385
389
386 def get_year(prevdate):
390 def get_year(prevdate):
387 return prevdate.year
391 return prevdate.year
388
392
389 now = now or datetime.datetime.now()
393 now = now or datetime.datetime.now()
390 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
394 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
391 deltas = {}
395 deltas = {}
392 future = False
396 future = False
393
397
394 if prevdate > now:
398 if prevdate > now:
395 now_old = now
399 now_old = now
396 now = prevdate
400 now = prevdate
397 prevdate = now_old
401 prevdate = now_old
398 future = True
402 future = True
399 if future:
403 if future:
400 prevdate = prevdate.replace(microsecond=0)
404 prevdate = prevdate.replace(microsecond=0)
401 # Get date parts deltas
405 # Get date parts deltas
402 for part in order:
406 for part in order:
403 rel_delta = _get_relative_delta(now, prevdate)
407 rel_delta = _get_relative_delta(now, prevdate)
404 deltas[part] = rel_delta[part]
408 deltas[part] = rel_delta[part]
405
409
406 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
410 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
407 # not 1 hour, -59 minutes and -59 seconds)
411 # not 1 hour, -59 minutes and -59 seconds)
408 offsets = [[5, 60], [4, 60], [3, 24]]
412 offsets = [[5, 60], [4, 60], [3, 24]]
409 for element in offsets: # seconds, minutes, hours
413 for element in offsets: # seconds, minutes, hours
410 num = element[0]
414 num = element[0]
411 length = element[1]
415 length = element[1]
412
416
413 part = order[num]
417 part = order[num]
414 carry_part = order[num - 1]
418 carry_part = order[num - 1]
415
419
416 if deltas[part] < 0:
420 if deltas[part] < 0:
417 deltas[part] += length
421 deltas[part] += length
418 deltas[carry_part] -= 1
422 deltas[carry_part] -= 1
419
423
420 # Same thing for days except that the increment depends on the (variable)
424 # Same thing for days except that the increment depends on the (variable)
421 # number of days in the month
425 # number of days in the month
422 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
426 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
423 if deltas['day'] < 0:
427 if deltas['day'] < 0:
424 if get_month(prevdate) == 2 and _is_leap_year(get_year(prevdate)):
428 if get_month(prevdate) == 2 and _is_leap_year(get_year(prevdate)):
425 deltas['day'] += 29
429 deltas['day'] += 29
426 else:
430 else:
427 deltas['day'] += month_lengths[get_month(prevdate) - 1]
431 deltas['day'] += month_lengths[get_month(prevdate) - 1]
428
432
429 deltas['month'] -= 1
433 deltas['month'] -= 1
430
434
431 if deltas['month'] < 0:
435 if deltas['month'] < 0:
432 deltas['month'] += 12
436 deltas['month'] += 12
433 deltas['year'] -= 1
437 deltas['year'] -= 1
434
438
435 # Format the result
439 # Format the result
436 if short_format:
440 if short_format:
437 fmt_funcs = {
441 fmt_funcs = {
438 'year': lambda d: u'%dy' % d,
442 'year': lambda d: u'%dy' % d,
439 'month': lambda d: u'%dm' % d,
443 'month': lambda d: u'%dm' % d,
440 'day': lambda d: u'%dd' % d,
444 'day': lambda d: u'%dd' % d,
441 'hour': lambda d: u'%dh' % d,
445 'hour': lambda d: u'%dh' % d,
442 'minute': lambda d: u'%dmin' % d,
446 'minute': lambda d: u'%dmin' % d,
443 'second': lambda d: u'%dsec' % d,
447 'second': lambda d: u'%dsec' % d,
444 }
448 }
445 else:
449 else:
446 fmt_funcs = {
450 fmt_funcs = {
447 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
451 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
448 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
452 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
449 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
453 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
450 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
454 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
451 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
455 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
452 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
456 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
453 }
457 }
454
458
455 i = 0
459 i = 0
456 for part in order:
460 for part in order:
457 value = deltas[part]
461 value = deltas[part]
458 if value != 0:
462 if value != 0:
459
463
460 if i < 5:
464 if i < 5:
461 sub_part = order[i + 1]
465 sub_part = order[i + 1]
462 sub_value = deltas[sub_part]
466 sub_value = deltas[sub_part]
463 else:
467 else:
464 sub_value = 0
468 sub_value = 0
465
469
466 if sub_value == 0 or show_short_version:
470 if sub_value == 0 or show_short_version:
467 _val = fmt_funcs[part](value)
471 _val = fmt_funcs[part](value)
468 if future:
472 if future:
469 if show_suffix:
473 if show_suffix:
470 return _(u'in %s') % _val
474 return _(u'in %s') % _val
471 else:
475 else:
472 return _val
476 return _val
473
477
474 else:
478 else:
475 if show_suffix:
479 if show_suffix:
476 return _(u'%s ago') % _val
480 return _(u'%s ago') % _val
477 else:
481 else:
478 return _val
482 return _val
479
483
480 val = fmt_funcs[part](value)
484 val = fmt_funcs[part](value)
481 val_detail = fmt_funcs[sub_part](sub_value)
485 val_detail = fmt_funcs[sub_part](sub_value)
482
486
483 if short_format:
487 if short_format:
484 datetime_tmpl = u'%s, %s'
488 datetime_tmpl = u'%s, %s'
485 if show_suffix:
489 if show_suffix:
486 datetime_tmpl = _(u'%s, %s ago')
490 datetime_tmpl = _(u'%s, %s ago')
487 if future:
491 if future:
488 datetime_tmpl = _(u'in %s, %s')
492 datetime_tmpl = _(u'in %s, %s')
489 else:
493 else:
490 datetime_tmpl = _(u'%s and %s')
494 datetime_tmpl = _(u'%s and %s')
491 if show_suffix:
495 if show_suffix:
492 datetime_tmpl = _(u'%s and %s ago')
496 datetime_tmpl = _(u'%s and %s ago')
493 if future:
497 if future:
494 datetime_tmpl = _(u'in %s and %s')
498 datetime_tmpl = _(u'in %s and %s')
495
499
496 return datetime_tmpl % (val, val_detail)
500 return datetime_tmpl % (val, val_detail)
497 i += 1
501 i += 1
498 return _(u'just now')
502 return _(u'just now')
499
503
500
504
501 def uri_filter(uri):
505 def uri_filter(uri):
502 """
506 """
503 Removes user:password from given url string
507 Removes user:password from given url string
504
508
505 :param uri:
509 :param uri:
506 :rtype: unicode
510 :rtype: unicode
507 :returns: filtered list of strings
511 :returns: filtered list of strings
508 """
512 """
509 if not uri:
513 if not uri:
510 return ''
514 return ''
511
515
512 proto = ''
516 proto = ''
513
517
514 for pat in ('https://', 'http://'):
518 for pat in ('https://', 'http://'):
515 if uri.startswith(pat):
519 if uri.startswith(pat):
516 uri = uri[len(pat):]
520 uri = uri[len(pat):]
517 proto = pat
521 proto = pat
518 break
522 break
519
523
520 # remove passwords and username
524 # remove passwords and username
521 uri = uri[uri.find('@') + 1:]
525 uri = uri[uri.find('@') + 1:]
522
526
523 # get the port
527 # get the port
524 cred_pos = uri.find(':')
528 cred_pos = uri.find(':')
525 if cred_pos == -1:
529 if cred_pos == -1:
526 host, port = uri, None
530 host, port = uri, None
527 else:
531 else:
528 host, port = uri[:cred_pos], uri[cred_pos + 1:]
532 host, port = uri[:cred_pos], uri[cred_pos + 1:]
529
533
530 return filter(None, [proto, host, port])
534 return filter(None, [proto, host, port])
531
535
532
536
533 def credentials_filter(uri):
537 def credentials_filter(uri):
534 """
538 """
535 Returns a url with removed credentials
539 Returns a url with removed credentials
536
540
537 :param uri:
541 :param uri:
538 """
542 """
539
543
540 uri = uri_filter(uri)
544 uri = uri_filter(uri)
541 # check if we have port
545 # check if we have port
542 if len(uri) > 2 and uri[2]:
546 if len(uri) > 2 and uri[2]:
543 uri[2] = ':' + uri[2]
547 uri[2] = ':' + uri[2]
544
548
545 return ''.join(uri)
549 return ''.join(uri)
546
550
547
551
548 def get_clone_url(uri_tmpl, qualifed_home_url, repo_name, repo_id, **override):
552 def get_clone_url(uri_tmpl, qualifed_home_url, repo_name, repo_id, **override):
549 parsed_url = urlobject.URLObject(qualifed_home_url)
553 parsed_url = urlobject.URLObject(qualifed_home_url)
550 decoded_path = safe_unicode(urllib.unquote(parsed_url.path.rstrip('/')))
554 decoded_path = safe_unicode(urllib.unquote(parsed_url.path.rstrip('/')))
551 args = {
555 args = {
552 'scheme': parsed_url.scheme,
556 'scheme': parsed_url.scheme,
553 'user': '',
557 'user': '',
554 # path if we use proxy-prefix
558 # path if we use proxy-prefix
555 'netloc': parsed_url.netloc+decoded_path,
559 'netloc': parsed_url.netloc+decoded_path,
556 'prefix': decoded_path,
560 'prefix': decoded_path,
557 'repo': repo_name,
561 'repo': repo_name,
558 'repoid': str(repo_id)
562 'repoid': str(repo_id)
559 }
563 }
560 args.update(override)
564 args.update(override)
561 args['user'] = urllib.quote(safe_str(args['user']))
565 args['user'] = urllib.quote(safe_str(args['user']))
562
566
563 for k, v in args.items():
567 for k, v in args.items():
564 uri_tmpl = uri_tmpl.replace('{%s}' % k, v)
568 uri_tmpl = uri_tmpl.replace('{%s}' % k, v)
565
569
566 # remove leading @ sign if it's present. Case of empty user
570 # remove leading @ sign if it's present. Case of empty user
567 url_obj = urlobject.URLObject(uri_tmpl)
571 url_obj = urlobject.URLObject(uri_tmpl)
568 url = url_obj.with_netloc(url_obj.netloc.lstrip('@'))
572 url = url_obj.with_netloc(url_obj.netloc.lstrip('@'))
569
573
570 return safe_unicode(url)
574 return safe_unicode(url)
571
575
572
576
573 def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None):
577 def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None):
574 """
578 """
575 Safe version of get_commit if this commit doesn't exists for a
579 Safe version of get_commit if this commit doesn't exists for a
576 repository it returns a Dummy one instead
580 repository it returns a Dummy one instead
577
581
578 :param repo: repository instance
582 :param repo: repository instance
579 :param commit_id: commit id as str
583 :param commit_id: commit id as str
580 :param pre_load: optional list of commit attributes to load
584 :param pre_load: optional list of commit attributes to load
581 """
585 """
582 # TODO(skreft): remove these circular imports
586 # TODO(skreft): remove these circular imports
583 from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit
587 from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit
584 from rhodecode.lib.vcs.exceptions import RepositoryError
588 from rhodecode.lib.vcs.exceptions import RepositoryError
585 if not isinstance(repo, BaseRepository):
589 if not isinstance(repo, BaseRepository):
586 raise Exception('You must pass an Repository '
590 raise Exception('You must pass an Repository '
587 'object as first argument got %s', type(repo))
591 'object as first argument got %s', type(repo))
588
592
589 try:
593 try:
590 commit = repo.get_commit(
594 commit = repo.get_commit(
591 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load)
595 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load)
592 except (RepositoryError, LookupError):
596 except (RepositoryError, LookupError):
593 commit = EmptyCommit()
597 commit = EmptyCommit()
594 return commit
598 return commit
595
599
596
600
597 def datetime_to_time(dt):
601 def datetime_to_time(dt):
598 if dt:
602 if dt:
599 return time.mktime(dt.timetuple())
603 return time.mktime(dt.timetuple())
600
604
601
605
602 def time_to_datetime(tm):
606 def time_to_datetime(tm):
603 if tm:
607 if tm:
604 if isinstance(tm, basestring):
608 if isinstance(tm, basestring):
605 try:
609 try:
606 tm = float(tm)
610 tm = float(tm)
607 except ValueError:
611 except ValueError:
608 return
612 return
609 return datetime.datetime.fromtimestamp(tm)
613 return datetime.datetime.fromtimestamp(tm)
610
614
611
615
612 def time_to_utcdatetime(tm):
616 def time_to_utcdatetime(tm):
613 if tm:
617 if tm:
614 if isinstance(tm, basestring):
618 if isinstance(tm, basestring):
615 try:
619 try:
616 tm = float(tm)
620 tm = float(tm)
617 except ValueError:
621 except ValueError:
618 return
622 return
619 return datetime.datetime.utcfromtimestamp(tm)
623 return datetime.datetime.utcfromtimestamp(tm)
620
624
621
625
622 MENTIONS_REGEX = re.compile(
626 MENTIONS_REGEX = re.compile(
623 # ^@ or @ without any special chars in front
627 # ^@ or @ without any special chars in front
624 r'(?:^@|[^a-zA-Z0-9\-\_\.]@)'
628 r'(?:^@|[^a-zA-Z0-9\-\_\.]@)'
625 # main body starts with letter, then can be . - _
629 # main body starts with letter, then can be . - _
626 r'([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)',
630 r'([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)',
627 re.VERBOSE | re.MULTILINE)
631 re.VERBOSE | re.MULTILINE)
628
632
629
633
630 def extract_mentioned_users(s):
634 def extract_mentioned_users(s):
631 """
635 """
632 Returns unique usernames from given string s that have @mention
636 Returns unique usernames from given string s that have @mention
633
637
634 :param s: string to get mentions
638 :param s: string to get mentions
635 """
639 """
636 usrs = set()
640 usrs = set()
637 for username in MENTIONS_REGEX.findall(s):
641 for username in MENTIONS_REGEX.findall(s):
638 usrs.add(username)
642 usrs.add(username)
639
643
640 return sorted(list(usrs), key=lambda k: k.lower())
644 return sorted(list(usrs), key=lambda k: k.lower())
641
645
642
646
643 class AttributeDict(dict):
647 class AttributeDict(dict):
644 def __getattr__(self, attr):
648 def __getattr__(self, attr):
645 return self.get(attr, None)
649 return self.get(attr, None)
646 __setattr__ = dict.__setitem__
650 __setattr__ = dict.__setitem__
647 __delattr__ = dict.__delitem__
651 __delattr__ = dict.__delitem__
648
652
649
653
650 def fix_PATH(os_=None):
654 def fix_PATH(os_=None):
651 """
655 """
652 Get current active python path, and append it to PATH variable to fix
656 Get current active python path, and append it to PATH variable to fix
653 issues of subprocess calls and different python versions
657 issues of subprocess calls and different python versions
654 """
658 """
655 if os_ is None:
659 if os_ is None:
656 import os
660 import os
657 else:
661 else:
658 os = os_
662 os = os_
659
663
660 cur_path = os.path.split(sys.executable)[0]
664 cur_path = os.path.split(sys.executable)[0]
661 if not os.environ['PATH'].startswith(cur_path):
665 if not os.environ['PATH'].startswith(cur_path):
662 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
666 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
663
667
664
668
665 def obfuscate_url_pw(engine):
669 def obfuscate_url_pw(engine):
666 _url = engine or ''
670 _url = engine or ''
667 try:
671 try:
668 _url = sqlalchemy.engine.url.make_url(engine)
672 _url = sqlalchemy.engine.url.make_url(engine)
669 if _url.password:
673 if _url.password:
670 _url.password = 'XXXXX'
674 _url.password = 'XXXXX'
671 except Exception:
675 except Exception:
672 pass
676 pass
673 return unicode(_url)
677 return unicode(_url)
674
678
675
679
676 def get_server_url(environ):
680 def get_server_url(environ):
677 req = webob.Request(environ)
681 req = webob.Request(environ)
678 return req.host_url + req.script_name
682 return req.host_url + req.script_name
679
683
680
684
681 def unique_id(hexlen=32):
685 def unique_id(hexlen=32):
682 alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz"
686 alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz"
683 return suuid(truncate_to=hexlen, alphabet=alphabet)
687 return suuid(truncate_to=hexlen, alphabet=alphabet)
684
688
685
689
686 def suuid(url=None, truncate_to=22, alphabet=None):
690 def suuid(url=None, truncate_to=22, alphabet=None):
687 """
691 """
688 Generate and return a short URL safe UUID.
692 Generate and return a short URL safe UUID.
689
693
690 If the url parameter is provided, set the namespace to the provided
694 If the url parameter is provided, set the namespace to the provided
691 URL and generate a UUID.
695 URL and generate a UUID.
692
696
693 :param url to get the uuid for
697 :param url to get the uuid for
694 :truncate_to: truncate the basic 22 UUID to shorter version
698 :truncate_to: truncate the basic 22 UUID to shorter version
695
699
696 The IDs won't be universally unique any longer, but the probability of
700 The IDs won't be universally unique any longer, but the probability of
697 a collision will still be very low.
701 a collision will still be very low.
698 """
702 """
699 # Define our alphabet.
703 # Define our alphabet.
700 _ALPHABET = alphabet or "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
704 _ALPHABET = alphabet or "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
701
705
702 # If no URL is given, generate a random UUID.
706 # If no URL is given, generate a random UUID.
703 if url is None:
707 if url is None:
704 unique_id = uuid.uuid4().int
708 unique_id = uuid.uuid4().int
705 else:
709 else:
706 unique_id = uuid.uuid3(uuid.NAMESPACE_URL, url).int
710 unique_id = uuid.uuid3(uuid.NAMESPACE_URL, url).int
707
711
708 alphabet_length = len(_ALPHABET)
712 alphabet_length = len(_ALPHABET)
709 output = []
713 output = []
710 while unique_id > 0:
714 while unique_id > 0:
711 digit = unique_id % alphabet_length
715 digit = unique_id % alphabet_length
712 output.append(_ALPHABET[digit])
716 output.append(_ALPHABET[digit])
713 unique_id = int(unique_id / alphabet_length)
717 unique_id = int(unique_id / alphabet_length)
714 return "".join(output)[:truncate_to]
718 return "".join(output)[:truncate_to]
715
719
716
720
717 def get_current_rhodecode_user():
721 def get_current_rhodecode_user():
718 """
722 """
719 Gets rhodecode user from threadlocal tmpl_context variable if it's
723 Gets rhodecode user from threadlocal tmpl_context variable if it's
720 defined, else returns None.
724 defined, else returns None.
721 """
725 """
722 from pylons import tmpl_context as c
726 from pylons import tmpl_context as c
723 if hasattr(c, 'rhodecode_user'):
727 if hasattr(c, 'rhodecode_user'):
724 return c.rhodecode_user
728 return c.rhodecode_user
725
729
726 return None
730 return None
727
731
728
732
729 def action_logger_generic(action, namespace=''):
733 def action_logger_generic(action, namespace=''):
730 """
734 """
731 A generic logger for actions useful to the system overview, tries to find
735 A generic logger for actions useful to the system overview, tries to find
732 an acting user for the context of the call otherwise reports unknown user
736 an acting user for the context of the call otherwise reports unknown user
733
737
734 :param action: logging message eg 'comment 5 deleted'
738 :param action: logging message eg 'comment 5 deleted'
735 :param type: string
739 :param type: string
736
740
737 :param namespace: namespace of the logging message eg. 'repo.comments'
741 :param namespace: namespace of the logging message eg. 'repo.comments'
738 :param type: string
742 :param type: string
739
743
740 """
744 """
741
745
742 logger_name = 'rhodecode.actions'
746 logger_name = 'rhodecode.actions'
743
747
744 if namespace:
748 if namespace:
745 logger_name += '.' + namespace
749 logger_name += '.' + namespace
746
750
747 log = logging.getLogger(logger_name)
751 log = logging.getLogger(logger_name)
748
752
749 # get a user if we can
753 # get a user if we can
750 user = get_current_rhodecode_user()
754 user = get_current_rhodecode_user()
751
755
752 logfunc = log.info
756 logfunc = log.info
753
757
754 if not user:
758 if not user:
755 user = '<unknown user>'
759 user = '<unknown user>'
756 logfunc = log.warning
760 logfunc = log.warning
757
761
758 logfunc('Logging action by {}: {}'.format(user, action))
762 logfunc('Logging action by {}: {}'.format(user, action))
759
763
760
764
761 def escape_split(text, sep=',', maxsplit=-1):
765 def escape_split(text, sep=',', maxsplit=-1):
762 r"""
766 r"""
763 Allows for escaping of the separator: e.g. arg='foo\, bar'
767 Allows for escaping of the separator: e.g. arg='foo\, bar'
764
768
765 It should be noted that the way bash et. al. do command line parsing, those
769 It should be noted that the way bash et. al. do command line parsing, those
766 single quotes are required.
770 single quotes are required.
767 """
771 """
768 escaped_sep = r'\%s' % sep
772 escaped_sep = r'\%s' % sep
769
773
770 if escaped_sep not in text:
774 if escaped_sep not in text:
771 return text.split(sep, maxsplit)
775 return text.split(sep, maxsplit)
772
776
773 before, _mid, after = text.partition(escaped_sep)
777 before, _mid, after = text.partition(escaped_sep)
774 startlist = before.split(sep, maxsplit) # a regular split is fine here
778 startlist = before.split(sep, maxsplit) # a regular split is fine here
775 unfinished = startlist[-1]
779 unfinished = startlist[-1]
776 startlist = startlist[:-1]
780 startlist = startlist[:-1]
777
781
778 # recurse because there may be more escaped separators
782 # recurse because there may be more escaped separators
779 endlist = escape_split(after, sep, maxsplit)
783 endlist = escape_split(after, sep, maxsplit)
780
784
781 # finish building the escaped value. we use endlist[0] becaue the first
785 # finish building the escaped value. we use endlist[0] becaue the first
782 # part of the string sent in recursion is the rest of the escaped value.
786 # part of the string sent in recursion is the rest of the escaped value.
783 unfinished += sep + endlist[0]
787 unfinished += sep + endlist[0]
784
788
785 return startlist + [unfinished] + endlist[1:] # put together all the parts
789 return startlist + [unfinished] + endlist[1:] # put together all the parts
786
790
787
791
788 class OptionalAttr(object):
792 class OptionalAttr(object):
789 """
793 """
790 Special Optional Option that defines other attribute. Example::
794 Special Optional Option that defines other attribute. Example::
791
795
792 def test(apiuser, userid=Optional(OAttr('apiuser')):
796 def test(apiuser, userid=Optional(OAttr('apiuser')):
793 user = Optional.extract(userid)
797 user = Optional.extract(userid)
794 # calls
798 # calls
795
799
796 """
800 """
797
801
798 def __init__(self, attr_name):
802 def __init__(self, attr_name):
799 self.attr_name = attr_name
803 self.attr_name = attr_name
800
804
801 def __repr__(self):
805 def __repr__(self):
802 return '<OptionalAttr:%s>' % self.attr_name
806 return '<OptionalAttr:%s>' % self.attr_name
803
807
804 def __call__(self):
808 def __call__(self):
805 return self
809 return self
806
810
807
811
808 # alias
812 # alias
809 OAttr = OptionalAttr
813 OAttr = OptionalAttr
810
814
811
815
812 class Optional(object):
816 class Optional(object):
813 """
817 """
814 Defines an optional parameter::
818 Defines an optional parameter::
815
819
816 param = param.getval() if isinstance(param, Optional) else param
820 param = param.getval() if isinstance(param, Optional) else param
817 param = param() if isinstance(param, Optional) else param
821 param = param() if isinstance(param, Optional) else param
818
822
819 is equivalent of::
823 is equivalent of::
820
824
821 param = Optional.extract(param)
825 param = Optional.extract(param)
822
826
823 """
827 """
824
828
825 def __init__(self, type_):
829 def __init__(self, type_):
826 self.type_ = type_
830 self.type_ = type_
827
831
828 def __repr__(self):
832 def __repr__(self):
829 return '<Optional:%s>' % self.type_.__repr__()
833 return '<Optional:%s>' % self.type_.__repr__()
830
834
831 def __call__(self):
835 def __call__(self):
832 return self.getval()
836 return self.getval()
833
837
834 def getval(self):
838 def getval(self):
835 """
839 """
836 returns value from this Optional instance
840 returns value from this Optional instance
837 """
841 """
838 if isinstance(self.type_, OAttr):
842 if isinstance(self.type_, OAttr):
839 # use params name
843 # use params name
840 return self.type_.attr_name
844 return self.type_.attr_name
841 return self.type_
845 return self.type_
842
846
843 @classmethod
847 @classmethod
844 def extract(cls, val):
848 def extract(cls, val):
845 """
849 """
846 Extracts value from Optional() instance
850 Extracts value from Optional() instance
847
851
848 :param val:
852 :param val:
849 :return: original value if it's not Optional instance else
853 :return: original value if it's not Optional instance else
850 value of instance
854 value of instance
851 """
855 """
852 if isinstance(val, cls):
856 if isinstance(val, cls):
853 return val.getval()
857 return val.getval()
854 return val
858 return val
@@ -1,164 +1,164 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 The application's model objects
22 The application's model objects
23
23
24 :example:
24 :example:
25
25
26 .. code-block:: python
26 .. code-block:: python
27
27
28 from paste.deploy import appconfig
28 from paste.deploy import appconfig
29 from pylons import config
29 from pylons import config
30 from sqlalchemy import engine_from_config
30 from sqlalchemy import engine_from_config
31 from rhodecode.config.environment import load_environment
31 from rhodecode.config.environment import load_environment
32
32
33 conf = appconfig('config:development.ini', relative_to = './../../')
33 conf = appconfig('config:development.ini', relative_to = './../../')
34 load_environment(conf.global_conf, conf.local_conf)
34 load_environment(conf.global_conf, conf.local_conf)
35
35
36 engine = engine_from_config(config, 'sqlalchemy.')
36 engine = engine_from_config(config, 'sqlalchemy.')
37 init_model(engine)
37 init_model(engine)
38 # RUN YOUR CODE HERE
38 # RUN YOUR CODE HERE
39
39
40 """
40 """
41
41
42
42
43 import logging
43 import logging
44
44
45 from pylons import config
45 from pylons import config
46 from pyramid.threadlocal import get_current_registry
46 from pyramid.threadlocal import get_current_registry
47
47
48 from rhodecode.model import meta, db
48 from rhodecode.model import meta, db
49 from rhodecode.lib.utils2 import obfuscate_url_pw
49 from rhodecode.lib.utils2 import obfuscate_url_pw, get_encryption_key
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 def init_model(engine, encryption_key=None):
54 def init_model(engine, encryption_key=None):
55 """
55 """
56 Initializes db session, bind the engine with the metadata,
56 Initializes db session, bind the engine with the metadata,
57 Call this before using any of the tables or classes in the model,
57 Call this before using any of the tables or classes in the model,
58 preferably once in application start
58 preferably once in application start
59
59
60 :param engine: engine to bind to
60 :param engine: engine to bind to
61 """
61 """
62 engine_str = obfuscate_url_pw(str(engine.url))
62 engine_str = obfuscate_url_pw(str(engine.url))
63 log.info("initializing db for %s", engine_str)
63 log.info("initializing db for %s", engine_str)
64 meta.Base.metadata.bind = engine
64 meta.Base.metadata.bind = engine
65 db.ENCRYPTION_KEY = encryption_key
65 db.ENCRYPTION_KEY = encryption_key
66
66
67
67
68 def init_model_encryption(migration_models):
68 def init_model_encryption(migration_models):
69 migration_models.ENCRYPTION_KEY = config['beaker.session.secret']
69 migration_models.ENCRYPTION_KEY = get_encryption_key(config)
70 db.ENCRYPTION_KEY = config['beaker.session.secret']
70 db.ENCRYPTION_KEY = get_encryption_key(config)
71
71
72
72
73 class BaseModel(object):
73 class BaseModel(object):
74 """
74 """
75 Base Model for all RhodeCode models, it adds sql alchemy session
75 Base Model for all RhodeCode models, it adds sql alchemy session
76 into instance of model
76 into instance of model
77
77
78 :param sa: If passed it reuses this session instead of creating a new one
78 :param sa: If passed it reuses this session instead of creating a new one
79 """
79 """
80
80
81 cls = None # override in child class
81 cls = None # override in child class
82
82
83 def __init__(self, sa=None):
83 def __init__(self, sa=None):
84 if sa is not None:
84 if sa is not None:
85 self.sa = sa
85 self.sa = sa
86 else:
86 else:
87 self.sa = meta.Session()
87 self.sa = meta.Session()
88
88
89 def _get_instance(self, cls, instance, callback=None):
89 def _get_instance(self, cls, instance, callback=None):
90 """
90 """
91 Gets instance of given cls using some simple lookup mechanism.
91 Gets instance of given cls using some simple lookup mechanism.
92
92
93 :param cls: class to fetch
93 :param cls: class to fetch
94 :param instance: int or Instance
94 :param instance: int or Instance
95 :param callback: callback to call if all lookups failed
95 :param callback: callback to call if all lookups failed
96 """
96 """
97
97
98 if isinstance(instance, cls):
98 if isinstance(instance, cls):
99 return instance
99 return instance
100 elif isinstance(instance, (int, long)):
100 elif isinstance(instance, (int, long)):
101 return cls.get(instance)
101 return cls.get(instance)
102 else:
102 else:
103 if instance:
103 if instance:
104 if callback is None:
104 if callback is None:
105 raise Exception(
105 raise Exception(
106 'given object must be int, long or Instance of %s '
106 'given object must be int, long or Instance of %s '
107 'got %s, no callback provided' % (cls, type(instance))
107 'got %s, no callback provided' % (cls, type(instance))
108 )
108 )
109 else:
109 else:
110 return callback(instance)
110 return callback(instance)
111
111
112 def _get_user(self, user):
112 def _get_user(self, user):
113 """
113 """
114 Helper method to get user by ID, or username fallback
114 Helper method to get user by ID, or username fallback
115
115
116 :param user: UserID, username, or User instance
116 :param user: UserID, username, or User instance
117 """
117 """
118 return self._get_instance(
118 return self._get_instance(
119 db.User, user, callback=db.User.get_by_username)
119 db.User, user, callback=db.User.get_by_username)
120
120
121 def _get_user_group(self, user_group):
121 def _get_user_group(self, user_group):
122 """
122 """
123 Helper method to get user by ID, or username fallback
123 Helper method to get user by ID, or username fallback
124
124
125 :param user_group: UserGroupID, user_group_name, or UserGroup instance
125 :param user_group: UserGroupID, user_group_name, or UserGroup instance
126 """
126 """
127 return self._get_instance(
127 return self._get_instance(
128 db.UserGroup, user_group, callback=db.UserGroup.get_by_group_name)
128 db.UserGroup, user_group, callback=db.UserGroup.get_by_group_name)
129
129
130 def _get_repo(self, repository):
130 def _get_repo(self, repository):
131 """
131 """
132 Helper method to get repository by ID, or repository name
132 Helper method to get repository by ID, or repository name
133
133
134 :param repository: RepoID, repository name or Repository Instance
134 :param repository: RepoID, repository name or Repository Instance
135 """
135 """
136 return self._get_instance(
136 return self._get_instance(
137 db.Repository, repository, callback=db.Repository.get_by_repo_name)
137 db.Repository, repository, callback=db.Repository.get_by_repo_name)
138
138
139 def _get_perm(self, permission):
139 def _get_perm(self, permission):
140 """
140 """
141 Helper method to get permission by ID, or permission name
141 Helper method to get permission by ID, or permission name
142
142
143 :param permission: PermissionID, permission_name or Permission instance
143 :param permission: PermissionID, permission_name or Permission instance
144 """
144 """
145 return self._get_instance(
145 return self._get_instance(
146 db.Permission, permission, callback=db.Permission.get_by_key)
146 db.Permission, permission, callback=db.Permission.get_by_key)
147
147
148 def send_event(self, event):
148 def send_event(self, event):
149 """
149 """
150 Helper method to send an event. This wraps the pyramid logic to send an
150 Helper method to send an event. This wraps the pyramid logic to send an
151 event.
151 event.
152 """
152 """
153 # For the first step we are using pyramids thread locals here. If the
153 # For the first step we are using pyramids thread locals here. If the
154 # event mechanism works out as a good solution we should think about
154 # event mechanism works out as a good solution we should think about
155 # passing the registry into the constructor to get rid of it.
155 # passing the registry into the constructor to get rid of it.
156 registry = get_current_registry()
156 registry = get_current_registry()
157 registry.notify(event)
157 registry.notify(event)
158
158
159 @classmethod
159 @classmethod
160 def get_all(cls):
160 def get_all(cls):
161 """
161 """
162 Returns all instances of what is defined in `cls` class variable
162 Returns all instances of what is defined in `cls` class variable
163 """
163 """
164 return cls.cls.getAll()
164 return cls.cls.getAll()
General Comments 0
You need to be logged in to leave comments. Login now