def get_multi_dict_from_python_dict(resp_headers_dict: dict) -> MultiDictProxy: """Construct an :class:`aiohttp.MultiDictProxy` instance from a Python dictionary. Note: For now, this method is used for test only. .. note:: Neither Python dictionary nor JSON supports multi-value key. The response headers returned by `aiohttp` is of immutable type :class:`aiohttp.MultiDictProxy` while the one returned by `aiohttpretty` is of :class:`aiohttp.MultiDict`. WB tests use the :class:`aiohttp.MultiDict` type for both files and folders during modification and returns the :class:`aiohttp.MultiDictProxy` type to imitate the behavior of `aiohttp`. :param dict resp_headers_dict: the raw response headers dictionary :rtype: :class:`aiohttp.MultiDictProxy` """ resp_headers = MultiDict(resp_headers_dict) google_hash = resp_headers.get('x-goog-hash', None) if google_hash: assert verify_raw_google_hash_header(google_hash) resp_headers.pop('x-goog-hash') google_hash_list = google_hash.split(',') for google_hash in google_hash_list: resp_headers.add('x-goog-hash', google_hash) return MultiDictProxy(resp_headers)
async def post(self) -> Response: request = self.request ctype = request.headers.get('content-type') logger.debug('Request Content-Type: %s', ctype) form: MultiDict if ctype == 'application/json': try: data: Any = await request.json() if not isinstance(data, dict): raise ValueError('Invalid request type') except ValueError as e: logger.warning('Invalid request: %s', e) raise HTTPBadRequest(reason='Invalid request') from e else: form = MultiDict(cast(Dict, data)) elif ctype == 'application/x-www-form-urlencoded': form = (await self.request.post()).copy() else: raise HTTPBadRequest(reason='Invalid content type') logger.debug('Form is: %s', form) accepts = parse_accept(request.headers.get('Accept')) response_ctype = choice_content_type( accepts, ['application/json', 'text/html', 'text/plain']) logger.debug('Content-type for response is: %s', response_ctype) error: Optional[str] = None error_description: Optional[str] = None if ('username' in form and 'password' in form): # Check incoming parameters username = str(form.pop('username')).strip() password = str(form.pop('password')).strip() # Check fields too long if (len(username) > FIELD_LENGTH or len(password) > FIELD_LENGTH): logger.warning( 'Recieve request with very long login/password field') raise HTTPBadRequest() # Ensure no redirect to external host next_uri = form.get('next') if next_uri: parsed = urlparse(next_uri) if parsed.scheme or parsed.netloc: logger.warning('Trying to do external redirect') raise HTTPBadRequest() # Finding user logger.debug('Try to find user %s in store', username) try: user = store.users.get_by_username(username) except KeyError: logger.warning('User %s not found', username) error = 'bigur_invalid_login' error_description = 'Invalid login or password' else: if user.verify_password(password): # Login successful logger.debug('Login for user %s successful', username) if response_ctype == 'application/json': response = json_response({'meta': {'status': 'ok'}}) else: # No next parameter, no way to redirect if not next_uri: response = Response(text='Login successful') # Redirecting else: response = Response(status=303, reason='See Other', charset='utf-8', headers={'Location': next_uri}) # Set cookie self.set_cookie(self.request, response, user.id) return response logger.warning('Password is incorrect for user %s', username) error = 'bigur_invalid_login' error_description = 'Invalid login or password' if response_ctype == 'application/json': return json_response({ 'meta': { 'status': 'error', 'error': error, 'message': error_description } }) else: # Show form context = { 'endpoint': self.request.app['config'].get( 'http_server.endpoints.login.path'), 'query': form, 'error': error, 'error_description': error_description, 'prefix': request.app['config'].get('http_server.static.prefix', '/') } return render_template('login_form.j2', self.request, context)