Example #1
0
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
Example #2
0
 def _filter(self, company: Company) -> bool:
     """
     Method determines whether the given item shall be included into the report
     """
     return company.is_processable(included_items=self._included_items,
                                   excluded_items=self._excluded_items,
                                   scope=self._scope)
Example #3
0
    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())
Example #4
0
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")