예제 #1
0
    def __init__(self, name, sygnal, config):
        super(WebpushPushkin, self).__init__(name, sygnal, config)

        nonunderstood = self.cfg.keys() - self.UNDERSTOOD_CONFIG_FIELDS
        if nonunderstood:
            logger.warning(
                "The following configuration fields are not understood: %s",
                nonunderstood,
            )

        self.http_pool = HTTPConnectionPool(reactor=sygnal.reactor)
        self.max_connections = self.get_config("max_connections",
                                               DEFAULT_MAX_CONNECTIONS)
        self.connection_semaphore = DeferredSemaphore(self.max_connections)
        self.http_pool.maxPersistentPerHost = self.max_connections

        tls_client_options_factory = ClientTLSOptionsFactory()

        # use the Sygnal global proxy configuration
        proxy_url = sygnal.config.get("proxy")

        self.http_agent = ProxyAgent(
            reactor=sygnal.reactor,
            pool=self.http_pool,
            contextFactory=tls_client_options_factory,
            proxy_url_str=proxy_url,
        )
        self.http_agent_wrapper = HttpAgentWrapper(self.http_agent)

        self.allowed_endpoints = None  # type: Optional[List[Pattern]]
        allowed_endpoints = self.get_config("allowed_endpoints")
        if allowed_endpoints:
            if not isinstance(allowed_endpoints, list):
                raise PushkinSetupException(
                    "'allowed_endpoints' should be a list or not set")
            self.allowed_endpoints = list(map(glob_to_regex,
                                              allowed_endpoints))
        privkey_filename = self.get_config("vapid_private_key")
        if not privkey_filename:
            raise PushkinSetupException(
                "'vapid_private_key' not set in config")
        if not os.path.exists(privkey_filename):
            raise PushkinSetupException(
                "path in 'vapid_private_key' does not exist")
        try:
            self.vapid_private_key = Vapid.from_file(
                private_key_file=privkey_filename)
        except VapidException as e:
            raise PushkinSetupException(
                "invalid 'vapid_private_key' file") from e
        self.vapid_contact_email = self.get_config("vapid_contact_email")
        if not self.vapid_contact_email:
            raise PushkinSetupException(
                "'vapid_contact_email' not set in config")
        self.ttl = self.get_config("ttl", DEFAULT_TTL)
        if not isinstance(self.ttl, int):
            raise PushkinSetupException("'ttl' must be an int if set")
