40 |
40 |
41 If userlist has a single '*' member, all users are considered members. |
41 If userlist has a single '*' member, all users are considered members. |
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 == [b'*'] or username in userlist |
46 |
46 |
47 |
47 |
48 def checkauthz(hgweb, req, op): |
48 def checkauthz(hgweb, req, op): |
49 '''Check permission for operation based on request data (including |
49 '''Check permission for operation based on request data (including |
50 authentication info). Return if op allowed, else raise an ErrorResponse |
50 authentication info). Return if op allowed, else raise an ErrorResponse |
51 exception.''' |
51 exception.''' |
52 |
52 |
53 user = req.remoteuser |
53 user = req.remoteuser |
54 |
54 |
55 deny_read = hgweb.configlist('web', 'deny_read') |
55 deny_read = hgweb.configlist(b'web', b'deny_read') |
56 if deny_read and (not user or ismember(hgweb.repo.ui, user, deny_read)): |
56 if deny_read and (not user or ismember(hgweb.repo.ui, user, deny_read)): |
57 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') |
57 raise ErrorResponse(HTTP_UNAUTHORIZED, b'read not authorized') |
58 |
58 |
59 allow_read = hgweb.configlist('web', 'allow_read') |
59 allow_read = hgweb.configlist(b'web', b'allow_read') |
60 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)): |
61 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') |
61 raise ErrorResponse(HTTP_UNAUTHORIZED, b'read not authorized') |
62 |
62 |
63 if op == 'pull' and not hgweb.allowpull: |
63 if op == b'pull' and not hgweb.allowpull: |
64 raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized') |
64 raise ErrorResponse(HTTP_UNAUTHORIZED, b'pull not authorized') |
65 elif op == 'pull' or op is None: # op is None for interface requests |
65 elif op == b'pull' or op is None: # op is None for interface requests |
66 return |
66 return |
67 |
67 |
68 # Allow LFS uploading via PUT requests |
68 # Allow LFS uploading via PUT requests |
69 if op == 'upload': |
69 if op == b'upload': |
70 if req.method != 'PUT': |
70 if req.method != b'PUT': |
71 msg = 'upload requires PUT request' |
71 msg = b'upload requires PUT request' |
72 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg) |
72 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg) |
73 # enforce that you can only push using POST requests |
73 # enforce that you can only push using POST requests |
74 elif req.method != 'POST': |
74 elif req.method != b'POST': |
75 msg = 'push requires POST request' |
75 msg = b'push requires POST request' |
76 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg) |
76 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg) |
77 |
77 |
78 # require ssl by default for pushing, auth info cannot be sniffed |
78 # require ssl by default for pushing, auth info cannot be sniffed |
79 # and replayed |
79 # and replayed |
80 if hgweb.configbool('web', 'push_ssl') and req.urlscheme != 'https': |
80 if hgweb.configbool(b'web', b'push_ssl') and req.urlscheme != b'https': |
81 raise ErrorResponse(HTTP_FORBIDDEN, 'ssl required') |
81 raise ErrorResponse(HTTP_FORBIDDEN, b'ssl required') |
82 |
82 |
83 deny = hgweb.configlist('web', 'deny_push') |
83 deny = hgweb.configlist(b'web', b'deny_push') |
84 if deny and (not user or ismember(hgweb.repo.ui, user, deny)): |
84 if deny and (not user or ismember(hgweb.repo.ui, user, deny)): |
85 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') |
85 raise ErrorResponse(HTTP_UNAUTHORIZED, b'push not authorized') |
86 |
86 |
87 allow = hgweb.configlist('web', 'allow-push') |
87 allow = hgweb.configlist(b'web', b'allow-push') |
88 if not (allow and ismember(hgweb.repo.ui, user, allow)): |
88 if not (allow and ismember(hgweb.repo.ui, user, allow)): |
89 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') |
89 raise ErrorResponse(HTTP_UNAUTHORIZED, b'push not authorized') |
90 |
90 |
91 |
91 |
92 # Hooks for hgweb permission checks; extensions can add hooks here. |
92 # Hooks for hgweb permission checks; extensions can add hooks here. |
93 # Each hook is invoked like this: hook(hgweb, request, operation), |
93 # Each hook is invoked like this: hook(hgweb, request, operation), |
94 # 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 |
126 self.continued = False |
126 self.continued = False |
127 |
127 |
128 def read(self, amt=-1): |
128 def read(self, amt=-1): |
129 if not self.continued: |
129 if not self.continued: |
130 self.continued = True |
130 self.continued = True |
131 self._write('HTTP/1.1 100 Continue\r\n\r\n') |
131 self._write(b'HTTP/1.1 100 Continue\r\n\r\n') |
132 return self.f.read(amt) |
132 return self.f.read(amt) |
133 |
133 |
134 def __getattr__(self, attr): |
134 def __getattr__(self, attr): |
135 if attr in ('close', 'readline', 'readlines', '__iter__'): |
135 if attr in (b'close', b'readline', b'readlines', b'__iter__'): |
136 return getattr(self.f, attr) |
136 return getattr(self.f, attr) |
137 raise AttributeError |
137 raise AttributeError |
138 |
138 |
139 |
139 |
140 def _statusmessage(code): |
140 def _statusmessage(code): |
156 else: |
156 else: |
157 return os.stat(spath) |
157 return os.stat(spath) |
158 |
158 |
159 |
159 |
160 def get_mtime(spath): |
160 def get_mtime(spath): |
161 return get_stat(spath, "00changelog.i")[stat.ST_MTIME] |
161 return get_stat(spath, b"00changelog.i")[stat.ST_MTIME] |
162 |
162 |
163 |
163 |
164 def ispathsafe(path): |
164 def ispathsafe(path): |
165 """Determine if a path is safe to use for filesystem access.""" |
165 """Determine if a path is safe to use for filesystem access.""" |
166 parts = path.split('/') |
166 parts = path.split(b'/') |
167 for part in parts: |
167 for part in parts: |
168 if ( |
168 if ( |
169 part in ('', pycompat.oscurdir, pycompat.ospardir) |
169 part in (b'', pycompat.oscurdir, pycompat.ospardir) |
170 or pycompat.ossep in part |
170 or pycompat.ossep in part |
171 or pycompat.osaltsep is not None |
171 or pycompat.osaltsep is not None |
172 and pycompat.osaltsep in part |
172 and pycompat.osaltsep in part |
173 ): |
173 ): |
174 return False |
174 return False |