Beispiel #1
0
    def async_get(self, request):
        """
        Serves role and capability information relative to the user by default or a specific role if one is specified.

        Query Parameters:
        * role: if specified, this will return which capability this particular role grants

        Example Response:
        {
            "read": {
                "asset": ["asset_1", "asset_2"],
                "workspace": ["workspace_1", "workspace_2", "workspace_3"],
                "geofence": "ALL"
            },
            "write": {
                "note": ["note_1", "note_2"],
                "playbook": ["playbook_1"]
            },
            general: ["workspace_write", "asset_manage", "note_manage", "beacon_view"],
        }

        If the user has access to all objects of a specific type, the value for the object type key in "read" or "write"
        will have the value "ALL" instead of an actual list.

        :param request: a dictionary representing the HTTP request
        """
        context = RequestContext.from_rest_request(request)
        query = request.get(QUERY, {})

        if ROLE in query:
            capabilities, accesses = yield self._permissions.get_capabilities_granted_by_role(context, query[ROLE])
        else:
            capabilities, accesses = yield self._permissions.get_user_capabilities(context)

        read, write = collections.defaultdict(set), collections.defaultdict(set)
        for capability in Capabilities.read_capabilities() + Capabilities.write_capabilities():
            access = accesses.get(capability, Access(quantity=AccessQuantity.NONE))
            object_type = capability.type.value
            if access.quantity == AccessQuantity.ALL:
                read[object_type] = ALL_ACCESS
                write[object_type] = ALL_ACCESS
            elif capability.level == LEVEL_READ:
                read[object_type] |= access.ids
            elif capability.level == LEVEL_WRITE:
                read[object_type] |= access.ids
                write[object_type] |= access.ids

        for object_type in read:
            read[object_type] = list(read[object_type]) if isinstance(read[object_type], set) else read[object_type]
        for object_type in write:
            write[object_type] = list(write[object_type]) if isinstance(write[object_type], set) else write[object_type]

        defer.returnValue({
            STATUS: http.OK,
            PAYLOAD: {
                CAPABILITIES: sorted(capabilities),
                READ: dict(read),
                WRITE: dict(write)
            }
        })
    async def get(self, request):
        """
        REST handler to fetch all apps visible to current user
        """

        authtoken = request[SESSION][AUTHTOKEN]
        user = request[SESSION][USER]
        auth_header = SplunkAuthHeader(authtoken)
        request_context = RequestContext(auth_header,
                                         current_user=user,
                                         system_auth_header=auth_header)

        try:
            async_splunk_client = self.async_client_factory.splunk_client()
            app_list = await fetch_app_names(request_context,
                                             async_splunk_client)
            payload = [{
                APP_NAME: app.app_name,
                DISPLAY_APP_NAME: app.display_app_name
            } for app in app_list]
            return {
                'payload': payload,
                'status': HTTPStatus.OK,
            }
        except Exception as e:
            return {'error': e.message}
    async def _update_credential(self, user, auth_header):
        ctx = RequestContext(auth_header, current_user=user)
        credential = await _fetch_credential_data(auth_header,
                                                  self.kvstore_client)

        if credential.session_type == constants.SPLUNK_SESSION_TOKEN_TYPE:
            temporary_session_key = await get_splunk_cookie(
                request_context=ctx,
                async_splunk_client=self.splunk_client,
                username=ctx.auth_header.username,
                password=ctx.auth_header.password)

            credential.session_key = temporary_session_key

        credential.last_update_time = get_current_timestamp_str()

        LOGGER.debug("flush update credentials")
        credential_post_response = await self.kvstore_client.async_kvstore_post_request(
            collection=constants.SUBSCRIPTION_CREDENTIALS_COLLECTION_NAME,
            data=credential.to_json(),
            owner=ctx.current_user,
            key_id=constants.SUBSCRIPTION_CREDENTIAL_GLOBAL,
            auth_header=auth_header)

        if credential_post_response.code != HTTPStatus.OK:
            error_message = await credential_post_response.text()
            raise SpacebridgeError(error_message,
                                   status_code=credential_post_response.code)
        LOGGER.debug("Updated credentials for username=%s", user)
Beispiel #4
0
    def _create_or_update_workspace(self, request, create):
        context = RequestContext.from_rest_request(request)
        payload = json.loads(request[PAYLOAD])

        workspace_proto = augmented_reality_pb2.ARWorkspaceData()
        if WORKSPACE_ID_LABEL in payload:
            workspace_proto.arWorkspaceId = payload[WORKSPACE_ID_LABEL]
        if WORKSPACE_TITLE_LABEL in payload:
            workspace_proto.title = payload[WORKSPACE_TITLE_LABEL]
        if KVSTORE_DATA_LABEL in payload:
            # We probably should update this API to accept an ARWorkspaceData in JSON format so we only have one way
            # to set things. I.e. if the ID or title is set in the following payload, it will overwrite the ID and title
            # set above.
            workspace_data = json.loads(payload[KVSTORE_DATA_LABEL]).get(
                WORKSPACE_DATA_LABEL, {})
            if isinstance(workspace_data, dict):
                workspace_data = json.dumps(workspace_data)
            workspace_proto = Parse(workspace_data, workspace_proto)

        if create:
            affected_workspace_id = yield ar_workspace_request_processor.create_ar_workspace(
                context, self.async_splunk_client, self.async_kvstore_client,
                self.async_ar_permissions_client, workspace_proto)
        else:
            affected_workspace_id = yield ar_workspace_request_processor.update_ar_workspace(
                context, self.async_client_factory.kvstore_client(),
                self.async_client_factory.ar_permissions_client(),
                workspace_proto)

        defer.returnValue({
            PAYLOAD: {
                WORKSPACE_ID_LABEL: affected_workspace_id
            },
            STATUS: http.OK
        })
