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>&lt;class 'roundup.exceptions.UsageError'&gt;: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>&lt;class 'roundup.exceptions.UsageError'&gt;: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

Roundup Issue Tracker: http://roundup-tracker.org/