Mercurial > p > roundup > code
comparison test/rest_common.py @ 5879:94a7669677ae
add permissions to control user of rest and xmlrpc API interfaces.
issue2551058: Add new permissions: 'Rest Access' and 'Xmlrpc Access'
to allow per-user access control to rest and xmlrpc interfaces using
roles.
Updated all schemas to add these new perms to all authenticated roles.
Error conditions in handle_xmlrpc were not working right in manual
testing. I tried to make it a little better, but I don't actually
understand how the fault xmlrpc object is supposed to be used. So I
may have messed something up. I'll try to ping the people who wrote
the xmlrpc code to have them review.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Fri, 27 Sep 2019 23:29:59 -0400 |
| parents | 1b57d8f3eb97 |
| children | 5d0873a4de4a |
comparison
equal
deleted
inserted
replaced
| 5878:1b57d8f3eb97 | 5879:94a7669677ae |
|---|---|
| 114 klass='user', | 114 klass='user', |
| 115 properties=('id', 'realname', 'address', 'username'), | 115 properties=('id', 'realname', 'address', 'username'), |
| 116 description="Allow jwt access to email", | 116 description="Allow jwt access to email", |
| 117 props_only=False) | 117 props_only=False) |
| 118 self.db.security.addPermissionToRole("User:email", jwt_perms) | 118 self.db.security.addPermissionToRole("User:email", jwt_perms) |
| 119 self.db.security.addPermissionToRole("User:email", "Rest Access") | |
| 120 | |
| 121 # add set of roles for testing jwt's. | |
| 122 # this is like the user:email role, but it missing access to the rest endpoint. | |
| 123 self.db.security.addRole(name="User:emailnorest", | |
| 124 description="allow email by jwt") | |
| 125 jwt_perms = self.db.security.addPermission(name='View', | |
| 126 klass='user', | |
| 127 properties=('id', 'realname', 'address', 'username'), | |
| 128 description="Allow jwt access to email but forget to allow rest", | |
| 129 props_only=False) | |
| 130 self.db.security.addPermissionToRole("User:emailnorest", jwt_perms) | |
| 131 | |
| 119 | 132 |
| 120 if jwt: | 133 if jwt: |
| 121 # must be 32 chars in length minimum (I think this is at least | 134 # must be 32 chars in length minimum (I think this is at least |
| 122 # 256 bits of data) | 135 # 256 bits of data) |
| 123 | 136 |
| 186 self.claim['user:email'] = copy(claim) | 199 self.claim['user:email'] = copy(claim) |
| 187 self.claim['user:email']['roles'] = [ "user:email" ] | 200 self.claim['user:email']['roles'] = [ "user:email" ] |
| 188 self.jwt['user:email'] = b2s(jwt.encode(self.claim['user:email'], secret, | 201 self.jwt['user:email'] = b2s(jwt.encode(self.claim['user:email'], secret, |
| 189 algorithm='HS256')) | 202 algorithm='HS256')) |
| 190 | 203 |
| 204 # generate valid claim with limited user:emailnorest role | |
| 205 self.claim['user:emailnorest'] = copy(claim) | |
| 206 self.claim['user:emailnorest']['roles'] = [ "user:emailnorest" ] | |
| 207 self.jwt['user:emailnorest'] = b2s(jwt.encode(self.claim['user:emailnorest'], secret, | |
| 208 algorithm='HS256')) | |
| 209 | |
| 191 self.db.tx_Source = 'web' | 210 self.db.tx_Source = 'web' |
| 192 | 211 |
| 193 self.db.issue.addprop(tx_Source=hyperdb.String()) | 212 self.db.issue.addprop(tx_Source=hyperdb.String()) |
| 194 self.db.issue.addprop(anint=hyperdb.Integer()) | 213 self.db.issue.addprop(anint=hyperdb.Integer()) |
| 195 self.db.issue.addprop(afloat=hyperdb.Number()) | 214 self.db.issue.addprop(afloat=hyperdb.Number()) |
| 3011 del(out[0]) | 3030 del(out[0]) |
| 3012 self.db.setCurrentUser('admin') | 3031 self.db.setCurrentUser('admin') |
| 3013 | 3032 |
| 3014 @skip_jwt | 3033 @skip_jwt |
| 3015 def test_user_email_jwt(self): | 3034 def test_user_email_jwt(self): |
| 3035 '''tests "Rest Access" permission present case''' | |
| 3016 # self.dummy_client.main() closes database, so | 3036 # self.dummy_client.main() closes database, so |
| 3017 # we need a new test with setup called for each test | 3037 # we need a new test with setup called for each test |
| 3018 out = [] | 3038 out = [] |
| 3019 def wh(s): | 3039 def wh(s): |
| 3020 out.append(s) | 3040 out.append(s) |
| 3076 # should have email addresses. Order is by id by default. | 3096 # should have email addresses. Order is by id by default. |
| 3077 self.assertTrue('address' in json_dict['data']['collection'][0]) | 3097 self.assertTrue('address' in json_dict['data']['collection'][0]) |
| 3078 self.assertTrue('address' in json_dict['data']['collection'][1]) | 3098 self.assertTrue('address' in json_dict['data']['collection'][1]) |
| 3079 self.assertTrue('address' in json_dict['data']['collection'][2]) | 3099 self.assertTrue('address' in json_dict['data']['collection'][2]) |
| 3080 | 3100 |
| 3101 @skip_jwt | |
| 3102 def test_user_emailnorest_jwt(self): | |
| 3103 '''tests "Rest Access" permission missing case''' | |
| 3104 # self.dummy_client.main() closes database, so | |
| 3105 # we need a new test with setup called for each test | |
| 3106 out = [] | |
| 3107 def wh(s): | |
| 3108 out.append(s) | |
| 3109 | |
| 3110 secret = self.db.config.WEB_JWT_SECRET | |
| 3111 | |
| 3112 # verify library and tokens are correct | |
| 3113 self.assertRaises(jwt.exceptions.InvalidTokenError, | |
| 3114 jwt.decode, self.jwt['expired'], | |
| 3115 secret, algorithms=['HS256'], | |
| 3116 audience=self.db.config.TRACKER_WEB, | |
| 3117 issuer=self.db.config.TRACKER_WEB) | |
| 3118 | |
| 3119 result = jwt.decode(self.jwt['user'], | |
| 3120 secret, algorithms=['HS256'], | |
| 3121 audience=self.db.config.TRACKER_WEB, | |
| 3122 issuer=self.db.config.TRACKER_WEB) | |
| 3123 self.assertEqual(self.claim['user'],result) | |
| 3124 | |
| 3125 result = jwt.decode(self.jwt['user:email'], | |
| 3126 secret, algorithms=['HS256'], | |
| 3127 audience=self.db.config.TRACKER_WEB, | |
| 3128 issuer=self.db.config.TRACKER_WEB) | |
| 3129 self.assertEqual(self.claim['user:email'],result) | |
| 3130 | |
| 3131 # set environment for all jwt tests | |
| 3132 env = { | |
| 3133 'PATH_INFO': 'rest/data/user', | |
| 3134 'HTTP_HOST': 'localhost', | |
| 3135 'TRACKER_NAME': 'rounduptest', | |
| 3136 "REQUEST_METHOD": "GET" | |
| 3137 } | |
| 3138 self.dummy_client = client.Client(self.instance, MockNull(), env, | |
| 3139 [], None) | |
| 3140 self.dummy_client.db = self.db | |
| 3141 self.dummy_client.request.headers.get = self.get_header | |
| 3142 self.empty_form = cgi.FieldStorage() | |
| 3143 self.terse_form = cgi.FieldStorage() | |
| 3144 self.terse_form.list = [ | |
| 3145 cgi.MiniFieldStorage('@verbose', '0'), | |
| 3146 ] | |
| 3147 self.dummy_client.form = cgi.FieldStorage() | |
| 3148 self.dummy_client.form.list = [ | |
| 3149 cgi.MiniFieldStorage('@fields', 'username,address'), | |
| 3150 ] | |
| 3151 # accumulate json output for further analysis | |
| 3152 self.dummy_client.write = wh | |
| 3153 | |
| 3154 # set up for limited user:email role token | |
| 3155 env['HTTP_AUTHORIZATION'] = 'bearer %s'%self.jwt['user:emailnorest'] | |
| 3156 self.dummy_client.main() | |
| 3157 json_dict = json.loads(b2s(out[0])) | |
| 3158 # user will be joe id 3 as auth works | |
| 3159 self.assertTrue('1', self.db.getuid()) | |
| 3160 { "error": { "status": 403, "msg": "Forbidden." } } | |
| 3161 self.assertTrue('error' in json_dict) | |
| 3162 self.assertTrue(json_dict['error']['status'], 403) | |
| 3163 self.assertTrue(json_dict['error']['msg'], "Forbidden.") | |
| 3081 | 3164 |
| 3082 @skip_jwt | 3165 @skip_jwt |
| 3083 def test_disabled_jwt(self): | 3166 def test_disabled_jwt(self): |
| 3084 # self.dummy_client.main() closes database, so | 3167 # self.dummy_client.main() closes database, so |
| 3085 # we need a new test with setup called for each test | 3168 # we need a new test with setup called for each test |