Beispiel #5
0
    def async_post(self, request):
        """Handles updates to asset groups."""
        context = RequestContext.from_rest_request(request)
        params = json.loads(request[PAYLOAD])
        if ACTION not in params or params[ACTION] not in {
                CREATE, UPDATE, DELETE
        }:
            raise SpacebridgeApiRequestError(message='Must provide an action',
                                             status_code=http.BAD_REQUEST)

        if params[ACTION] == DELETE:
            asset_group_ids = params[ASSET_GROUP_IDS]
            deleted_ids = yield delete_asset_groups(context, asset_group_ids,
                                                    self._kvstore,
                                                    self._permissions)
            defer.returnValue({
                PAYLOAD: {
                    ASSET_GROUP_IDS: deleted_ids
                },
                STATUS: http.OK
            })

        if ASSET_GROUPS not in params:
            raise SpacebridgeApiRequestError(
                'Must provide asset groups to update',
                status_code=http.BAD_REQUEST)
        asset_groups = params[ASSET_GROUPS]
        for asset_group in asset_groups:
            validate_asset_or_group_objects(
                asset_group.get(ASSET_GROUP_OBJECTS), new_workspace_title=None)
            if ASSET_GROUP_ID in asset_group:
                asset_group[KEY] = asset_group[ASSET_GROUP_ID]

        asset_group_data = []
        for asset_group in asset_groups:
            parsed_group = AssetGroup.from_json(asset_group)
            if NEW_WORKSPACE_TITLE in asset_group:
                # Today the only time NEW_WORKSPACE_TITLE will be specified is when there is only one asset group being
                # updated so it's safe to assume that this will not spawn many KV store requests.
                created_workspace_id = yield create_ar_workspace(
                    context,
                    self._splunk,
                    self._kvstore,
                    self._permissions,
                    ARWorkspaceData(title=asset_group[NEW_WORKSPACE_TITLE]),
                    dashboard_id=parsed_group.dashboard_id)
                parsed_group.workspace_id = created_workspace_id
            asset_group_data.append(parsed_group)

        affected_groups = yield bulk_update_asset_groups(
            context, asset_group_data, self._kvstore, self._permissions)
        defer.returnValue({
            PAYLOAD: {
                ASSET_GROUP_IDS_RESPONSE_KEY: affected_groups
            },
            STATUS:
            http.OK if params[ACTION] == UPDATE else http.CREATED
        })
Beispiel #6
0
async def do_trigger(alert_payload):
    auth_header = SplunkAuthHeader(alert_payload[SESSION_KEY])

    # Use default URI for alerts
    try:
        uri = rest.makeSplunkdUri()
    except Exception as e:
        LOGGER.exception("Failed to generate default URI")

    if not uri:
        return

    mtls_spacebridge_client = None
    mtls_enabled = config.get_mtls_enabled()
    if mtls_enabled:
        mtls_spacebridge_client = build_mtls_spacebridge_client(alert_payload[SESSION_KEY])

    async_client_factory = AsyncClientFactory(uri, spacebridge_client=mtls_spacebridge_client)
    async_kvstore_client = async_client_factory.kvstore_client()
    async_splunk_client = async_client_factory.splunk_client()
    async_spacebridge_client = async_client_factory.spacebridge_client()

    alert_sid = alert_payload[SEARCH_ID]
    preprocess_payload(alert_payload)

    # Default to empty string so urllib.quote doesn't fail if user doesn't exist
    user = alert_payload[RESULT].get(USER, '')
    request_context = RequestContext(auth_header=auth_header, is_alert=True, current_user=user)

    LOGGER.info("get_registered_devices alert_sid=%s", alert_sid)
    registered_devices = await get_registered_devices(request_context, async_kvstore_client, alert_payload)
    LOGGER.info("get_registered_devices ok alert_sid=%s", alert_sid)

    alert = await build_alert(request_context, alert_payload, async_splunk_client, async_kvstore_client)
    LOGGER.info("persist_alert alert_id=%s", alert_sid)
    response = persist_alert(alert, auth_header.session_token)
    LOGGER.info("persist_alert ok succeeded alert_id=%s", alert_sid)

    # If we get a proper response from KV Store, then we get the key of the stored alert
    # and create a (device_id, alert_id, timestamp) triplet for each device that should
    # receive the alert

    if response is not None and "_key" in response.keys():
        alert_id = response["_key"]
        alert.notification.alert_id = alert_id

        # Persisting (recipient device, alert id) pairs and sending push notifications happens simultaneously via async
        LOGGER.info("persist_recipient_devices alert_id=%s", alert_id)
        await persist_recipient_devices(request_context, alert_id, registered_devices, alert_payload,
                                        async_kvstore_client)
        LOGGER.info("persist_recipient_devices ok alert_id=%s", alert_id)
        LOGGER.info("send_push_notifications starting registered_devices=%s", len(registered_devices))
        await send_push_notifications(
            request_context, alert.notification, registered_devices, async_kvstore_client, async_spacebridge_client,
            async_splunk_client)
