##// END OF EJS Templates
ui: always report untrusted hgrc files when debug enabled...
Ry4an Brase -
r13493:95b0d4c1 stable
parent child Browse files
Show More
@@ -1,636 +1,636 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import errno, getpass, os, socket, sys, tempfile, traceback
10 10 import config, util, error
11 11
12 12 class ui(object):
13 13 def __init__(self, src=None):
14 14 self._buffers = []
15 15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 16 self._reportuntrusted = True
17 17 self._ocfg = config.config() # overlay
18 18 self._tcfg = config.config() # trusted
19 19 self._ucfg = config.config() # untrusted
20 20 self._trustusers = set()
21 21 self._trustgroups = set()
22 22
23 23 if src:
24 24 self._tcfg = src._tcfg.copy()
25 25 self._ucfg = src._ucfg.copy()
26 26 self._ocfg = src._ocfg.copy()
27 27 self._trustusers = src._trustusers.copy()
28 28 self._trustgroups = src._trustgroups.copy()
29 29 self.environ = src.environ
30 30 self.fixconfig()
31 31 else:
32 32 # shared read-only environment
33 33 self.environ = os.environ
34 34 # we always trust global config files
35 35 for f in util.rcpath():
36 36 self.readconfig(f, trust=True)
37 37
38 38 def copy(self):
39 39 return self.__class__(self)
40 40
41 41 def _is_trusted(self, fp, f):
42 42 st = util.fstat(fp)
43 43 if util.isowner(st):
44 44 return True
45 45
46 46 tusers, tgroups = self._trustusers, self._trustgroups
47 47 if '*' in tusers or '*' in tgroups:
48 48 return True
49 49
50 50 user = util.username(st.st_uid)
51 51 group = util.groupname(st.st_gid)
52 52 if user in tusers or group in tgroups or user == util.username():
53 53 return True
54 54
55 55 if self._reportuntrusted:
56 56 self.warn(_('Not trusting file %s from untrusted '
57 57 'user %s, group %s\n') % (f, user, group))
58 58 return False
59 59
60 60 def readconfig(self, filename, root=None, trust=False,
61 61 sections=None, remap=None):
62 62 try:
63 63 fp = open(filename)
64 64 except IOError:
65 65 if not sections: # ignore unless we were looking for something
66 66 return
67 67 raise
68 68
69 69 cfg = config.config()
70 70 trusted = sections or trust or self._is_trusted(fp, filename)
71 71
72 72 try:
73 73 cfg.read(filename, fp, sections=sections, remap=remap)
74 74 except error.ConfigError, inst:
75 75 if trusted:
76 76 raise
77 77 self.warn(_("Ignored: %s\n") % str(inst))
78 78
79 79 if self.plain():
80 80 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
81 81 'logtemplate', 'style',
82 82 'traceback', 'verbose'):
83 83 if k in cfg['ui']:
84 84 del cfg['ui'][k]
85 85 for k, v in cfg.items('alias'):
86 86 del cfg['alias'][k]
87 87 for k, v in cfg.items('defaults'):
88 88 del cfg['defaults'][k]
89 89
90 90 if trusted:
91 91 self._tcfg.update(cfg)
92 92 self._tcfg.update(self._ocfg)
93 93 self._ucfg.update(cfg)
94 94 self._ucfg.update(self._ocfg)
95 95
96 96 if root is None:
97 97 root = os.path.expanduser('~')
98 98 self.fixconfig(root=root)
99 99
100 100 def fixconfig(self, root=None, section=None):
101 101 if section in (None, 'paths'):
102 102 # expand vars and ~
103 103 # translate paths relative to root (or home) into absolute paths
104 104 root = root or os.getcwd()
105 105 for c in self._tcfg, self._ucfg, self._ocfg:
106 106 for n, p in c.items('paths'):
107 107 if not p:
108 108 continue
109 109 if '%%' in p:
110 110 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
111 111 % (n, p, self.configsource('paths', n)))
112 112 p = p.replace('%%', '%')
113 113 p = util.expandpath(p)
114 114 if '://' not in p and not os.path.isabs(p):
115 115 p = os.path.normpath(os.path.join(root, p))
116 116 c.set("paths", n, p)
117 117
118 118 if section in (None, 'ui'):
119 119 # update ui options
120 120 self.debugflag = self.configbool('ui', 'debug')
121 121 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
122 122 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
123 123 if self.verbose and self.quiet:
124 124 self.quiet = self.verbose = False
125 self._reportuntrusted = self.configbool("ui", "report_untrusted",
126 True)
125 self._reportuntrusted = self.debugflag or self.configbool("ui",
126 "report_untrusted", True)
127 127 self.tracebackflag = self.configbool('ui', 'traceback', False)
128 128
129 129 if section in (None, 'trusted'):
130 130 # update trust information
131 131 self._trustusers.update(self.configlist('trusted', 'users'))
132 132 self._trustgroups.update(self.configlist('trusted', 'groups'))
133 133
134 134 def setconfig(self, section, name, value, overlay=True):
135 135 if overlay:
136 136 self._ocfg.set(section, name, value)
137 137 self._tcfg.set(section, name, value)
138 138 self._ucfg.set(section, name, value)
139 139 self.fixconfig(section=section)
140 140
141 141 def _data(self, untrusted):
142 142 return untrusted and self._ucfg or self._tcfg
143 143
144 144 def configsource(self, section, name, untrusted=False):
145 145 return self._data(untrusted).source(section, name) or 'none'
146 146
147 147 def config(self, section, name, default=None, untrusted=False):
148 148 value = self._data(untrusted).get(section, name, default)
149 149 if self.debugflag and not untrusted and self._reportuntrusted:
150 150 uvalue = self._ucfg.get(section, name)
151 151 if uvalue is not None and uvalue != value:
152 152 self.debug(_("ignoring untrusted configuration option "
153 153 "%s.%s = %s\n") % (section, name, uvalue))
154 154 return value
155 155
156 156 def configpath(self, section, name, default=None, untrusted=False):
157 157 'get a path config item, expanded relative to config file'
158 158 v = self.config(section, name, default, untrusted)
159 159 if not os.path.isabs(v) or "://" not in v:
160 160 src = self.configsource(section, name, untrusted)
161 161 if ':' in src:
162 162 base = os.path.dirname(src.rsplit(':'))
163 163 v = os.path.join(base, os.path.expanduser(v))
164 164 return v
165 165
166 166 def configbool(self, section, name, default=False, untrusted=False):
167 167 v = self.config(section, name, None, untrusted)
168 168 if v is None:
169 169 return default
170 170 if isinstance(v, bool):
171 171 return v
172 172 b = util.parsebool(v)
173 173 if b is None:
174 174 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
175 175 % (section, name, v))
176 176 return b
177 177
178 178 def configlist(self, section, name, default=None, untrusted=False):
179 179 """Return a list of comma/space separated strings"""
180 180
181 181 def _parse_plain(parts, s, offset):
182 182 whitespace = False
183 183 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
184 184 whitespace = True
185 185 offset += 1
186 186 if offset >= len(s):
187 187 return None, parts, offset
188 188 if whitespace:
189 189 parts.append('')
190 190 if s[offset] == '"' and not parts[-1]:
191 191 return _parse_quote, parts, offset + 1
192 192 elif s[offset] == '"' and parts[-1][-1] == '\\':
193 193 parts[-1] = parts[-1][:-1] + s[offset]
194 194 return _parse_plain, parts, offset + 1
195 195 parts[-1] += s[offset]
196 196 return _parse_plain, parts, offset + 1
197 197
198 198 def _parse_quote(parts, s, offset):
199 199 if offset < len(s) and s[offset] == '"': # ""
200 200 parts.append('')
201 201 offset += 1
202 202 while offset < len(s) and (s[offset].isspace() or
203 203 s[offset] == ','):
204 204 offset += 1
205 205 return _parse_plain, parts, offset
206 206
207 207 while offset < len(s) and s[offset] != '"':
208 208 if (s[offset] == '\\' and offset + 1 < len(s)
209 209 and s[offset + 1] == '"'):
210 210 offset += 1
211 211 parts[-1] += '"'
212 212 else:
213 213 parts[-1] += s[offset]
214 214 offset += 1
215 215
216 216 if offset >= len(s):
217 217 real_parts = _configlist(parts[-1])
218 218 if not real_parts:
219 219 parts[-1] = '"'
220 220 else:
221 221 real_parts[0] = '"' + real_parts[0]
222 222 parts = parts[:-1]
223 223 parts.extend(real_parts)
224 224 return None, parts, offset
225 225
226 226 offset += 1
227 227 while offset < len(s) and s[offset] in [' ', ',']:
228 228 offset += 1
229 229
230 230 if offset < len(s):
231 231 if offset + 1 == len(s) and s[offset] == '"':
232 232 parts[-1] += '"'
233 233 offset += 1
234 234 else:
235 235 parts.append('')
236 236 else:
237 237 return None, parts, offset
238 238
239 239 return _parse_plain, parts, offset
240 240
241 241 def _configlist(s):
242 242 s = s.rstrip(' ,')
243 243 if not s:
244 244 return []
245 245 parser, parts, offset = _parse_plain, [''], 0
246 246 while parser:
247 247 parser, parts, offset = parser(parts, s, offset)
248 248 return parts
249 249
250 250 result = self.config(section, name, untrusted=untrusted)
251 251 if result is None:
252 252 result = default or []
253 253 if isinstance(result, basestring):
254 254 result = _configlist(result.lstrip(' ,\n'))
255 255 if result is None:
256 256 result = default or []
257 257 return result
258 258
259 259 def has_section(self, section, untrusted=False):
260 260 '''tell whether section exists in config.'''
261 261 return section in self._data(untrusted)
262 262
263 263 def configitems(self, section, untrusted=False):
264 264 items = self._data(untrusted).items(section)
265 265 if self.debugflag and not untrusted and self._reportuntrusted:
266 266 for k, v in self._ucfg.items(section):
267 267 if self._tcfg.get(section, k) != v:
268 268 self.debug(_("ignoring untrusted configuration option "
269 269 "%s.%s = %s\n") % (section, k, v))
270 270 return items
271 271
272 272 def walkconfig(self, untrusted=False):
273 273 cfg = self._data(untrusted)
274 274 for section in cfg.sections():
275 275 for name, value in self.configitems(section, untrusted):
276 276 yield section, name, str(value).replace('\n', '\\n')
277 277
278 278 def plain(self):
279 279 '''is plain mode active?
280 280
281 281 Plain mode means that all configuration variables which affect the
282 282 behavior and output of Mercurial should be ignored. Additionally, the
283 283 output should be stable, reproducible and suitable for use in scripts or
284 284 applications.
285 285
286 286 The only way to trigger plain mode is by setting the `HGPLAIN'
287 287 environment variable.
288 288 '''
289 289 return 'HGPLAIN' in os.environ
290 290
291 291 def username(self):
292 292 """Return default username to be used in commits.
293 293
294 294 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
295 295 and stop searching if one of these is set.
296 296 If not found and ui.askusername is True, ask the user, else use
297 297 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
298 298 """
299 299 user = os.environ.get("HGUSER")
300 300 if user is None:
301 301 user = self.config("ui", "username")
302 302 if user is not None:
303 303 user = os.path.expandvars(user)
304 304 if user is None:
305 305 user = os.environ.get("EMAIL")
306 306 if user is None and self.configbool("ui", "askusername"):
307 307 user = self.prompt(_("enter a commit username:"), default=None)
308 308 if user is None and not self.interactive():
309 309 try:
310 310 user = '%s@%s' % (util.getuser(), socket.getfqdn())
311 311 self.warn(_("No username found, using '%s' instead\n") % user)
312 312 except KeyError:
313 313 pass
314 314 if not user:
315 315 raise util.Abort(_('no username supplied (see "hg help config")'))
316 316 if "\n" in user:
317 317 raise util.Abort(_("username %s contains a newline\n") % repr(user))
318 318 return user
319 319
320 320 def shortuser(self, user):
321 321 """Return a short representation of a user name or email address."""
322 322 if not self.verbose:
323 323 user = util.shortuser(user)
324 324 return user
325 325
326 326 def expandpath(self, loc, default=None):
327 327 """Return repository location relative to cwd or from [paths]"""
328 328 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
329 329 return loc
330 330
331 331 path = self.config('paths', loc)
332 332 if not path and default is not None:
333 333 path = self.config('paths', default)
334 334 return path or loc
335 335
336 336 def pushbuffer(self):
337 337 self._buffers.append([])
338 338
339 339 def popbuffer(self, labeled=False):
340 340 '''pop the last buffer and return the buffered output
341 341
342 342 If labeled is True, any labels associated with buffered
343 343 output will be handled. By default, this has no effect
344 344 on the output returned, but extensions and GUI tools may
345 345 handle this argument and returned styled output. If output
346 346 is being buffered so it can be captured and parsed or
347 347 processed, labeled should not be set to True.
348 348 '''
349 349 return "".join(self._buffers.pop())
350 350
351 351 def write(self, *args, **opts):
352 352 '''write args to output
353 353
354 354 By default, this method simply writes to the buffer or stdout,
355 355 but extensions or GUI tools may override this method,
356 356 write_err(), popbuffer(), and label() to style output from
357 357 various parts of hg.
358 358
359 359 An optional keyword argument, "label", can be passed in.
360 360 This should be a string containing label names separated by
361 361 space. Label names take the form of "topic.type". For example,
362 362 ui.debug() issues a label of "ui.debug".
363 363
364 364 When labeling output for a specific command, a label of
365 365 "cmdname.type" is recommended. For example, status issues
366 366 a label of "status.modified" for modified files.
367 367 '''
368 368 if self._buffers:
369 369 self._buffers[-1].extend([str(a) for a in args])
370 370 else:
371 371 for a in args:
372 372 sys.stdout.write(str(a))
373 373
374 374 def write_err(self, *args, **opts):
375 375 try:
376 376 if not getattr(sys.stdout, 'closed', False):
377 377 sys.stdout.flush()
378 378 for a in args:
379 379 sys.stderr.write(str(a))
380 380 # stderr may be buffered under win32 when redirected to files,
381 381 # including stdout.
382 382 if not getattr(sys.stderr, 'closed', False):
383 383 sys.stderr.flush()
384 384 except IOError, inst:
385 385 if inst.errno not in (errno.EPIPE, errno.EIO):
386 386 raise
387 387
388 388 def flush(self):
389 389 try: sys.stdout.flush()
390 390 except: pass
391 391 try: sys.stderr.flush()
392 392 except: pass
393 393
394 394 def interactive(self):
395 395 '''is interactive input allowed?
396 396
397 397 An interactive session is a session where input can be reasonably read
398 398 from `sys.stdin'. If this function returns false, any attempt to read
399 399 from stdin should fail with an error, unless a sensible default has been
400 400 specified.
401 401
402 402 Interactiveness is triggered by the value of the `ui.interactive'
403 403 configuration variable or - if it is unset - when `sys.stdin' points
404 404 to a terminal device.
405 405
406 406 This function refers to input only; for output, see `ui.formatted()'.
407 407 '''
408 408 i = self.configbool("ui", "interactive", None)
409 409 if i is None:
410 410 try:
411 411 return sys.stdin.isatty()
412 412 except AttributeError:
413 413 # some environments replace stdin without implementing isatty
414 414 # usually those are non-interactive
415 415 return False
416 416
417 417 return i
418 418
419 419 def termwidth(self):
420 420 '''how wide is the terminal in columns?
421 421 '''
422 422 if 'COLUMNS' in os.environ:
423 423 try:
424 424 return int(os.environ['COLUMNS'])
425 425 except ValueError:
426 426 pass
427 427 return util.termwidth()
428 428
429 429 def formatted(self):
430 430 '''should formatted output be used?
431 431
432 432 It is often desirable to format the output to suite the output medium.
433 433 Examples of this are truncating long lines or colorizing messages.
434 434 However, this is not often not desirable when piping output into other
435 435 utilities, e.g. `grep'.
436 436
437 437 Formatted output is triggered by the value of the `ui.formatted'
438 438 configuration variable or - if it is unset - when `sys.stdout' points
439 439 to a terminal device. Please note that `ui.formatted' should be
440 440 considered an implementation detail; it is not intended for use outside
441 441 Mercurial or its extensions.
442 442
443 443 This function refers to output only; for input, see `ui.interactive()'.
444 444 This function always returns false when in plain mode, see `ui.plain()'.
445 445 '''
446 446 if self.plain():
447 447 return False
448 448
449 449 i = self.configbool("ui", "formatted", None)
450 450 if i is None:
451 451 try:
452 452 return sys.stdout.isatty()
453 453 except AttributeError:
454 454 # some environments replace stdout without implementing isatty
455 455 # usually those are non-interactive
456 456 return False
457 457
458 458 return i
459 459
460 460 def _readline(self, prompt=''):
461 461 if sys.stdin.isatty():
462 462 try:
463 463 # magically add command line editing support, where
464 464 # available
465 465 import readline
466 466 # force demandimport to really load the module
467 467 readline.read_history_file
468 468 # windows sometimes raises something other than ImportError
469 469 except Exception:
470 470 pass
471 471 line = raw_input(prompt)
472 472 # When stdin is in binary mode on Windows, it can cause
473 473 # raw_input() to emit an extra trailing carriage return
474 474 if os.linesep == '\r\n' and line and line[-1] == '\r':
475 475 line = line[:-1]
476 476 return line
477 477
478 478 def prompt(self, msg, default="y"):
479 479 """Prompt user with msg, read response.
480 480 If ui is not interactive, the default is returned.
481 481 """
482 482 if not self.interactive():
483 483 self.write(msg, ' ', default, "\n")
484 484 return default
485 485 try:
486 486 r = self._readline(msg + ' ')
487 487 if not r:
488 488 return default
489 489 return r
490 490 except EOFError:
491 491 raise util.Abort(_('response expected'))
492 492
493 493 def promptchoice(self, msg, choices, default=0):
494 494 """Prompt user with msg, read response, and ensure it matches
495 495 one of the provided choices. The index of the choice is returned.
496 496 choices is a sequence of acceptable responses with the format:
497 497 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
498 498 If ui is not interactive, the default is returned.
499 499 """
500 500 resps = [s[s.index('&')+1].lower() for s in choices]
501 501 while True:
502 502 r = self.prompt(msg, resps[default])
503 503 if r.lower() in resps:
504 504 return resps.index(r.lower())
505 505 self.write(_("unrecognized response\n"))
506 506
507 507 def getpass(self, prompt=None, default=None):
508 508 if not self.interactive():
509 509 return default
510 510 try:
511 511 return getpass.getpass(prompt or _('password: '))
512 512 except EOFError:
513 513 raise util.Abort(_('response expected'))
514 514 def status(self, *msg, **opts):
515 515 '''write status message to output (if ui.quiet is False)
516 516
517 517 This adds an output label of "ui.status".
518 518 '''
519 519 if not self.quiet:
520 520 opts['label'] = opts.get('label', '') + ' ui.status'
521 521 self.write(*msg, **opts)
522 522 def warn(self, *msg, **opts):
523 523 '''write warning message to output (stderr)
524 524
525 525 This adds an output label of "ui.warning".
526 526 '''
527 527 opts['label'] = opts.get('label', '') + ' ui.warning'
528 528 self.write_err(*msg, **opts)
529 529 def note(self, *msg, **opts):
530 530 '''write note to output (if ui.verbose is True)
531 531
532 532 This adds an output label of "ui.note".
533 533 '''
534 534 if self.verbose:
535 535 opts['label'] = opts.get('label', '') + ' ui.note'
536 536 self.write(*msg, **opts)
537 537 def debug(self, *msg, **opts):
538 538 '''write debug message to output (if ui.debugflag is True)
539 539
540 540 This adds an output label of "ui.debug".
541 541 '''
542 542 if self.debugflag:
543 543 opts['label'] = opts.get('label', '') + ' ui.debug'
544 544 self.write(*msg, **opts)
545 545 def edit(self, text, user):
546 546 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
547 547 text=True)
548 548 try:
549 549 f = os.fdopen(fd, "w")
550 550 f.write(text)
551 551 f.close()
552 552
553 553 editor = self.geteditor()
554 554
555 555 util.system("%s \"%s\"" % (editor, name),
556 556 environ={'HGUSER': user},
557 557 onerr=util.Abort, errprefix=_("edit failed"))
558 558
559 559 f = open(name)
560 560 t = f.read()
561 561 f.close()
562 562 finally:
563 563 os.unlink(name)
564 564
565 565 return t
566 566
567 567 def traceback(self, exc=None):
568 568 '''print exception traceback if traceback printing enabled.
569 569 only to call in exception handler. returns true if traceback
570 570 printed.'''
571 571 if self.tracebackflag:
572 572 if exc:
573 573 traceback.print_exception(exc[0], exc[1], exc[2])
574 574 else:
575 575 traceback.print_exc()
576 576 return self.tracebackflag
577 577
578 578 def geteditor(self):
579 579 '''return editor to use'''
580 580 return (os.environ.get("HGEDITOR") or
581 581 self.config("ui", "editor") or
582 582 os.environ.get("VISUAL") or
583 583 os.environ.get("EDITOR", "vi"))
584 584
585 585 def progress(self, topic, pos, item="", unit="", total=None):
586 586 '''show a progress message
587 587
588 588 With stock hg, this is simply a debug message that is hidden
589 589 by default, but with extensions or GUI tools it may be
590 590 visible. 'topic' is the current operation, 'item' is a
591 591 non-numeric marker of the current position (ie the currently
592 592 in-process file), 'pos' is the current numeric position (ie
593 593 revision, bytes, etc.), unit is a corresponding unit label,
594 594 and total is the highest expected pos.
595 595
596 596 Multiple nested topics may be active at a time.
597 597
598 598 All topics should be marked closed by setting pos to None at
599 599 termination.
600 600 '''
601 601
602 602 if pos is None or not self.debugflag:
603 603 return
604 604
605 605 if unit:
606 606 unit = ' ' + unit
607 607 if item:
608 608 item = ' ' + item
609 609
610 610 if total:
611 611 pct = 100.0 * pos / total
612 612 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
613 613 % (topic, item, pos, total, unit, pct))
614 614 else:
615 615 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
616 616
617 617 def log(self, service, message):
618 618 '''hook for logging facility extensions
619 619
620 620 service should be a readily-identifiable subsystem, which will
621 621 allow filtering.
622 622 message should be a newline-terminated string to log.
623 623 '''
624 624 pass
625 625
626 626 def label(self, msg, label):
627 627 '''style msg based on supplied label
628 628
629 629 Like ui.write(), this just returns msg unchanged, but extensions
630 630 and GUI tools can override it to allow styling output without
631 631 writing it.
632 632
633 633 ui.write(s, 'label') is equivalent to
634 634 ui.write(ui.label(s, 'label')).
635 635 '''
636 636 return msg
@@ -1,189 +1,197 b''
1 1 # Since it's not easy to write a test that portably deals
2 2 # with files from different users/groups, we cheat a bit by
3 3 # monkey-patching some functions in the util module
4 4
5 5 import os
6 6 from mercurial import ui, util, error
7 7
8 8 hgrc = os.environ['HGRCPATH']
9 9 f = open(hgrc)
10 10 basehgrc = f.read()
11 11 f.close()
12 12
13 13 def testui(user='foo', group='bar', tusers=(), tgroups=(),
14 cuser='foo', cgroup='bar', debug=False, silent=False):
14 cuser='foo', cgroup='bar', debug=False, silent=False,
15 report=True):
15 16 # user, group => owners of the file
16 17 # tusers, tgroups => trusted users/groups
17 18 # cuser, cgroup => user/group of the current process
18 19
19 20 # write a global hgrc with the list of trusted users/groups and
20 21 # some setting so that we can be sure it was read
21 22 f = open(hgrc, 'w')
22 23 f.write(basehgrc)
23 24 f.write('\n[paths]\n')
24 25 f.write('global = /some/path\n\n')
25 26
26 27 if tusers or tgroups:
27 28 f.write('[trusted]\n')
28 29 if tusers:
29 30 f.write('users = %s\n' % ', '.join(tusers))
30 31 if tgroups:
31 32 f.write('groups = %s\n' % ', '.join(tgroups))
32 33 f.close()
33 34
34 35 # override the functions that give names to uids and gids
35 36 def username(uid=None):
36 37 if uid is None:
37 38 return cuser
38 39 return user
39 40 util.username = username
40 41
41 42 def groupname(gid=None):
42 43 if gid is None:
43 44 return 'bar'
44 45 return group
45 46 util.groupname = groupname
46 47
47 48 def isowner(st):
48 49 return user == cuser
49 50 util.isowner = isowner
50 51
51 52 # try to read everything
52 53 #print '# File belongs to user %s, group %s' % (user, group)
53 54 #print '# trusted users = %s; trusted groups = %s' % (tusers, tgroups)
54 55 kind = ('different', 'same')
55 56 who = ('', 'user', 'group', 'user and the group')
56 57 trusted = who[(user in tusers) + 2*(group in tgroups)]
57 58 if trusted:
58 59 trusted = ', but we trust the ' + trusted
59 60 print '# %s user, %s group%s' % (kind[user == cuser], kind[group == cgroup],
60 61 trusted)
61 62
62 63 u = ui.ui()
63 64 u.setconfig('ui', 'debug', str(bool(debug)))
65 u.setconfig('ui', 'report_untrusted', str(bool(report)))
64 66 u.readconfig('.hg/hgrc')
65 67 if silent:
66 68 return u
67 69 print 'trusted'
68 70 for name, path in u.configitems('paths'):
69 71 print ' ', name, '=', path
70 72 print 'untrusted'
71 73 for name, path in u.configitems('paths', untrusted=True):
72 74 print '.',
73 75 u.config('paths', name) # warning with debug=True
74 76 print '.',
75 77 u.config('paths', name, untrusted=True) # no warnings
76 78 print name, '=', path
77 79 print
78 80
79 81 return u
80 82
81 83 os.mkdir('repo')
82 84 os.chdir('repo')
83 85 os.mkdir('.hg')
84 86 f = open('.hg/hgrc', 'w')
85 87 f.write('[paths]\n')
86 88 f.write('local = /another/path\n\n')
87 89 f.close()
88 90
89 91 #print '# Everything is run by user foo, group bar\n'
90 92
91 93 # same user, same group
92 94 testui()
93 95 # same user, different group
94 96 testui(group='def')
95 97 # different user, same group
96 98 testui(user='abc')
97 99 # ... but we trust the group
98 100 testui(user='abc', tgroups=['bar'])
99 101 # different user, different group
100 102 testui(user='abc', group='def')
101 103 # ... but we trust the user
102 104 testui(user='abc', group='def', tusers=['abc'])
103 105 # ... but we trust the group
104 106 testui(user='abc', group='def', tgroups=['def'])
105 107 # ... but we trust the user and the group
106 108 testui(user='abc', group='def', tusers=['abc'], tgroups=['def'])
107 109 # ... but we trust all users
108 110 print '# we trust all users'
109 111 testui(user='abc', group='def', tusers=['*'])
110 112 # ... but we trust all groups
111 113 print '# we trust all groups'
112 114 testui(user='abc', group='def', tgroups=['*'])
113 115 # ... but we trust the whole universe
114 116 print '# we trust all users and groups'
115 117 testui(user='abc', group='def', tusers=['*'], tgroups=['*'])
116 118 # ... check that users and groups are in different namespaces
117 119 print "# we don't get confused by users and groups with the same name"
118 120 testui(user='abc', group='def', tusers=['def'], tgroups=['abc'])
119 121 # ... lists of user names work
120 122 print "# list of user names"
121 123 testui(user='abc', group='def', tusers=['foo', 'xyz', 'abc', 'bleh'],
122 124 tgroups=['bar', 'baz', 'qux'])
123 125 # ... lists of group names work
124 126 print "# list of group names"
125 127 testui(user='abc', group='def', tusers=['foo', 'xyz', 'bleh'],
126 128 tgroups=['bar', 'def', 'baz', 'qux'])
127 129
128 130 print "# Can't figure out the name of the user running this process"
129 131 testui(user='abc', group='def', cuser=None)
130 132
131 133 print "# prints debug warnings"
132 134 u = testui(user='abc', group='def', cuser='foo', debug=True)
133 135
136 print "# report_untrusted enabled without debug hides warnings"
137 u = testui(user='abc', group='def', cuser='foo', report=False)
138
139 print "# report_untrusted enabled with debug shows warnings"
140 u = testui(user='abc', group='def', cuser='foo', debug=True, report=False)
141
134 142 print "# ui.readconfig sections"
135 143 filename = 'foobar'
136 144 f = open(filename, 'w')
137 145 f.write('[foobar]\n')
138 146 f.write('baz = quux\n')
139 147 f.close()
140 148 u.readconfig(filename, sections = ['foobar'])
141 149 print u.config('foobar', 'baz')
142 150
143 151 print
144 152 print "# read trusted, untrusted, new ui, trusted"
145 153 u = ui.ui()
146 154 u.setconfig('ui', 'debug', 'on')
147 155 u.readconfig(filename)
148 156 u2 = u.copy()
149 157 def username(uid=None):
150 158 return 'foo'
151 159 util.username = username
152 160 u2.readconfig('.hg/hgrc')
153 161 print 'trusted:'
154 162 print u2.config('foobar', 'baz')
155 163 print 'untrusted:'
156 164 print u2.config('foobar', 'baz', untrusted=True)
157 165
158 166 print
159 167 print "# error handling"
160 168
161 169 def assertraises(f, exc=util.Abort):
162 170 try:
163 171 f()
164 172 except exc, inst:
165 173 print 'raised', inst.__class__.__name__
166 174 else:
167 175 print 'no exception?!'
168 176
169 177 print "# file doesn't exist"
170 178 os.unlink('.hg/hgrc')
171 179 assert not os.path.exists('.hg/hgrc')
172 180 testui(debug=True, silent=True)
173 181 testui(user='abc', group='def', debug=True, silent=True)
174 182
175 183 print
176 184 print "# parse error"
177 185 f = open('.hg/hgrc', 'w')
178 186 f.write('foo')
179 187 f.close()
180 188
181 189 try:
182 190 testui(user='abc', group='def', silent=True)
183 191 except error.ParseError, inst:
184 192 print inst
185 193
186 194 try:
187 195 testui(debug=True, silent=True)
188 196 except error.ParseError, inst:
189 197 print inst
@@ -1,160 +1,179 b''
1 1 # same user, same group
2 2 trusted
3 3 global = /some/path
4 4 local = /another/path
5 5 untrusted
6 6 . . global = /some/path
7 7 . . local = /another/path
8 8
9 9 # same user, different group
10 10 trusted
11 11 global = /some/path
12 12 local = /another/path
13 13 untrusted
14 14 . . global = /some/path
15 15 . . local = /another/path
16 16
17 17 # different user, same group
18 18 Not trusting file .hg/hgrc from untrusted user abc, group bar
19 19 trusted
20 20 global = /some/path
21 21 untrusted
22 22 . . global = /some/path
23 23 . . local = /another/path
24 24
25 25 # different user, same group, but we trust the group
26 26 trusted
27 27 global = /some/path
28 28 local = /another/path
29 29 untrusted
30 30 . . global = /some/path
31 31 . . local = /another/path
32 32
33 33 # different user, different group
34 34 Not trusting file .hg/hgrc from untrusted user abc, group def
35 35 trusted
36 36 global = /some/path
37 37 untrusted
38 38 . . global = /some/path
39 39 . . local = /another/path
40 40
41 41 # different user, different group, but we trust the user
42 42 trusted
43 43 global = /some/path
44 44 local = /another/path
45 45 untrusted
46 46 . . global = /some/path
47 47 . . local = /another/path
48 48
49 49 # different user, different group, but we trust the group
50 50 trusted
51 51 global = /some/path
52 52 local = /another/path
53 53 untrusted
54 54 . . global = /some/path
55 55 . . local = /another/path
56 56
57 57 # different user, different group, but we trust the user and the group
58 58 trusted
59 59 global = /some/path
60 60 local = /another/path
61 61 untrusted
62 62 . . global = /some/path
63 63 . . local = /another/path
64 64
65 65 # we trust all users
66 66 # different user, different group
67 67 trusted
68 68 global = /some/path
69 69 local = /another/path
70 70 untrusted
71 71 . . global = /some/path
72 72 . . local = /another/path
73 73
74 74 # we trust all groups
75 75 # different user, different group
76 76 trusted
77 77 global = /some/path
78 78 local = /another/path
79 79 untrusted
80 80 . . global = /some/path
81 81 . . local = /another/path
82 82
83 83 # we trust all users and groups
84 84 # different user, different group
85 85 trusted
86 86 global = /some/path
87 87 local = /another/path
88 88 untrusted
89 89 . . global = /some/path
90 90 . . local = /another/path
91 91
92 92 # we don't get confused by users and groups with the same name
93 93 # different user, different group
94 94 Not trusting file .hg/hgrc from untrusted user abc, group def
95 95 trusted
96 96 global = /some/path
97 97 untrusted
98 98 . . global = /some/path
99 99 . . local = /another/path
100 100
101 101 # list of user names
102 102 # different user, different group, but we trust the user
103 103 trusted
104 104 global = /some/path
105 105 local = /another/path
106 106 untrusted
107 107 . . global = /some/path
108 108 . . local = /another/path
109 109
110 110 # list of group names
111 111 # different user, different group, but we trust the group
112 112 trusted
113 113 global = /some/path
114 114 local = /another/path
115 115 untrusted
116 116 . . global = /some/path
117 117 . . local = /another/path
118 118
119 119 # Can't figure out the name of the user running this process
120 120 # different user, different group
121 121 Not trusting file .hg/hgrc from untrusted user abc, group def
122 122 trusted
123 123 global = /some/path
124 124 untrusted
125 125 . . global = /some/path
126 126 . . local = /another/path
127 127
128 128 # prints debug warnings
129 129 # different user, different group
130 130 Not trusting file .hg/hgrc from untrusted user abc, group def
131 131 trusted
132 132 ignoring untrusted configuration option paths.local = /another/path
133 133 global = /some/path
134 134 untrusted
135 135 . . global = /some/path
136 136 .ignoring untrusted configuration option paths.local = /another/path
137 137 . local = /another/path
138 138
139 # report_untrusted enabled without debug hides warnings
140 # different user, different group
141 trusted
142 global = /some/path
143 untrusted
144 . . global = /some/path
145 . . local = /another/path
146
147 # report_untrusted enabled with debug shows warnings
148 # different user, different group
149 Not trusting file .hg/hgrc from untrusted user abc, group def
150 trusted
151 ignoring untrusted configuration option paths.local = /another/path
152 global = /some/path
153 untrusted
154 . . global = /some/path
155 .ignoring untrusted configuration option paths.local = /another/path
156 . local = /another/path
157
139 158 # ui.readconfig sections
140 159 quux
141 160
142 161 # read trusted, untrusted, new ui, trusted
143 162 Not trusting file foobar from untrusted user abc, group def
144 163 trusted:
145 164 ignoring untrusted configuration option foobar.baz = quux
146 165 None
147 166 untrusted:
148 167 quux
149 168
150 169 # error handling
151 170 # file doesn't exist
152 171 # same user, same group
153 172 # different user, different group
154 173
155 174 # parse error
156 175 # different user, different group
157 176 Not trusting file .hg/hgrc from untrusted user abc, group def
158 177 ('foo', '.hg/hgrc:1')
159 178 # same user, same group
160 179 ('foo', '.hg/hgrc:1')
General Comments 0
You need to be logged in to leave comments. Login now