python - Cron 作业无法使用 admin_required 装饰器访问 url

标签 python unit-testing google-app-engine flask cron

根据文档,应该允许 Cron 作业访问受管理员保护的 View 。但是,如果我在 GET 方法上使用 @admin_required 装饰器,则会收到 302 错误。

app.yaml中我定义了这个:

- url: /generator
  script: run.news.app
  login: admin

View :

class GeneratorView(MethodView):
    @admin_required
    def get(self):
        return 'success', 200

urls.py

app.add_url_rule('/generator', 'generator', view_func=GeneratorView.as_view('generator'))

计划任务:

cron:
- description: Scrape every 3 hours
  url: /generator
  schedule: every 3 hours synchronized

装饰器:

def admin_required(func):
    """Requires App Engine admin credentials"""

    @wraps(func)
    def decorated_view(*args, **kwargs):
        if users.get_current_user():
            if not users.is_current_user_admin():
                abort(401)  # Unauthorized
            return func(*args, **kwargs)
        return redirect(users.create_login_url(request.url))

    return decorated_view

有趣的是,当我删除 admin_required 装饰器时,由于 app.yaml 中的 login: admin ,该 URL 仍然仅受管理员保护。

但是,由于缺少装饰器,我的单元测试未通过授权检查。

def test_generator_fails_as_normal_user(self):
        self.setCurrentUser(u'john@example.com', u'123')
        rv = self.client.get('/generator')
        self.assertEqual(rv.status_code, 401)

AssertionError: 200 != 401

如果我把装饰器放回去,单元测试就会通过,而 cron 作业就会失败。有什么建议吗?

最佳答案

单元测试的 self.client.get 毫无疑问不会一直返回到 app.yaml 进行路由 - 因此,如果您删除了在装饰器中执行的应用程序级别检查,它允许非管理员用户通过。

然而,真正的问题是,当 cron 访问该 URL 时,装饰器没有发现任何人“登录”。 https://cloud.google.com/appengine/docs/python/config/cron#Python_app_yaml_Securing_URLs_for_cron 暗示了这一点(尽管它肯定应该更清楚/明确地记录下来!) :

Note: While cron jobs can use URL paths restricted with login: admin, they cannot use URL paths restricted with login: required.

这表明服务基础设施不会通过检查当前登录的用户来验证 cron 请求,因为它找不到任何用户。相反,它依赖于请求中的 header :

Requests from the Cron Service will also contain a HTTP header:

X-AppEngine-Cron: true

The X-AppEngine-Cron header is set internally by Google App Engine. If your request handler finds this header it can trust that the request is a cron request. If the header is present in an external user request to your app, it is stripped, except for requests from logged in administrators of the application, who are allowed to set the header for testing purposes.

因此,您的装饰器必须检查 self.request 处的 header - 如果找到 X-AppEngine-Cron: true,则必须让请求通过,否则它可以继续执行您现在正在执行的检查。

我不太确定您应该如何最好地在您选择的网络框架中获取请求的 header ,您没有提到这一点,但如果它是例如 webapp2 那么类似:

@wraps(func)
def decorated_view(self, *args, **kwargs):
    if self.request.headers.get('X-AppEngine-Cron') == 'true':
        return func(self, *args, **kwargs)
    # continue here with the other checks you do now

应该可以解决问题。

关于python - Cron 作业无法使用 admin_required 装饰器访问 url,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28524617/

相关文章:

python - 使用 pandas 追加、连接、连接或合并两个数据框

python - ZeroRPC 发布/订阅聚合结果

firebase - 是否可以模拟经过身份验证的 Firebase 用户,以便 Jest 运行完整测试,同时保留数据库规则?

google-app-engine - 使用 Golang channel 处理 HTTP 请求

python - 如何在球面上绘制具有方位角和仰角的热图以及每个位置的相应值

c++ - 单元测试 - 仅在 "int main()"左右重新编译以减少编译时间

c# - 从 AutoFixture/AutoMaq 请求模拟时,NUnit 会忽略测试

python - 本地测试 GAE 任务队列从自动到手动的缩放

python - 谷歌应用引擎上的 webapp、tipfy 或 django

python Flask - 在服务器上找不到请求的 URL。 (但为什么?)