Beispiel #7
0
def do_trigger(reactor, alert_payload):
    auth_header = SplunkAuthHeader(alert_payload[SESSION_KEY])

    # Use default URI for alerts
    try:
        uri = rest.makeSplunkdUri()
    except Exception as e:
        LOGGER.exception("Failed to generate default URI. {}".format(e))

    if not uri:
        return

    async_client_factory = AsyncClientFactory(uri)
    async_kvstore_client = async_client_factory.kvstore_client()
    async_splunk_client = async_client_factory.splunk_client()
    async_spacebridge_client = async_client_factory.spacebridge_client()

    alert_sid = alert_payload[SEARCH_ID]
    preprocess_payload(alert_payload)

    request_context = RequestContext(auth_header=auth_header, is_alert=True)

    LOGGER.info("get_registered_devices alert_sid=%s" % alert_sid)
    registered_devices = yield get_registered_devices(request_context,
                                                      async_kvstore_client,
                                                      alert_payload)
    LOGGER.info("get_registered_devices ok alert_sid=%s" % alert_sid)

    alert = yield build_alert(request_context, alert_payload,
                              async_splunk_client, async_kvstore_client)
    LOGGER.info("persist_alert alert_id=%s" % alert_sid)
    response = persist_alert(alert, auth_header.session_token)
    LOGGER.info("persist_alert ok succeeded alert_id=%s" % alert_sid)

    # If we get a proper response from KV Store, then we get the key of the stored alert
    # and create a (device_id, alert_id, timestamp) triplet for each device that should
    # receive the alert

    if response is not None and "_key" in response.keys():
        alert_id = response["_key"]
        alert.notification.alert_id = alert_id

        # Persisting (recipient device, alert id) pairs and sending push notifications happens simultaneously via async
        LOGGER.info("persist_recipient_devices alert_id=%s" % alert_id)
        persist_recipient_devices(request_context, alert_id,
                                  registered_devices, alert_payload,
                                  async_kvstore_client)
        LOGGER.info("persist_recipient_devices ok alert_id=%s" % alert_id)
        LOGGER.info("send_push_notifications starting registered_devices=%s" %
                    len(registered_devices))
        yield send_push_notifications(request_context, alert.notification,
                                      registered_devices, async_kvstore_client,
                                      async_spacebridge_client,
                                      async_splunk_client)
Beispiel #8
0
 def async_delete(self, request):
     context = RequestContext.from_rest_request(request)
     role = request.get(QUERY, {}).get(ROLE)
     if not role:
         raise SpacebridgeApiRequestError('Delete role request must specify the URL encoded parameter "role".',
                                          status_code=http.BAD_REQUEST)
     yield self._permissions.delete_role(context, role)
     defer.returnValue({
         PAYLOAD: {ROLE: role},
         STATUS: http.OK
     })
Beispiel #9
0
    def async_get(self, request):
        context = RequestContext.from_rest_request(request)
        location_based_assets = yield wait_for_all(
            [self._get_beacons(context),
             self._get_geofences(context)],
            raise_on_first_exception=True)

        payload = []
        for grouping in location_based_assets:
            payload.extend(grouping)

        defer.returnValue({STATUS: http.OK, PAYLOAD: payload})
Beispiel #10
0
 def async_get(self, request):
     """Returns a list of asset groups. If no asset group IDs are specified, all will be returned."""
     context = RequestContext.from_rest_request(request)
     asset_group_ids = request[QUERY].get(ASSET_GROUP_IDS)
     asset_groups = yield get_asset_groups(context, asset_group_ids,
                                           self._kvstore, self._permissions)
     defer.returnValue({
         PAYLOAD: {
             ASSET_GROUPS:
             [asset_group.to_dict() for asset_group in asset_groups]
         },
         STATUS: http.OK
     })
Beispiel #11
0
 def _create_or_update_role(self, request, role_operation_fn):
     context = RequestContext.from_rest_request(request)
     payload = json.loads(request[PAYLOAD]) if PAYLOAD in request else None
     if not payload or not payload.get(ROLE):
         raise SpacebridgeApiRequestError('Request payload must contain the "role" parameter.',
                                          status_code=http.BAD_REQUEST)
     role = yield role_operation_fn(
         request_context=context,
         name=payload[ROLE],
         parent_roles=payload.get(IMPORTED_ROLES),
         general_capabilities=payload.get(GENERAL_CAPABILITIES),
         object_capabilities=[ObjectCapability.from_dict(oc) for oc in payload.get(OBJECT_CAPABILITIES, [])])
     defer.returnValue(role)
Beispiel #12
0
    async def get(self, request):
        """
        Handler which retrieves kvstore app_list data for the current user
        """
        """
        REST handler to fetch the selected app list for the current user
        """
        authtoken = request[SESSION][AUTHTOKEN]
        user = request[SESSION][USER]
        auth_header = SplunkAuthHeader(authtoken)
        request_context = RequestContext(auth_header,
                                         current_user=user,
                                         system_auth_header=auth_header)

        try:
            """
            This method will process a DashboardAppListGetRequest.  This will return the list of app_names found under the
            dashboard_app_list key in the user_meta KVStore collection.

            :param request_context:
            :param client_single_request:
            :param single_server_response:
            :param async_client_factory:
            :return:
            """
            # async clients
            async_kvstore_client = self.async_client_factory.kvstore_client()
            async_splunk_client = self.async_client_factory.splunk_client()

            # Get dashboard_meta collection if key exists
            selected_apps = await fetch_dashboard_app_list_with_default(
                request_context=request_context,
                async_kvstore_client=async_kvstore_client,
                async_splunk_client=async_splunk_client)

            app_list = await fetch_app_names(request_context,
                                             async_splunk_client)
            app_dict = get_app_dict(app_list)
            # This filters out apps that are invalid from displaying in the app selection tab
            payload = [{
                APP_NAME: app,
                DISPLAY_APP_NAME: app_dict[app]
            } for app in selected_apps if app in app_dict]

            return {
                'payload': payload,
                'status': HTTPStatus.OK,
            }
        except Exception as e:
            return {'error': e.message}
