Show More
@@ -0,0 +1,113 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # Since it's not easy to write a test that portably deals | |||
|
3 | # with files from different users/groups, we cheat a bit by | |||
|
4 | # monkey-patching some functions in the util module | |||
|
5 | ||||
|
6 | import os | |||
|
7 | from mercurial import ui, util | |||
|
8 | ||||
|
9 | hgrc = os.environ['HGRCPATH'] | |||
|
10 | ||||
|
11 | def testui(user='foo', group='bar', tusers=(), tgroups=(), | |||
|
12 | cuser='foo', cgroup='bar', debug=False): | |||
|
13 | # user, group => owners of the file | |||
|
14 | # tusers, tgroups => trusted users/groups | |||
|
15 | # cuser, cgroup => user/group of the current process | |||
|
16 | ||||
|
17 | # write a global hgrc with the list of trusted users/groups and | |||
|
18 | # some setting so that we can be sure it was read | |||
|
19 | f = open(hgrc, 'w') | |||
|
20 | f.write('[paths]\n') | |||
|
21 | f.write('global = /some/path\n\n') | |||
|
22 | ||||
|
23 | if tusers or tgroups: | |||
|
24 | f.write('[trusted]\n') | |||
|
25 | if tusers: | |||
|
26 | f.write('users = %s\n' % ', '.join(tusers)) | |||
|
27 | if tgroups: | |||
|
28 | f.write('groups = %s\n' % ', '.join(tgroups)) | |||
|
29 | f.close() | |||
|
30 | ||||
|
31 | # override the functions that give names to uids and gids | |||
|
32 | def username(uid=None): | |||
|
33 | if uid is None: | |||
|
34 | return cuser | |||
|
35 | return user | |||
|
36 | util.username = username | |||
|
37 | ||||
|
38 | def groupname(gid=None): | |||
|
39 | if gid is None: | |||
|
40 | return 'bar' | |||
|
41 | return group | |||
|
42 | util.groupname = groupname | |||
|
43 | ||||
|
44 | # try to read everything | |||
|
45 | #print '# File belongs to user %s, group %s' % (user, group) | |||
|
46 | #print '# trusted users = %s; trusted groups = %s' % (tusers, tgroups) | |||
|
47 | kind = ('different', 'same') | |||
|
48 | who = ('', 'user', 'group', 'user and the group') | |||
|
49 | trusted = who[(user in tusers) + 2*(group in tgroups)] | |||
|
50 | if trusted: | |||
|
51 | trusted = ', but we trust the ' + trusted | |||
|
52 | print '# %s user, %s group%s' % (kind[user == cuser], kind[group == cgroup], | |||
|
53 | trusted) | |||
|
54 | ||||
|
55 | parentui = ui.ui() | |||
|
56 | parentui.updateopts(debug=debug) | |||
|
57 | u = ui.ui(parentui=parentui) | |||
|
58 | u.readconfig('.hg/hgrc') | |||
|
59 | for name, path in u.configitems('paths'): | |||
|
60 | print ' ', name, '=', path | |||
|
61 | ||||
|
62 | ||||
|
63 | return u | |||
|
64 | ||||
|
65 | os.mkdir('repo') | |||
|
66 | os.chdir('repo') | |||
|
67 | os.mkdir('.hg') | |||
|
68 | f = open('.hg/hgrc', 'w') | |||
|
69 | f.write('[paths]\n') | |||
|
70 | f.write('local = /another/path\n\n') | |||
|
71 | f.close() | |||
|
72 | ||||
|
73 | #print '# Everything is run by user foo, group bar\n' | |||
|
74 | ||||
|
75 | # same user, same group | |||
|
76 | testui() | |||
|
77 | # same user, different group | |||
|
78 | testui(group='def') | |||
|
79 | # different user, same group | |||
|
80 | testui(user='abc') | |||
|
81 | # ... but we trust the group | |||
|
82 | testui(user='abc', tgroups=['bar']) | |||
|
83 | # different user, different group | |||
|
84 | testui(user='abc', group='def') | |||
|
85 | # ... but we trust the user | |||
|
86 | testui(user='abc', group='def', tusers=['abc']) | |||
|
87 | # ... but we trust the group | |||
|
88 | testui(user='abc', group='def', tgroups=['def']) | |||
|
89 | # ... but we trust the user and the group | |||
|
90 | testui(user='abc', group='def', tusers=['abc'], tgroups=['def']) | |||
|
91 | # ... but we trust all users | |||
|
92 | print '# we trust all users' | |||
|
93 | testui(user='abc', group='def', tusers=['*']) | |||
|
94 | # ... but we trust all groups | |||
|
95 | print '# we trust all groups' | |||
|
96 | testui(user='abc', group='def', tgroups=['*']) | |||
|
97 | # ... but we trust the whole universe | |||
|
98 | print '# we trust all users and groups' | |||
|
99 | testui(user='abc', group='def', tusers=['*'], tgroups=['*']) | |||
|
100 | # ... check that users and groups are in different namespaces | |||
|
101 | print "# we don't get confused by users and groups with the same name" | |||
|
102 | testui(user='abc', group='def', tusers=['def'], tgroups=['abc']) | |||
|
103 | # ... lists of user names work | |||
|
104 | print "# list of user names" | |||
|
105 | testui(user='abc', group='def', tusers=['foo', 'xyz', 'abc', 'bleh'], | |||
|
106 | tgroups=['bar', 'baz', 'qux']) | |||
|
107 | # ... lists of group names work | |||
|
108 | print "# list of group names" | |||
|
109 | testui(user='abc', group='def', tusers=['foo', 'xyz', 'bleh'], | |||
|
110 | tgroups=['bar', 'def', 'baz', 'qux']) | |||
|
111 | ||||
|
112 | print "# Can't figure out the name of the user running this process" | |||
|
113 | testui(user='abc', group='def', cuser=None) |
@@ -0,0 +1,67 b'' | |||||
|
1 | # same user, same group | |||
|
2 | global = /some/path | |||
|
3 | local = /another/path | |||
|
4 | ||||
|
5 | # same user, different group | |||
|
6 | global = /some/path | |||
|
7 | local = /another/path | |||
|
8 | ||||
|
9 | # different user, same group | |||
|
10 | Not reading file .hg/hgrc from untrusted user abc, group bar | |||
|
11 | global = /some/path | |||
|
12 | ||||
|
13 | # different user, same group, but we trust the group | |||
|
14 | global = /some/path | |||
|
15 | local = /another/path | |||
|
16 | ||||
|
17 | # different user, different group | |||
|
18 | Not reading file .hg/hgrc from untrusted user abc, group def | |||
|
19 | global = /some/path | |||
|
20 | ||||
|
21 | # different user, different group, but we trust the user | |||
|
22 | global = /some/path | |||
|
23 | local = /another/path | |||
|
24 | ||||
|
25 | # different user, different group, but we trust the group | |||
|
26 | global = /some/path | |||
|
27 | local = /another/path | |||
|
28 | ||||
|
29 | # different user, different group, but we trust the user and the group | |||
|
30 | global = /some/path | |||
|
31 | local = /another/path | |||
|
32 | ||||
|
33 | # we trust all users | |||
|
34 | # different user, different group | |||
|
35 | global = /some/path | |||
|
36 | local = /another/path | |||
|
37 | ||||
|
38 | # we trust all groups | |||
|
39 | # different user, different group | |||
|
40 | global = /some/path | |||
|
41 | local = /another/path | |||
|
42 | ||||
|
43 | # we trust all users and groups | |||
|
44 | # different user, different group | |||
|
45 | global = /some/path | |||
|
46 | local = /another/path | |||
|
47 | ||||
|
48 | # we don't get confused by users and groups with the same name | |||
|
49 | # different user, different group | |||
|
50 | Not reading file .hg/hgrc from untrusted user abc, group def | |||
|
51 | global = /some/path | |||
|
52 | ||||
|
53 | # list of user names | |||
|
54 | # different user, different group, but we trust the user | |||
|
55 | global = /some/path | |||
|
56 | local = /another/path | |||
|
57 | ||||
|
58 | # list of group names | |||
|
59 | # different user, different group, but we trust the group | |||
|
60 | global = /some/path | |||
|
61 | local = /another/path | |||
|
62 | ||||
|
63 | # Can't figure out the name of the user running this process | |||
|
64 | # different user, different group | |||
|
65 | global = /some/path | |||
|
66 | local = /another/path | |||
|
67 |
@@ -50,6 +50,8 b' installed.' | |||||
50 | particular repository. This file is not version-controlled, and |
|
50 | particular repository. This file is not version-controlled, and | |
51 | will not get transferred during a "clone" operation. Options in |
|
51 | will not get transferred during a "clone" operation. Options in | |
52 | this file override options in all other configuration files. |
|
52 | this file override options in all other configuration files. | |
|
53 | On Unix, this file is only read if it belongs to a trusted user | |||
|
54 | or to a trusted group. | |||
53 |
|
55 | |||
54 | SYNTAX |
|
56 | SYNTAX | |
55 | ------ |
|
57 | ------ | |
@@ -364,6 +366,17 b' server::' | |||||
364 | 6Mbps), uncompressed streaming is slower, because of the extra |
|
366 | 6Mbps), uncompressed streaming is slower, because of the extra | |
365 | data transfer overhead. Default is False. |
|
367 | data transfer overhead. Default is False. | |
366 |
|
368 | |||
|
369 | trusted:: | |||
|
370 | Mercurial will only read the .hg/hgrc file from a repository if | |||
|
371 | it belongs to a trusted user or to a trusted group. This section | |||
|
372 | specifies what users and groups are trusted. The current user is | |||
|
373 | always trusted. To trust everybody, list a user or a group with | |||
|
374 | name "*". | |||
|
375 | users;; | |||
|
376 | Comma-separated list of trusted users. | |||
|
377 | groups;; | |||
|
378 | Comma-separated list of trusted groups. | |||
|
379 | ||||
367 | ui:: |
|
380 | ui:: | |
368 | User interface controls. |
|
381 | User interface controls. | |
369 | debug;; |
|
382 | debug;; |
@@ -39,6 +39,8 b' class ui(object):' | |||||
39 | self.debugflag = debug |
|
39 | self.debugflag = debug | |
40 | self.interactive = interactive |
|
40 | self.interactive = interactive | |
41 | self.traceback = traceback |
|
41 | self.traceback = traceback | |
|
42 | self.trusted_users = {} | |||
|
43 | self.trusted_groups = {} | |||
42 | self.cdata = util.configparser() |
|
44 | self.cdata = util.configparser() | |
43 | self.readconfig(util.rcpath()) |
|
45 | self.readconfig(util.rcpath()) | |
44 | self.updateopts(verbose, debug, quiet, interactive) |
|
46 | self.updateopts(verbose, debug, quiet, interactive) | |
@@ -46,6 +48,8 b' class ui(object):' | |||||
46 | # parentui may point to an ui object which is already a child |
|
48 | # parentui may point to an ui object which is already a child | |
47 | self.parentui = parentui.parentui or parentui |
|
49 | self.parentui = parentui.parentui or parentui | |
48 | self.readhooks = self.parentui.readhooks[:] |
|
50 | self.readhooks = self.parentui.readhooks[:] | |
|
51 | self.trusted_users = parentui.trusted_users.copy() | |||
|
52 | self.trusted_groups = parentui.trusted_groups.copy() | |||
49 | self.cdata = dupconfig(self.parentui.cdata) |
|
53 | self.cdata = dupconfig(self.parentui.cdata) | |
50 | if self.parentui.overlay: |
|
54 | if self.parentui.overlay: | |
51 | self.overlay = dupconfig(self.parentui.overlay) |
|
55 | self.overlay = dupconfig(self.parentui.overlay) | |
@@ -82,12 +86,32 b' class ui(object):' | |||||
82 | elif self.verbose and self.quiet: |
|
86 | elif self.verbose and self.quiet: | |
83 | self.quiet = self.verbose = False |
|
87 | self.quiet = self.verbose = False | |
84 |
|
88 | |||
|
89 | def _is_trusted(self, fp, f, warn=True): | |||
|
90 | tusers = self.trusted_users | |||
|
91 | tgroups = self.trusted_groups | |||
|
92 | if (tusers or tgroups) and '*' not in tusers and '*' not in tgroups: | |||
|
93 | st = util.fstat(fp) | |||
|
94 | user = util.username(st.st_uid) | |||
|
95 | group = util.groupname(st.st_gid) | |||
|
96 | if user not in tusers and group not in tgroups: | |||
|
97 | if warn: | |||
|
98 | self.warn(_('Not reading file %s from untrusted ' | |||
|
99 | 'user %s, group %s\n') % (f, user, group)) | |||
|
100 | return False | |||
|
101 | return True | |||
|
102 | ||||
85 | def readconfig(self, fn, root=None): |
|
103 | def readconfig(self, fn, root=None): | |
86 | if isinstance(fn, basestring): |
|
104 | if isinstance(fn, basestring): | |
87 | fn = [fn] |
|
105 | fn = [fn] | |
88 | for f in fn: |
|
106 | for f in fn: | |
89 | try: |
|
107 | try: | |
90 |
|
|
108 | fp = open(f) | |
|
109 | except IOError: | |||
|
110 | continue | |||
|
111 | if not self._is_trusted(fp, f): | |||
|
112 | continue | |||
|
113 | try: | |||
|
114 | self.cdata.readfp(fp, f) | |||
91 | except ConfigParser.ParsingError, inst: |
|
115 | except ConfigParser.ParsingError, inst: | |
92 | raise util.Abort(_("Failed to parse %s\n%s") % (f, inst)) |
|
116 | raise util.Abort(_("Failed to parse %s\n%s") % (f, inst)) | |
93 | # override data from config files with data set with ui.setconfig |
|
117 | # override data from config files with data set with ui.setconfig | |
@@ -144,6 +168,16 b' class ui(object):' | |||||
144 | if name is None or name == 'interactive': |
|
168 | if name is None or name == 'interactive': | |
145 | self.interactive = self.configbool("ui", "interactive", True) |
|
169 | self.interactive = self.configbool("ui", "interactive", True) | |
146 |
|
170 | |||
|
171 | # update trust information | |||
|
172 | if section is None or section == 'trusted': | |||
|
173 | user = util.username() | |||
|
174 | if user is not None: | |||
|
175 | self.trusted_users[user] = 1 | |||
|
176 | for user in self.configlist('trusted', 'users'): | |||
|
177 | self.trusted_users[user] = 1 | |||
|
178 | for group in self.configlist('trusted', 'groups'): | |||
|
179 | self.trusted_groups[group] = 1 | |||
|
180 | ||||
147 | def setconfig(self, section, name, value): |
|
181 | def setconfig(self, section, name, value): | |
148 | if not self.overlay: |
|
182 | if not self.overlay: | |
149 | self.overlay = util.configparser() |
|
183 | self.overlay = util.configparser() |
@@ -519,6 +519,36 b' def is_win_9x():' | |||||
519 | except AttributeError: |
|
519 | except AttributeError: | |
520 | return os.name == 'nt' and 'command' in os.environ.get('comspec', '') |
|
520 | return os.name == 'nt' and 'command' in os.environ.get('comspec', '') | |
521 |
|
521 | |||
|
522 | def username(uid=None): | |||
|
523 | """Return the name of the user with the given uid. | |||
|
524 | ||||
|
525 | If uid is None, return the name of the current user.""" | |||
|
526 | try: | |||
|
527 | import pwd | |||
|
528 | if uid is None: | |||
|
529 | uid = os.getuid() | |||
|
530 | try: | |||
|
531 | return pwd.getpwuid(uid)[0] | |||
|
532 | except KeyError: | |||
|
533 | return str(uid) | |||
|
534 | except ImportError: | |||
|
535 | return None | |||
|
536 | ||||
|
537 | def groupname(gid=None): | |||
|
538 | """Return the name of the group with the given gid. | |||
|
539 | ||||
|
540 | If gid is None, return the name of the current group.""" | |||
|
541 | try: | |||
|
542 | import grp | |||
|
543 | if gid is None: | |||
|
544 | gid = os.getgid() | |||
|
545 | try: | |||
|
546 | return grp.getgrgid(gid)[0] | |||
|
547 | except KeyError: | |||
|
548 | return str(gid) | |||
|
549 | except ImportError: | |||
|
550 | return None | |||
|
551 | ||||
522 | # Platform specific variants |
|
552 | # Platform specific variants | |
523 | if os.name == 'nt': |
|
553 | if os.name == 'nt': | |
524 | demandload(globals(), "msvcrt") |
|
554 | demandload(globals(), "msvcrt") |
General Comments 0
You need to be logged in to leave comments.
Login now