from __future__ import absolute_import, division, print_function, unicode_literals

from base64 import b64encode

from aspen.http.request import Request
from aspen.http.response import Response
from environment import Environment

from gratipay import wireup
from gratipay.security.csrf import REASON_BAD_TOKEN, REASON_NO_CSRF_COOKIE
from gratipay.security.user import SESSION
from gratipay.testing import Harness


class Tests(Harness):

    def setUp(self):
        Harness.setUp(self)

        # Grab configuration from the environment, storing for later.
        env = wireup.env()
        self.environ = env.environ

        # Change env, doesn't change self.environ.
        env.canonical_scheme = 'https'
        env.canonical_host = 'gratipay.com'

        wireup.canonical(env)

    def tearDown(self):
        Harness.tearDown(self)
        reset = Environment(CANONICAL_SCHEME=unicode, CANONICAL_HOST=unicode, environ=self.environ)
        wireup.canonical(reset)

    def test_canonize_canonizes(self):
        response = self.client.GxT( "/"
                                  , HTTP_HOST=b'gratipay.com'
                                  , HTTP_X_FORWARDED_PROTO=b'http'
                                   )
        assert response.code == 302
        assert response.headers['Location'] == b'https://gratipay.com/'

    def test_no_cookies_over_http(self):
        """
        We don't want to send cookies over HTTP, especially not CSRF and
        session cookies, for obvious security reasons.
        """
        self.make_participant('alice')
        redirect = self.client.GET( "/"
                                  , auth_as='alice'
                                  , HTTP_X_FORWARDED_PROTO=b'http'
                                  , HTTP_HOST=b'gratipay.com'
                                  , raise_immediately=False
                                   )
        assert redirect.code == 302
        assert not redirect.headers.cookie

    def test_session_cookie_not_set_under_API_key_auth(self):
        alice = self.make_participant('alice', claimed_time='now')
        api_key = alice.recreate_api_key()

        auth_header = b'Basic ' + b64encode(b'%s:%s' % (alice.id, api_key))
        response = self.client.GET( '/alice/public.json'
                                  , HTTP_AUTHORIZATION=auth_header
                                  , HTTP_X_FORWARDED_PROTO=b'https'
                                  , HTTP_HOST=b'gratipay.com'
                                   )

        assert response.code == 200
        assert SESSION not in response.headers.cookie

    def test_old_API_key_auth(self):
        alice = self.make_participant('alice', claimed_time='now')
        api_key = alice.recreate_api_key()

        auth_header = b'Basic ' + b64encode(b'%s:' % api_key)
        response = self.client.GET( '/alice/public.json'
                                  , HTTP_AUTHORIZATION=auth_header
                                  , HTTP_X_FORWARDED_PROTO=b'https'
                                  , HTTP_HOST=b'gratipay.com'
                                   )

        assert response.code == 200

    def test_bad_userid_returns_401(self):
        self.make_participant('alice', claimed_time='now')
        auth_header = b'Basic ' + b64encode(b'foo:')
        response = self.client.GxT( '/alice/public.json'
                                  , HTTP_AUTHORIZATION=auth_header
                                  , HTTP_X_FORWARDED_PROTO=b'https'
                                  , HTTP_HOST=b'gratipay.com'
                                   )
        assert response.code == 401

    def test_early_failures_dont_break_everything(self):
        old_from_wsgi = Request.from_wsgi
        def broken_from_wsgi(*a, **kw):
            raise Response(400)
        try:
            Request.from_wsgi = classmethod(broken_from_wsgi)
            assert self.client.GET("/", raise_immediately=False).code == 400
        finally:
            Request.from_wsgi = old_from_wsgi


class Tests2(Harness):

    def test_error_spt_works(self):
        r = self.client.POST('/', csrf_token=False, raise_immediately=False)
        assert r.code == 403

    def test_no_csrf_cookie(self):
        r = self.client.PxST('/', csrf_token=False)
        assert r.code == 403
        assert r.body == REASON_NO_CSRF_COOKIE

    def test_bad_csrf_cookie(self):
        r = self.client.PxST('/', csrf_token=b'bad_token')
        assert r.code == 403
        assert r.body == REASON_BAD_TOKEN

@@ -4214,33 +4214,33 @@
  = self.client.P
-x
+O
 ST('/', csrf_tok
@@ -4243,24 +4243,49 @@
 _token=False
+, raise_immediately=False
 )%0A        as
@@ -4313,34 +4313,24 @@
       assert
- r.body ==
  REASON_NO_C
@@ -4339,16 +4339,75 @@
 F_COOKIE
+ in r.body%0A        assert b'csrf_token' in r.headers.cookie
 %0A%0A    de
@@ -4461,17 +4461,17 @@
 client.P
-x
+O
 ST('/', 
@@ -4493,16 +4493,41 @@
 d_token'
+, raise_immediately=False
 )%0A      
@@ -4567,18 +4567,8 @@
 sert
- r.body ==
  REA
@@ -4580,9 +4580,87 @@
 AD_TOKEN
+ in r.body%0A        assert r.headers.cookie%5Bb'csrf_token'%5D.value != 'bad_token'
 %0A

improve CSRF tests