コード例 #1
0
ファイル: resource.py プロジェクト: marlemion/grapi
 def json(self, req, obj, fields, all_fields, multi=False, expand=None):
     data = self.get_fields(req, obj, fields, all_fields)
     if not multi:
         data['@odata.context'] = req.path
     if expand:
         data.update(expand)
     return _dumpb_json(data)
コード例 #2
0
ファイル: resource.py プロジェクト: marlemion/grapi
 def json_multi(self, req, obj, fields, all_fields, top, skip, count, deltalink, add_count=False):
     header = b'{\n'
     header += b'  "@odata.context": "%s",\n' % req.path.encode('utf-8')
     if add_count:
         header += b'  "@odata.count": "%d",\n' % count
     if deltalink:
         header += b'  "@odata.deltaLink": "%s",\n' % deltalink
     else:
         path = req.path
         if req.query_string:
             args = self.parse_qs(req)
             if '$skip' in args:
                 del args['$skip']
         else:
             args = {}
         args['$skip'] = skip+top
         nextLink = path + '?' + _encode_qs(list(args.items()))
         header += b'  "@odata.nextLink": "%s",\n' % (_dumpb_json(nextLink)[1:-1])
     header += b'  "value": [\n'
     yield header
     first = True
     try:
         for o in obj:
             if isinstance(o, tuple):
                 o, resource = o
                 all_fields = resource.fields
             if not first:
                 yield b',\n'
             first = False
             wa = self.json(req, o, fields, all_fields, multi=True)
             yield b'\n'.join([b'    '+line for line in wa.splitlines()])
     except Exception:
         logging.exception("failed to marshal %s JSON response", req.path)
     yield b'\n  ]\n}'
コード例 #3
0
ファイル: subscription.py プロジェクト: bplus/grapi
    def on_get(self, req, resp, subscriptionid=None):
        record = _record(req, self.options)

        if subscriptionid:
            try:
                subscription, sink, userid = record.subscriptions[
                    subscriptionid]
            except KeyError:
                resp.status = falcon.HTTP_404
                return
            data = _export_subscription(subscription)
        else:
            user = record.user
            userid = user.userid
            data = {
                '@odata.context':
                req.path,
                'value': [
                    _export_subscription(subscription)
                    for (subscription, _,
                         uid) in record.subscriptions.values() if uid == userid
                ],  # TODO doesn't scale
            }

        resp.content_type = "application/json"
        resp.body = _dumpb_json(data)
コード例 #4
0
ファイル: subscription.py プロジェクト: unikiteam/grapi
    def on_patch(self, req, resp, subscriptionid=None):
        if not subscriptionid:
            raise utils.HTTPBadRequest('missing required subscriptionid')

        record = _record(req, self.options)

        try:
            subscription, sink, userid = record.subscriptions[subscriptionid]
        except KeyError:
            resp.status = falcon.HTTP_404
            return

        fields = self.load_json(req)
        self.validate_json(update_subscription_schema, fields)

        for k, v in fields.items():
            if v and k == 'expirationDateTime':
                # NOTE(longsleep): Setting a dict key which is already there is threadsafe in current CPython implementations.
                try:
                    dateutil.parser.parse(v)
                    subscription['expirationDateTime'] = v
                except ValueError:
                    raise utils.HTTPBadRequest('expirationDateTime is not a valid datetime string')

        if sink.expired:
            sink.expired = False
            logging.debug('subscription updated before it expired, id:%s', subscriptionid)

        data = _export_subscription(subscription)

        resp.content_type = "application/json"
        resp.body = _dumpb_json(data)
コード例 #5
0
    def on_patch_subscriptions_by_id(self, req, resp, subscriptionid):
        """Handle PATCH request.

        Args:
            req (Request): Falcon request object.
            resp (Response): Falcon response object.
            subscriptionid (str): subscription ID.
        """
        record = _record(req, self.options)

        try:
            subscription, sink, _ = record.subscriptions[subscriptionid]
        except KeyError:
            resp.status = falcon.HTTP_404
            return

        json_data = req.context.json_data

        for k, v in json_data.items():
            if v and k == 'expirationDateTime':
                # NOTE(longsleep): Setting a dict key which is already there is threadsafe in current CPython implementations.
                try:
                    dateutil.parser.parse(v)
                    subscription['expirationDateTime'] = v
                except ValueError:
                    raise utils.HTTPBadRequest('expirationDateTime is not a valid datetime string')

        if sink.expired:
            sink.expired = False
            logging.debug('subscription updated before it expired, id:%s', subscriptionid)

        data = _export_subscription(subscription)
        resp.body = _dumpb_json(data)
        resp.status = falcon.HTTP_200
コード例 #6
0
ファイル: subscription.py プロジェクト: filipnavara/grapi
    def on_get_subscriptions_by_id(self, req, resp, subscriptionid):
        """Handle GET request - return by subscription ID.

        Args:
            req (Request): Falcon request object.
            resp (Response): Falcon response object.
            subscriptionid (str): subscription ID.
        """
        record = _record(req, self.options)
        try:
            subscription = record.subscriptions[subscriptionid][0]
        except KeyError:
            raise utils.HTTPNotFound()
        data = _export_subscription(subscription)
        resp.body = _dumpb_json(data)
        resp.status = falcon.HTTP_200
