Mercurial > p > roundup > code
comparison test/test_cgi.py @ 5376:64b05e24dbd8
Python 3 preparation: convert print to a function.
Tool-assisted patch. It is possible that some "from __future__ import
print_function" are not in fact needed, if a file only uses print()
with a single string as an argument and so would work fine in Python 2
without that import.
| author | Joseph Myers <jsm@polyomino.org.uk> |
|---|---|
| date | Tue, 24 Jul 2018 09:54:52 +0000 |
| parents | 351763d6400a |
| children | 0942fe89e82e |
comparison
equal
deleted
inserted
replaced
| 5375:1ad46057ae4a | 5376:64b05e24dbd8 |
|---|---|
| 6 # | 6 # |
| 7 # This module is distributed in the hope that it will be useful, | 7 # This module is distributed in the hope that it will be useful, |
| 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| 10 | 10 |
| 11 from __future__ import print_function | |
| 11 import unittest, os, shutil, errno, sys, difflib, cgi, re, StringIO | 12 import unittest, os, shutil, errno, sys, difflib, cgi, re, StringIO |
| 12 | 13 |
| 13 from roundup.cgi import client, actions, exceptions | 14 from roundup.cgi import client, actions, exceptions |
| 14 from roundup.cgi.exceptions import FormError, NotFound | 15 from roundup.cgi.exceptions import FormError, NotFound |
| 15 from roundup.exceptions import UsageError | 16 from roundup.exceptions import UsageError |
| 864 HTMLProperty.is_edit_ok = lambda x : True | 865 HTMLProperty.is_edit_ok = lambda x : True |
| 865 | 866 |
| 866 # test with no headers and config by default requires 1 | 867 # test with no headers and config by default requires 1 |
| 867 cl.inner_main() | 868 cl.inner_main() |
| 868 match_at=out[0].find('Unable to verify sufficient headers') | 869 match_at=out[0].find('Unable to verify sufficient headers') |
| 869 print "result of subtest 1:", out[0] | 870 print("result of subtest 1:", out[0]) |
| 870 self.assertNotEqual(match_at, -1) | 871 self.assertNotEqual(match_at, -1) |
| 871 del(out[0]) | 872 del(out[0]) |
| 872 | 873 |
| 873 # all the rest of these allow at least one header to pass | 874 # all the rest of these allow at least one header to pass |
| 874 # and the edit happens with a redirect back to issue 1 | 875 # and the edit happens with a redirect back to issue 1 |
| 875 cl.env['HTTP_REFERER'] = 'http://whoami.com/path/' | 876 cl.env['HTTP_REFERER'] = 'http://whoami.com/path/' |
| 876 cl.inner_main() | 877 cl.inner_main() |
| 877 match_at=out[0].find('Redirecting to <a href="http://whoami.com/path/issue1?@ok_message') | 878 match_at=out[0].find('Redirecting to <a href="http://whoami.com/path/issue1?@ok_message') |
| 878 print "result of subtest 2:", out[0] | 879 print("result of subtest 2:", out[0]) |
| 879 self.assertEqual(match_at, 0) | 880 self.assertEqual(match_at, 0) |
| 880 del(cl.env['HTTP_REFERER']) | 881 del(cl.env['HTTP_REFERER']) |
| 881 del(out[0]) | 882 del(out[0]) |
| 882 | 883 |
| 883 cl.env['HTTP_ORIGIN'] = 'http://whoami.com' | 884 cl.env['HTTP_ORIGIN'] = 'http://whoami.com' |
| 884 cl.inner_main() | 885 cl.inner_main() |
| 885 match_at=out[0].find('Redirecting to <a href="http://whoami.com/path/issue1?@ok_message') | 886 match_at=out[0].find('Redirecting to <a href="http://whoami.com/path/issue1?@ok_message') |
| 886 print "result of subtest 3:", out[0] | 887 print("result of subtest 3:", out[0]) |
| 887 self.assertEqual(match_at, 0) | 888 self.assertEqual(match_at, 0) |
| 888 del(cl.env['HTTP_ORIGIN']) | 889 del(cl.env['HTTP_ORIGIN']) |
| 889 del(out[0]) | 890 del(out[0]) |
| 890 | 891 |
| 891 cl.env['HTTP_X-FORWARDED-HOST'] = 'whoami.com' | 892 cl.env['HTTP_X-FORWARDED-HOST'] = 'whoami.com' |
| 895 # the proxy's name for the web server and not the name | 896 # the proxy's name for the web server and not the name |
| 896 # thatis exposed to the world. | 897 # thatis exposed to the world. |
| 897 cl.env['HTTP_HOST'] = 'frontend1.whoami.net' | 898 cl.env['HTTP_HOST'] = 'frontend1.whoami.net' |
| 898 cl.inner_main() | 899 cl.inner_main() |
| 899 match_at=out[0].find('Redirecting to <a href="http://whoami.com/path/issue1?@ok_message') | 900 match_at=out[0].find('Redirecting to <a href="http://whoami.com/path/issue1?@ok_message') |
| 900 print "result of subtest 4:", out[0] | 901 print("result of subtest 4:", out[0]) |
| 901 self.assertNotEqual(match_at, -1) | 902 self.assertNotEqual(match_at, -1) |
| 902 del(cl.env['HTTP_X-FORWARDED-HOST']) | 903 del(cl.env['HTTP_X-FORWARDED-HOST']) |
| 903 del(cl.env['HTTP_HOST']) | 904 del(cl.env['HTTP_HOST']) |
| 904 del(out[0]) | 905 del(out[0]) |
| 905 | 906 |
| 906 cl.env['HTTP_HOST'] = 'whoami.com' | 907 cl.env['HTTP_HOST'] = 'whoami.com' |
| 907 cl.inner_main() | 908 cl.inner_main() |
| 908 match_at=out[0].find('Redirecting to <a href="http://whoami.com/path/issue1?@ok_message') | 909 match_at=out[0].find('Redirecting to <a href="http://whoami.com/path/issue1?@ok_message') |
| 909 print "result of subtest 5:", out[0] | 910 print("result of subtest 5:", out[0]) |
| 910 self.assertEqual(match_at, 0) | 911 self.assertEqual(match_at, 0) |
| 911 del(cl.env['HTTP_HOST']) | 912 del(cl.env['HTTP_HOST']) |
| 912 del(out[0]) | 913 del(out[0]) |
| 913 | 914 |
| 914 # try failing headers | 915 # try failing headers |
| 915 cl.env['HTTP_X-FORWARDED-HOST'] = 'whoami.net' | 916 cl.env['HTTP_X-FORWARDED-HOST'] = 'whoami.net' |
| 916 # this raises an error as the header check passes and | 917 # this raises an error as the header check passes and |
| 917 # it did the edit and tries to send mail. | 918 # it did the edit and tries to send mail. |
| 918 cl.inner_main() | 919 cl.inner_main() |
| 919 match_at=out[0].find('Invalid X-FORWARDED-HOST whoami.net') | 920 match_at=out[0].find('Invalid X-FORWARDED-HOST whoami.net') |
| 920 print "result of subtest 6:", out[0] | 921 print("result of subtest 6:", out[0]) |
| 921 self.assertNotEqual(match_at, -1) | 922 self.assertNotEqual(match_at, -1) |
| 922 del(cl.env['HTTP_X-FORWARDED-HOST']) | 923 del(cl.env['HTTP_X-FORWARDED-HOST']) |
| 923 del(out[0]) | 924 del(out[0]) |
| 924 | 925 |
| 925 # header checks succeed | 926 # header checks succeed |
| 928 | 929 |
| 929 # roundup will report a missing token. | 930 # roundup will report a missing token. |
| 930 cl.db.config['WEB_CSRF_ENFORCE_TOKEN'] = 'required' | 931 cl.db.config['WEB_CSRF_ENFORCE_TOKEN'] = 'required' |
| 931 cl.inner_main() | 932 cl.inner_main() |
| 932 match_at=out[0].find('<p>Csrf token is missing.</p>') | 933 match_at=out[0].find('<p>Csrf token is missing.</p>') |
| 933 print "result of subtest 6a:", out[0], match_at | 934 print("result of subtest 6a:", out[0], match_at) |
| 934 self.assertEqual(match_at, 33) | 935 self.assertEqual(match_at, 33) |
| 935 del(out[0]) | 936 del(out[0]) |
| 936 cl.db.config['WEB_CSRF_ENFORCE_TOKEN'] = 'yes' | 937 cl.db.config['WEB_CSRF_ENFORCE_TOKEN'] = 'yes' |
| 937 | 938 |
| 938 import copy | 939 import copy |
| 941 # add a bogus csrf field to the form and rerun the inner_main | 942 # add a bogus csrf field to the form and rerun the inner_main |
| 942 cl.form = db_test_base.makeForm(form2) | 943 cl.form = db_test_base.makeForm(form2) |
| 943 | 944 |
| 944 cl.inner_main() | 945 cl.inner_main() |
| 945 match_at=out[0].find('Invalid csrf token found: booogus') | 946 match_at=out[0].find('Invalid csrf token found: booogus') |
| 946 print "result of subtest 7:", out[0] | 947 print("result of subtest 7:", out[0]) |
| 947 self.assertEqual(match_at, 36) | 948 self.assertEqual(match_at, 36) |
| 948 del(out[0]) | 949 del(out[0]) |
| 949 | 950 |
| 950 form2 = copy.copy(form) | 951 form2 = copy.copy(form) |
| 951 nonce = anti_csrf_nonce(cl, cl) | 952 nonce = anti_csrf_nonce(cl, cl) |
| 952 # verify that we can see the nonce | 953 # verify that we can see the nonce |
| 953 otks = cl.db.getOTKManager() | 954 otks = cl.db.getOTKManager() |
| 954 isitthere = otks.exists(nonce) | 955 isitthere = otks.exists(nonce) |
| 955 print "result of subtest 8:", isitthere | 956 print("result of subtest 8:", isitthere) |
| 956 print "otks: user, session", otks.get(nonce, 'uid', default=None), \ | 957 print("otks: user, session", otks.get(nonce, 'uid', default=None), |
| 957 otks.get(nonce, 'session', default=None) | 958 otks.get(nonce, 'session', default=None)) |
| 958 self.assertEqual(isitthere, True) | 959 self.assertEqual(isitthere, True) |
| 959 | 960 |
| 960 form2.update({'@csrf': nonce}) | 961 form2.update({'@csrf': nonce}) |
| 961 # add a real csrf field to the form and rerun the inner_main | 962 # add a real csrf field to the form and rerun the inner_main |
| 962 cl.form = db_test_base.makeForm(form2) | 963 cl.form = db_test_base.makeForm(form2) |
| 963 cl.inner_main() | 964 cl.inner_main() |
| 964 # csrf passes and redirects to the new issue. | 965 # csrf passes and redirects to the new issue. |
| 965 match_at=out[0].find('Redirecting to <a href="http://whoami.com/path/issue1?@ok_message') | 966 match_at=out[0].find('Redirecting to <a href="http://whoami.com/path/issue1?@ok_message') |
| 966 print "result of subtest 9:", out[0] | 967 print("result of subtest 9:", out[0]) |
| 967 self.assertEqual(match_at, 0) | 968 self.assertEqual(match_at, 0) |
| 968 del(out[0]) | 969 del(out[0]) |
| 969 | 970 |
| 970 # try a replay attack | 971 # try a replay attack |
| 971 cl.inner_main() | 972 cl.inner_main() |
| 972 # This should fail as token was wiped by last run. | 973 # This should fail as token was wiped by last run. |
| 973 match_at=out[0].find('Invalid csrf token found: %s'%nonce) | 974 match_at=out[0].find('Invalid csrf token found: %s'%nonce) |
| 974 print "replay of csrf after post use", out[0] | 975 print("replay of csrf after post use", out[0]) |
| 975 print "result of subtest 10:", out[0] | 976 print("result of subtest 10:", out[0]) |
| 976 self.assertEqual(match_at, 36) | 977 self.assertEqual(match_at, 36) |
| 977 del(out[0]) | 978 del(out[0]) |
| 978 | 979 |
| 979 # make sure that a get deletes the csrf. | 980 # make sure that a get deletes the csrf. |
| 980 cl.env['REQUEST_METHOD'] = 'GET' | 981 cl.env['REQUEST_METHOD'] = 'GET' |
| 985 # add a real csrf field to the form and rerun the inner_main | 986 # add a real csrf field to the form and rerun the inner_main |
| 986 cl.form = db_test_base.makeForm(form2) | 987 cl.form = db_test_base.makeForm(form2) |
| 987 cl.inner_main() | 988 cl.inner_main() |
| 988 # csrf passes but fail creating new issue because not a post | 989 # csrf passes but fail creating new issue because not a post |
| 989 match_at=out[0].find('<p>Invalid request</p>') | 990 match_at=out[0].find('<p>Invalid request</p>') |
| 990 print "result of subtest 11:", out[0] | 991 print("result of subtest 11:", out[0]) |
| 991 self.assertEqual(match_at, 33) | 992 self.assertEqual(match_at, 33) |
| 992 del(out[0]) | 993 del(out[0]) |
| 993 | 994 |
| 994 # the token should be gone | 995 # the token should be gone |
| 995 isitthere = otks.exists(nonce) | 996 isitthere = otks.exists(nonce) |
| 996 print "result of subtest 12:", isitthere | 997 print("result of subtest 12:", isitthere) |
| 997 self.assertEqual(isitthere, False) | 998 self.assertEqual(isitthere, False) |
| 998 | 999 |
| 999 # change to post and should fail w/ invalid csrf | 1000 # change to post and should fail w/ invalid csrf |
| 1000 # since get deleted the token. | 1001 # since get deleted the token. |
| 1001 cl.env.update({'REQUEST_METHOD': 'POST'}) | 1002 cl.env.update({'REQUEST_METHOD': 'POST'}) |
| 1002 print cl.env | 1003 print(cl.env) |
| 1003 cl.inner_main() | 1004 cl.inner_main() |
| 1004 match_at=out[0].find('Invalid csrf token found: %s'%nonce) | 1005 match_at=out[0].find('Invalid csrf token found: %s'%nonce) |
| 1005 print "post failure after get", out[0] | 1006 print("post failure after get", out[0]) |
| 1006 print "result of subtest 13:", out[0] | 1007 print("result of subtest 13:", out[0]) |
| 1007 self.assertEqual(match_at, 36) | 1008 self.assertEqual(match_at, 36) |
| 1008 del(out[0]) | 1009 del(out[0]) |
| 1009 | 1010 |
| 1010 del(cl.env['HTTP_REFERER']) | 1011 del(cl.env['HTTP_REFERER']) |
| 1011 | 1012 |
| 1052 # ship the form with the value holding the xml value. | 1053 # ship the form with the value holding the xml value. |
| 1053 # I have no clue why this works but .... | 1054 # I have no clue why this works but .... |
| 1054 cl.form = MockNull(file = True, value = "<?xml version='1.0'?>\n<methodCall>\n<methodName>display</methodName>\n<params>\n<param>\n<value><string>user1</string></value>\n</param>\n<param>\n<value><string>username</string></value>\n</param>\n</params>\n</methodCall>\n" ) | 1055 cl.form = MockNull(file = True, value = "<?xml version='1.0'?>\n<methodCall>\n<methodName>display</methodName>\n<params>\n<param>\n<value><string>user1</string></value>\n</param>\n<param>\n<value><string>username</string></value>\n</param>\n</params>\n</methodCall>\n" ) |
| 1055 answer ="<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><struct>\n<member>\n<name>username</name>\n<value><string>admin</string></value>\n</member>\n</struct></value>\n</param>\n</params>\n</methodResponse>\n" | 1056 answer ="<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><struct>\n<member>\n<name>username</name>\n<value><string>admin</string></value>\n</member>\n</struct></value>\n</param>\n</params>\n</methodResponse>\n" |
| 1056 cl.handle_xmlrpc() | 1057 cl.handle_xmlrpc() |
| 1057 print out | 1058 print(out) |
| 1058 self.assertEqual(out[0], answer) | 1059 self.assertEqual(out[0], answer) |
| 1059 del(out[0]) | 1060 del(out[0]) |
| 1060 | 1061 |
| 1061 # remove the X-REQUESTED-WITH header and get an xmlrpc fault returned | 1062 # remove the X-REQUESTED-WITH header and get an xmlrpc fault returned |
| 1062 del(cl.env['HTTP_X-REQUESTED-WITH']) | 1063 del(cl.env['HTTP_X-REQUESTED-WITH']) |
| 1063 cl.handle_xmlrpc() | 1064 cl.handle_xmlrpc() |
| 1064 output="<?xml version='1.0'?>\n<methodResponse>\n<fault>\n<value><struct>\n<member>\n<name>faultCode</name>\n<value><int>1</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string><class 'roundup.exceptions.UsageError'>:Required Header Missing</string></value>\n</member>\n</struct></value>\n</fault>\n</methodResponse>\n" | 1065 output="<?xml version='1.0'?>\n<methodResponse>\n<fault>\n<value><struct>\n<member>\n<name>faultCode</name>\n<value><int>1</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string><class 'roundup.exceptions.UsageError'>:Required Header Missing</string></value>\n</member>\n</struct></value>\n</fault>\n</methodResponse>\n" |
| 1065 print out[0] | 1066 print(out[0]) |
| 1066 self.assertEqual(output,out[0]) | 1067 self.assertEqual(output,out[0]) |
| 1067 del(out[0]) | 1068 del(out[0]) |
| 1068 | 1069 |
| 1069 # change config to not require X-REQUESTED-WITH header | 1070 # change config to not require X-REQUESTED-WITH header |
| 1070 cl.db.config['WEB_CSRF_ENFORCE_HEADER_X-REQUESTED-WITH'] = 'logfailure' | 1071 cl.db.config['WEB_CSRF_ENFORCE_HEADER_X-REQUESTED-WITH'] = 'logfailure' |
| 1071 cl.handle_xmlrpc() | 1072 cl.handle_xmlrpc() |
| 1072 print out | 1073 print(out) |
| 1073 self.assertEqual(out[0], answer) | 1074 self.assertEqual(out[0], answer) |
| 1074 del(out[0]) | 1075 del(out[0]) |
| 1075 | 1076 |
| 1076 # | 1077 # |
| 1077 # SECURITY | 1078 # SECURITY |
| 1607 (self.client.classname, self.client.template, self.client.nodeid), | 1608 (self.client.classname, self.client.template, self.client.nodeid), |
| 1608 ('user', 'forgotten|item', None)) | 1609 ('user', 'forgotten|item', None)) |
| 1609 self.assertEqual(self.client._ok_message, []) | 1610 self.assertEqual(self.client._ok_message, []) |
| 1610 | 1611 |
| 1611 result = self.client.renderContext() | 1612 result = self.client.renderContext() |
| 1612 print result | 1613 print(result) |
| 1613 # sha1sum of classic tracker user.forgotten.template must be found | 1614 # sha1sum of classic tracker user.forgotten.template must be found |
| 1614 sha1sum = '<!-- SHA: f93570f95f861da40f9c45bbd2b049bb3a7c0fc5 -->' | 1615 sha1sum = '<!-- SHA: f93570f95f861da40f9c45bbd2b049bb3a7c0fc5 -->' |
| 1615 self.assertNotEqual(-1, result.index(sha1sum)) | 1616 self.assertNotEqual(-1, result.index(sha1sum)) |
| 1616 | 1617 |
| 1617 # now set an error in the form to get error template user.item.html | 1618 # now set an error in the form to get error template user.item.html |
| 1624 ('user', 'forgotten|item', None)) | 1625 ('user', 'forgotten|item', None)) |
| 1625 self.assertEqual(self.client._ok_message, []) | 1626 self.assertEqual(self.client._ok_message, []) |
| 1626 self.assertEqual(self.client._error_message, ["this is an error"]) | 1627 self.assertEqual(self.client._error_message, ["this is an error"]) |
| 1627 | 1628 |
| 1628 result = self.client.renderContext() | 1629 result = self.client.renderContext() |
| 1629 print result | 1630 print(result) |
| 1630 # sha1sum of classic tracker user.item.template must be found | 1631 # sha1sum of classic tracker user.item.template must be found |
| 1631 sha1sum = '<!-- SHA: 3b7ce7cbf24f77733c9b9f64a569d6429390cc3f -->' | 1632 sha1sum = '<!-- SHA: 3b7ce7cbf24f77733c9b9f64a569d6429390cc3f -->' |
| 1632 self.assertNotEqual(-1, result.index(sha1sum)) | 1633 self.assertNotEqual(-1, result.index(sha1sum)) |
| 1633 | 1634 |
| 1634 | 1635 |