Beispiel #13
0
def cleanup_deleted_roles(splunk, kvstore, permissions, auth_header):
    context = RequestContext(auth_header)
    current_role_names = yield _get_all_role_names(splunk, auth_header)
    roles_that_no_longer_exist = yield _get_roles_in_capabilities_table_other_than(
        auth_header, kvstore, current_role_names)
    if roles_that_no_longer_exist:
        yield permissions.delete_roles_from_capabilities_table(
            context, roles_that_no_longer_exist)
        LOGGER.debug('Removed stale data for roles=%s',
                     roles_that_no_longer_exist)
    else:
        LOGGER.debug(
            "No roles have been deleted since last iteration roles=%s.",
            current_role_names)
Beispiel #14
0
    def get(self, request):
        """
        Handler which passes off requests to the relevant GET handler
        """

        request_type = request[QUERY].get(REQUEST_TYPE)

        if request_type not in VALID_GET_REQUESTS:
            return {
                'payload': 'No request_type supplied',
                'status': http.BAD_REQUEST,
            }

        authtoken = request[SESSION][AUTHTOKEN]
        user = request[SESSION][USER]
        auth_header = SplunkAuthHeader(authtoken)

        request_context = RequestContext(auth_header,
                                         current_user=user,
                                         system_auth_header=auth_header)

        if request_type == DASHBOARD_LIST_REQUEST:
            response = dashboard_list_request(request,
                                              self.async_client_factory,
                                              request_context)

        elif request_type == APP_LIST_REQUEST:
            response = get_app_list_request(request, self.async_client_factory,
                                            request_context)

        elif request_type == ALL_APPS_REQUEST:
            response = get_all_apps_request(request, self.async_client_factory,
                                            request_context)

        elif request_type == TV_LIST_REQUEST:
            response = get_tv_request(request, self.async_kvstore_client,
                                      request_context)

        elif request_type == TV_BOOKMARK_REQUEST:
            response = get_tv_bookmark_request(request,
                                               self.async_kvstore_client,
                                               request_context)

        status = http.OK
        if 'error' in response:
            status = http.INTERNAL_SERVER_ERROR
        return {
            'payload': response,
            'status': status,
        }
Beispiel #15
0
 def async_delete(self, request):
     """Deletes a workspace by ID."""
     context = RequestContext.from_rest_request(request)
     workspace_id = extract_parameter(request[QUERY], WORKSPACE_ID_LABEL,
                                      QUERY)
     deleted_ids = yield ar_workspace_request_processor.delete_ar_workspace(
         context, self.async_client_factory.kvstore_client(),
         self.async_client_factory.ar_permissions_client(), [workspace_id])
     defer.returnValue({
         PAYLOAD: {
             WORKSPACE_ID_LABEL: deleted_ids
         },
         STATUS: http.OK,
     })
 def async_get(self, request):
     context = RequestContext.from_rest_request(request)
     try:
         phantom_metadata = yield get_phantom_metadata(
             context, self._kvstore)
         defer.returnValue({
             STATUS: http.OK,
             PAYLOAD: phantom_metadata.to_dict()
         })
     except NoRegisteredPhantomInstanceException:
         defer.returnValue({
             STATUS: http.NO_CONTENT,
             PAYLOAD:
             'No existing configuration'  # The payload attribute is required for all responses.
         })
Beispiel #17
0
 def async_get(self, request):
     """Looks up assets from KV store."""
     context = RequestContext.from_rest_request(request)
     asset_ids = request[QUERY].get(ASSET_IDS)
     assets = yield get_assets(context,
                               asset_ids,
                               self._kvstore,
                               self._permissions,
                               ungroup_assets=False)
     defer.returnValue({
         PAYLOAD: {
             ASSETS: [asset.to_dict() for asset in assets]
         },
         STATUS: http.OK
     })
Beispiel #18
0
    def async_get(self, request):
        """Returns workspaces from KV store.

        If no workspace IDs are provided in the request, data for all workspaces is returned.
        """
        context = RequestContext.from_rest_request(request)
        workspace_ids = request[QUERY].get(WORKSPACE_ID_LABEL) or []
        if not isinstance(workspace_ids, list):
            workspace_ids = [workspace_ids]
        workspaces = yield ar_workspace_request_processor.get_ar_workspaces(
            context,
            self.async_kvstore_client,
            self.async_ar_permissions_client,
            workspace_ids,
            json_format=True)
        defer.returnValue({
            PAYLOAD: {
                'ar_workspaces': workspaces
            },
            STATUS: http.OK
        })
    async def get(self, request):
        """
        Lists one or more dashboards in the form of JSON formatted DashboardDescription messages.

        Request Parameters
            offset         the number of dashboards to skip
            max_results    the maximum number of dashboards to return
            dashboard_ids  one or more dashboard IDs to query
            minimal_list   0 for verbose descriptions and 1 for minimal descriptions
        """
        context = RequestContext.from_rest_request(request)
        response = await dashboard_list_request(request,
                                                self.async_client_factory,
                                                context)
        return {
            PAYLOAD:
            response,
            STATUS:
            HTTPStatus.INTERNAL_SERVER_ERROR
            if 'error' in response else HTTPStatus.OK,
        }
