# Authors: # John Dennis # # Copyright (C) 2012 Red Hat # see file 'COPYING' for use and warranty information # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import datetime import email.utils from ipapython.cookie import Cookie from ipapython.ipautil import datetime_from_utctimestamp import pytest pytestmark = pytest.mark.tier0 class TestParse: def test_parse(self): # Empty string s = '' cookies = Cookie.parse(s) assert len(cookies) == 0 # Invalid single token s = 'color' with pytest.raises(ValueError): cookies = Cookie.parse(s) # Invalid single token that's keyword s = 'HttpOnly' with pytest.raises(ValueError): cookies = Cookie.parse(s) # Invalid key/value pair whose key is a keyword s = 'domain=example.com' with pytest.raises(ValueError): cookies = Cookie.parse(s) # 1 cookie with empty value s = 'color=' cookies = Cookie.parse(s) assert len(cookies) == 1 cookie = cookies[0] assert cookie.key == 'color' assert cookie.value == '' assert cookie.domain is None assert cookie.path is None assert cookie.max_age is None assert cookie.expires is None assert cookie.secure is None assert cookie.httponly is None assert str(cookie) == "color=" assert cookie.http_cookie() == "color=;" # 1 cookie with name/value s = 'color=blue' cookies = Cookie.parse(s) assert len(cookies) == 1 cookie = cookies[0] assert cookie.key == 'color' assert cookie.value == 'blue' assert cookie.domain is None assert cookie.path is None assert cookie.max_age is None assert cookie.expires is None assert cookie.secure is None assert cookie.httponly is None assert str(cookie) == "color=blue" assert cookie.http_cookie() == "color=blue;" # 1 cookie with whose value is quoted # Use "get by name" utility to extract specific cookie s = 'color="blue"' cookie = Cookie.get_named_cookie_from_string(s, 'color') assert cookie is not None, Cookie assert cookie.key == 'color' assert cookie.value == 'blue' assert cookie.domain is None assert cookie.path is None assert cookie.max_age is None assert cookie.expires is None assert cookie.secure is None assert cookie.httponly is None assert str(cookie) == "color=blue" assert cookie.http_cookie() == "color=blue;" # 1 cookie with name/value and domain, path attributes. # Change up the whitespace a bit. s = 'color =blue; domain= example.com ; path = /toplevel ' cookies = Cookie.parse(s) assert len(cookies) == 1 cookie = cookies[0] assert cookie.key == 'color' assert cookie.value == 'blue' assert cookie.domain == 'example.com' assert cookie.path == '/toplevel' assert cookie.max_age is None assert cookie.expires is None assert cookie.secure is None assert cookie.httponly is None assert str(cookie) == "color=blue; Domain=example.com; Path=/toplevel" assert cookie.http_cookie() == "color=blue;" # 2 cookies, various attributes s = 'color=blue; Max-Age=3600; temperature=hot; HttpOnly' cookies = Cookie.parse(s) assert len(cookies) == 2 cookie = cookies[0] assert cookie.key == 'color' assert cookie.value == 'blue' assert cookie.domain is None assert cookie.path is None assert cookie.max_age == 3600 assert cookie.expires is None assert cookie.secure is None assert cookie.httponly is None assert str(cookie) == "color=blue; Max-Age=3600" assert cookie.http_cookie() == "color=blue;" cookie = cookies[1] assert cookie.key == 'temperature' assert cookie.value == 'hot' assert cookie.domain is None assert cookie.path is None assert cookie.max_age is None assert cookie.expires is None assert cookie.secure is None assert cookie.httponly is True assert str(cookie) == "temperature=hot; HttpOnly" assert cookie.http_cookie() == "temperature=hot;" class TestExpires: @pytest.fixture(autouse=True) def expires_setup(self): # Force microseconds to zero because cookie timestamps only have second resolution self.now = datetime.datetime.now( tz=datetime.timezone.utc).replace(microsecond=0) self.now_timestamp = datetime_from_utctimestamp( self.now.utctimetuple(), units=1).timestamp() self.now_string = email.utils.formatdate(self.now_timestamp, usegmt=True) self.max_age = 3600 # 1 hour self.age_expiration = self.now + datetime.timedelta(seconds=self.max_age) self.age_timestamp = datetime_from_utctimestamp( self.age_expiration.utctimetuple(), units=1).timestamp() self.age_string = email.utils.formatdate(self.age_timestamp, usegmt=True) self.expires = self.now + datetime.timedelta(days=1) # 1 day self.expires_timestamp = datetime_from_utctimestamp( self.expires.utctimetuple(), units=1).timestamp() self.expires_string = email.utils.formatdate(self.expires_timestamp, usegmt=True) def test_expires(self): # 1 cookie with name/value and no Max-Age and no Expires s = 'color=blue;' cookies = Cookie.parse(s) assert len(cookies) == 1 cookie = cookies[0] # Force timestamp to known value cookie.timestamp = self.now assert cookie.key == 'color' assert cookie.value == 'blue' assert cookie.domain is None assert cookie.path is None assert cookie.max_age is None assert cookie.expires is None assert cookie.secure is None assert cookie.httponly is None assert str(cookie) == "color=blue" assert cookie.get_expiration() is None # Normalize assert cookie.normalize_expiration() is None assert cookie.max_age is None assert cookie.expires is None assert str(cookie) == "color=blue" # 1 cookie with name/value and Max-Age s = 'color=blue; max-age=%d' % (self.max_age) cookies = Cookie.parse(s) assert len(cookies) == 1 cookie = cookies[0] # Force timestamp to known value cookie.timestamp = self.now assert cookie.key == 'color' assert cookie.value == 'blue' assert cookie.domain is None assert cookie.path is None assert cookie.max_age == self.max_age assert cookie.expires is None assert cookie.secure is None assert cookie.httponly is None assert str(cookie) == "color=blue; Max-Age=%d" % (self.max_age) assert cookie.get_expiration() == self.age_expiration # Normalize assert cookie.normalize_expiration() == self.age_expiration assert cookie.max_age is None assert cookie.expires == self.age_expiration assert str(cookie) == "color=blue; Expires=%s" % (self.age_string) # 1 cookie with name/value and Expires s = 'color=blue; Expires=%s' % (self.expires_string) cookies = Cookie.parse(s) assert len(cookies) == 1 cookie = cookies[0] # Force timestamp to known value cookie.timestamp = self.now assert cookie.key == 'color' assert cookie.value == 'blue' assert cookie.domain is None assert cookie.path is None assert cookie.max_age is None assert cookie.expires == self.expires assert cookie.secure is None assert cookie.httponly is None assert str(cookie) == "color=blue; Expires=%s" % (self.expires_string) assert cookie.get_expiration() == self.expires # Normalize assert cookie.normalize_expiration() == self.expires assert cookie.max_age is None assert cookie.expires == self.expires assert str(cookie) == "color=blue; Expires=%s" % (self.expires_string) # 1 cookie with name/value witht both Max-Age and Expires, Max-Age takes precedence s = 'color=blue; Expires=%s; max-age=%d' % (self.expires_string, self.max_age) cookies = Cookie.parse(s) assert len(cookies) == 1 cookie = cookies[0] # Force timestamp to known value cookie.timestamp = self.now assert cookie.key == 'color' assert cookie.value == 'blue' assert cookie.domain is None assert cookie.path is None assert cookie.max_age == self.max_age assert cookie.expires == self.expires assert cookie.secure is None assert cookie.httponly is None expected = "color=blue; Max-Age={}; Expires={}".format( self.max_age, self.expires_string) assert str(cookie) == expected assert cookie.get_expiration() == self.age_expiration # Normalize assert cookie.normalize_expiration() == self.age_expiration assert cookie.max_age is None assert cookie.expires == self.age_expiration assert str(cookie) == "color=blue; Expires=%s" % (self.age_string) # Verify different types can be assigned to the timestamp and # expires attribute. cookie = Cookie('color', 'blue') cookie.timestamp = self.now assert cookie.timestamp == self.now cookie.timestamp = self.now_timestamp assert cookie.timestamp == self.now cookie.timestamp = self.now_string assert cookie.timestamp == self.now assert cookie.expires is None cookie.expires = self.expires assert cookie.expires == self.expires cookie.expires = self.expires_timestamp assert cookie.expires == self.expires cookie.expires = self.expires_string assert cookie.expires == self.expires class TestInvalidAttributes: def test_invalid(self): # Invalid Max-Age s = 'color=blue; Max-Age=over-the-hill' with pytest.raises(ValueError): Cookie.parse(s) cookie = Cookie('color', 'blue') with pytest.raises(ValueError): cookie.max_age = 'over-the-hill' # Invalid Expires s = 'color=blue; Expires=Sun, 06 Xxx 1994 08:49:37 GMT' with pytest.raises(ValueError): Cookie.parse(s) cookie = Cookie('color', 'blue') with pytest.raises(ValueError): cookie.expires = 'Sun, 06 Xxx 1994 08:49:37 GMT' class TestAttributes: def test_attributes(self): cookie = Cookie('color', 'blue') assert cookie.key == 'color' assert cookie.value == 'blue' assert cookie.domain is None assert cookie.path is None assert cookie.max_age is None assert cookie.expires is None assert cookie.secure is None assert cookie.httponly is None cookie.domain = 'example.com' assert cookie.domain == 'example.com' cookie.domain = None assert cookie.domain is None cookie.path = '/toplevel' assert cookie.path == '/toplevel' cookie.path = None assert cookie.path is None cookie.max_age = 400 assert cookie.max_age == 400 cookie.max_age = None assert cookie.max_age is None cookie.expires = 'Sun, 06 Nov 1994 08:49:37 GMT' assert cookie.expires == datetime.datetime( 1994, 11, 6, 8, 49, 37, tzinfo=datetime.timezone.utc) cookie.expires = None assert cookie.expires is None cookie.secure = True assert cookie.secure is True assert str(cookie) == "color=blue; Secure" cookie.secure = False assert cookie.secure is False assert str(cookie) == "color=blue" cookie.secure = None assert cookie.secure is None assert str(cookie) == "color=blue" cookie.httponly = True assert cookie.httponly is True assert str(cookie) == "color=blue; HttpOnly" cookie.httponly = False assert cookie.httponly is False assert str(cookie) == "color=blue" cookie.httponly = None assert cookie.httponly is None assert str(cookie) == "color=blue" class TestHTTPReturn: @pytest.fixture(autouse=True) def http_return_setup(self): self.url = 'http://www.foo.bar.com/one/two' def test_no_attributes(self): cookie = Cookie('color', 'blue') assert cookie.http_return_ok(self.url) def test_domain(self): cookie = Cookie('color', 'blue', domain='www.foo.bar.com') assert cookie.http_return_ok(self.url) cookie = Cookie('color', 'blue', domain='.foo.bar.com') assert cookie.http_return_ok(self.url) cookie = Cookie('color', 'blue', domain='.bar.com') assert cookie.http_return_ok(self.url) cookie = Cookie('color', 'blue', domain='bar.com') with pytest.raises(Cookie.URLMismatch): assert cookie.http_return_ok(self.url) cookie = Cookie('color', 'blue', domain='bogus.com') with pytest.raises(Cookie.URLMismatch): assert cookie.http_return_ok(self.url) cookie = Cookie('color', 'blue', domain='www.foo.bar.com') with pytest.raises(Cookie.URLMismatch): assert cookie.http_return_ok('http://192.168.1.1/one/two') def test_path(self): cookie = Cookie('color', 'blue') assert cookie.http_return_ok(self.url) cookie = Cookie('color', 'blue', path='/') assert cookie.http_return_ok(self.url) cookie = Cookie('color', 'blue', path='/one') assert cookie.http_return_ok(self.url) cookie = Cookie('color', 'blue', path='/oneX') with pytest.raises(Cookie.URLMismatch): assert cookie.http_return_ok(self.url) def test_expires(self): now = datetime.datetime.utcnow().replace(microsecond=0) # expires 1 day from now expires = now + datetime.timedelta(days=1) cookie = Cookie('color', 'blue', expires=expires) assert cookie.http_return_ok(self.url) # expired 1 day ago expires = now + datetime.timedelta(days=-1) cookie = Cookie('color', 'blue', expires=expires) with pytest.raises(Cookie.Expired): assert cookie.http_return_ok(self.url) def test_httponly(self): cookie = Cookie('color', 'blue', httponly=True) assert cookie.http_return_ok('http://example.com') assert cookie.http_return_ok('https://example.com') with pytest.raises(Cookie.URLMismatch): assert cookie.http_return_ok('ftp://example.com') def test_secure(self): cookie = Cookie('color', 'blue', secure=True) assert cookie.http_return_ok('https://Xexample.com') with pytest.raises(Cookie.URLMismatch): assert cookie.http_return_ok('http://Xexample.com') class TestNormalization: @pytest.fixture(autouse=True) def normalization_setup(self): # Force microseconds to zero because cookie timestamps only have second resolution self.now = datetime.datetime.now( tz=datetime.timezone.utc).replace(microsecond=0) self.now_timestamp = datetime_from_utctimestamp( self.now.utctimetuple(), units=1).timestamp() self.now_string = email.utils.formatdate(self.now_timestamp, usegmt=True) self.max_age = 3600 # 1 hour self.age_expiration = self.now + datetime.timedelta(seconds=self.max_age) self.age_timestamp = datetime_from_utctimestamp( self.age_expiration.utctimetuple(), units=1).timestamp() self.age_string = email.utils.formatdate(self.age_timestamp, usegmt=True) self.expires = self.now + datetime.timedelta(days=1) # 1 day self.expires_timestamp = datetime_from_utctimestamp( self.expires.utctimetuple(), units=1).timestamp() self.expires_string = email.utils.formatdate(self.expires_timestamp, usegmt=True) def test_path_normalization(self): assert Cookie.normalize_url_path('') == '/' assert Cookie.normalize_url_path('foo') == '/' assert Cookie.normalize_url_path('foo/') == '/' assert Cookie.normalize_url_path('/foo') == '/' assert Cookie.normalize_url_path('/foo/') == '/foo' assert Cookie.normalize_url_path('/Foo/bar') == '/foo' assert Cookie.normalize_url_path('/foo/baR/') == '/foo/bar' def test_normalization(self): cookie = Cookie('color', 'blue', expires=self.expires) cookie.timestamp = self.now_timestamp assert cookie.domain is None assert cookie.path is None url = 'http://example.COM/foo' cookie.normalize(url) assert cookie.domain == 'example.com' assert cookie.path == '/' assert cookie.expires == self.expires cookie = Cookie('color', 'blue', max_age=self.max_age) cookie.timestamp = self.now_timestamp assert cookie.domain is None assert cookie.path is None url = 'http://example.com/foo/' cookie.normalize(url) assert cookie.domain == 'example.com' assert cookie.path == '/foo' assert cookie.expires == self.age_expiration cookie = Cookie('color', 'blue') url = 'http://example.com/foo' cookie.normalize(url) assert cookie.domain == 'example.com' assert cookie.path == '/' cookie = Cookie('color', 'blue') url = 'http://example.com/foo/bar' cookie.normalize(url) assert cookie.domain == 'example.com' assert cookie.path == '/foo' cookie = Cookie('color', 'blue') url = 'http://example.com/foo/bar/' cookie.normalize(url) assert cookie.domain == 'example.com' assert cookie.path == '/foo/bar'