def report_stats(call: APICall, company: str, request: ReportStatsOptionRequest): if not StatisticsReporter.supported: result = ReportStatsOptionResponse(supported=False) else: enabled = request.enabled with translate_errors_context(): query = Company.objects(id=company) if enabled is None: stats_option = query.first().defaults.stats_option else: stats_option = ReportStatsOption( enabled=enabled, enabled_time=datetime.utcnow(), enabled_version=get_version(), enabled_user=call.identity.user, ) updated = query.update(defaults__stats_option=stats_option) if not updated: raise errors.server_error.InternalError( f"Failed setting report_stats to {enabled}" ) data = stats_option.to_mongo() data["current_version"] = get_version() result = ReportStatsOptionResponse(**data) call.result.data_model = result
def start_reporter(cls): """ Periodically send statistics reports for companies who have opted in. Note: in trains we usually have only a single company """ if not cls.supported: return report_interval = timedelta( hours=config.get("apiserver.statistics.report_interval_hours", 24)) sleep(report_interval.total_seconds()) while not ThreadsManager.terminating: try: for company in Company.objects( defaults__stats_option__enabled=True).only("id"): stats = cls.get_statistics(company.id) cls.send_queue.put(stats) except Exception as ex: log.exception(f"Failed collecting stats: {str(ex)}") sleep(report_interval.total_seconds())
def validate_impersonation(endpoint, call): """ Validate impersonation headers and set impersonated identity and authorization data accordingly. :returns True is impersonating, False otherwise """ try: act_as = call.act_as impersonate_as = call.impersonate_as if not impersonate_as and not act_as: return elif impersonate_as and act_as: raise errors.bad_request.InvalidHeaders( "only one allowed", headers=tuple(call.impersonation_headers.keys())) identity = call.auth.identity # verify this user is allowed to impersonate at all if identity.role not in Role.get_system_roles() | {Role.admin}: raise errors.bad_request.ImpersonationError( "impersonation not allowed", role=identity.role) # get the impersonated user's info user_id = act_as or impersonate_as if identity.role in [Role.root]: # only root is allowed to impersonate users in other companies query = dict(id=user_id) else: query = dict(id=user_id, company=identity.company) user = User.objects(**query).first() if not user: raise errors.bad_request.ImpersonationError( "unknown user", **query) company = Company.objects(id=user.company).only("name").first() if not company: query.update(company=user.company) raise errors.bad_request.ImpersonationError( "unknown company for user", **query) # create impersonation payload if act_as: # act as a user, using your own role and permissions call.impersonation = Payload( auth_type=None, identity=Identity( user=user.id, company=user.company, role=identity.role, user_name=f"{identity.user_name} (acting as {user.name})", company_name=company.name, ), ) elif impersonate_as: # impersonate as a user, using his own identity and permissions (required additional validation to verify # impersonated user is allowed to access the endpoint) service, _, action = endpoint.name.partition(".") call.impersonation = authorize_impersonation( user=user, identity=Identity( user=user.id, company=user.company, role=user.role, user_name= f"{user.name} (impersonated by {identity.user_name})", company_name=company.name, ), service=service, action=action, call=call, ) else: return False return True except APIError: raise except Exception: raise errors.server_error.InternalError("validating impersonation")