Check if user A can access user B's resource

This test uses two users, generates their tokens
and tries to use token B to access the user A's
profile.  If it succeeds this shows a security
vulnerability exists.

Change-Id: I67c89f74985e598999080f6bd89b55934df686ef
Implements: blueprint test-unauthed
This commit is contained in:
Henry Yamauchi 2016-02-10 16:10:22 -06:00
parent 1ad29ce562
commit 0369024089
7 changed files with 236 additions and 0 deletions

View File

@ -1,10 +1,17 @@
[syntribos]
endpoint=<yourapiendpoint>
#version=v2 # used for cross auth tests (-t AUTH_WITH_SOMEONE_ELSE_TOKEN)
[user]
username=<yourusername>
password=<yourpassword>
user_id=<youruserid>
# used for cross auth tests (-t AUTH_WITH_SOMEONE_ELSE_TOKEN)
#[alt_user]
#username=<alt_username>
#password=<alt_password>
#user_id=<alt_userid>
[auth]
endpoint=<yourkeystoneurl>

View File

@ -0,0 +1,3 @@
GET /v2.0/users/USER_ID HTTP/1.1
Accept: application/json
X-Auth-Token: CALL_EXTERNAL|syntribos.extensions.identity.client:get_token_v2:["user"]|

View File

@ -23,3 +23,7 @@ class MainConfig(data_interfaces.ConfigSectionInterface):
@property
def endpoint(self):
return self.get("endpoint")
@property
def version(self):
return self.get("version")

View File

@ -0,0 +1,15 @@
"""
Copyright 2015 Rackspace
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

View File

@ -0,0 +1,122 @@
"""
Copyright 2016 Rackspace
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import os
from syntribos.clients.http import client
from syntribos.issue import Issue
import syntribos.tests.auth.datagen
from syntribos.tests import base
data_dir = os.environ.get("CAFE_DATA_DIR_PATH")
class BaseAuthTestCase(base.BaseTestCase):
client = client()
failure_keys = None
success_keys = None
@classmethod
def data_driven_failure_cases(cls):
failure_assertions = []
if cls.failure_keys is None:
return []
for line in cls.failure_keys:
failure_assertions.append((cls.assertNotIn,
line, cls.resp.content))
return failure_assertions
@classmethod
def data_driven_pass_cases(cls):
if cls.success_keys is None:
return True
for s in cls.success_keys:
if s in cls.resp.content:
return True
return False
@classmethod
def setUpClass(cls):
super(BaseAuthTestCase, cls).setUpClass()
cls.issues = []
cls.failures = []
cls.resp = cls.client.request(
method=cls.request.method, url=cls.request.url,
headers=cls.request.headers, params=cls.request.params,
data=cls.request.data)
@classmethod
def tearDownClass(cls):
super(BaseAuthTestCase, cls).tearDownClass()
for issue in cls.issues:
if issue.failure:
cls.failures.append(issue.as_dict())
def test_case(self):
text = ("This request did not fail with 404 (User not found)"
" therefore it indicates that authentication with"
" another user's token was successful.")
self.register_issue(
Issue(test="try_alt_user_token",
severity="High",
text=text,
assertions=[(self.assertTrue, self.resp.status_code == 404)])
)
self.test_issues()
@classmethod
def get_test_cases(cls, filename, file_content):
"""Generates the test cases
For this particular test, only a single test
is created (in addition to the base case, that is)
"""
alt_user_config = syntribos.extensions.identity.config.UserConfig(
section_name='alt_user')
alt_user_id = alt_user_config.user_id
if alt_user_id is None:
return
request_obj = syntribos.tests.auth.datagen.AuthParser.create_request(
file_content, os.environ.get("SYNTRIBOS_ENDPOINT"))
prepared_copy = request_obj.get_prepared_copy()
cls.init_response = cls.client.send_request(prepared_copy)
prefix_name = "{filename}_{test_name}_{fuzz_file}_".format(
filename=filename, test_name=cls.test_name, fuzz_file='auth')
main_config = syntribos.config.MainConfig()
version = main_config.version
if version is None or version == 'v2':
alt_token = syntribos.extensions.identity.client.get_token_v2(
'alt_user', 'auth')
else:
alt_token = syntribos.extensions.identity.client.get_token_v3(
'alt_user', 'auth')
alt_user_token_request = request_obj.get_prepared_copy()
for h in alt_user_token_request.headers:
if 'x-auth-token' == h.lower():
alt_user_token_request.headers[h] = alt_token
test_name = prefix_name + 'another_users_token'
def test_gen(test_name, request):
yield (test_name, request)
for name, req in test_gen(test_name, alt_user_token_request):
c = cls.extend_class(test_name,
{"request": alt_user_token_request})
yield c

View File

@ -0,0 +1,58 @@
"""
Copyright 2016 Rackspace
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from syntribos.clients.http.models import RequestHelperMixin
from syntribos.clients.http.models import RequestObject
from syntribos.clients.http import parser
from syntribos.extensions.identity.config import UserConfig
class AuthMixin(object):
"""AuthMixin Class
AuthBehavior provides utility methods to manipulate data before
a request object is created.
"""
@staticmethod
def remove_braces(string):
return string.replace("}", "").replace("{", "")
class AuthRequest(RequestObject, AuthMixin, RequestHelperMixin):
"""AuthRequest Class
This class specializes the generic RequestObject to
create an auth test specific class.
"""
def prepare_request(self, auth_type=None):
super(AuthRequest, self).prepare_request()
if auth_type != "url":
self.url = self.remove_braces(self.url)
user_config = UserConfig(section_name='user')
user_id = user_config.user_id
self.url = self.url.replace('USER_ID', user_id)
class AuthParser(parser):
"""AuthParser Class
This class is a container class to hold
an auth request object type.
"""
request_model_type = AuthRequest

View File

@ -0,0 +1,27 @@
"""
Copyright 2016 Rackspace
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from syntribos.tests.auth import base_auth
class AuthWithSomeoneElsesToken(base_auth.BaseAuthTestCase):
"""AuthWithSomeoneElsesToken Class
This is just a specialization of the base auth test class
which supplies the test name and type.
"""
test_name = "AUTH_WITH_SOMEONE_ELSE_TOKEN"
test_type = "headers"