Beispiel #20
0
 def __init__(self,
              input_config,
              encryption_context,
              session_key,
              async_splunk_client,
              parent_process_monitor=None,
              cluster_monitor=None,
              async_client_factory=None,
              async_kvstore_client=None,
              async_spacebridge_client=AsyncSpacebridgeClient()):
     """
     Subscription Manager constructor
     :param input_config:
     :param encryption_context:
     :param session_key:
     :param async_kvstore_client:
     :param async_splunk_client:
     :param async_spacebridge_client:
     """
     self.input_config = input_config
     self.encryption_context = encryption_context
     self.session_key = session_key
     self.parent_process_monitor = parent_process_monitor
     self.cluster_monitor = cluster_monitor
     self.async_splunk_client = async_splunk_client
     self.async_spacebridge_client = async_spacebridge_client
     self.system_auth_header = SplunkAuthHeader(self.session_key)
     if not async_client_factory:
         uri = get_uri(self.session_key)
         async_client_factory = AsyncClientFactory(uri)
     self.async_client_factory = async_client_factory
     if not async_kvstore_client:
         async_kvstore_client = self.async_client_factory.kvstore_client()
     self.async_kvstore_client = async_kvstore_client
     self.request_context = RequestContext(
         auth_header=self.system_auth_header,
         current_user=constants.ADMIN,
         system_auth_header=self.system_auth_header)
Beispiel #21
0
    def delete(self, request):
        """
        Handler which passes off requests to the relevant DELETE handler
        """
        authtoken = request[SESSION][AUTHTOKEN]
        request_type = request[QUERY].get(REQUEST_TYPE)
        user = request[SESSION][USER]
        auth_header = SplunkAuthHeader(authtoken)
        system_auth_header = auth_header
        request_context = RequestContext(auth_header,
                                         current_user=user,
                                         system_auth_header=system_auth_header)

        if request_type not in VALID_DELETE_REQUESTS:
            return {
                'payload':
                'Invalid request_type supplied or request_type not present',
                'status': http.BAD_REQUEST,
            }

        if request_type == TV_BOOKMARK_REQUEST:
            response = delete_tv_bookmark_request(request,
                                                  self.async_kvstore_client,
                                                  request_context)

        elif request_type == TV_CONFIG_REQUEST:
            response = delete_tv_config_request(request,
                                                self.async_client_factory,
                                                request_context)

        status = http.OK
        if 'error' in response:
            status = http.INTERNAL_SERVER_ERROR

        return {
            'payload': response,
            'status': status,
        }
def build_tv_subscription_updates(tv_subscriptions, request_context,
                                  async_kvstore_client):
    tv_subscription_user_device_map = {}
    for subscription in tv_subscriptions:
        # Don't rely on subscription user to be lowercase
        subscription_user = subscription.user.lower()
        if subscription_user not in tv_subscription_user_device_map:
            tv_subscription_user_device_map[subscription_user] = {}
        tv_subscription_user_device_map[subscription_user][
            subscription.device_id] = subscription

    LOGGER.debug('tv subscription_user_device_map=%s',
                 tv_subscription_user_device_map)

    tv_user_subscription_map = {
        user: [v for _, v in subscription.items()]
        for user, subscription in tv_subscription_user_device_map.items()
    }

    request_context = RequestContext(
        auth_header=request_context.system_auth_header,
        current_user=constants.ADMIN,
        system_auth_header=request_context.system_auth_header)

    tv_config_json_list = yield fetch_active_tv_configs(
        tv_user_subscription_map, async_kvstore_client, request_context)
    LOGGER.debug('tv_config_json_list=%s', tv_config_json_list)
    subscription_update_tuples = []
    for tv_config_json in tv_config_json_list:
        tv_config = TVConfig(**tv_config_json)
        tv_config_user = tv_config._user.lower()
        subscription = tv_subscription_user_device_map[tv_config_user][
            tv_config.device_id]
        subscription_update = DroneModeTVEvent(
            data_object=tv_config, event_type=TVEventType.TV_CONFIG)
        subscription_update_tuples.append((subscription, subscription_update))
    defer.returnValue(subscription_update_tuples)
    def async_post(self, request):
        context = RequestContext.from_rest_request(request)
        if PAYLOAD not in request:
            raise SpacebridgeApiRequestError(
                'Request must contain a JSON payload containing the field "hostname" set '
                'to the Phantom hostname to use.',
                status_code=http.BAD_REQUEST)
        request_payload = json.loads(request[PAYLOAD])
        if 'username' not in request_payload or 'password' not in request_payload:
            raise SpacebridgeApiRequestError(
                'Request must include a Phantom username and password.',
                status_code=http.BAD_REQUEST)
        phantom_metadata = PhantomMetadata.from_json(request_payload)
        username, password = request_payload['username'], request_payload[
            'password']

        if request_payload.get('health_check_only', False):
            response = yield self._health_check(phantom_metadata, username,
                                                password)
        else:
            response = yield self._set_phantom_metadata(
                context, phantom_metadata, username, password)

        defer.returnValue(response)
