首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >python django Mock SAML响应来自使用python3-saml的onelogin.saml.auth库

python django Mock SAML响应来自使用python3-saml的onelogin.saml.auth库
EN

Stack Overflow用户
提问于 2020-09-03 21:17:11
回答 1查看 535关注 0票数 0

我已经为我们的django后端应用程序(SP)实现了通过SAML登录的可能性,作为使用Keycloak的IDP im。它工作得很好,但我想要编写测试,以确保所有逻辑都正确执行。为此,我想生成一个post请求,将SAML作为主体,模拟(unittest.mock.patch)真正的请求。但我坚持了下来。

下面是我的django视图,当我尝试通过SAML登录时,它接受get和post请求:

代码语言:javascript
复制
class SamlLoginView(View):

    @staticmethod
    def prepare_django_request(request):
        if 'HTTP_X_FORWARDED_FOR' in request.META:
            server_port = 443
        else:
            server_port = request.META.get('SERVER_PORT')

        result = {
            'https': 'on' if request.is_secure() else 'off',
            'http_host': request.META['HTTP_HOST'],
            'script_name': request.META['PATH_INFO'],
            'server_port': server_port,
            'get_data': request.GET.copy(),
            'post_data': request.POST.copy(),
        }
        return result

    @never_cache
    def get(self, *args, **kwargs):
        req = SamlLoginView.prepare_django_request(self.request)
        auth = OneLogin_Saml2_Auth(req, settings.SAML_IDP_SETTINGS)
        return_url = self.request.GET.get('next') or settings.LOGIN_REDIRECT_URL
        return HttpResponseRedirect(auth.login(return_to=return_url))

    @never_cache
    def post(self, *args, **kwargs):
        req = SamlLoginView.prepare_django_request(self.request)
        print(req['post_data']['SAMLResponse'])
        auth = OneLogin_Saml2_Auth(req, settings.SAML_IDP_SETTINGS)
        auth.process_response()
        errors = auth.get_errors()
        if not errors:
            if auth.is_authenticated():
                logger.info("Login", extra={'action': 'login',
                                            'userid': auth.get_nameid()})
                user = authenticate(request=self.request,
                                    saml_authentication=auth)
                login(self.request, user)
                return HttpResponseRedirect("/")
            else:
                raise PermissionDenied()

        else:
            return HttpResponseBadRequest("Error when processing SAML Response: %s" % (', '.join(errors)))

在我的测试中,我想直接调用post方法,其中将有一个saml:

代码语言:javascript
复制
class TestSamlLogin(TestCase):

    def test_saml_auth(self, prepare):
        client = APIClient()
        url = reverse_lazy("miri_auth:samllogin")
        saml_resp='<xml with saml response>'
        resp = client.post(url, data=saml_resp)

但很明显,它显示request.POST是空的。

然后我决定对prepare_django_request函数做一个模拟,并手动插入这个saml:

代码语言:javascript
复制
def mocked_prepare_request(request):
    post_query_dict = QueryDict(mutable=True)
    post_data = {
        'SAMLResponse': saml_xml,
        'RelayState': '/accounts/profile/'
    }
    post_query_dict.update(post_data)
    result = {
        'https': 'on',
        'http_host': '<http-host>',
        'script_name': '/api/auth/samllogin/',
        'server_port': '443',
        'get_data': {},
        'post_data': post_query_dict,
    }
    return result

class TestSamlLogin(TestCase):
        
   @patch('miri_auth.views.SamlLoginView.prepare_django_request', side_effect=mocked_prepare_request)
    def test_saml_auth(self, prepare):
        client = APIClient()
        url = reverse_lazy("miri_auth:samllogin")
        saml_resp='<xml with saml response>'
        resp = client.post(url, data=saml_resp)

根据我传递saml_xml的方式,如果我把它定义为字符串,它会抛出不同的错误:

代码语言:javascript
复制
with open(os.path.join(TEST_FILES_PATH, 'saml.xml')) as f:
        saml_xml = " ".join([x.strip() for x in f])

它返回:lxml.etree.XMLSyntaxError: Start tag expected, '<' not found, line 1, column 1,尽管我在xml验证器中检查了saml_xml的输出,它表明xml是有效的。当我试图提前将文件解析成xml时,我得到了另一个错误,我试图用来解析的库:

代码语言:javascript
复制
import xml.etree.ElementTree as ET
from xml.dom import minidom
from lxml import etree
tree = etree.parse(os.path.join(TEST_FILES_PATH, 'saml.xml'))

它返回:TypeError: argument should be a bytes-like object or ASCII string, not '_ElementTree'

调试这些错误并没有给我带来任何解决方案。

如果任何人对如何实现这一点有任何想法(使用SAML模拟响应),或者我在哪里犯了错误,我将很高兴听到。

提前感谢

EN

回答 1

Stack Overflow用户

发布于 2020-09-04 15:56:27

我意识到SAML响应必须编码:

代码语言:javascript
复制
with open(os.path.join(TEST_FILES_PATH, 'saml.xml')) as f:
            saml_xml = " ".join([x.strip() for x in f])
            base64_saml = base64.b64encode(saml_xml.encode('ascii')).decode('ascii')
            post_data = {'SAMLResponse': base64_saml, 'RelayState': '/accounts/profile/'}
            url = reverse_lazy("miri_auth:samllogin")
            request = self.client.post(url, post_data)

但是现在我得到了以下错误:

代码语言:javascript
复制
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=280:obj=sha256:subj=unknown:error=12:invalid data:data and digest do not match
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63724493

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档