草庐IT

python - 为什么这些自定义 Flask session 接口(interface)的测试失败了?

coder 2023-08-21 原文

我正在 Flask 中编写一个混合的单页 Web/PhoneGap 应用程序。由于 PhoneGap 应用程序中的 cookie 基本不可用,因此我实现了自定义 session interface这完全避免了 cookie。它将 session 数据存储在应用程序数据库中,并在 HTTP 请求和响应主体中显式传递 session ID。

我创建了一个 GitHub repository减少测试用例。它本身仍然是一个相当大的项目,但自述文件应该可以帮助您快速找到自己的方法。该存储库包括七个测试,在使用 Flask 默认的基于 cookie 的 session 接口(interface)时全部成功,而在我的自定义 session 接口(interface)中全部失败。主要问题似乎是数据有时不会保留在 session 对象上,但这是神秘的,因为 session 对象继承自 Python 的内置 dict,它不应该自发地忘记数据。另外,session的界面比较简单明了,和Flask's example Redis session snippet相比似乎没有什么明显的错误。 .

更令人沮丧的是,自定义 session 界面似乎在实际应用程序中可以正常工作。只有单元测试失败。但是,由于这个原因,假设 session 接口(interface)在所有情况下都能正常工作是不安全的。

非常感谢您的帮助。

编辑: Gist 不接受简化的测试用例,因为它包含目录。我现在将它移动到一个成熟的 GitHub 存储库。完成后我会再次更新这篇文章。

新编辑:将精简的测试用例移至适当的 GitHub 存储库。自述文件仍然提到“this Gist”,抱歉。

最佳答案

您的问题主要归结为在您的测试请求中提供 session token 。如果您不提供 token ,则 session 为空。

我假设您的实际应用程序正确发送了 session token ,因此似乎可以正常工作。

修复测试用例以正确通过并不需要太多。

每个请求都尝试根据 post 参数加载 session

在您的 session 实现中:

def open_session(self, app, request):
    s = Session()
    if 't' in request.form:
        ....

    return s

这意味着每个不是POST(或PUT)并且没有发送t的请求都会 有一个空白 session 。

而基于 cookie 的实现将始终具有可用的 session token 并且能够加载以前请求的 session 。

这是您的示例测试之一:

def test_authorize_captcha_expired(self):
    with self.client as c:
        with c.session_transaction() as s:
            s['captcha-answer'] = u'one two three'.split()
            s['captcha-expires'] = datetime.today() - timedelta(minutes=1)
        self.assertEqual(c.post('/test', data={
            'ca': 'one two three',
        }).status_code, 400)

您还没有为发送到 /test 的帖子提供 t 值。因此它得到一个空白 没有 captcha-expires key 和 KeyError 的 session 被引发。

您的 session 需要一个“ token ” key 才能保存

在您的 session 实现中:

def save_session(self, app, session, response):
    if session.modified and 'token' in session:
        ...
        # save session to database
        ...

因此当你有:

with c.session_transaction() as s:
    s['captcha-answer'] = u'one two three'.split()
    s['captcha-expires'] = datetime.today() - timedelta(minutes=1)

实际上没有 session 被写入数据库。对于任何后续请求 利用。请注意,它确实确实需要写入数据库,因为 open_session 会在每次请求时尝试从数据库加载一些内容。

要解决大多数情况,您需要在创建 session 时提供一个“ token ”,并为使用它的任何请求提供一个带有该 token 的“t”。

因此我上面使用的示例测试最终会像这样:

def test_authorize_captcha_expired(self):
    with self.client as c:
        token = generate_key(SystemRandom())
        with c.session_transaction() as s:
            s['token'] = token
            s['captcha-answer'] = u'one two three'.split()
            s['captcha-expires'] = datetime.today() - timedelta(minutes=1)
        self.assertEqual(c.post('/test', data={
            'ca': 'one two three',
            't': token
        }).status_code, 400)

当你用 json 响应时你改变了 token

...但是您在发出后续请求时并未使用新 token

def test_reply_to_reflection_passthrough(self):
    with self.client as c:
        token = 'abcdef'

        ...

        response2 = c.post('/reflection/1/reply', data={
            'p': 'test4',
            'r': 'testmessage',
            't': token,
        }, ...

到这里,/reflection/1/reply 的帖子生成了一个新的 token 并保存,因此关键键 last-reply 不在 由 abcdef 标识的 session 。如果这是基于 cookie 的 session ,那么 last-reply 将可用于下一个请求。

所以要修复此测试...使用新 token

def test_reply_to_reflection_passthrough(self):
    with self.client as c:

        ...

        response2 = c.post('/reflection/1/reply', data={

        ...

        token = session['token']
        with c.session_transaction(method="POST", data={'t':token}) as s:
            s['token'] = token
            s['last-request'] = datetime.now() - timedelta(milliseconds=1001)

        response3 = c.post('/reflection/1/reply', data={

        ...

重定向将丢失 session token

在测试test_bump中:

def test_bump(self):
    response = self.client.post(
        '/admin/tip/action/',
        data = {'action': 'Bump', 'rowid': '1',},
        follow_redirects=True )
    self.assertIn(' tips have been bumped.', response.data)

/admin/tip/action 的帖子返回重定向。

此处您正在检查是否存在即显消息。和闪讯 存储在 session 中。

对于基于 cookie 的 session , session ID 将随后续重定向请求再次发送。

由于您的 session ID 被指定为一个 post 值,它不会再次发送, session 和 flash 消息都将丢失。

解决这个问题的方法不是遵循重定向,而是通过 flasks flash 方法检查 session 中的数据集。

def test_bump(self):
    with self.client as c:
        token = generate_key(SystemRandom())
        with c.session_transaction() as s:
            s['token'] = token
        c.post('/admin/tip/action/',
               data={'action': 'Bump', 'rowid': '1', 't': token})

        with c.session_transaction(method="POST", data={'t': token}) as s:
            self.assertIn(' tips have been bumped.', s['_flashes'][0][1])

就是这样

我已经发送了一个拉取请求,其中包含我上面描述的更改,您会发现默认的 flask session 和您的 session 实现的测试现在都通过了。

关于python - 为什么这些自定义 Flask session 接口(interface)的测试失败了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32483063/

有关python - 为什么这些自定义 Flask session 接口(interface)的测试失败了?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  5. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  6. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  7. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  8. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  9. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  10. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

随机推荐