async def do_traverse(request, parent, path): """Traverse for the code API.""" if not path: return parent, path assert request is not None # could be used for permissions, etc if IContainer.providedBy(parent) and \ path[0] != request._db_id: # Tried to access a container outside the request raise HTTPUnauthorized() if IApplication.providedBy(parent) and \ path[0] != request._container_id: # Tried to access a container outside the request raise HTTPUnauthorized() try: if path[0].startswith('_') or path[0] in ('.', '..'): raise HTTPUnauthorized() context = parent[path[0]] except TypeError: return parent, path except KeyError: return parent, path context._v_parent = parent return await traverse(request, context, path[1:])
async def token_validation(self, request, handler): """Check security access of this layer.""" hassio_token = request.headers.get(HEADER_TOKEN) # Ignore security check for rule in NO_SECURITY_CHECK: if rule.match(request.path): _LOGGER.debug("Passthrough %s", request.path) return await handler(request) # Unknown API access if not hassio_token: _LOGGER.warning("Invalid token for access %s", request.path) raise HTTPUnauthorized() # Home-Assistant if hassio_token == self.sys_homeassistant.uuid: _LOGGER.debug("%s access from Home-Assistant", request.path) request[REQUEST_FROM] = 'homeassistant' # Host if hassio_token == self.sys_machine_id: _LOGGER.debug("%s access from Host", request.path) request[REQUEST_FROM] = 'host' # Add-on addon = self.sys_addons.from_uuid(hassio_token) if addon: _LOGGER.info("%s access from %s", request.path, addon.slug) request[REQUEST_FROM] = addon.slug if not request.get(REQUEST_FROM): raise HTTPUnauthorized() return await handler(request)
async def _handle( self, request: web.Request, token: str, path: str ) -> Union[web.Response, web.StreamResponse, web.WebSocketResponse]: """Route data to Hass.io ingress service.""" # validate token if token != self._valid_token: try: auth = self._hass.auth refresh_token = await auth.async_validate_access_token(token) if refresh_token is None: raise HTTPUnauthorized() from None # remember the token as valid self._valid_token = token except Exception: raise HTTPUnauthorized() from None try: # Websockettoken if _is_websocket(request): return await self._handle_websocket(request, token, path) # Request return await self._handle_request(request, token, path) except aiohttp.ClientError as err: _LOGGER.debug("Ingress error with %s / %s: %s", token, path, err) raise HTTPBadGateway() from None
async def validate_token(self, token: str) -> dict: """ Request the JWT token validation to the associated UAA service Args: token: JWT token to validate Returns: user authenticated if the token is valid """ async with ClientSession() as client: validate_token_url = self.URL_BASE.format(protocol=self._protocol, host=self._host, port=self._port, path=self.PATHS['validate_token']) try: async with client.post(validate_token_url, data=dict(token=token)) as response: if response.status == 200: response_json = await response.json() elif response.status == 500: raise HTTPUnauthorized(reason='Wrong authorization token') else: raise HTTPInternalServerError(reason='Error calling uaa service') except Exception as ex: if not isinstance(ex, HTTPUnauthorized) and not isinstance(ex, HTTPInternalServerError): raise ConnectionError(f'Error calling uaa service {str(ex)}') else: raise ex if 'id' in response_json: return response_json else: raise HTTPUnauthorized(reason='Wrong authorization token')
async def validate_user_credentials(self, username, token): session = self.session_map try: if session[username]['token'] == token: return session[username] except KeyError: raise HTTPUnauthorized() raise HTTPUnauthorized()
async def check_auth(self, request): auth = request.headers.get('AUTHORIZATION') if not auth.startswith('Bearer '): raise HTTPUnauthorized() token = auth[7:] if token not in self.tokens: raise HTTPUnauthorized() if not self.tokens[token]: raise HTTPUnauthorized()
def _check_access(self, request: web.Request): """Check if this call is from Supervisor.""" # Check caller IP oppio_ip = os.environ["OPPIO"].split(":")[0] if request[KEY_REAL_IP] != ip_address(oppio_ip): _LOGGER.error("Invalid auth request from %s", request[KEY_REAL_IP]) raise HTTPUnauthorized() # Check caller token if request[KEY_OPP_USER].id != self.user.id: _LOGGER.error("Invalid auth request from %s", request[KEY_OPP_USER].name) raise HTTPUnauthorized()
async def wrapped(request, **kwargs): nonlocal provider oauth_endpoint_path = None oauth_email_regex = None provider_confs = request.app.config.get('OAUTH_PROVIDERS', {}) if provider is None and 'default' in provider_confs: provider = 'default' if provider: try: provider_config = provider_confs[provider] except KeyError: if provider == "default" and provider_confs: provider_config = next(iter(provider_confs.values())) else: raise OAuthConfigurationException( "No provider named {} configured".format(provider)) oauth_endpoint_path = provider_config.get('ENDPOINT_PATH', None) oauth_email_regex = provider_config.get('EMAIL_REGEX', None) if not oauth_endpoint_path: oauth_endpoint_path = request.app.config.OAUTH_ENDPOINT_PATH if not oauth_email_regex: oauth_email_regex = request.app.config.OAUTH_EMAIL_REGEX # Do core oauth authentication once per session if SES_ACCESS_TOKEN_FIELD not in request[SES_SESSION_NAME]: if not redirect_login: raise HTTPUnauthorized(reason=f'User is not authenticated.') if provider: request[SES_SESSION_NAME][SES_OAUTH_PROVIDER_FIELD] = provider request[SES_SESSION_NAME][ SES_AFTER_AUTH_REDIR_FIELD] = request.path return redirect(oauth_endpoint_path) # Shortcircuit out if we don't care about user info if not add_user_info: response = async_handler(request, **kwargs) else: # Otherwise retrieve the user info once per session user = await fetch_user_info(request, provider, oauth_endpoint_path, email_regex or oauth_email_regex) if user is None: raise HTTPUnauthorized(reason=f'Cannot fetch user info.') response = async_handler(request, user, **kwargs) return await response if isawaitable(response) else response
async def handle(request): """Handle incoming request.""" if not request.app[KEY_HASS].is_running: return web.Response(status=503) authenticated = request.get(KEY_AUTHENTICATED, False) if view.requires_auth: if authenticated: if "deprecate_warning_message" in request: _LOGGER.warning(request["deprecate_warning_message"]) await process_success_login(request) else: raise HTTPUnauthorized() _LOGGER.debug( "Serving %s to %s (auth: %s)", request.path, request.get(KEY_REAL_IP), authenticated, ) try: result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = await result except vol.Invalid: raise HTTPBadRequest() except exceptions.ServiceNotFound: raise HTTPInternalServerError() except exceptions.Unauthorized: raise HTTPUnauthorized() if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = 200 if isinstance(result, tuple): result, status_code = result if isinstance(result, str): result = result.encode("utf-8") elif result is None: result = b"" elif not isinstance(result, bytes): assert False, ("Result should be None, string, bytes or Response. " "Got: {}").format(result) return web.Response(body=result, status=status_code)
async def handle(request: web.Request) -> web.StreamResponse: """Handle incoming request.""" if request.app[KEY_HASS].is_stopping: return web.Response(status=HTTP_SERVICE_UNAVAILABLE) authenticated = request.get(KEY_AUTHENTICATED, False) if view.requires_auth and not authenticated: raise HTTPUnauthorized() _LOGGER.debug( "Serving %s to %s (auth: %s)", request.path, request.remote, authenticated, ) try: result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = await result except vol.Invalid as err: raise HTTPBadRequest() from err except exceptions.ServiceNotFound as err: raise HTTPInternalServerError() from err except exceptions.Unauthorized as err: raise HTTPUnauthorized() from err if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = HTTP_OK if isinstance(result, tuple): result, status_code = result if isinstance(result, bytes): bresult = result elif isinstance(result, str): bresult = result.encode("utf-8") elif result is None: bresult = b"" else: assert ( False ), f"Result should be None, string, bytes or Response. Got: {result}" return web.Response(body=bresult, status=status_code)
def _check_access(self, request: web.Request): """Check if this call is from Supervisor.""" # Check caller IP hassio_ip = os.environ["HASSIO"].split(":")[0] if ip_address(request.transport.get_extra_info("peername") [0]) != ip_address(hassio_ip): _LOGGER.error("Invalid auth request from %s", request.remote) raise HTTPUnauthorized() # Check caller token if request[KEY_HASS_USER].id != self.user.id: _LOGGER.error("Invalid auth request from %s", request[KEY_HASS_USER].name) raise HTTPUnauthorized()
async def auth_check(request, handler): if 'ADMIN_USER' not in os.environ or 'ADMIN_PASSWORD' not in os.environ: raise Exception('Admin login credentials not set') if 'Authorization' not in request.headers: raise HTTPUnauthorized() auth = BasicAuth.decode(request.headers['Authorization']) if (auth.login != os.environ['ADMIN_USER'] or auth.password != os.environ['ADMIN_PASSWORD']): raise HTTPUnauthorized() response = await handler(request) return response
async def __wrapper__(request: Request): user = await get_auth_user(request) if user is None: raise HTTPUnauthorized() if ensure_admin and not user.is_admin: raise HTTPForbidden() return await handler(request)
async def wrap_api(api, *args, **kwargs): """Return API information.""" coresys: CoreSys = api.coresys request: Request = args[0] if request[REQUEST_FROM] != coresys.homeassistant: raise HTTPUnauthorized() return await method(api, *args, **kwargs)
def apply_cors(request): """Second part of the cors function to validate.""" from plone.server import app_settings headers = {} origin = request.headers.get('Origin', None) if origin: if not any([ fnmatch.fnmatchcase(origin, o) for o in app_settings['cors']['allow_origin'] ]): logger.error('Origin %s not allowed' % origin) raise HTTPUnauthorized() elif request.headers.get('Access-Control-Allow-Credentials', False): headers['Access-Control-Allow-Origin', origin] else: if any([o == "*" for o in app_settings['cors']['allow_origin']]): headers['Access-Control-Allow-Origin'] = '*' else: headers['Access-Control-Allow-Origin'] = origin if request.headers.get('Access-Control-Request-Method', None) != 'OPTIONS': if app_settings['cors']['allow_credentials']: headers['Access-Control-Allow-Credentials'] = 'True' if len(app_settings['cors']['allow_headers']): headers['Access-Control-Expose-Headers'] = \ ', '.join(app_settings['cors']['allow_headers']) return headers
async def _add_auth_info(self, request: web.Request): """Adds the authentication information, if any, to the request. Catches exceptions from decoding the payload and converts them to HTTP exceptions to be propagated. If authentication is disabled via :attr:`~_ignore_auth` doesn't do anything. Headers are kept in a `CIMultiDictProxy`_ so case of the header is not important. .. _CIMultiDictProxy: https://multidict.readthedocs.io/en/stable/multidict.html#multidict.CIMultiDictProxy """ if self._ignore_auth: return None try: oidc_data = request.headers["X-Amzn-Oidc-Data"] except KeyError: logger.warning("No 'X-Amzn-Oidc-Data' header present. Dropping request.") raise HTTPProxyAuthenticationRequired() try: request["auth_payload"] = (self._header_name, await self._decode_payload(oidc_data)) except ExpiredSignatureError: logger.warning("Got expired token. Dropping request.") raise HTTPUnauthorized() except DecodeError as e: logger.warning("Couldn't decode token. Dropping request.") logger.debug("Couldn't decode token: %s" % e) raise HTTPBadRequest()
async def jwt_auth(request, handler): def _get_token(): raw_authorization = request.headers.get('Authorization', '') items = raw_authorization.split(' ') return items[1] if len(items) > 1 else None token = _get_token() if request.path != '/login': if token: try: request.jwt_data = jwt.decode(token, JWT_SECRET_KEY) except jwt.InvalidTokenError: raise HTTPUnauthorized(text='Token decode error') else: raise HTTPUnauthorized(text='Missing token') return await handler(request)
async def session_post(request: Request): session_maker = request.app['db_session_manager'] session: Session = session_maker() try: data = await request.json() if data: user = session.query(Users).filter_by(login=data['login']).first() if not user: return HTTPNotFound() user_id = user.id if UsersSchema.check_password_hash(data['password'], user.password): response = HTTPAccepted() await remember(request, response, json.dumps({'user_id': user_id, 'login': user.login})) return response else: response = HTTPUnauthorized() if user_id: await forget(request, response) return response else: return HTTPBadRequest() except Exception: raise finally: session.close()
def apply_cors(request: IRequest) -> dict: # deprecated, will be removed in next major release headers = {} origin = request.headers.get('Origin', None) if origin: if not any([ fnmatch.fnmatchcase(origin, o) for o in app_settings['cors']['allow_origin'] ]): logger.error('Origin %s not allowed' % origin) raise HTTPUnauthorized() elif request.headers.get('Access-Control-Allow-Credentials', False): headers['Access-Control-Allow-Origin', origin] else: if any([o == "*" for o in app_settings['cors']['allow_origin']]): headers['Access-Control-Allow-Origin'] = '*' else: headers['Access-Control-Allow-Origin'] = origin if request.headers.get('Access-Control-Request-Method', None) != 'OPTIONS': if app_settings['cors']['allow_credentials']: headers['Access-Control-Allow-Credentials'] = 'True' if len(app_settings['cors']['allow_headers']): headers['Access-Control-Expose-Headers'] = \ ', '.join(app_settings['cors']['allow_headers']) return headers
async def validate(self, token): """Return the user from the token.""" if token.get('type') != 'bearer': return None if '.' not in token.get('token', ''): # quick way to check if actually might be jwt return None try: try: validated_jwt = jwt.decode( token['token'], app_settings['jwt']['secret'], algorithms=[app_settings['jwt']['algorithm']]) except jwt.exceptions.ExpiredSignatureError: logger.warn("Token Expired") raise HTTPUnauthorized() except jwt.InvalidIssuedAtError: logger.warn("Back to the future") validated_jwt = jwt.decode( token['token'], app_settings['jwt']['secret'], algorithms=[app_settings['jwt']['algorithm']], options=NON_IAT_VERIFY) user = GuillotinaUser(self.request) user.name = validated_jwt['fullname'] user.id = validated_jwt['sub'] return user except jwt.exceptions.DecodeError: pass return None
def _process_auth( self, request: Request ) -> Tuple['Config', Union[SimpleNamespace, 'User'], Optional[str]]: config = self.config(request) if not config: raise HTTPNotFound() hass_user = request.get('hass_user') request_id = request.headers.get('X-Request-Id') remote_accepted = False if config.diagnostics_mode: remote_address = ipaddress.ip_address(request.remote) for network in config.diagnostics_mode: if remote_address in network: remote_accepted = True break if remote_accepted: # Facilitate the use of diagnostics mode by adding dummy data to the request if not hass_user: hass_user = SimpleNamespace(id=999999) if not request_id: request_id = str(uuid4()).upper() elif not hass_user: raise HTTPUnauthorized() elif not request_id: raise HTTPBadRequest() return config, hass_user, request_id
async def handler( self, request: web.Request ) -> Union[web.Response, web.StreamResponse, web.WebSocketResponse]: """Route data to Supervisor ingress service.""" # Check Ingress Session session = request.cookies.get(COOKIE_INGRESS) if not self.sys_ingress.validate_session(session): _LOGGER.warning("No valid ingress session %s", session) raise HTTPUnauthorized() # Process requests addon = self._extract_addon(request) path = request.match_info.get("path") try: # Websocket if _is_websocket(request): return await self._handle_websocket(request, addon, path) # Request return await self._handle_request(request, addon, path) except aiohttp.ClientError as err: _LOGGER.error("Ingress error: %s", err) raise HTTPBadGateway()
async def security_layer(request, handler): """Check security access of this layer.""" coresys = request.app['coresys'] hassio_token = request.headers.get(HEADER_TOKEN) # Ignore security check for rule in NO_SECURITY_CHECK: if rule.match(request.path): _LOGGER.debug("Passthrough %s", request.path) return await handler(request) # Need to be removed later if not hassio_token: _LOGGER.warning("No valid Hass.io token for API access!") request[REQUEST_FROM] = 'UNKNOWN' return await handler(request) # Home-Assistant if hassio_token == coresys.homeassistant.uuid: _LOGGER.debug("%s access from Home-Assistant", request.path) request[REQUEST_FROM] = 'homeassistant' return await handler(request) # Add-on addon = coresys.addons.from_uuid(hassio_token) if addon: _LOGGER.info("%s access from %s", request.path, addon.slug) request[REQUEST_FROM] = addon.slug return await handler(request) raise HTTPUnauthorized()
async def auth(self, request: web.Request) -> bool: """Process login request.""" addon = request[REQUEST_FROM] if not addon.access_auth_api: raise APIForbidden("Can't use Open Peer Power auth!") # BasicAuth if AUTHORIZATION in request.headers: return await self._process_basic(request, addon) # Json if request.headers.get(CONTENT_TYPE) == CONTENT_TYPE_JSON: data = await request.json() return await self._process_dict(request, addon, data) # URL encoded if request.headers.get(CONTENT_TYPE) == CONTENT_TYPE_URL: data = await request.post() return await self._process_dict(request, addon, data) raise HTTPUnauthorized(headers={ WWW_AUTHENTICATE: 'Basic realm="Open Peer Power Authentication"' })
async def auth_wrapper(request): if 'X-API-KEY' not in request.headers: raise HTTPUnauthorized(text='No API Key provided') key = request.headers['X-API-KEY'] if key != config["API_KEY"] or not config["API_KEY"]: raise HTTPForbidden(text='Wrong API Key') return await func(request)
async def get(self, request): """Return the onboarding status.""" if self._data["done"]: raise HTTPUnauthorized() hass = request.app["hass"] info = await async_get_system_info(hass) return self.json({"installation_type": info["installation_type"]})
async def _check_login(self, username, password): """Check User credentials.""" provider = self._get_provider() try: await provider.async_validate_login(username, password) except HomeAssistantError: raise HTTPUnauthorized() from None
async def validate_session(self, request: web.Request) -> Dict[str, Any]: """Validate session and extending how long it's valid for.""" data = await api_validate(VALIDATE_SESSION_DATA, request) # Check Ingress Session if not self.sys_ingress.validate_session(data[ATTR_SESSION]): _LOGGER.warning("No valid ingress session %s", data[ATTR_SESSION]) raise HTTPUnauthorized()
async def login_user(username, password): db_mgr = await DatabaseManager.get_instance() result = await db_mgr.execute_query( "select username from users where username = %s " "and password = %s", username, password) if result[0] is None: raise HTTPUnauthorized(reason="Bad login or password") print("User logged in ", username)
async def post(self): if not await self.has_permission(): raise HTTPUnauthorized() drive: Drive = self.request.app['drive'] se: SearchEngine = self.request.app['se'] await se.clear_cache() changes = [_ async for _ in drive.sync()] return json_response(changes)