##// END OF EJS Templates
obfuscate password in logs for engine connection string
marcink -
r2882:12fce5e4 beta
parent child Browse files
Show More
@@ -1,499 +1,507 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Some simple helper functions
7 7
8 8 :created_on: Jan 5, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import re
27 27 import time
28 28 import datetime
29 29 from pylons.i18n.translation import _, ungettext
30 30 from rhodecode.lib.vcs.utils.lazy import LazyProperty
31 31
32 32
33 33 def __get_lem():
34 34 """
35 35 Get language extension map based on what's inside pygments lexers
36 36 """
37 37 from pygments import lexers
38 38 from string import lower
39 39 from collections import defaultdict
40 40
41 41 d = defaultdict(lambda: [])
42 42
43 43 def __clean(s):
44 44 s = s.lstrip('*')
45 45 s = s.lstrip('.')
46 46
47 47 if s.find('[') != -1:
48 48 exts = []
49 49 start, stop = s.find('['), s.find(']')
50 50
51 51 for suffix in s[start + 1:stop]:
52 52 exts.append(s[:s.find('[')] + suffix)
53 53 return map(lower, exts)
54 54 else:
55 55 return map(lower, [s])
56 56
57 57 for lx, t in sorted(lexers.LEXERS.items()):
58 58 m = map(__clean, t[-2])
59 59 if m:
60 60 m = reduce(lambda x, y: x + y, m)
61 61 for ext in m:
62 62 desc = lx.replace('Lexer', '')
63 63 d[ext].append(desc)
64 64
65 65 return dict(d)
66 66
67 67 def str2bool(_str):
68 68 """
69 69 returs True/False value from given string, it tries to translate the
70 70 string into boolean
71 71
72 72 :param _str: string value to translate into boolean
73 73 :rtype: boolean
74 74 :returns: boolean from given string
75 75 """
76 76 if _str is None:
77 77 return False
78 78 if _str in (True, False):
79 79 return _str
80 80 _str = str(_str).strip().lower()
81 81 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
82 82
83 83
84 84 def convert_line_endings(line, mode):
85 85 """
86 86 Converts a given line "line end" accordingly to given mode
87 87
88 88 Available modes are::
89 89 0 - Unix
90 90 1 - Mac
91 91 2 - DOS
92 92
93 93 :param line: given line to convert
94 94 :param mode: mode to convert to
95 95 :rtype: str
96 96 :return: converted line according to mode
97 97 """
98 98 from string import replace
99 99
100 100 if mode == 0:
101 101 line = replace(line, '\r\n', '\n')
102 102 line = replace(line, '\r', '\n')
103 103 elif mode == 1:
104 104 line = replace(line, '\r\n', '\r')
105 105 line = replace(line, '\n', '\r')
106 106 elif mode == 2:
107 107 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
108 108 return line
109 109
110 110
111 111 def detect_mode(line, default):
112 112 """
113 113 Detects line break for given line, if line break couldn't be found
114 114 given default value is returned
115 115
116 116 :param line: str line
117 117 :param default: default
118 118 :rtype: int
119 119 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
120 120 """
121 121 if line.endswith('\r\n'):
122 122 return 2
123 123 elif line.endswith('\n'):
124 124 return 0
125 125 elif line.endswith('\r'):
126 126 return 1
127 127 else:
128 128 return default
129 129
130 130
131 131 def generate_api_key(username, salt=None):
132 132 """
133 133 Generates unique API key for given username, if salt is not given
134 134 it'll be generated from some random string
135 135
136 136 :param username: username as string
137 137 :param salt: salt to hash generate KEY
138 138 :rtype: str
139 139 :returns: sha1 hash from username+salt
140 140 """
141 141 from tempfile import _RandomNameSequence
142 142 import hashlib
143 143
144 144 if salt is None:
145 145 salt = _RandomNameSequence().next()
146 146
147 147 return hashlib.sha1(username + salt).hexdigest()
148 148
149 149
150 150 def safe_int(val, default=None):
151 151 """
152 152 Returns int() of val if val is not convertable to int use default
153 153 instead
154 154
155 155 :param val:
156 156 :param default:
157 157 """
158 158
159 159 try:
160 160 val = int(val)
161 161 except ValueError:
162 162 val = default
163 163
164 164 return val
165 165
166 166
167 167 def safe_unicode(str_, from_encoding=None):
168 168 """
169 169 safe unicode function. Does few trick to turn str_ into unicode
170 170
171 171 In case of UnicodeDecode error we try to return it with encoding detected
172 172 by chardet library if it fails fallback to unicode with errors replaced
173 173
174 174 :param str_: string to decode
175 175 :rtype: unicode
176 176 :returns: unicode object
177 177 """
178 178 if isinstance(str_, unicode):
179 179 return str_
180 180
181 181 if not from_encoding:
182 182 import rhodecode
183 183 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
184 184 from_encoding = DEFAULT_ENCODING
185 185
186 186 try:
187 187 return unicode(str_)
188 188 except UnicodeDecodeError:
189 189 pass
190 190
191 191 try:
192 192 return unicode(str_, from_encoding)
193 193 except UnicodeDecodeError:
194 194 pass
195 195
196 196 try:
197 197 import chardet
198 198 encoding = chardet.detect(str_)['encoding']
199 199 if encoding is None:
200 200 raise Exception()
201 201 return str_.decode(encoding)
202 202 except (ImportError, UnicodeDecodeError, Exception):
203 203 return unicode(str_, from_encoding, 'replace')
204 204
205 205
206 206 def safe_str(unicode_, to_encoding=None):
207 207 """
208 208 safe str function. Does few trick to turn unicode_ into string
209 209
210 210 In case of UnicodeEncodeError we try to return it with encoding detected
211 211 by chardet library if it fails fallback to string with errors replaced
212 212
213 213 :param unicode_: unicode to encode
214 214 :rtype: str
215 215 :returns: str object
216 216 """
217 217
218 218 # if it's not basestr cast to str
219 219 if not isinstance(unicode_, basestring):
220 220 return str(unicode_)
221 221
222 222 if isinstance(unicode_, str):
223 223 return unicode_
224 224
225 225 if not to_encoding:
226 226 import rhodecode
227 227 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
228 228 to_encoding = DEFAULT_ENCODING
229 229
230 230 try:
231 231 return unicode_.encode(to_encoding)
232 232 except UnicodeEncodeError:
233 233 pass
234 234
235 235 try:
236 236 import chardet
237 237 encoding = chardet.detect(unicode_)['encoding']
238 238 if encoding is None:
239 239 raise UnicodeEncodeError()
240 240
241 241 return unicode_.encode(encoding)
242 242 except (ImportError, UnicodeEncodeError):
243 243 return unicode_.encode(to_encoding, 'replace')
244 244
245 245 return safe_str
246 246
247 247
248 248 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
249 249 """
250 250 Custom engine_from_config functions that makes sure we use NullPool for
251 251 file based sqlite databases. This prevents errors on sqlite. This only
252 252 applies to sqlalchemy versions < 0.7.0
253 253
254 254 """
255 255 import sqlalchemy
256 256 from sqlalchemy import engine_from_config as efc
257 257 import logging
258 258
259 259 if int(sqlalchemy.__version__.split('.')[1]) < 7:
260 260
261 261 # This solution should work for sqlalchemy < 0.7.0, and should use
262 262 # proxy=TimerProxy() for execution time profiling
263 263
264 264 from sqlalchemy.pool import NullPool
265 265 url = configuration[prefix + 'url']
266 266
267 267 if url.startswith('sqlite'):
268 268 kwargs.update({'poolclass': NullPool})
269 269 return efc(configuration, prefix, **kwargs)
270 270 else:
271 271 import time
272 272 from sqlalchemy import event
273 273 from sqlalchemy.engine import Engine
274 274
275 275 log = logging.getLogger('sqlalchemy.engine')
276 276 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
277 277 engine = efc(configuration, prefix, **kwargs)
278 278
279 279 def color_sql(sql):
280 280 COLOR_SEQ = "\033[1;%dm"
281 281 COLOR_SQL = YELLOW
282 282 normal = '\x1b[0m'
283 283 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
284 284
285 285 if configuration['debug']:
286 286 #attach events only for debug configuration
287 287
288 288 def before_cursor_execute(conn, cursor, statement,
289 289 parameters, context, executemany):
290 290 context._query_start_time = time.time()
291 291 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
292 292
293 293 def after_cursor_execute(conn, cursor, statement,
294 294 parameters, context, executemany):
295 295 total = time.time() - context._query_start_time
296 296 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
297 297
298 298 event.listen(engine, "before_cursor_execute",
299 299 before_cursor_execute)
300 300 event.listen(engine, "after_cursor_execute",
301 301 after_cursor_execute)
302 302
303 303 return engine
304 304
305 305
306 306 def age(prevdate):
307 307 """
308 308 turns a datetime into an age string.
309 309
310 310 :param prevdate: datetime object
311 311 :rtype: unicode
312 312 :returns: unicode words describing age
313 313 """
314 314
315 315 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
316 316 deltas = {}
317 317
318 318 # Get date parts deltas
319 319 now = datetime.datetime.now()
320 320 for part in order:
321 321 deltas[part] = getattr(now, part) - getattr(prevdate, part)
322 322
323 323 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
324 324 # not 1 hour, -59 minutes and -59 seconds)
325 325
326 326 for num, length in [(5, 60), (4, 60), (3, 24)]: # seconds, minutes, hours
327 327 part = order[num]
328 328 carry_part = order[num - 1]
329 329
330 330 if deltas[part] < 0:
331 331 deltas[part] += length
332 332 deltas[carry_part] -= 1
333 333
334 334 # Same thing for days except that the increment depends on the (variable)
335 335 # number of days in the month
336 336 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
337 337 if deltas['day'] < 0:
338 338 if prevdate.month == 2 and (prevdate.year % 4 == 0 and
339 339 (prevdate.year % 100 != 0 or prevdate.year % 400 == 0)):
340 340 deltas['day'] += 29
341 341 else:
342 342 deltas['day'] += month_lengths[prevdate.month - 1]
343 343
344 344 deltas['month'] -= 1
345 345
346 346 if deltas['month'] < 0:
347 347 deltas['month'] += 12
348 348 deltas['year'] -= 1
349 349
350 350 # Format the result
351 351 fmt_funcs = {
352 352 'year': lambda d: ungettext(u'%d year', '%d years', d) % d,
353 353 'month': lambda d: ungettext(u'%d month', '%d months', d) % d,
354 354 'day': lambda d: ungettext(u'%d day', '%d days', d) % d,
355 355 'hour': lambda d: ungettext(u'%d hour', '%d hours', d) % d,
356 356 'minute': lambda d: ungettext(u'%d minute', '%d minutes', d) % d,
357 357 'second': lambda d: ungettext(u'%d second', '%d seconds', d) % d,
358 358 }
359 359
360 360 for i, part in enumerate(order):
361 361 value = deltas[part]
362 362 if value == 0:
363 363 continue
364 364
365 365 if i < 5:
366 366 sub_part = order[i + 1]
367 367 sub_value = deltas[sub_part]
368 368 else:
369 369 sub_value = 0
370 370
371 371 if sub_value == 0:
372 372 return _(u'%s ago') % fmt_funcs[part](value)
373 373
374 374 return _(u'%s and %s ago') % (fmt_funcs[part](value),
375 375 fmt_funcs[sub_part](sub_value))
376 376
377 377 return _(u'just now')
378 378
379 379
380 380 def uri_filter(uri):
381 381 """
382 382 Removes user:password from given url string
383 383
384 384 :param uri:
385 385 :rtype: unicode
386 386 :returns: filtered list of strings
387 387 """
388 388 if not uri:
389 389 return ''
390 390
391 391 proto = ''
392 392
393 393 for pat in ('https://', 'http://'):
394 394 if uri.startswith(pat):
395 395 uri = uri[len(pat):]
396 396 proto = pat
397 397 break
398 398
399 399 # remove passwords and username
400 400 uri = uri[uri.find('@') + 1:]
401 401
402 402 # get the port
403 403 cred_pos = uri.find(':')
404 404 if cred_pos == -1:
405 405 host, port = uri, None
406 406 else:
407 407 host, port = uri[:cred_pos], uri[cred_pos + 1:]
408 408
409 409 return filter(None, [proto, host, port])
410 410
411 411
412 412 def credentials_filter(uri):
413 413 """
414 414 Returns a url with removed credentials
415 415
416 416 :param uri:
417 417 """
418 418
419 419 uri = uri_filter(uri)
420 420 #check if we have port
421 421 if len(uri) > 2 and uri[2]:
422 422 uri[2] = ':' + uri[2]
423 423
424 424 return ''.join(uri)
425 425
426 426
427 427 def get_changeset_safe(repo, rev):
428 428 """
429 429 Safe version of get_changeset if this changeset doesn't exists for a
430 430 repo it returns a Dummy one instead
431 431
432 432 :param repo:
433 433 :param rev:
434 434 """
435 435 from rhodecode.lib.vcs.backends.base import BaseRepository
436 436 from rhodecode.lib.vcs.exceptions import RepositoryError
437 437 from rhodecode.lib.vcs.backends.base import EmptyChangeset
438 438 if not isinstance(repo, BaseRepository):
439 439 raise Exception('You must pass an Repository '
440 440 'object as first argument got %s', type(repo))
441 441
442 442 try:
443 443 cs = repo.get_changeset(rev)
444 444 except RepositoryError:
445 445 cs = EmptyChangeset(requested_revision=rev)
446 446 return cs
447 447
448 448
449 449 def datetime_to_time(dt):
450 450 if dt:
451 451 return time.mktime(dt.timetuple())
452 452
453 453
454 454 def time_to_datetime(tm):
455 455 if tm:
456 456 if isinstance(tm, basestring):
457 457 try:
458 458 tm = float(tm)
459 459 except ValueError:
460 460 return
461 461 return datetime.datetime.fromtimestamp(tm)
462 462
463 463 MENTIONS_REGEX = r'(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)(?:\s{1})'
464 464
465 465
466 466 def extract_mentioned_users(s):
467 467 """
468 468 Returns unique usernames from given string s that have @mention
469 469
470 470 :param s: string to get mentions
471 471 """
472 472 usrs = set()
473 473 for username in re.findall(MENTIONS_REGEX, s):
474 474 usrs.add(username)
475 475
476 476 return sorted(list(usrs), key=lambda k: k.lower())
477 477
478 478
479 479 class AttributeDict(dict):
480 480 def __getattr__(self, attr):
481 481 return self.get(attr, None)
482 482 __setattr__ = dict.__setitem__
483 483 __delattr__ = dict.__delitem__
484 484
485 485
486 486 def fix_PATH(os_=None):
487 487 """
488 488 Get current active python path, and append it to PATH variable to fix issues
489 489 of subprocess calls and different python versions
490 490 """
491 491 import sys
492 492 if os_ is None:
493 493 import os
494 494 else:
495 495 os = os_
496 496
497 497 cur_path = os.path.split(sys.executable)[0]
498 498 if not os.environ['PATH'].startswith(cur_path):
499 499 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
500
501
502 def obfuscate_url_pw(engine):
503 from sqlalchemy.engine import url
504 url = url.make_url(engine)
505 if url.password:
506 url.password = 'XXXXX'
507 return str(url) No newline at end of file
@@ -1,139 +1,140 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.__init__
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 The application's model objects
7 7
8 8 :created_on: Nov 25, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12
13 13
14 14 :example:
15 15
16 16 .. code-block:: python
17 17
18 18 from paste.deploy import appconfig
19 19 from pylons import config
20 20 from sqlalchemy import engine_from_config
21 21 from rhodecode.config.environment import load_environment
22 22
23 23 conf = appconfig('config:development.ini', relative_to = './../../')
24 24 load_environment(conf.global_conf, conf.local_conf)
25 25
26 26 engine = engine_from_config(config, 'sqlalchemy.')
27 27 init_model(engine)
28 28 # RUN YOUR CODE HERE
29 29
30 30 """
31 31 # This program is free software: you can redistribute it and/or modify
32 32 # it under the terms of the GNU General Public License as published by
33 33 # the Free Software Foundation, either version 3 of the License, or
34 34 # (at your option) any later version.
35 35 #
36 36 # This program is distributed in the hope that it will be useful,
37 37 # but WITHOUT ANY WARRANTY; without even the implied warranty of
38 38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 39 # GNU General Public License for more details.
40 40 #
41 41 # You should have received a copy of the GNU General Public License
42 42 # along with this program. If not, see <http://www.gnu.org/licenses/>.
43 43
44 44 import logging
45 45 from rhodecode.model import meta
46 from rhodecode.lib.utils2 import safe_str
46 from rhodecode.lib.utils2 import safe_str, obfuscate_url_pw
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50
51 51 def init_model(engine):
52 52 """
53 53 Initializes db session, bind the engine with the metadata,
54 54 Call this before using any of the tables or classes in the model,
55 55 preferably once in application start
56 56
57 57 :param engine: engine to bind to
58 58 """
59 log.info("initializing db for %s" % engine)
59 engine_str = obfuscate_url_pw(str(engine.url))
60 log.info("initializing db for %s" % engine_str)
60 61 meta.Base.metadata.bind = engine
61 62
62 63
63 64 class BaseModel(object):
64 65 """
65 66 Base Model for all RhodeCode models, it adds sql alchemy session
66 67 into instance of model
67 68
68 69 :param sa: If passed it reuses this session instead of creating a new one
69 70 """
70 71
71 72 cls = None # override in child class
72 73
73 74 def __init__(self, sa=None):
74 75 if sa is not None:
75 76 self.sa = sa
76 77 else:
77 78 self.sa = meta.Session()
78 79
79 80 def _get_instance(self, cls, instance, callback=None):
80 81 """
81 82 Get's instance of given cls using some simple lookup mechanism.
82 83
83 84 :param cls: class to fetch
84 85 :param instance: int or Instance
85 86 :param callback: callback to call if all lookups failed
86 87 """
87 88
88 89 if isinstance(instance, cls):
89 90 return instance
90 91 elif isinstance(instance, (int, long)) or safe_str(instance).isdigit():
91 92 return cls.get(instance)
92 93 else:
93 94 if instance:
94 95 if callback is None:
95 96 raise Exception(
96 97 'given object must be int, long or Instance of %s '
97 98 'got %s, no callback provided' % (cls, type(instance))
98 99 )
99 100 else:
100 101 return callback(instance)
101 102
102 103 def _get_user(self, user):
103 104 """
104 105 Helper method to get user by ID, or username fallback
105 106
106 107 :param user:
107 108 :type user: UserID, username, or User instance
108 109 """
109 110 from rhodecode.model.db import User
110 111 return self._get_instance(User, user,
111 112 callback=User.get_by_username)
112 113
113 114 def _get_repo(self, repository):
114 115 """
115 116 Helper method to get repository by ID, or repository name
116 117
117 118 :param repository:
118 119 :type repository: RepoID, repository name or Repository Instance
119 120 """
120 121 from rhodecode.model.db import Repository
121 122 return self._get_instance(Repository, repository,
122 123 callback=Repository.get_by_repo_name)
123 124
124 125 def _get_perm(self, permission):
125 126 """
126 127 Helper method to get permission by ID, or permission name
127 128
128 129 :param permission:
129 130 :type permission: PermissionID, permission_name or Permission instance
130 131 """
131 132 from rhodecode.model.db import Permission
132 133 return self._get_instance(Permission, permission,
133 134 callback=Permission.get_by_key)
134 135
135 136 def get_all(self):
136 137 """
137 138 Returns all instances of what is defined in `cls` class variable
138 139 """
139 140 return self.cls.getAll()
General Comments 0
You need to be logged in to leave comments. Login now