Beispiel #24
0
    async def post(self, request):
        """
        Handler which creates a new app_list data entry in kvstore for the
        current user
        """
        authtoken = request[SESSION][AUTHTOKEN]
        user = request[SESSION][USER]
        auth_header = SplunkAuthHeader(authtoken)
        request_context = RequestContext(auth_header,
                                         current_user=user,
                                         system_auth_header=auth_header)

        async_splunk_client = self.async_client_factory.splunk_client()
        async_kvstore_client = self.async_client_factory.kvstore_client()

        total_app_list = await fetch_app_names(request_context,
                                               async_splunk_client)
        total_app_name_list = [app.app_name for app in total_app_list]

        selected_app_names = validate_write_request(request, total_app_list)
        # validate all app names
        for app_name in selected_app_names:
            if app_name not in total_app_name_list:
                error_message = f"The appName={app_name} is invalid.  Unable to set appName list."
                return {'error': error_message}

        # Store names in kvstore
        dashboard_app_list = await set_dashboard_app_list(
            request_context=request_context,
            app_names=selected_app_names,
            async_kvstore_client=async_kvstore_client,
            async_splunk_client=async_splunk_client)
        return {
            'payload': dashboard_app_list.app_names,
            'status': HTTPStatus.OK,
        }
async def process_message(message_sender, client_application_message, server_application_message, async_client_factory,
                          encryption_context, server_response_id, system_auth_header, shard_id):
    device_id = py23.b64encode_to_str(message_sender)

    encryption_context = encryption_context

    if client_application_message.HasField(CLIENT_SINGLE_REQUEST):
        request_object = client_application_message.clientSingleRequest
        response_object = server_application_message.serverSingleResponse
        response_object.replyToMessageId = request_object.requestId
        response_object.serverVersion = str(app_version())

        processor = process_request
        request_type = CLIENT_SINGLE_REQUEST

    elif client_application_message.HasField(CLIENT_SUBSCRIPTION_MESSAGE):
        request_object = client_application_message.clientSubscriptionMessage

        # pings have a special case response, really they could be in their own modular input
        if client_application_message.clientSubscriptionMessage.HasField(RequestType.CLIENT_SUBSCRIPTION_PING.value):
            response_object = server_application_message.serverSubscriptionPing
        else:
            response_object = server_application_message.serverSubscriptionResponse
            response_object.replyToMessageId = request_object.requestId
            response_object.serverVersion = str(app_version())

        processor = process_subscription
        request_type = CLIENT_SUBSCRIPTION_MESSAGE

    else:
        LOGGER.warn("No suitable message type found client application request")
        return "device_id={}".format(device_id)

    response_object.requestId = server_response_id

    if request_object.HasField("runAs"):
        LOGGER.debug("run as credentials is present")
        auth_header = await parse_run_as_credentials(encryption_context, system_auth_header,
                                                     async_client_factory, request_object.runAs)
    else:
        auth_header = parse_session_token(encryption_context, request_object.sessionToken)

    request_context = RequestContext(auth_header, device_id=device_id,
                                     raw_device_id=message_sender, request_id=request_object.requestId,
                                     current_user=auth_header.username, system_auth_header=system_auth_header,
                                     client_version=request_object.clientVersion, user_agent=request_object.userAgent,
                                     shard_id=shard_id)

    try:
        validate_client_version(request_object, response_object)
        await auth_header.validate(async_client_factory.splunk_client(), LOGGER, request_context)

        # Create a new ContextVar Context for this request to encapsulate the request_context to this specific request.
        # This is necessary otherwise the request_context variable will be set in the global namespace and different
        # might access each other's request context.
        request_ctx_var = contextvars.ContextVar(REQUEST_CONTEXT)
        request_ctx_var.set(request_context)
        ctx = contextvars.copy_context()

        should_send_response = await ctx.run(processor, request_context, encryption_context, request_object,
                                             response_object, async_client_factory)

        # If we aren't sending a response in this request clear the server_application_message
        if not should_send_response:
            server_application_message.Clear()

    except OperationHaltedError:
        server_application_message.ClearField('app_message')
    except SpacebridgeCompanionAppError as e:
        LOGGER.warn("CompanionAppError error=%s", e.message)
        e.set_proto(response_object)
    except SpacebridgeError as e:
        LOGGER.exception("SpacebridgeError during process_message")
        e.set_proto(response_object)
    except Exception as e:
        LOGGER.exception("Unhandled exception during process_message")
        response_object.error.code = common_pb2.Error.ERROR_UNKNOWN
        response_object.error.message = str(e)
    LOGGER.info('Finished processing message. {}'.format(request_context))
    return request_context
