comparison mercurial/hgweb/common.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 8d9322b6e687
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
42 Can be overridden by extensions to provide more complex authorization 42 Can be overridden by extensions to provide more complex authorization
43 schemes. 43 schemes.
44 """ 44 """
45 return userlist == ['*'] or username in userlist 45 return userlist == ['*'] or username in userlist
46 46
47
47 def checkauthz(hgweb, req, op): 48 def checkauthz(hgweb, req, op):
48 '''Check permission for operation based on request data (including 49 '''Check permission for operation based on request data (including
49 authentication info). Return if op allowed, else raise an ErrorResponse 50 authentication info). Return if op allowed, else raise an ErrorResponse
50 exception.''' 51 exception.'''
51 52
59 if allow_read and (not ismember(hgweb.repo.ui, user, allow_read)): 60 if allow_read and (not ismember(hgweb.repo.ui, user, allow_read)):
60 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') 61 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
61 62
62 if op == 'pull' and not hgweb.allowpull: 63 if op == 'pull' and not hgweb.allowpull:
63 raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized') 64 raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized')
64 elif op == 'pull' or op is None: # op is None for interface requests 65 elif op == 'pull' or op is None: # op is None for interface requests
65 return 66 return
66 67
67 # Allow LFS uploading via PUT requests 68 # Allow LFS uploading via PUT requests
68 if op == 'upload': 69 if op == 'upload':
69 if req.method != 'PUT': 70 if req.method != 'PUT':
84 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') 85 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
85 86
86 allow = hgweb.configlist('web', 'allow-push') 87 allow = hgweb.configlist('web', 'allow-push')
87 if not (allow and ismember(hgweb.repo.ui, user, allow)): 88 if not (allow and ismember(hgweb.repo.ui, user, allow)):
88 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') 89 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
90
89 91
90 # Hooks for hgweb permission checks; extensions can add hooks here. 92 # Hooks for hgweb permission checks; extensions can add hooks here.
91 # Each hook is invoked like this: hook(hgweb, request, operation), 93 # Each hook is invoked like this: hook(hgweb, request, operation),
92 # where operation is either read, pull, push or upload. Hooks should either 94 # where operation is either read, pull, push or upload. Hooks should either
93 # raise an ErrorResponse exception, or just return. 95 # raise an ErrorResponse exception, or just return.
106 if headers is None: 108 if headers is None:
107 headers = [] 109 headers = []
108 self.headers = headers 110 self.headers = headers
109 self.message = message 111 self.message = message
110 112
113
111 class continuereader(object): 114 class continuereader(object):
112 """File object wrapper to handle HTTP 100-continue. 115 """File object wrapper to handle HTTP 100-continue.
113 116
114 This is used by servers so they automatically handle Expect: 100-continue 117 This is used by servers so they automatically handle Expect: 100-continue
115 request headers. On first read of the request body, the 100 Continue 118 request headers. On first read of the request body, the 100 Continue
116 response is sent. This should trigger the client into actually sending 119 response is sent. This should trigger the client into actually sending
117 the request body. 120 the request body.
118 """ 121 """
122
119 def __init__(self, f, write): 123 def __init__(self, f, write):
120 self.f = f 124 self.f = f
121 self._write = write 125 self._write = write
122 self.continued = False 126 self.continued = False
123 127
130 def __getattr__(self, attr): 134 def __getattr__(self, attr):
131 if attr in ('close', 'readline', 'readlines', '__iter__'): 135 if attr in ('close', 'readline', 'readlines', '__iter__'):
132 return getattr(self.f, attr) 136 return getattr(self.f, attr)
133 raise AttributeError 137 raise AttributeError
134 138
139
135 def _statusmessage(code): 140 def _statusmessage(code):
136 responses = httpserver.basehttprequesthandler.responses 141 responses = httpserver.basehttprequesthandler.responses
137 return pycompat.bytesurl( 142 return pycompat.bytesurl(
138 responses.get(code, (r'Error', r'Unknown error'))[0]) 143 responses.get(code, (r'Error', r'Unknown error'))[0]
144 )
145
139 146
140 def statusmessage(code, message=None): 147 def statusmessage(code, message=None):
141 return '%d %s' % (code, message or _statusmessage(code)) 148 return '%d %s' % (code, message or _statusmessage(code))
149
142 150
143 def get_stat(spath, fn): 151 def get_stat(spath, fn):
144 """stat fn if it exists, spath otherwise""" 152 """stat fn if it exists, spath otherwise"""
145 cl_path = os.path.join(spath, fn) 153 cl_path = os.path.join(spath, fn)
146 if os.path.exists(cl_path): 154 if os.path.exists(cl_path):
147 return os.stat(cl_path) 155 return os.stat(cl_path)
148 else: 156 else:
149 return os.stat(spath) 157 return os.stat(spath)
150 158
159
151 def get_mtime(spath): 160 def get_mtime(spath):
152 return get_stat(spath, "00changelog.i")[stat.ST_MTIME] 161 return get_stat(spath, "00changelog.i")[stat.ST_MTIME]
162
153 163
154 def ispathsafe(path): 164 def ispathsafe(path):
155 """Determine if a path is safe to use for filesystem access.""" 165 """Determine if a path is safe to use for filesystem access."""
156 parts = path.split('/') 166 parts = path.split('/')
157 for part in parts: 167 for part in parts:
158 if (part in ('', pycompat.oscurdir, pycompat.ospardir) or 168 if (
159 pycompat.ossep in part or 169 part in ('', pycompat.oscurdir, pycompat.ospardir)
160 pycompat.osaltsep is not None and pycompat.osaltsep in part): 170 or pycompat.ossep in part
171 or pycompat.osaltsep is not None
172 and pycompat.osaltsep in part
173 ):
161 return False 174 return False
162 175
163 return True 176 return True
177
164 178
165 def staticfile(directory, fname, res): 179 def staticfile(directory, fname, res):
166 """return a file inside directory with guessed Content-Type header 180 """return a file inside directory with guessed Content-Type header
167 181
168 fname always uses '/' as directory separator and isn't allowed to 182 fname always uses '/' as directory separator and isn't allowed to
182 if os.path.exists(path): 196 if os.path.exists(path):
183 break 197 break
184 try: 198 try:
185 os.stat(path) 199 os.stat(path)
186 ct = pycompat.sysbytes( 200 ct = pycompat.sysbytes(
187 mimetypes.guess_type(pycompat.fsdecode(path))[0] or r"text/plain") 201 mimetypes.guess_type(pycompat.fsdecode(path))[0] or r"text/plain"
202 )
188 with open(path, 'rb') as fh: 203 with open(path, 'rb') as fh:
189 data = fh.read() 204 data = fh.read()
190 205
191 res.headers['Content-Type'] = ct 206 res.headers['Content-Type'] = ct
192 res.setbodybytes(data) 207 res.setbodybytes(data)
195 raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename') 210 raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename')
196 except OSError as err: 211 except OSError as err:
197 if err.errno == errno.ENOENT: 212 if err.errno == errno.ENOENT:
198 raise ErrorResponse(HTTP_NOT_FOUND) 213 raise ErrorResponse(HTTP_NOT_FOUND)
199 else: 214 else:
200 raise ErrorResponse(HTTP_SERVER_ERROR, 215 raise ErrorResponse(
201 encoding.strtolocal(err.strerror)) 216 HTTP_SERVER_ERROR, encoding.strtolocal(err.strerror)
217 )
218
202 219
203 def paritygen(stripecount, offset=0): 220 def paritygen(stripecount, offset=0):
204 """count parity of horizontal stripes for easier reading""" 221 """count parity of horizontal stripes for easier reading"""
205 if stripecount and offset: 222 if stripecount and offset:
206 # account for offset, e.g. due to building the list in reverse 223 # account for offset, e.g. due to building the list in reverse
214 count += 1 231 count += 1
215 if stripecount and count >= stripecount: 232 if stripecount and count >= stripecount:
216 parity = 1 - parity 233 parity = 1 - parity
217 count = 0 234 count = 0
218 235
236
219 def get_contact(config): 237 def get_contact(config):
220 """Return repo contact information or empty string. 238 """Return repo contact information or empty string.
221 239
222 web.contact is the primary source, but if that is not set, try 240 web.contact is the primary source, but if that is not set, try
223 ui.username or $EMAIL as a fallback to display something useful. 241 ui.username or $EMAIL as a fallback to display something useful.
224 """ 242 """
225 return (config("web", "contact") or 243 return (
226 config("ui", "username") or 244 config("web", "contact")
227 encoding.environ.get("EMAIL") or "") 245 or config("ui", "username")
246 or encoding.environ.get("EMAIL")
247 or ""
248 )
249
228 250
229 def cspvalues(ui): 251 def cspvalues(ui):
230 """Obtain the Content-Security-Policy header and nonce value. 252 """Obtain the Content-Security-Policy header and nonce value.
231 253
232 Returns a 2-tuple of the CSP header value and the nonce value. 254 Returns a 2-tuple of the CSP header value and the nonce value.