Show More
@@ -40,3 +40,4 b' b032bec2c0a651ca0ddecb65714bfe6770f67d70' | |||||
40 | 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 0 iD8DBQBNvTy4ywK+sNU5EO8RAmp8AJ9QnxK4jTJ7G722MyeBxf0UXEdGwACgtlM7BKtNQfbEH/fOW5y+45W88VI= |
|
40 | 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 0 iD8DBQBNvTy4ywK+sNU5EO8RAmp8AJ9QnxK4jTJ7G722MyeBxf0UXEdGwACgtlM7BKtNQfbEH/fOW5y+45W88VI= | |
41 | 733af5d9f6b22387913e1d11350fb8cb7c1487dd 0 iD8DBQBN5q/8ywK+sNU5EO8RArRGAKCNGT94GKIYtSuwZ57z1sQbcw6uLACfffpbMV4NAPMl8womAwg+7ZPKnIU= |
|
41 | 733af5d9f6b22387913e1d11350fb8cb7c1487dd 0 iD8DBQBN5q/8ywK+sNU5EO8RArRGAKCNGT94GKIYtSuwZ57z1sQbcw6uLACfffpbMV4NAPMl8womAwg+7ZPKnIU= | |
42 | de9eb6b1da4fc522b1cab16d86ca166204c24f25 0 iD8DBQBODhfhywK+sNU5EO8RAr2+AJ4ugbAj8ae8/K0bYZzx3sascIAg1QCeK3b+zbbVVqd3b7CDpwFnaX8kTd4= |
|
42 | de9eb6b1da4fc522b1cab16d86ca166204c24f25 0 iD8DBQBODhfhywK+sNU5EO8RAr2+AJ4ugbAj8ae8/K0bYZzx3sascIAg1QCeK3b+zbbVVqd3b7CDpwFnaX8kTd4= | |
|
43 | 4a43e23b8c55b4566b8200bf69fe2158485a2634 0 iD8DBQBONzIMywK+sNU5EO8RAj5SAJ0aPS3+JHnyI6bHB2Fl0LImbDmagwCdGbDLp1S7TFobxXudOH49bX45Iik= |
@@ -52,3 +52,4 b' b032bec2c0a651ca0ddecb65714bfe6770f67d70' | |||||
52 | 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 1.8.3 |
|
52 | 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 1.8.3 | |
53 | 733af5d9f6b22387913e1d11350fb8cb7c1487dd 1.8.4 |
|
53 | 733af5d9f6b22387913e1d11350fb8cb7c1487dd 1.8.4 | |
54 | de9eb6b1da4fc522b1cab16d86ca166204c24f25 1.9 |
|
54 | de9eb6b1da4fc522b1cab16d86ca166204c24f25 1.9 | |
|
55 | 4a43e23b8c55b4566b8200bf69fe2158485a2634 1.9.1 |
@@ -266,7 +266,9 b' Supported arguments:' | |||||
266 | Optional. Username to authenticate with. If not given, and the |
|
266 | Optional. Username to authenticate with. If not given, and the | |
267 | remote site requires basic or digest authentication, the user will |
|
267 | remote site requires basic or digest authentication, the user will | |
268 | be prompted for it. Environment variables are expanded in the |
|
268 | be prompted for it. Environment variables are expanded in the | |
269 | username letting you do ``foo.username = $USER``. |
|
269 | username letting you do ``foo.username = $USER``. If the URI | |
|
270 | includes a username, only ``[auth]`` entries with a matching | |||
|
271 | username or without a username will be considered. | |||
270 |
|
272 | |||
271 | ``password`` |
|
273 | ``password`` | |
272 | Optional. Password to authenticate with. If not given, and the |
|
274 | Optional. Password to authenticate with. If not given, and the | |
@@ -1158,6 +1160,13 b' The full set of options is:' | |||||
1158 | be present in this list. The contents of the allow_push list are |
|
1160 | be present in this list. The contents of the allow_push list are | |
1159 | examined after the deny_push list. |
|
1161 | examined after the deny_push list. | |
1160 |
|
1162 | |||
|
1163 | ``guessmime`` | |||
|
1164 | Control MIME types for raw download of file content. | |||
|
1165 | Set to True to let hgweb guess the content type from the file | |||
|
1166 | extension. This will serve HTML files as ``text/html`` and might | |||
|
1167 | allow cross-site scripting attacks when serving untrusted | |||
|
1168 | repositories. Default is False. | |||
|
1169 | ||||
1161 | ``allow_read`` |
|
1170 | ``allow_read`` | |
1162 | If the user has not already been denied repository access due to |
|
1171 | If the user has not already been denied repository access due to | |
1163 | the contents of deny_read, this list determines whether to grant |
|
1172 | the contents of deny_read, this list determines whether to grant |
@@ -32,6 +32,8 b' def log(web, req, tmpl):' | |||||
32 | return changelog(web, req, tmpl) |
|
32 | return changelog(web, req, tmpl) | |
33 |
|
33 | |||
34 | def rawfile(web, req, tmpl): |
|
34 | def rawfile(web, req, tmpl): | |
|
35 | guessmime = web.configbool('web', 'guessmime', False) | |||
|
36 | ||||
35 | path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) |
|
37 | path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) | |
36 | if not path: |
|
38 | if not path: | |
37 | content = manifest(web, req, tmpl) |
|
39 | content = manifest(web, req, tmpl) | |
@@ -50,9 +52,11 b' def rawfile(web, req, tmpl):' | |||||
50 |
|
52 | |||
51 | path = fctx.path() |
|
53 | path = fctx.path() | |
52 | text = fctx.data() |
|
54 | text = fctx.data() | |
53 | mt = mimetypes.guess_type(path)[0] |
|
55 | mt = 'application/binary' | |
54 |
if |
|
56 | if guessmime: | |
55 | mt = binary(text) and 'application/octet-stream' or 'text/plain' |
|
57 | mt = mimetypes.guess_type(path)[0] | |
|
58 | if mt is None: | |||
|
59 | mt = binary(text) and 'application/binary' or 'text/plain' | |||
56 | if mt.startswith('text/'): |
|
60 | if mt.startswith('text/'): | |
57 | mt += '; charset="%s"' % encoding.encoding |
|
61 | mt += '; charset="%s"' % encoding.encoding | |
58 |
|
62 |
@@ -72,10 +72,19 b' def readauthforuri(ui, uri):' | |||||
72 | gdict[setting] = val |
|
72 | gdict[setting] = val | |
73 |
|
73 | |||
74 | # Find the best match |
|
74 | # Find the best match | |
|
75 | uri = util.url(uri) | |||
|
76 | user = uri.user | |||
|
77 | uri.user = uri.password = None | |||
|
78 | uri = str(uri) | |||
75 | scheme, hostpath = uri.split('://', 1) |
|
79 | scheme, hostpath = uri.split('://', 1) | |
|
80 | bestuser = None | |||
76 | bestlen = 0 |
|
81 | bestlen = 0 | |
77 | bestauth = None |
|
82 | bestauth = None | |
78 | for group, auth in config.iteritems(): |
|
83 | for group, auth in config.iteritems(): | |
|
84 | if user and user != auth.get('username', user): | |||
|
85 | # If a username was set in the URI, the entry username | |||
|
86 | # must either match it or be unset | |||
|
87 | continue | |||
79 | prefix = auth.get('prefix') |
|
88 | prefix = auth.get('prefix') | |
80 | if not prefix: |
|
89 | if not prefix: | |
81 | continue |
|
90 | continue | |
@@ -85,9 +94,14 b' def readauthforuri(ui, uri):' | |||||
85 | else: |
|
94 | else: | |
86 | schemes = (auth.get('schemes') or 'https').split() |
|
95 | schemes = (auth.get('schemes') or 'https').split() | |
87 | if (prefix == '*' or hostpath.startswith(prefix)) and \ |
|
96 | if (prefix == '*' or hostpath.startswith(prefix)) and \ | |
88 |
len(prefix) > bestlen and |
|
97 | (len(prefix) > bestlen or (len(prefix) == bestlen and \ | |
|
98 | not bestuser and 'username' in auth)) \ | |||
|
99 | and scheme in schemes: | |||
89 | bestlen = len(prefix) |
|
100 | bestlen = len(prefix) | |
90 | bestauth = group, auth |
|
101 | bestauth = group, auth | |
|
102 | bestuser = auth.get('username') | |||
|
103 | if user and not bestuser: | |||
|
104 | auth['username'] = user | |||
91 | return bestauth |
|
105 | return bestauth | |
92 |
|
106 | |||
93 | # Mercurial (at least until we can remove the old codepath) requires |
|
107 | # Mercurial (at least until we can remove the old codepath) requires |
@@ -25,7 +25,7 b' class passwordmgr(urllib2.HTTPPasswordMg' | |||||
25 | self._writedebug(user, passwd) |
|
25 | self._writedebug(user, passwd) | |
26 | return (user, passwd) |
|
26 | return (user, passwd) | |
27 |
|
27 | |||
28 | if not user: |
|
28 | if not user or not passwd: | |
29 | res = httpconnectionmod.readauthforuri(self.ui, authuri) |
|
29 | res = httpconnectionmod.readauthforuri(self.ui, authuri) | |
30 | if res: |
|
30 | if res: | |
31 | group, auth = res |
|
31 | group, auth = res |
@@ -1,5 +1,5 b'' | |||||
1 | from mercurial import demandimport; demandimport.enable() |
|
1 | from mercurial import demandimport; demandimport.enable() | |
2 | from mercurial import ui |
|
2 | from mercurial import ui, util | |
3 | from mercurial import url |
|
3 | from mercurial import url | |
4 | from mercurial.error import Abort |
|
4 | from mercurial.error import Abort | |
5 |
|
5 | |||
@@ -19,13 +19,16 b' def dumpdict(dict):' | |||||
19 | return '{' + ', '.join(['%s: %s' % (k, dict[k]) |
|
19 | return '{' + ', '.join(['%s: %s' % (k, dict[k]) | |
20 | for k in sorted(dict.iterkeys())]) + '}' |
|
20 | for k in sorted(dict.iterkeys())]) + '}' | |
21 |
|
21 | |||
22 | def test(auth): |
|
22 | def test(auth, urls=None): | |
23 | print 'CFG:', dumpdict(auth) |
|
23 | print 'CFG:', dumpdict(auth) | |
24 | prefixes = set() |
|
24 | prefixes = set() | |
25 | for k in auth: |
|
25 | for k in auth: | |
26 | prefixes.add(k.split('.', 1)[0]) |
|
26 | prefixes.add(k.split('.', 1)[0]) | |
27 | for p in prefixes: |
|
27 | for p in prefixes: | |
28 |
|
|
28 | for name in ('.username', '.password'): | |
|
29 | if (p + name) not in auth: | |||
|
30 | auth[p + name] = p | |||
|
31 | auth = dict((k, v) for k, v in auth.iteritems() if v is not None) | |||
29 |
|
32 | |||
30 | ui = writeauth(auth) |
|
33 | ui = writeauth(auth) | |
31 |
|
34 | |||
@@ -33,16 +36,26 b' def test(auth):' | |||||
33 | print 'URI:', uri |
|
36 | print 'URI:', uri | |
34 | try: |
|
37 | try: | |
35 | pm = url.passwordmgr(ui) |
|
38 | pm = url.passwordmgr(ui) | |
|
39 | authinfo = util.url(uri).authinfo()[1] | |||
|
40 | if authinfo is not None: | |||
|
41 | pm.add_password(*authinfo) | |||
36 | print ' ', pm.find_user_password('test', uri) |
|
42 | print ' ', pm.find_user_password('test', uri) | |
37 | except Abort, e: |
|
43 | except Abort, e: | |
38 | print 'abort' |
|
44 | print 'abort' | |
39 |
|
45 | |||
40 | _test('http://example.org/foo') |
|
46 | if not urls: | |
41 | _test('http://example.org/foo/bar') |
|
47 | urls = [ | |
42 |
|
|
48 | 'http://example.org/foo', | |
43 |
|
|
49 | 'http://example.org/foo/bar', | |
44 |
|
|
50 | 'http://example.org/bar', | |
45 |
|
|
51 | 'https://example.org/foo', | |
|
52 | 'https://example.org/foo/bar', | |||
|
53 | 'https://example.org/bar', | |||
|
54 | 'https://x@example.org/bar', | |||
|
55 | 'https://y@example.org/bar', | |||
|
56 | ] | |||
|
57 | for u in urls: | |||
|
58 | _test(u) | |||
46 |
|
59 | |||
47 |
|
60 | |||
48 | print '\n*** Test in-uri schemes\n' |
|
61 | print '\n*** Test in-uri schemes\n' | |
@@ -62,3 +75,23 b" test({'x.prefix': 'http://example.org/fo" | |||||
62 | test({'x.prefix': 'http://example.org/foo', |
|
75 | test({'x.prefix': 'http://example.org/foo', | |
63 | 'y.prefix': 'http://example.org/foo/bar'}) |
|
76 | 'y.prefix': 'http://example.org/foo/bar'}) | |
64 | test({'x.prefix': '*', 'y.prefix': 'https://example.org/bar'}) |
|
77 | test({'x.prefix': '*', 'y.prefix': 'https://example.org/bar'}) | |
|
78 | ||||
|
79 | print '\n*** Test user matching\n' | |||
|
80 | test({'x.prefix': 'http://example.org/foo', | |||
|
81 | 'x.username': None, | |||
|
82 | 'x.password': 'xpassword'}, | |||
|
83 | urls=['http://y@example.org/foo']) | |||
|
84 | test({'x.prefix': 'http://example.org/foo', | |||
|
85 | 'x.username': None, | |||
|
86 | 'x.password': 'xpassword', | |||
|
87 | 'y.prefix': 'http://example.org/foo', | |||
|
88 | 'y.username': 'y', | |||
|
89 | 'y.password': 'ypassword'}, | |||
|
90 | urls=['http://y@example.org/foo']) | |||
|
91 | test({'x.prefix': 'http://example.org/foo/bar', | |||
|
92 | 'x.username': None, | |||
|
93 | 'x.password': 'xpassword', | |||
|
94 | 'y.prefix': 'http://example.org/foo', | |||
|
95 | 'y.username': 'y', | |||
|
96 | 'y.password': 'ypassword'}, | |||
|
97 | urls=['http://y@example.org/foo/bar']) |
@@ -14,6 +14,10 b' URI: https://example.org/foo/bar' | |||||
14 | abort |
|
14 | abort | |
15 | URI: https://example.org/bar |
|
15 | URI: https://example.org/bar | |
16 | abort |
|
16 | abort | |
|
17 | URI: https://x@example.org/bar | |||
|
18 | abort | |||
|
19 | URI: https://y@example.org/bar | |||
|
20 | abort | |||
17 | CFG: {x.prefix: https://example.org} |
|
21 | CFG: {x.prefix: https://example.org} | |
18 | URI: http://example.org/foo |
|
22 | URI: http://example.org/foo | |
19 | abort |
|
23 | abort | |
@@ -27,6 +31,10 b' URI: https://example.org/foo/bar' | |||||
27 | ('x', 'x') |
|
31 | ('x', 'x') | |
28 | URI: https://example.org/bar |
|
32 | URI: https://example.org/bar | |
29 | ('x', 'x') |
|
33 | ('x', 'x') | |
|
34 | URI: https://x@example.org/bar | |||
|
35 | ('x', 'x') | |||
|
36 | URI: https://y@example.org/bar | |||
|
37 | abort | |||
30 | CFG: {x.prefix: http://example.org, x.schemes: https} |
|
38 | CFG: {x.prefix: http://example.org, x.schemes: https} | |
31 | URI: http://example.org/foo |
|
39 | URI: http://example.org/foo | |
32 | ('x', 'x') |
|
40 | ('x', 'x') | |
@@ -40,6 +48,10 b' URI: https://example.org/foo/bar' | |||||
40 | abort |
|
48 | abort | |
41 | URI: https://example.org/bar |
|
49 | URI: https://example.org/bar | |
42 | abort |
|
50 | abort | |
|
51 | URI: https://x@example.org/bar | |||
|
52 | abort | |||
|
53 | URI: https://y@example.org/bar | |||
|
54 | abort | |||
43 | CFG: {x.prefix: https://example.org, x.schemes: http} |
|
55 | CFG: {x.prefix: https://example.org, x.schemes: http} | |
44 | URI: http://example.org/foo |
|
56 | URI: http://example.org/foo | |
45 | abort |
|
57 | abort | |
@@ -53,6 +65,10 b' URI: https://example.org/foo/bar' | |||||
53 | ('x', 'x') |
|
65 | ('x', 'x') | |
54 | URI: https://example.org/bar |
|
66 | URI: https://example.org/bar | |
55 | ('x', 'x') |
|
67 | ('x', 'x') | |
|
68 | URI: https://x@example.org/bar | |||
|
69 | ('x', 'x') | |||
|
70 | URI: https://y@example.org/bar | |||
|
71 | abort | |||
56 |
|
72 | |||
57 | *** Test separately configured schemes |
|
73 | *** Test separately configured schemes | |
58 |
|
74 | |||
@@ -69,6 +85,10 b' URI: https://example.org/foo/bar' | |||||
69 | abort |
|
85 | abort | |
70 | URI: https://example.org/bar |
|
86 | URI: https://example.org/bar | |
71 | abort |
|
87 | abort | |
|
88 | URI: https://x@example.org/bar | |||
|
89 | abort | |||
|
90 | URI: https://y@example.org/bar | |||
|
91 | abort | |||
72 | CFG: {x.prefix: example.org, x.schemes: https} |
|
92 | CFG: {x.prefix: example.org, x.schemes: https} | |
73 | URI: http://example.org/foo |
|
93 | URI: http://example.org/foo | |
74 | abort |
|
94 | abort | |
@@ -82,6 +102,10 b' URI: https://example.org/foo/bar' | |||||
82 | ('x', 'x') |
|
102 | ('x', 'x') | |
83 | URI: https://example.org/bar |
|
103 | URI: https://example.org/bar | |
84 | ('x', 'x') |
|
104 | ('x', 'x') | |
|
105 | URI: https://x@example.org/bar | |||
|
106 | ('x', 'x') | |||
|
107 | URI: https://y@example.org/bar | |||
|
108 | abort | |||
85 | CFG: {x.prefix: example.org, x.schemes: http https} |
|
109 | CFG: {x.prefix: example.org, x.schemes: http https} | |
86 | URI: http://example.org/foo |
|
110 | URI: http://example.org/foo | |
87 | ('x', 'x') |
|
111 | ('x', 'x') | |
@@ -95,6 +119,10 b' URI: https://example.org/foo/bar' | |||||
95 | ('x', 'x') |
|
119 | ('x', 'x') | |
96 | URI: https://example.org/bar |
|
120 | URI: https://example.org/bar | |
97 | ('x', 'x') |
|
121 | ('x', 'x') | |
|
122 | URI: https://x@example.org/bar | |||
|
123 | ('x', 'x') | |||
|
124 | URI: https://y@example.org/bar | |||
|
125 | abort | |||
98 |
|
126 | |||
99 | *** Test prefix matching |
|
127 | *** Test prefix matching | |
100 |
|
128 | |||
@@ -111,6 +139,10 b' URI: https://example.org/foo/bar' | |||||
111 | abort |
|
139 | abort | |
112 | URI: https://example.org/bar |
|
140 | URI: https://example.org/bar | |
113 | abort |
|
141 | abort | |
|
142 | URI: https://x@example.org/bar | |||
|
143 | abort | |||
|
144 | URI: https://y@example.org/bar | |||
|
145 | abort | |||
114 | CFG: {x.prefix: http://example.org/foo, y.prefix: http://example.org/foo/bar} |
|
146 | CFG: {x.prefix: http://example.org/foo, y.prefix: http://example.org/foo/bar} | |
115 | URI: http://example.org/foo |
|
147 | URI: http://example.org/foo | |
116 | ('x', 'x') |
|
148 | ('x', 'x') | |
@@ -124,6 +156,10 b' URI: https://example.org/foo/bar' | |||||
124 | abort |
|
156 | abort | |
125 | URI: https://example.org/bar |
|
157 | URI: https://example.org/bar | |
126 | abort |
|
158 | abort | |
|
159 | URI: https://x@example.org/bar | |||
|
160 | abort | |||
|
161 | URI: https://y@example.org/bar | |||
|
162 | abort | |||
127 | CFG: {x.prefix: *, y.prefix: https://example.org/bar} |
|
163 | CFG: {x.prefix: *, y.prefix: https://example.org/bar} | |
128 | URI: http://example.org/foo |
|
164 | URI: http://example.org/foo | |
129 | abort |
|
165 | abort | |
@@ -137,3 +173,19 b' URI: https://example.org/foo/bar' | |||||
137 | ('x', 'x') |
|
173 | ('x', 'x') | |
138 | URI: https://example.org/bar |
|
174 | URI: https://example.org/bar | |
139 | ('y', 'y') |
|
175 | ('y', 'y') | |
|
176 | URI: https://x@example.org/bar | |||
|
177 | ('x', 'x') | |||
|
178 | URI: https://y@example.org/bar | |||
|
179 | ('y', 'y') | |||
|
180 | ||||
|
181 | *** Test user matching | |||
|
182 | ||||
|
183 | CFG: {x.password: xpassword, x.prefix: http://example.org/foo, x.username: None} | |||
|
184 | URI: http://y@example.org/foo | |||
|
185 | ('y', 'xpassword') | |||
|
186 | CFG: {x.password: xpassword, x.prefix: http://example.org/foo, x.username: None, y.password: ypassword, y.prefix: http://example.org/foo, y.username: y} | |||
|
187 | URI: http://y@example.org/foo | |||
|
188 | ('y', 'ypassword') | |||
|
189 | CFG: {x.password: xpassword, x.prefix: http://example.org/foo/bar, x.username: None, y.password: ypassword, y.prefix: http://example.org/foo, y.username: y} | |||
|
190 | URI: http://y@example.org/foo/bar | |||
|
191 | ('y', 'xpassword') |
@@ -22,6 +22,28 b' Test raw style of hgweb' | |||||
22 | $ sleep 1 # wait for server to scream and die |
|
22 | $ sleep 1 # wait for server to scream and die | |
23 | $ cat getoutput.txt |
|
23 | $ cat getoutput.txt | |
24 | 200 Script output follows |
|
24 | 200 Script output follows | |
|
25 | content-type: application/binary | |||
|
26 | content-length: 157 | |||
|
27 | content-disposition: inline; filename="some \"text\".txt" | |||
|
28 | ||||
|
29 | This is just some random text | |||
|
30 | that will go inside the file and take a few lines. | |||
|
31 | It is very boring to read, but computers don't | |||
|
32 | care about things like that. | |||
|
33 | $ cat access.log error.log | |||
|
34 | 127.0.0.1 - - [*] "GET /?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw HTTP/1.1" 200 - (glob) | |||
|
35 | ||||
|
36 | $ rm access.log error.log | |||
|
37 | $ hg serve -p $HGPORT -A access.log -E error.log -d --pid-file=hg.pid \ | |||
|
38 | > --config web.guessmime=True | |||
|
39 | ||||
|
40 | $ cat hg.pid >> $DAEMON_PIDS | |||
|
41 | $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw' content-type content-length content-disposition) >getoutput.txt & | |||
|
42 | $ sleep 5 | |||
|
43 | $ kill `cat hg.pid` | |||
|
44 | $ sleep 1 # wait for server to scream and die | |||
|
45 | $ cat getoutput.txt | |||
|
46 | 200 Script output follows | |||
25 | content-type: text/plain; charset="ascii" |
|
47 | content-type: text/plain; charset="ascii" | |
26 | content-length: 157 |
|
48 | content-length: 157 | |
27 | content-disposition: inline; filename="some \"text\".txt" |
|
49 | content-disposition: inline; filename="some \"text\".txt" |
General Comments 0
You need to be logged in to leave comments.
Login now