Beispiel #26
0
    def post(self, request):
        """
        Handler which passes off requests to the relevant POST handler
        """
        authtoken = request[SESSION][AUTHTOKEN]
        request_type = request[QUERY].get(REQUEST_TYPE)
        user = request[SESSION][USER]
        auth_header = SplunkAuthHeader(authtoken)
        system_auth_header = auth_header
        request_context = RequestContext(auth_header,
                                         current_user=user,
                                         system_auth_header=system_auth_header)

        if request_type not in VALID_POST_REQUESTS:
            return {
                'payload': 'No request_type supplied',
                'status': http.BAD_REQUEST,
            }

        if request_type == APP_LIST_REQUEST:
            response = post_app_list_request(request,
                                             self.async_client_factory,
                                             request_context)

        elif request_type == TV_CONFIG_REQUEST:
            response = post_tv_config_request(request,
                                              self.async_client_factory,
                                              request_context)

        elif request_type == TV_CONFIG_BULK_REQUEST:
            response = post_tv_config_bulk_request(request,
                                                   self.async_client_factory,
                                                   request_context)

        elif request_type == TV_BOOKMARK_REQUEST:
            response = post_tv_bookmark_request(request,
                                                self.async_kvstore_client,
                                                request_context)

        elif request_type == ACTIVATE_TV_BOOKMARK_REQUEST:
            response = activate_tv_bookmark_request(request,
                                                    self.async_client_factory,
                                                    request_context)

        elif request_type == DRONE_MODE_IPAD_SUBSCRIBE_REQUEST:
            response = drone_mode_ipad_subscribe_request(
                request, self.async_client_factory, request_context)

        elif request_type == DRONE_MODE_TV_SUBSCRIBE_REQUEST:
            response = drone_mode_tv_subscribe_request(
                request, self.async_client_factory, request_context)

        elif request_type == MPC_BROADCAST_REQUEST:
            response = mpc_broadcast_request(request,
                                             self.async_client_factory,
                                             request_context)

        elif request_type == TV_INTERACTION_REQUEST:
            response = tv_interaction_request(request,
                                              self.async_client_factory,
                                              request_context)

        elif request_type == TV_CAPTAIN_URL_REQUEST:
            response = tv_captain_url_request(request,
                                              self.async_client_factory,
                                              request_context)

        elif request_type == SUBSCRIPTION_PING:
            response = subscription_ping(request, self.async_client_factory,
                                         request_context)

        status = http.OK
        if 'error' in response:
            status = http.INTERNAL_SERVER_ERROR

        return {
            'payload': response,
            'status': status,
        }
Beispiel #27
0
    async def get_environment_meta(self, device_info, username):
        """
        Fetch environment metadata
        return (EnvironmentMetadata)
        """
        try:
            version_get_response = VersionGetResponse()

            # SSG version and min client version
            version_get_response.cloudgatewayAppVersion = str(app_version())
            version_get_response.minimumClientVersion = str(
                minimum_build(device_info.app_id))

            # Companion Apps
            companion_app_list = await fetch_registered_apps(
                self.system_auth_header, self.async_splunk_client)
            for key, app in companion_app_list.items():
                companion = version_get_response.companionApps.add()
                companion.appId = key
                companion.appVersion = app[constants.VERSION]

            # Splunk Version
            splunk_version = await self.async_telemetry_client.get_splunk_version(
                self.system_auth_header)
            version_get_response.splunkVersion = splunk_version

            # Telemetry Instance Id
            telemetry_instance_id = await self.async_telemetry_client.get_telemetry_instance_id(
                self.system_auth_header)
            version_get_response.instanceId = telemetry_instance_id

            # Installation Environment
            installation_environment = await self.async_telemetry_client.get_installation_environment(
                self.system_auth_header)

            installation_environment_proto = VersionGetResponse.CLOUD \
                if installation_environment is InstallationEnvironment.CLOUD \
                else VersionGetResponse.ENTERPRISE
            version_get_response.installationEnvironment = installation_environment_proto

            # Deployment friendly name
            deployment_friendly_name = await _get_deployment_friendly_name(
                self.system_auth_header, self.async_kvstore_client,
                RequestContext(self.system_auth_header))
            version_get_response.deploymentFriendlyName = deployment_friendly_name

            # device name
            version_get_response.deviceName = self.build_device_name(
                device_info, username)

            return EnvironmentMetadata(
                version_get_response.SerializeToString(),
                "{}.{}".format(constants.SPLAPP_APP_ID,
                               constants.VERSION_GET_RESPONSE))

        except Exception as e:
            LOGGER.exception("Exception fetching environment data")
            return EnvironmentMetadata(
                VersionGetResponse().SerializeToString(),
                "{}.{}".format(constants.SPLAPP_APP_ID,
                               constants.VERSION_GET_RESPONSE))
 def async_delete(self, request):
     context = RequestContext.from_rest_request(request)
     yield delete_phantom_metadata(context, self._kvstore)
     defer.returnValue({STATUS: http.OK, PAYLOAD: {'success': True}})