コード例 #7
0
    def on_get(self, req, resp):
        """Handle GET request - return all subscriptions.

        Args:
            req (Request): Falcon request object.
            resp (Response): Falcon response object.
        """
        record = _record(req, self.options)
        userid = record.user.userid
        data = {
            '@odata.context': req.path,
            'value': [
                _export_subscription(subscription)
                for subscription, _, uid in record.subscriptions.values()
                if uid == userid
            ],  # TODO doesn't scale
        }

        resp.body = _dumpb_json(data)
        resp.status = falcon.HTTP_200
コード例 #8
0
    def on_post(self, req, resp):
        """Handle POST request.

        Args:
            req (Request): Falcon request object.
            resp (Response): Falcon response object.
        """
        subscription_id = req.context.request_id
        verify = not self.options or not self.options.insecure
        json_data = req.context.json_data

        max_retry = config.SUBSCRIPTION_INTERNAL_RETRY
        retry = 0
        while retry != max_retry:
            retry += 1
            try:
                self._add_subscription(
                    req, subscription_id, self.options, verify, json_data
                )
                break
            except MAPIErrorNoSupport:
                logging.exception(
                    "subscription not possible right now, trying %d/%d",
                    retry, max_retry
                )
                # A short nap for the resetting connection.
                time.sleep(0.5)
        else:
            raise falcon.HTTPInternalServerError(
                description="subscription is not possible, please retry"
            )

        # Prepare response.
        resp.status = falcon.HTTP_201
        resp.content_type = "application/json"
        resp.body = _dumpb_json(_export_subscription(json_data))

        if self.options and self.options.with_metrics:
            SUBSCR_COUNT.inc()
            SUBSCR_ACTIVE.inc()
コード例 #9
0
ファイル: subscription.py プロジェクト: unikiteam/grapi
    def on_post(self, req, resp):
        record = _record(req, self.options)
        server = record.server
        user = record.user
        store = record.store
        fields = self.load_json(req)
        self.validate_json(subscription_schema, fields)

        id_ = str(uuid.uuid4())

        verify = not self.options or not self.options.insecure

        # Validate URL.
        try:
            # Enforce URL to valid and to be public, unless running insecure.
            if not validators.url(fields['notificationUrl'], public=True):
                if verify:
                    raise ValueError('url validator failed')
                else:
                    logging.warning('ignored notification url validation error (insecure enabled), auth_user:%s, id:%s, url:%s', server.auth_user, id_, fields['notificationUrl'])
            notificationUrl = urlparse(fields['notificationUrl'])
            if notificationUrl.scheme != 'https':
                if not verify and notificationUrl.scheme == 'http':
                    logging.warning('allowing unencrypted notification url (insecure enabled), auth_user:%s, id:%s, url:%s', server.auth_user, id_, fields['notificationUrl'])
                else:
                    raise ValueError('must use https scheme')
        except Exception:
            logging.debug('invalid subscription notification url, auth_user:%s, id:%s, url:%s', server.auth_user, id_, fields['notificationUrl'], exc_info=True)
            raise utils.HTTPBadRequest("Subscription notification url invalid.")

        # Validate webhook.
        validationToken = str(uuid.uuid4())
        try:  # TODO async
            logging.debug('validating subscription notification url, auth_user:%s, id:%s, url:%s', server.auth_user, id_, fields['notificationUrl'])
            r = REQUEST_SESSION.post(fields['notificationUrl']+'?validationToken='+validationToken, timeout=10, verify=verify)  # TODO(longsleep): Add timeout configuration.
            if r.text != validationToken:
                logging.debug('subscription validation failed, validation token mismatch, id:%s, url:%s', id_, fields['notificationUrl'])
                raise utils.HTTPBadRequest("Subscription validation request failed.")
        except Exception:
            logging.exception('subscription validation request error, id:%s, url:%s', id_, fields['notificationUrl'])
            raise utils.HTTPBadRequest("Subscription validation request failed.")

        # Validate subscription data.
        subscription_object = _subscription_object(store, fields['resource'])
        if not subscription_object:
            logging.error('subscription object is invalid, id:%s, resource:%s', id_, fields['resource'])
            raise utils.HTTPBadRequest("Subscription object invalid.")
        target, folder_types, data_type = subscription_object

        # Create subscription.
        subscription = fields
        subscription['id'] = id_
        subscription['_datatype'] = data_type

        sink = SubscriptionSink(store, self.options, subscription)
        object_types = ['item']  # TODO folders not supported by graph atm?
        event_types = [x.strip() for x in subscription['changeType'].split(',')]

        try:
            target.subscribe(sink, object_types=object_types,
                             event_types=event_types, folder_types=folder_types)
        except MAPIErrorNoSupport:
            # Mhm connection is borked.
            # TODO(longsleep): Clean up and start from new.
            # TODO(longsleep): Add internal retry, do not throw exception to client.
            logging.exception('subscription not possible right now, resetting connection')
            raise falcon.HTTPInternalServerError(description='subscription not possible, please retry')

        record.subscriptions[id_] = (subscription, sink, user.userid)
        logging.debug(
            'subscription created, auth_user:%s, id:%s, target:%s, object_types:%s, event_types:%s, folder_types:%s',
            server.auth_user, id_, target, object_types, event_types, folder_types
        )

        resp.content_type = "application/json"
        resp.body = _dumpb_json(_export_subscription(subscription))
        resp.status = falcon.HTTP_201

        if self.options and self.options.with_metrics:
            SUBSCR_COUNT.inc()
            SUBSCR_ACTIVE.inc()