예제 #2
0
def webpush(subscription_info,
            data=None,
            vapid_private_key=None,
            vapid_claims=None,
            content_encoding="aes128gcm",
            curl=False,
            timeout=None,
            ttl=0,
            verbose=False,
            headers=None):
    """
        One call solution to endcode and send `data` to the endpoint
        contained in `subscription_info` using optional VAPID auth headers.

        in example:

        .. code-block:: python

        from pywebpush import python

        webpush(
            subscription_info={
                "endpoint": "https://push.example.com/v1/abcd",
                "keys": {"p256dh": "0123abcd...",
                         "auth": "001122..."}
                 },
            data="Mary had a little lamb, with a nice mint jelly",
            vapid_private_key="path/to/key.pem",
            vapid_claims={"sub": "*****@*****.**"}
            )

        No additional method call is required. Any non-success will throw a
        `WebPushException`.

    :param subscription_info: Provided by the client call
    :type subscription_info: dict
    :param data: Serialized data to send
    :type data: str
    :param vapid_private_key: Vapid instance or path to vapid private key PEM \
                              or encoded str
    :type vapid_private_key: Union[Vapid, str]
    :param vapid_claims: Dictionary of claims ('sub' required)
    :type vapid_claims: dict
    :param content_encoding: Optional content type string
    :type content_encoding: str
    :param curl: Return as "curl" string instead of sending
    :type curl: bool
    :param timeout: POST requests timeout
    :type timeout: float or tuple
    :param ttl: Time To Live
    :type ttl: int
    :param verbose: Provide verbose feedback
    :type verbose: bool
    :return requests.Response or string
    :param headers: Dictionary of extra HTTP headers to include
    :type headers: dict

    """
    if headers is None:
        headers = dict()
    else:
        # Ensure we don't leak VAPID headers by mutating the passed in dict.
        headers = headers.copy()

    vapid_headers = None
    if vapid_claims:
        if verbose:
            print("Generating VAPID headers...")
        if not vapid_claims.get('aud'):
            url = urlparse(subscription_info.get('endpoint'))
            aud = "{}://{}".format(url.scheme, url.netloc)
            vapid_claims['aud'] = aud
        # Remember, passed structures are mutable in python.
        # It's possible that a previously set `exp` field is no longer valid.
        if (not vapid_claims.get('exp')
                or vapid_claims.get('exp') < int(time.time())):
            # encryption lives for 12 hours
            vapid_claims['exp'] = int(time.time()) + (12 * 60 * 60)
            if verbose:
                print("Setting VAPID expry to {}...".format(
                    vapid_claims['exp']))
        if not vapid_private_key:
            raise WebPushException("VAPID dict missing 'private_key'")
        if isinstance(vapid_private_key, Vapid):
            vv = vapid_private_key
        elif os.path.isfile(vapid_private_key):
            # Presume that key from file is handled correctly by
            # py_vapid.
            vv = Vapid.from_file(
                private_key_file=vapid_private_key)  # pragma no cover
        else:
            vv = Vapid.from_string(private_key=vapid_private_key)
        if verbose:
            print("\t claims: {}".format(vapid_claims))
        vapid_headers = vv.sign(vapid_claims)
        if verbose:
            print("\t headers: {}".format(vapid_headers))
        headers.update(vapid_headers)

    response = WebPusher(subscription_info, verbose=verbose).send(
        data,
        headers,
        ttl=ttl,
        content_encoding=content_encoding,
        curl=curl,
        timeout=timeout,
    )
    if not curl and response.status_code > 202:
        raise WebPushException("Push failed: {} {}\nResponse body:{}".format(
            response.status_code, response.reason, response.text),
                               response=response)
    return response
예제 #3
0
def webpush(subscription_info,
            data=None,
            vapid_private_key=None,
            vapid_claims=None,
            content_encoding="aesgcm",
            curl=False,
            timeout=None):
    """
        One call solution to endcode and send `data` to the endpoint
        contained in `subscription_info` using optional VAPID auth headers.

        in example:

        .. code-block:: python

        from pywebpush import python

        webpush(
            subscription_info={
                "endpoint": "https://push.example.com/v1/abcd",
                "keys": {"p256dh": "0123abcd...",
                         "auth": "001122..."}
                 },
            data="Mary had a little lamb, with a nice mint jelly",
            vapid_private_key="path/to/key.pem",
            vapid_claims={"sub": "*****@*****.**"}
            )

        No additional method call is required. Any non-success will throw a
        `WebPushException`.

    :param subscription_info: Provided by the client call
    :type subscription_info: dict
    :param data: Serialized data to send
    :type data: str
    :param vapid_private_key: Path to vapid private key PEM or encoded str
    :type vapid_private_key: str
    :param vapid_claims: Dictionary of claims ('sub' required)
    :type vapid_claims: dict
    :param content_encoding: Optional content type string
    :type content_encoding: str
    :param curl: Return as "curl" string instead of sending
    :type curl: bool
    :param timeout: POST requests timeout
    :type timeout: float or tuple
    :return requests.Response or string

    """
    vapid_headers = None
    if vapid_claims:
        if not vapid_claims.get('aud'):
            url = urlparse(subscription_info.get('endpoint'))
            aud = "{}://{}".format(url.scheme, url.netloc)
            vapid_claims['aud'] = aud
        if not vapid_private_key:
            raise WebPushException("VAPID dict missing 'private_key'")
        if os.path.isfile(vapid_private_key):
            # Presume that key from file is handled correctly by
            # py_vapid.
            vv = Vapid.from_file(
                private_key_file=vapid_private_key)  # pragma no cover
        else:
            vv = Vapid.from_raw(private_raw=vapid_private_key.encode())
        vapid_headers = vv.sign(vapid_claims)
    result = WebPusher(subscription_info).send(
        data,
        vapid_headers,
        content_encoding=content_encoding,
        curl=curl,
        timeout=timeout,
    )
    if not curl and result.status_code > 202:
        raise WebPushException("Push failed: {}: {}".format(
            result, result.text))
    return result
