added auth, request parser, sql_tests
This commit is contained in:
parent
19598a404b
commit
3d3b3f44ee
|
@ -0,0 +1,28 @@
|
|||
_syntribos()
|
||||
{
|
||||
local cur prev opts
|
||||
COMPREPLY=()
|
||||
cur=$(echo "${COMP_WORDS[COMP_CWORD]}"|sed 's/\\/\\\\/g')
|
||||
prev=$(echo "${COMP_WORDS[COMP_CWORD-1]}"|sed 's/\\/\\\\/g')
|
||||
|
||||
if [[ ${COMP_CWORD} == 1 ]]; then
|
||||
opts=$(python -c "from cafe.drivers.unittest.autocomplete import print_configs;print_configs()" 2>/dev/null)
|
||||
elif [[ ${COMP_CWORD} == 2 ]]; then
|
||||
COMPREPLY=( $(compgen -o filenames -A file "$cur") )
|
||||
elif [[ ${cur} == -* ]]; then
|
||||
opts="--help --test-types"
|
||||
else
|
||||
opts=$(python -c "from syntribos.runner import Runner;Runner.populate_tests();from syntribos.tests.base import test_table;from pprint import pprint;[pprint(i) for i in test_table.keys()]" 2>/dev/null)
|
||||
fi
|
||||
|
||||
if [[ ${COMP_CWORD} -ne 2 ]]; then
|
||||
opts=$(echo $opts|sed 's/\\/\\\\/g')
|
||||
COMPREPLY=( $(compgen -W '${opts}' -- ${cur}) )
|
||||
COMPREPLY="${COMPREPLY} "
|
||||
fi
|
||||
#sed for windows backslash
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -o nospace -F _syntribos syntribos
|
6
setup.py
6
setup.py
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
Copyright 2013 Rackspace
|
||||
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.
|
||||
|
@ -18,7 +18,7 @@ from setuptools import setup, find_packages
|
|||
|
||||
requires = open('pip-requires').readlines()
|
||||
setup(
|
||||
name='apiscan',
|
||||
name='syntribos',
|
||||
version='0.0.1',
|
||||
description=('API Security Scanner'),
|
||||
long_description='{0}\n\n{1}'.format(
|
||||
|
@ -31,6 +31,8 @@ setup(
|
|||
install_requires=requires,
|
||||
license=open('LICENSE').read(),
|
||||
zip_safe=False,
|
||||
entry_points={'console_scripts': [
|
||||
'syntribos = syntribos.runner:entry_point']},
|
||||
classifiers=(
|
||||
'Development Status :: 1 - Planning',
|
||||
'Intended Audience :: Developers',
|
||||
|
|
|
@ -1,92 +1,84 @@
|
|||
import json
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from cafe.drivers.unittest.datasets import DatasetList
|
||||
from cafe.engine.behaviors import BaseBehavior
|
||||
|
||||
|
||||
class DynamicDataGen(DatasetList):
|
||||
strings = None
|
||||
|
||||
def __init__(self, name, data, ignore_var):
|
||||
for stri in self.strings:
|
||||
ctr = 0
|
||||
class FuzzBehavior(BaseBehavior):
|
||||
@classmethod
|
||||
def fuzz_body(cls, strings, data, skip_var, name):
|
||||
for str_num, stri in enumerate(strings, 1):
|
||||
if isinstance(data, dict):
|
||||
for dic in self._build_combinations(
|
||||
name, stri, data, ignore_var):
|
||||
self.append_new_dataset(
|
||||
"{0}-{1}-{2}".format(self.test_name, name, ctr), {
|
||||
"model": json.dumps(dic).replace(
|
||||
ignore_var, "")})
|
||||
ctr += 1
|
||||
model_iter = cls._build_combinations(stri, data, skip_var)
|
||||
elif isinstance(data, ElementTree.Element):
|
||||
for element in self._build_xml_combinations(
|
||||
name, stri, data, ignore_var):
|
||||
self.append_new_dataset(
|
||||
"{0}-{1}-{2}".format(self.test_name, name, ctr), {
|
||||
"model": ElementTree.tostring(element).replace(
|
||||
ignore_var, "")})
|
||||
ctr += 1
|
||||
model_iter = cls._build_xml_combinations(stri, data, skip_var)
|
||||
else:
|
||||
raise TypeError("Format not recognized!")
|
||||
for model_num, model in enumerate(model_iter, 1):
|
||||
name = "{0}_str{1}_model{2}".format(name, str_num, model_num)
|
||||
if isinstance(dict):
|
||||
string = json.dumps(model)
|
||||
else:
|
||||
string = ElementTree.tostring(model)
|
||||
string = string.replace(skip_var, "")
|
||||
yield (name, string)
|
||||
|
||||
def _build_combinations(self, name, stri, dic, ignore_var):
|
||||
@classmethod
|
||||
def _build_combinations(cls, stri, dic, skip_var):
|
||||
for key, val in dic.iteritems():
|
||||
if ignore_var in key:
|
||||
if skip_var in key:
|
||||
continue
|
||||
elif isinstance(val, dict):
|
||||
for ret in self._build_combinations(
|
||||
name, stri, val, ignore_var):
|
||||
yield self._merge_dictionaries(dic, {key: ret})
|
||||
for ret in cls._build_combinations(stri, val, skip_var):
|
||||
yield cls._merge_dictionaries(dic, {key: ret})
|
||||
elif isinstance(val, list):
|
||||
for i, v in enumerate(val):
|
||||
list_ = [_ for _ in val]
|
||||
if isinstance(v, dict):
|
||||
for ret in self._build_combinations(
|
||||
name, stri, v, ignore_var):
|
||||
for ret in cls._build_combinations(stri, v, skip_var):
|
||||
list_[i] = ret.copy()
|
||||
yield self._merge_dictionaries(dic, {key: list_})
|
||||
yield cls._merge_dictionaries(dic, {key: list_})
|
||||
else:
|
||||
list_[i] = stri
|
||||
yield self._merge_dictionaries(dic, {key: list_})
|
||||
yield cls._merge_dictionaries(dic, {key: list_})
|
||||
else:
|
||||
yield self._merge_dictionaries(dic, {key: stri})
|
||||
yield cls._merge_dictionaries(dic, {key: stri})
|
||||
|
||||
def _merge_dictionaries(self, x, y):
|
||||
@staticmethod
|
||||
def _merge_dictionaries(x, y):
|
||||
z = x.copy()
|
||||
z.update(y)
|
||||
return z
|
||||
|
||||
def _build_xml_combinations(self, name, stri, ele, ignore_var):
|
||||
if ignore_var not in ele.tag:
|
||||
@classmethod
|
||||
def _build_xml_combinations(cls, stri, ele, skip_var):
|
||||
if skip_var not in ele.tag:
|
||||
if ele.text is not None:
|
||||
yield self._update_element(ele, stri)
|
||||
for attr in self._build_combinations(
|
||||
name, stri, ele.attrib, ignore_var):
|
||||
yield self._update_attribs(ele, attr)
|
||||
yield cls._update_element(ele, stri)
|
||||
for attr in cls._build_combinations(stri, ele.attrib, skip_var):
|
||||
yield cls._update_attribs(ele, attr)
|
||||
for i, element in enumerate(list(ele)):
|
||||
for ret in self._build_xml_combinations(
|
||||
name, stri, element, ignore_var):
|
||||
for ret in cls._build_xml_combinations(
|
||||
stri, element, skip_var):
|
||||
list_ = [_ for _ in ele.getchildren()]
|
||||
list_[i] = ret.copy()
|
||||
yield self._update_inner_element(ele, list_)
|
||||
yield cls._update_inner_element(ele, list_)
|
||||
|
||||
def _update_element(self, ele, stri):
|
||||
@staticmethod
|
||||
def _update_element(ele, stri):
|
||||
ret = ele.copy()
|
||||
ret.text = stri
|
||||
return ret
|
||||
|
||||
def _update_attribs(self, ele, attribs):
|
||||
@staticmethod
|
||||
def _update_attribs(ele, attribs):
|
||||
ret = ele.copy()
|
||||
ret.attrib = attribs
|
||||
return ret
|
||||
|
||||
def _update_inner_element(self, ele, list_):
|
||||
@staticmethod
|
||||
def _update_inner_element(ele, list_):
|
||||
ret = ele.copy()
|
||||
for i, v in enumerate(list_):
|
||||
ret[i] = v
|
||||
return ret
|
||||
|
||||
|
||||
class ExampleDynamicDataGen(DynamicDataGen):
|
||||
strings = ["abc", "def"]
|
||||
test_name = "Example"
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
from cafe.engine.behaviors import BaseBehavior
|
||||
from cafe.engine.http.client import AutoMarshallingHTTPClient
|
||||
|
||||
from syntribos.extensions.rax_auth.models import auth_models
|
||||
from syntribos.extensions.rax_auth.auth_config import (
|
||||
UserAuthConfig, UserConfig)
|
||||
|
||||
|
||||
class TokensBehavior(BaseBehavior):
|
||||
@classmethod
|
||||
def get_access_data_config(cls, user_config, userauth_config):
|
||||
return cls.get_access_data(
|
||||
url=user_config.endpoint or userauth_config.endpoint,
|
||||
username=user_config.username,
|
||||
password=user_config.password,
|
||||
tenant_name=user_config.tenant_name,
|
||||
tenant_id=user_config.tenant_id,
|
||||
token=user_config.token,
|
||||
api_key=user_config.api_key,
|
||||
serialize_format=userauth_config.serialize_format,
|
||||
deserialize_format=userauth_config.deserialize_format)
|
||||
|
||||
@classmethod
|
||||
def get_access_data(cls, *args, **kwargs):
|
||||
return cls.authenticate(*args, **kwargs).entity
|
||||
|
||||
@classmethod
|
||||
def authenticate(
|
||||
cls, url, username=None, password=None, tenant_name=None,
|
||||
tenant_id=None, token=None, api_key=None, rsa_key=None,
|
||||
domain=None, serialize_format="json", deserialize_format="json"):
|
||||
url = '{0}/tokens'.format(url)
|
||||
client = AutoMarshallingHTTPClient(
|
||||
serialize_format, deserialize_format)
|
||||
|
||||
client.default_headers["Content-Type"] = "application/{0}".format(
|
||||
serialize_format)
|
||||
client.default_headers["Accept"] = "application/{0}".format(
|
||||
deserialize_format)
|
||||
|
||||
kwargs = {}
|
||||
kwargs["tenant_name"] = tenant_name
|
||||
kwargs["tenant_id"] = tenant_id
|
||||
|
||||
if password is not None:
|
||||
kwargs["password_creds"] = auth_models.PasswordCredentials(
|
||||
username=username, password=password)
|
||||
|
||||
if token is not None:
|
||||
kwargs["token_creds"] = auth_models.Token(id_=token)
|
||||
|
||||
if api_key is not None:
|
||||
kwargs["api_key_creds"] = auth_models.APIKeyCredentials(
|
||||
username=username, api_key=api_key)
|
||||
|
||||
request_entity = auth_models.Auth(**kwargs)
|
||||
|
||||
r = client.request(
|
||||
"POST", url, request_entity=request_entity,
|
||||
response_entity_type=auth_models.AuthResponse)
|
||||
if not r.ok:
|
||||
raise Exception("Failed to authenticate")
|
||||
|
||||
r.entity = auth_models.AuthResponse.deserialize(
|
||||
r.content, deserialize_format)
|
||||
if r.entity is None:
|
||||
raise Exception("Failed to parse Auth response Body")
|
||||
return r
|
||||
|
||||
|
||||
def get_access(section_name):
|
||||
return TokensBehavior.get_access_data_config(
|
||||
UserConfig(section_name=section_name), UserAuthConfig())
|
|
@ -0,0 +1,52 @@
|
|||
from cafe.engine.models.data_interfaces import ConfigSectionInterface
|
||||
|
||||
|
||||
class UserAuthConfig(ConfigSectionInterface):
|
||||
SECTION_NAME = 'auth'
|
||||
|
||||
@property
|
||||
def endpoint(self):
|
||||
return self.get("endpoint")
|
||||
|
||||
@property
|
||||
def serialize_format(self):
|
||||
return self.get("serialize_format", "json")
|
||||
|
||||
@property
|
||||
def deserialize_format(self):
|
||||
return self.get("deserialize_format", "json")
|
||||
|
||||
|
||||
class UserConfig(ConfigSectionInterface):
|
||||
SECTION_NAME = 'user'
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
return self.get("username")
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
return self.get_raw("password")
|
||||
|
||||
@property
|
||||
def api_key(self):
|
||||
return self.get_raw("api_key")
|
||||
|
||||
@property
|
||||
def tenant_id(self):
|
||||
return self.get("tenant_id")
|
||||
|
||||
@property
|
||||
def tenant_name(self):
|
||||
return self.get("tenant_name")
|
||||
|
||||
@property
|
||||
def token(self):
|
||||
return self.get("token")
|
||||
|
||||
@property
|
||||
def endpoint(self):
|
||||
"""Added to allow different users to auth at different endpoints. For
|
||||
example the admin user needs to use an internal endpoint.
|
||||
"""
|
||||
return self.get("endpoint")
|
|
@ -0,0 +1,452 @@
|
|||
import json
|
||||
from xml.etree import ElementTree as ET
|
||||
from cafe.engine.models.base import (
|
||||
AutoMarshallingModel, AutoMarshallingListModel)
|
||||
|
||||
|
||||
class V2_0Constants(object):
|
||||
XML_NS = 'http://docs.openstack.org/identity/api/v2.0'
|
||||
XML_NS_OPENSTACK_COMMON = 'http://docs.openstack.org/common/api/v1.0'
|
||||
XML_NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
|
||||
XML_NS_OS_KSADM = \
|
||||
'http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0'
|
||||
XML_NS_OS_KSEC2 = \
|
||||
'http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0'
|
||||
XML_NS_RAX_KSQA = \
|
||||
'http://docs.rackspace.com/identity/api/ext/RAX-KSQA/v1.0'
|
||||
XML_NS_RAX_KSKEY = \
|
||||
'http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0'
|
||||
XML_NS_RAX_AUTH = \
|
||||
'http://docs.rackspace.com/identity/api/ext/RAX-AUTH/v1.0'
|
||||
XML_NS_RAX_KSGRP = \
|
||||
'http://docs.rackspace.com/identity/api/ext/RAX-KSGRP/v1.0'
|
||||
XML_NS_ATOM = 'http://www.w3.org/2005/Atom'
|
||||
|
||||
|
||||
class BaseIdentityModel(AutoMarshallingModel):
|
||||
_namespaces = V2_0Constants
|
||||
|
||||
def __init__(self, kwargs):
|
||||
super(BaseIdentityModel, self).__init__()
|
||||
for var in kwargs:
|
||||
if var != "self" and not var.startswith("_"):
|
||||
setattr(self, var, kwargs.get(var))
|
||||
|
||||
@classmethod
|
||||
def _remove_xml_namespaces(cls, element):
|
||||
for key, value in cls._namespaces.__dict__.items():
|
||||
if key.startswith("__"):
|
||||
continue
|
||||
element = cls._remove_namespace(element, value)
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def _remove_namespace(cls, element, XML_NS):
|
||||
return cls._remove_xml_etree_namespace(element, XML_NS)
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
data_dict = json.loads(serialized_str)
|
||||
return cls._dict_to_obj(data_dict)
|
||||
|
||||
@classmethod
|
||||
def _xml_to_obj(cls, serialized_str):
|
||||
element = ET.fromstring(serialized_str)
|
||||
return cls._xml_ele_to_obj(
|
||||
cls._remove_xml_namespaces(element))
|
||||
|
||||
def _obj_to_json(self):
|
||||
return json.dumps(self._obj_to_dict())
|
||||
|
||||
def _obj_to_xml(self):
|
||||
element = self._obj_to_xml_ele()
|
||||
return ET.tostring(element)
|
||||
|
||||
@staticmethod
|
||||
def _find(element, tag):
|
||||
if element is None:
|
||||
return ET.Element(None)
|
||||
new_element = element.find(tag)
|
||||
if new_element is None:
|
||||
return ET.Element(None)
|
||||
return new_element
|
||||
|
||||
|
||||
class BaseIdentityListModel(AutoMarshallingListModel, BaseIdentityModel):
|
||||
pass
|
||||
|
||||
|
||||
class EmptyModel(object):
|
||||
def _obj_to_dict(self):
|
||||
return None
|
||||
|
||||
def _obj_to_xml_ele(self):
|
||||
return ET.Element(None)
|
||||
|
||||
|
||||
class AuthResponse(BaseIdentityModel):
|
||||
def __init__(
|
||||
self, token=None, service_catalog=None, user=None):
|
||||
super(AuthResponse, self).__init__(locals())
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, data):
|
||||
return cls(
|
||||
token=AuthResponseToken._dict_to_obj(data.get("token")),
|
||||
user=User._dict_to_obj(data.get("user")),
|
||||
service_catalog=ServiceCatalog._dict_to_obj(
|
||||
data.get("serviceCatalog")))
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
data = json.loads(serialized_str)
|
||||
return cls._dict_to_obj(data.get("access"))
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, element):
|
||||
service_catalog = ServiceCatalog._xml_ele_to_obj(
|
||||
cls._find(element, "serviceCatalog"))
|
||||
return cls(
|
||||
token=AuthResponseToken._xml_ele_to_obj(
|
||||
cls._find(element, "token")),
|
||||
user=User._xml_ele_to_obj(cls._find(element, "user")),
|
||||
service_catalog=service_catalog)
|
||||
|
||||
def get_service(self, name):
|
||||
for service in self.service_catalog:
|
||||
if service.name == name:
|
||||
return service
|
||||
return None
|
||||
|
||||
|
||||
class Tenant(BaseIdentityModel):
|
||||
def __init__(self, name=None, id_=None):
|
||||
super(Tenant, self).__init__(locals())
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, element):
|
||||
if element is None:
|
||||
return cls()
|
||||
return cls(
|
||||
name=element.attrib.get("name"), id_=element.attrib.get("id"))
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, data):
|
||||
if data is None:
|
||||
return cls()
|
||||
return cls(
|
||||
name=data.get("name"), id_=data.get("id"))
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
data = json.loads(serialized_str)
|
||||
return cls._dict_to_obj(data.get("tenant"))
|
||||
|
||||
|
||||
class AuthResponseToken(BaseIdentityModel):
|
||||
def __init__(
|
||||
self, id_=None, expires=None, tenant=None, authenticated_by=None):
|
||||
super(AuthResponseToken, self).__init__(locals())
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, data):
|
||||
return cls(id_=data.get("id"),
|
||||
expires=data.get("expires"),
|
||||
tenant=Tenant._dict_to_obj(data.get("tenant")),
|
||||
authenticated_by=data.get("RAX-AUTH:authenticatedBy"))
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, element):
|
||||
authenticated_by = cls._find(element, "authenticatedBy")
|
||||
authenticated_by = authenticated_by.findtext("credential")
|
||||
return cls(
|
||||
id_=element.attrib.get("id"),
|
||||
expires=element.attrib.get("expires"),
|
||||
tenant=Tenant._xml_ele_to_obj(cls._find(element, "tenant")),
|
||||
authenticated_by=authenticated_by)
|
||||
|
||||
|
||||
class ServiceCatalog(BaseIdentityListModel):
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, data):
|
||||
return ServiceCatalog(
|
||||
[Service._dict_to_obj(service) for service in data])
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, element):
|
||||
return ServiceCatalog(
|
||||
[Service._xml_ele_to_obj(service) for service in element])
|
||||
|
||||
|
||||
class User(BaseIdentityModel):
|
||||
def __init__(
|
||||
self, id_=None, name=None, roles=None,
|
||||
rax_auth_default_region=None, rax_auth_federated=None):
|
||||
super(User, self).__init__(locals())
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, data):
|
||||
if data is None:
|
||||
return cls()
|
||||
region = data.get("RAX-AUTH:defaultRegion")
|
||||
if isinstance(region, list):
|
||||
region = region[0]
|
||||
return cls(
|
||||
id_=data.get("id"),
|
||||
name=data.get("name"),
|
||||
roles=Roles._dict_to_obj(data.get("roles")),
|
||||
rax_auth_default_region=region,
|
||||
rax_auth_federated=data.get("RAX-AUTH:federated"))
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, element):
|
||||
return cls(
|
||||
id_=element.attrib.get("id"),
|
||||
name=element.attrib.get("name"),
|
||||
roles=Roles._xml_ele_to_obj(cls._find(element, "roles")),
|
||||
rax_auth_default_region=element.attrib.get("defaultRegion"),
|
||||
rax_auth_federated=element.attrib.get("federated"))
|
||||
|
||||
|
||||
class Service(BaseIdentityModel):
|
||||
def __init__(self, endpoints=None, name=None, type_=None):
|
||||
super(Service, self).__init__(locals())
|
||||
|
||||
def get_endpoint(self, region):
|
||||
"""
|
||||
Returns the endpoint that matches the provided region,
|
||||
or None if such an endpoint is not found
|
||||
"""
|
||||
for endpoint in self.endpoints:
|
||||
if getattr(endpoint, "region"):
|
||||
if str(endpoint.region).lower() == str(region).lower():
|
||||
return endpoint
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, data):
|
||||
return cls(
|
||||
endpoints=Endpoints._dict_to_obj(data.get("endpoints")),
|
||||
name=data.get("name"), type_=data.get("type"))
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, element):
|
||||
return cls(
|
||||
endpoints=Endpoints._xml_ele_to_obj(
|
||||
element.findall("endpoint")),
|
||||
name=element.attrib.get("name"),
|
||||
type_=element.attrib.get("type"))
|
||||
|
||||
|
||||
class Endpoints(BaseIdentityListModel):
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, elements):
|
||||
if not elements:
|
||||
return cls()
|
||||
return cls([Endpoint._xml_ele_to_obj(endp) for endp in elements])
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, data):
|
||||
if not data:
|
||||
return cls()
|
||||
return cls([Endpoint._dict_to_obj(endpoint) for endpoint in data])
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, string):
|
||||
data = json.loads(string)
|
||||
return cls._dict_to_obj(data.get("endpoints"))
|
||||
|
||||
|
||||
class Endpoint(BaseIdentityModel):
|
||||
def __init__(self, id_=None, tenant_id=None, region=None, type_=None,
|
||||
public_url=None, internal_url=None, admin_url=None,
|
||||
version_id=None, version_info=None, version_list=None,
|
||||
name=None):
|
||||
super(Endpoint, self).__init__(locals())
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, element):
|
||||
version = element.find("version")
|
||||
version_attrib = version.attrib if version is not None else {}
|
||||
return cls(
|
||||
id_=element.attrib.get("id"),
|
||||
tenant_id=element.attrib.get("tenantId"),
|
||||
region=element.attrib.get("region"),
|
||||
type_=element.attrib.get("type"),
|
||||
name=element.attrib.get("name"),
|
||||
public_url=element.attrib.get("publicURL"),
|
||||
internal_url=element.attrib.get("internalURL"),
|
||||
admin_url=element.attrib.get("adminURL"),
|
||||
version_id=version_attrib.get("id"),
|
||||
version_info=version_attrib.get("info"),
|
||||
version_list=version_attrib.get("list"))
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, data):
|
||||
return cls(
|
||||
id_=data.get("id"),
|
||||
tenant_id=data.get("tenantId"),
|
||||
region=data.get("region"),
|
||||
type_=data.get("type"),
|
||||
name=data.get("name"),
|
||||
public_url=data.get("publicURL"),
|
||||
internal_url=data.get("internalURL"),
|
||||
admin_url=data.get("adminURL"),
|
||||
version_id=data.get("versionId"),
|
||||
version_info=data.get("versionInfo"),
|
||||
version_list=data.get("versionList"))
|
||||
|
||||
|
||||
class Roles(BaseIdentityListModel):
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, elements):
|
||||
return Roles(
|
||||
[Role._xml_ele_to_obj(element) for element in elements])
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, data):
|
||||
return Roles([Role._dict_to_obj(obj) for obj in data])
|
||||
|
||||
|
||||
class Role(BaseIdentityModel):
|
||||
def __init__(
|
||||
self, id_=None, name=None, description=None,
|
||||
rax_auth_propagate=None, tenant_id=None, service_id=None):
|
||||
super(Role, self).__init__(locals())
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, element):
|
||||
if element is None:
|
||||
return None
|
||||
return cls(
|
||||
id_=element.attrib.get("id"), name=element.attrib.get("name"),
|
||||
description=element.attrib.get("description"),
|
||||
rax_auth_propagate=element.attrib.get("propagate"),
|
||||
service_id=element.attrib.get("serviceId"),
|
||||
tenant_id=element.attrib.get("tenantId"))
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, data):
|
||||
if data is None:
|
||||
return None
|
||||
return cls(
|
||||
id_=data.get("id"), name=data.get("name"),
|
||||
description=data.get("description"),
|
||||
rax_auth_propagate=data.get("rax-auth:propagate"),
|
||||
service_id=data.get("serviceId"),
|
||||
tenant_id=data.get("tenantId"))
|
||||
|
||||
|
||||
class Auth(BaseIdentityModel):
|
||||
xmlns = V2_0Constants.XML_NS
|
||||
|
||||
def __init__(
|
||||
self, password_creds=None, rsa_creds=None, token_creds=None,
|
||||
api_key_creds=None, domain=None, tenant_name=None, tenant_id=None):
|
||||
password_creds = password_creds or EmptyModel()
|
||||
token_creds = token_creds or EmptyModel()
|
||||
api_key_creds = api_key_creds or EmptyModel()
|
||||
super(Auth, self).__init__(locals())
|
||||
|
||||
def _obj_to_dict(self):
|
||||
attrs = {
|
||||
"tenantName": self.tenant_name,
|
||||
"tenantId": self.tenant_id,
|
||||
"passwordCredentials": self.password_creds._obj_to_dict(),
|
||||
"RAX-KSKEY:apiKeyCredentials": self.api_key_creds._obj_to_dict(),
|
||||
"token": self.token_creds._obj_to_dict()}
|
||||
return {'auth': self._remove_empty_values(attrs)}
|
||||
|
||||
def _obj_to_xml_ele(self):
|
||||
element = ET.Element('auth')
|
||||
element = self._set_xml_etree_element(
|
||||
element, {"tenantName": self.tenant_name, "xmlns": self.xmlns,
|
||||
"tenantId": self.tenant_id})
|
||||
element.append(self.password_creds._obj_to_xml_ele())
|
||||
element.append(self.rsa_creds._obj_to_xml_ele())
|
||||
element.append(self.token_creds._obj_to_xml_ele())
|
||||
element.append(self.api_key_creds._obj_to_xml_ele())
|
||||
element.append(self.domain._obj_to_xml_ele())
|
||||
return element
|
||||
|
||||
|
||||
class PasswordCredentials(BaseIdentityModel):
|
||||
|
||||
def __init__(self, username=None, password=None):
|
||||
super(PasswordCredentials, self).__init__(locals())
|
||||
|
||||
def _obj_to_dict(self):
|
||||
attrs = {"username": self.username, "password": self.password}
|
||||
return self._remove_empty_values(attrs)
|
||||
|
||||
def _obj_to_xml_ele(self):
|
||||
element = ET.Element('passwordCredentials')
|
||||
attrs = {"username": self.username, "password": self.password}
|
||||
return self._set_xml_etree_element(element, attrs)
|
||||
|
||||
|
||||
class Token(BaseIdentityModel):
|
||||
def __init__(self, id_=None):
|
||||
super(Token, self).__init__(locals())
|
||||
|
||||
def _obj_to_dict(self):
|
||||
attrs = {"id": self.id_}
|
||||
return self._remove_empty_values(attrs)
|
||||
|
||||
def _obj_to_xml_ele(self):
|
||||
element = ET.Element('token')
|
||||
attrs = {"id": self.id_}
|
||||
return self._set_xml_etree_element(element, attrs)
|
||||
|
||||
|
||||
class APIKeyCredentials(BaseIdentityModel):
|
||||
def __init__(self, username=None, api_key=None):
|
||||
xmlns = V2_0Constants.XML_NS_RAX_KSKEY
|
||||
super(APIKeyCredentials, self).__init__(locals())
|
||||
|
||||
def _obj_to_dict(self):
|
||||
attrs = {"username": self.username, "apiKey": self.api_key}
|
||||
return self._remove_empty_values(attrs)
|
||||
|
||||
def _obj_to_xml_ele(self):
|
||||
element = ET.Element('apiKeyCredentials')
|
||||
attrs = {
|
||||
"username": self.username, "apiKey": self.api_key,
|
||||
"xmlns": self.xmlns}
|
||||
return self._set_xml_etree_element(element, attrs)
|
||||
|
||||
|
||||
class RSACredentials(BaseIdentityModel):
|
||||
xmlns = V2_0Constants.XML_NS_RAX_AUTH
|
||||
|
||||
def __init__(self, username=None, rsa_key=None):
|
||||
super(RSACredentials, self).__init__(locals())
|
||||
|
||||
def _obj_to_dict(self):
|
||||
attrs = {"username": self.username, "tokenKey": self.rsa_key}
|
||||
return self._remove_empty_values(attrs)
|
||||
|
||||
def _obj_to_xml_ele(self):
|
||||
element = ET.Element('RAX-AUTH:rsaCredentials')
|
||||
attrs = {
|
||||
"username": self.username, "tokenKey": self.rsa_key,
|
||||
"xmlns:RAX-AUTH": self.xmlns}
|
||||
return self._set_xml_etree_element(element, attrs)
|
||||
|
||||
|
||||
class Domain(BaseIdentityModel):
|
||||
xmlns = V2_0Constants.XML_NS_RAX_AUTH
|
||||
|
||||
def __init__(self, name=None):
|
||||
super(Domain, self).__init__(locals())
|
||||
|
||||
def _obj_to_dict(self):
|
||||
attrs = {"name": self.name}
|
||||
return self._remove_empty_values(attrs)
|
||||
|
||||
def _obj_to_xml_ele(self):
|
||||
element = ET.Element("RAX-AUTH:domain")
|
||||
attrs = {"name": self.name, "xmlns:RAX-AUTH": self.xmlns}
|
||||
return self._set_xml_etree_element(element, attrs)
|
|
@ -0,0 +1,45 @@
|
|||
import urlparse
|
||||
|
||||
from cafe.engine.behaviors import BaseBehavior
|
||||
|
||||
|
||||
class RequestObject(object):
|
||||
def __init__(
|
||||
self, method, url, headers=None, params=None, data=None):
|
||||
self.method = method
|
||||
self.url = url
|
||||
self.headers = headers or {}
|
||||
self.params = params or {}
|
||||
self.data = data or ""
|
||||
|
||||
|
||||
class RequestCreator(BaseBehavior):
|
||||
@staticmethod
|
||||
def _parse(string, endpoint):
|
||||
params = None
|
||||
lines = string.splitlines()
|
||||
method, url, http_version = lines[0].split()
|
||||
url = url.split("?", 1)
|
||||
if len(url) > 1:
|
||||
params = {}
|
||||
for param in url[1].split("&"):
|
||||
param = param.split("=", 1)
|
||||
if len(param) > 1:
|
||||
params[param[0]] = param[1]
|
||||
else:
|
||||
params[param[0]] = ""
|
||||
url = url[0]
|
||||
url = urlparse.urljoin(endpoint, url)
|
||||
for index, line in enumerate(lines):
|
||||
if line == "":
|
||||
break
|
||||
headers = {}
|
||||
for line in lines[1:index]:
|
||||
key, value = line.split(":", 1)
|
||||
headers[key] = value.strip()
|
||||
data = "\n".join(lines[index+1:])
|
||||
return RequestObject(method, url, headers, params, data)
|
||||
|
||||
@classmethod
|
||||
def create_request():
|
||||
pass
|
|
@ -0,0 +1,141 @@
|
|||
from __future__ import print_function
|
||||
import argparse
|
||||
import os
|
||||
import pkgutil
|
||||
import requests
|
||||
import sys
|
||||
|
||||
from cafe.common.reporting.cclogging import init_root_log_handler
|
||||
from cafe.configurator.managers import TestEnvManager
|
||||
from cafe.drivers.unittest.arguments import ConfigAction
|
||||
from cafe.drivers.base import print_exception
|
||||
|
||||
from syntribos import tests as package
|
||||
#from syntribos.request_parser import RequestParser
|
||||
|
||||
|
||||
class InputType(object):
|
||||
def __init__(self, mode, bufsize):
|
||||
self._mode = mode
|
||||
self._bufsize = bufsize
|
||||
|
||||
def __call__(self, string):
|
||||
if string == '-':
|
||||
fp = sys.stdin
|
||||
yield fp.name, fp.read()
|
||||
elif os.path.isdir(string):
|
||||
for path, _, files in os.walk(string):
|
||||
for file_ in files:
|
||||
file_path = os.path.join(path, file_)
|
||||
fp = open(file_path, self._mode, self._bufsize)
|
||||
yield file_path, fp.read()
|
||||
fp.close()
|
||||
elif os.path.isfile(string):
|
||||
try:
|
||||
fp = open(string, self._mode, self._bufsize)
|
||||
yield fp.name, fp.read()
|
||||
fp.close()
|
||||
except Exception as e:
|
||||
message = "can't open {}:{}"
|
||||
raise Exception(message.format(string, e))
|
||||
else:
|
||||
message = "can't open {} not a readable file or dir"
|
||||
raise Exception(message.format(string))
|
||||
|
||||
|
||||
class SyntribosCLI(argparse.ArgumentParser):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SyntribosCLI, self).__init__(*args, **kwargs)
|
||||
self._add_args()
|
||||
|
||||
def _add_args(self):
|
||||
self.add_argument(
|
||||
"config", metavar="<config>", action=ConfigAction,
|
||||
help="test config. Looks in the ~/.opencafe/configs directory."
|
||||
"Example: compute/dev.environ")
|
||||
|
||||
self.add_argument(
|
||||
"input", metavar="<input_file>", type=InputType('rb', 0),
|
||||
help="<input file|directory of files|-(for stdin)>")
|
||||
|
||||
self.add_argument(
|
||||
"-t", "--test_types", metavar="TEST_TYPES", nargs="*", default=[],
|
||||
help="Test types to run against api")
|
||||
|
||||
|
||||
class Runner(object):
|
||||
@classmethod
|
||||
def populate_tests(cls):
|
||||
if not os.environ.get("CAFE_CONFIG_FILE_PATH"):
|
||||
os.environ["CAFE_CONFIG_FILE_PATH"] = "/dev/null"
|
||||
for importer, modname, ispkg in pkgutil.walk_packages(
|
||||
path=package.__path__,
|
||||
prefix=package.__name__ + '.',
|
||||
onerror=lambda x: None):
|
||||
__import__(modname, fromlist=[])
|
||||
|
||||
@staticmethod
|
||||
def print_symbol():
|
||||
""" Syntribos radiation symbol """
|
||||
border = '-' * 40
|
||||
symbol = """
|
||||
Syntribos
|
||||
xxxxxxx
|
||||
x xxxxxxxxxxxxx x
|
||||
x xxxxxxxxxxx x
|
||||
xxxxxxxxx
|
||||
x xxxxxxx x
|
||||
xxxxx
|
||||
x xxx x
|
||||
x
|
||||
xxxxxxxxxxxxxxx xxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxx xxxxxxxxxxxxx
|
||||
xxxxxxxxxxx xxxxxxxxxxx
|
||||
xxxxxxxxx xxxxxxxxx
|
||||
xxxxxx xxxxxx
|
||||
xxx xxx
|
||||
x x
|
||||
x
|
||||
=== Automated API Scanning ==="""
|
||||
|
||||
print(border)
|
||||
print(symbol)
|
||||
print(border)
|
||||
|
||||
@staticmethod
|
||||
def print_log():
|
||||
test_log = os.environ.get("CAFE_TEST_LOG_PATH")
|
||||
if test_log:
|
||||
print("=" * 150)
|
||||
print("LOG PATH..........: {0}".format(test_log))
|
||||
print("=" * 150)
|
||||
|
||||
@classmethod
|
||||
def run(cls):
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
try:
|
||||
cls.print_symbol()
|
||||
usage = """
|
||||
syntribos <config> <input_file> --test-types=TEST_TYPES
|
||||
syntribos <config> <input_file> -t TEST_TYPE TEST_TYPE ...
|
||||
syntribos <config> <input_file>
|
||||
"""
|
||||
args, unknown = SyntribosCLI(usage=usage).parse_known_args()
|
||||
test_env_manager = TestEnvManager(
|
||||
"", args.config, test_repo_package_name="os")
|
||||
test_env_manager.finalize()
|
||||
cls.print_log()
|
||||
init_root_log_handler()
|
||||
cls.populate_tests()
|
||||
|
||||
except Exception as e:
|
||||
print_exception(
|
||||
file_="runner.py", method="entry_point", exception=e)
|
||||
exit(1)
|
||||
|
||||
|
||||
def entry_point():
|
||||
return Runner.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
entry_point()
|
|
@ -0,0 +1,78 @@
|
|||
import os
|
||||
|
||||
from cafe.engine.models.data_interfaces import ConfigSectionInterface
|
||||
from cafe.engine.http.client import HTTPClient
|
||||
|
||||
|
||||
data_dir = os.environ.get("CAFE_DATA_DIR_PATH")
|
||||
test_table = {}
|
||||
|
||||
|
||||
class TestType(type):
|
||||
def __new__(cls, cls_name, cls_parents, cls_attr):
|
||||
new_class = super(TestType, cls).__new__(
|
||||
cls, cls_name, cls_parents, cls_attr)
|
||||
test_name = getattr(new_class, "test_name", None)
|
||||
if test_name is not None:
|
||||
if test_name in test_table:
|
||||
msg = "Test name already used {}".format(test_name)
|
||||
raise Exception(msg)
|
||||
test_table[test_name] = new_class
|
||||
return new_class
|
||||
|
||||
|
||||
class BaseConfig(ConfigSectionInterface):
|
||||
SECTION_NAME = None
|
||||
|
||||
@property
|
||||
def percent(self):
|
||||
return self.get("percent")
|
||||
|
||||
|
||||
class BaseTest(object):
|
||||
"""
|
||||
Base for building new tests
|
||||
"""
|
||||
__metaclass__ = TestType
|
||||
test_name = None
|
||||
test_type = None
|
||||
filename = None
|
||||
config = None
|
||||
|
||||
@classmethod
|
||||
def validate_test(cls, response):
|
||||
return all([
|
||||
response.status_code < 500,
|
||||
cls.validate_length(response, cls.config.percent)])
|
||||
|
||||
@classmethod
|
||||
def get_strings(cls):
|
||||
path = os.path.join(data_dir, cls.filename)
|
||||
return open(path, "rb")
|
||||
|
||||
@classmethod
|
||||
def validate_length(cls, response, percent=5):
|
||||
if getattr(cls, "init_response", False) is False:
|
||||
raise NotImplemented
|
||||
resp_len = len(response.content)
|
||||
req_len = len(response.request.data)
|
||||
iresp_len = len(cls.init_response.content)
|
||||
ireq_len = len(cls.init_response.request.data)
|
||||
request_diff = req_len - ireq_len
|
||||
response_diff = resp_len - iresp_len
|
||||
if request_diff == response_diff:
|
||||
return True
|
||||
elif resp_len == iresp_len:
|
||||
return True
|
||||
elif percent:
|
||||
if abs(float(response_diff) / iresp_len) <= (percent / 100.0):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def run_test(cls, request, init_response):
|
||||
client = HTTPClient()
|
||||
r = client.request(
|
||||
method=request.method, url=request.url, headers=request.headers,
|
||||
params=request.params, data=request.data)
|
||||
return cls.validate_test(r, init_response)
|
|
@ -0,0 +1,23 @@
|
|||
from syntribos.tests.base import BaseTest, BaseConfig
|
||||
|
||||
|
||||
class SQLInjectionBody(BaseTest):
|
||||
test_name = "SQL_INJECTION_BODY"
|
||||
test_type = "BODY"
|
||||
filename = "sql-injection.txt"
|
||||
config = BaseConfig(section_name=test_name)
|
||||
|
||||
|
||||
class SQLInjectionParams(SQLInjectionBody):
|
||||
test_name = "SQL_INJECTION_PARAMS"
|
||||
test_type = "PARAMS"
|
||||
|
||||
|
||||
class SQLInjectionHeaders(SQLInjectionBody):
|
||||
test_name = "SQL_INJECTION_HEADERS"
|
||||
test_type = "HEADERS"
|
||||
|
||||
|
||||
class SQLInjectionURL(SQLInjectionBody):
|
||||
test_name = "SQL_INJECTION_URL"
|
||||
test_type = "URL"
|
Loading…
Reference in New Issue