def process_message(message_sender, client_application_message, server_application_message, async_client_factory,
                    encryption_context, server_response_id, system_auth_header, shard_id):
    device_id = py23.b64encode_to_str(message_sender)

    encryption_context = encryption_context

    if client_application_message.HasField(CLIENT_SINGLE_REQUEST):
        request_object = client_application_message.clientSingleRequest
        response_object = server_application_message.serverSingleResponse
        response_object.replyToMessageId = request_object.requestId
        response_object.serverVersion = str(app_version())

        processor = process_request
        request_type = CLIENT_SINGLE_REQUEST

    elif client_application_message.HasField(CLIENT_SUBSCRIPTION_MESSAGE):
        request_object = client_application_message.clientSubscriptionMessage

        # pings have a special case response, really they could be in their own modular input
        if client_application_message.clientSubscriptionMessage.HasField(RequestType.CLIENT_SUBSCRIPTION_PING.value):
            response_object = server_application_message.serverSubscriptionPing
        else:
            response_object = server_application_message.serverSubscriptionResponse
            response_object.replyToMessageId = request_object.requestId
            response_object.serverVersion = str(app_version())

        processor = process_subscription
        request_type = CLIENT_SUBSCRIPTION_MESSAGE

    else:
        LOGGER.warn("No suitable message type found client application request")
        defer.returnValue("device_id={}".format(device_id))

    response_object.requestId = server_response_id

    if request_object.HasField("runAs"):
        LOGGER.debug("run as credentials is present")
        auth_header = yield parse_run_as_credentials(encryption_context, system_auth_header,
                                                     async_client_factory, request_object.runAs)
    else:
        auth_header = parse_session_token(encryption_context, request_object.sessionToken)

    request_context = RequestContext(auth_header, device_id=device_id,
                                     raw_device_id=message_sender, request_id=request_object.requestId,
                                     current_user=auth_header.username, system_auth_header=system_auth_header,
                                     client_version=request_object.clientVersion, user_agent=request_object.userAgent,
                                     shard_id=shard_id)

    try:
        validate_client_version(request_object, response_object)
        yield auth_header.validate(async_client_factory.splunk_client(), LOGGER, request_context)
        should_send_response = yield context.call({'request_context': request_context}, processor, request_context,
                                                  encryption_context, request_object, response_object,
                                                  async_client_factory)

        # If we aren't sending a response in this request clear the server_application_message
        if not should_send_response:
            server_application_message.Clear()

    except OperationHaltedError:
        server_application_message.ClearField('app_message')
    except SpacebridgeError as e:
        LOGGER.exception("SpacebridgeError during process_message")
        e.set_proto(response_object)

    except Exception as e:
        LOGGER.exception("Unhandled exception during process_message")
        response_object.error.code = common_pb2.Error.ERROR_UNKNOWN
        response_object.error.message = str(e)

    LOGGER.info('Finished processing message. {}'.format(request_context))
    defer.returnValue(request_context)
Beispiel #30
0
    def async_post(self, request):
        """Handles creating and modifying assets.

        The payload parameters used by this handler include:
            - action: determines what to do with the given assets. Either update, create, or delete.
            - assets: a list of JSON document assets to update or create.
            - asset_objects: a list of objects to associate with the given asset or asset group.
            - asset_group_id: the ID of an existing asset group to associate with the above assets.
            - asset_group_name: the name of a new asset group to create and associate with the above assets.
            - new_workspace_title: the name of a new workspace to create and associate with the given assets or asset
                                   group.
            - overwrite: bool indicating whether to overwrite existing asset data.
        """
        context = RequestContext.from_rest_request(request)
        params = json.loads(request[PAYLOAD])
        if ACTION not in params or params[ACTION] not in {
                CREATE, UPDATE, DELETE
        }:
            raise SpacebridgeApiRequestError(message='Must provide an action',
                                             status_code=http.BAD_REQUEST)

        if params.get(ACTION) == DELETE:
            deleted_ids = yield delete_assets(context, params.get(ASSET_IDS),
                                              self._kvstore, self._permissions)
            defer.returnValue({
                PAYLOAD: {
                    ASSET_IDS: deleted_ids
                },
                STATUS: http.OK
            })

        yield self._check_user_can_do_update(context, params)

        # Create a workspace if the user specifies a new workspace title
        dashboard_id, workspace_id = _get_asset_object_ids(params)
        if params.get(NEW_WORKSPACE_TITLE):
            workspace_id = yield create_ar_workspace(
                context,
                self._splunk,
                self._kvstore,
                self._permissions,
                ARWorkspaceData(title=params.get(NEW_WORKSPACE_TITLE)),
                dashboard_id=dashboard_id)
            LOGGER.debug('Created workspace_id=%s', workspace_id)

        # Create an asset group if the user specifies a new group name
        asset_group_id = params.get(ASSET_GROUP_ID)
        if params.get(ASSET_GROUP_NAME):
            asset_group = AssetGroup(name=params.get(ASSET_GROUP_NAME),
                                     key=asset_group_id,
                                     dashboard_id=dashboard_id,
                                     workspace_id=workspace_id)
            try:
                asset_group_id = yield create_asset_group(
                    context, asset_group, self._kvstore, self._permissions)
            except AssetGroupAlreadyExists as e:
                LOGGER.debug(e)

        assets = [
            AssetData(asset_name=asset.get(ASSET_NAME),
                      asset_id=asset.get(ASSET_ID),
                      asset_group=asset_group_id,
                      is_splunk_generated=asset.get(ASSET_TYPE)
                      or ASSET_ID in asset,
                      dashboard_description_pb=DashboardDescription(
                          dashboardId=dashboard_id),
                      ar_workspace_data_pb=ARWorkspaceData(
                          arWorkspaceId=workspace_id))
            for asset in params.get(ASSETS, [])
        ]
        affected_ids = yield self._kvstore.async_batch_save_request(
            auth_header=context.auth_header,
            collection=ASSETS_COLLECTION_NAME,
            entries=[asset.to_dict(jsonify_objects=True) for asset in assets])
        if not asset_group_id:
            yield self._permissions.register_objects(
                context,
                ARObjectType.ASSET,
                affected_ids,
                check_if_objects_exist=True)

        # Return the IDs that were affected by this request
        defer.returnValue({
            PAYLOAD: {
                'asset_ids': affected_ids or [],
                'workspace_id': workspace_id,
                'asset_group_id': asset_group_id,
            },
            STATUS:
            http.OK if params[ACTION] == UPDATE else http.CREATED
        })