예제 #4
0
def webpush(subscription_info,
            data=None,
            vapid_private_key=None,
            vapid_claims=None,
            content_encoding="aesgcm",
            curl=False):
    """
        One call solution to endcode and send `data` to the endpoint
        contained in `subscription_info` using optional VAPID auth headers.

        in example:

        .. code-block:: python

        from pywebpush import python

        webpush(
            subscription_info={
                "endpoint": "https://push.example.com/v1/abcd",
                "keys": {"p256dh": "0123abcd...",
                         "auth": "001122..."}
                 },
            data="Mary had a little lamb, with a nice mint jelly",
            vapid_private_key="path/to/key.pem",
            vapid_claims={"sub": "*****@*****.**"}
            )

        No additional method call is required. Any non-success will throw a
        `WebPushException`.

    :param subscription_info: Provided by the client call
    :type subscription_info: dict
    :param data: Serialized data to send
    :type data: str
    :param vapid_private_key: Path to vapid private key PEM or encoded str
    :type vapid_private_key: str
    :param vapid_claims: Dictionary of claims ('sub' required)
    :type vapid_claims: dict
    :param content_encoding: Optional content type string
    :type content_encoding: str
    :param curl: Return as "curl" string instead of sending
    :type curl: bool
    :return requests.Response or string

    """
    vapid_headers = None
    if vapid_claims:
        if not vapid_claims.get('aud'):
            url = urlparse(subscription_info.get('endpoint'))
            aud = "{}://{}".format(url.scheme, url.netloc)
            vapid_claims['aud'] = aud
        if not vapid_private_key:
            raise WebPushException("VAPID dict missing 'private_key'")
        if os.path.isfile(vapid_private_key):
            # Presume that key from file is handled correctly by
            # py_vapid.
            vv = Vapid.from_file(
                private_key_file=vapid_private_key)  # pragma no cover
        else:
            vv = Vapid.from_raw(private_raw=vapid_private_key.encode())
        vapid_headers = vv.sign(vapid_claims)
    result = WebPusher(subscription_info).send(
        data,
        vapid_headers,
        content_encoding=content_encoding,
        curl=curl,
    )
    if not curl and result.status_code > 202:
        raise WebPushException("Push failed: {}: {}".format(
            result, result.text))
    return result
예제 #5
0
        "p256dh":
        "BK7h-R0UgDeT89jhWi76-FlTtlEr3DbVBnrr34qmK91Husli_Fazu7vo7kW1mg9F_qhNzrs2glbrc6wfqGFsXks=",
        "auth": "CyOHiGNXPcT5Slo9UMx2uA=="
    }
}
data = 'aaaaaa'
vapid_private_key = PRIVATE_KEY = '''
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg2xyYpqQhOaIdSbqH
UwM3+ySvF47MoJyAFUNaHM7g/zOhRANCAAT554ztzCpjiIFOxNfEIicSzNPOZTIB
Y1+CGl+LDfM5RlUNERFdfZYRqMmwvX7ydq7UiASkspWqdVVKZnLCzPD3  
'''.strip()
vapid_claims = {"sub": "mailto:[email protected]"}

vapid_headers = None
if vapid_claims:
    if not vapid_claims.get('aud'):
        url = urlparse(subscription_info.get('endpoint'))
        aud = "{}://{}".format(url.scheme, url.netloc)
        vapid_claims['aud'] = aud
    if os.path.isfile(vapid_private_key):
        vv = Vapid.from_file(
            private_key_file=vapid_private_key)  # pragma no cover
    else:
        vv = Vapid.from_string(private_key=vapid_private_key)
    vapid_headers = vv.sign(vapid_claims)

result = WebPusher(subscription_info,
                   requests_session).send(data, vapid_headers)

print result.text