Google App Engine - Securing url of cron python - python

I'm a newbie to google app engine. I want the security restriction for url of cron so that it shouldn't be accessible by url directly. For this I've already read the docs and some of Q&As ([Google app engine: security of cron jobs).
I implemented the login : admin solution suggested in this link. But I failed to implement security as self.request.headers.get('X-AppEngine-Cron') is always None, whether it is cron or accessed via url directly.
So I don't know from where is the request coming (from cron or direct access)
def cron_method(BaseRestHandler):
def check_if_cron(self, *args, **kwargs):
if self.request.headers.get('X-AppEngine-Cron') is None:
logging.info("error-- not cron")
self.UNAUTH = "cron"
self.raise_exception()
else:
return BaseRestHandler(self, *args, **kwargs)
return check_if_cron
I used customized handler BaseRestHandler for other authentications.
#cron_method
def put(self):
logging.info("inside put--")
This is called via taskqueue from the get method of the class.
The problem is I didn't get header X-AppEngine-Cron
Any other logic or method will be appreciated.
Thanks In Advance.

It seems you attempted to make the check a decorator.
But your code shows the decorator applied to a put() method, not a get() method - the cron executes only on a get().
Also your decorator doesn't look quite right to me. Shouldn't a decorator take as argument a function and return some locally defined function which executes (not returns) the function received as argument?
I'd suggest you go back to basics - try to make the header check in the get method of the handler itself and only after you get that working consider further, more complex changes like the pulling the check in a decorator.
It is more likely that your decorator is not working than GAE's documented infra to not be working. Keeping things simple (at first) would at least help your investigation effort be pointed in a better direction.
Try this:
def cron_method(handler_method):
def check_if_cron(self, *args, **kwargs):
if self.request.headers.get('X-AppEngine-Cron') is None:
logging.info("error-- not cron")
self.UNAUTH = "cron"
self.raise_exception()
else:
handler_method(self, *args, **kwargs)
return check_if_cron
As for the invocations from the task queue - those requests are no longer cron requests, even if the tasks are created and enqueued by a cron request.
From Securing task handler URLs:
If a task performs sensitive operations (such as modifying data), you
might want to secure its worker URL to prevent a malicious external
user from calling it directly. You can prevent users from accessing
task URLs by restricting access to App Engine administrators.
Task requests themselves are issued by App Engine and can always
target restricted URL.
You can restrict a URL by adding the login: admin element to the
handler configuration in your app.yaml file.
If you want to also prevent manual access to those URLs (i.e. restrict it only to task queue requests) you can perform header checks similar to the cron one. The header values are listed in Reading request headers. Personally I picked X-AppEngine-TaskName.

Related

Setting browser/os restrictions on Django

I’m new to django programming, not python, and could do with a hand.
I am attempting to make a website exclusive to a certain device. I have created a disallow page accessible by '/disallow/'. How do I go about running os/browser checks that then redirect in the event the os/browser is not on the verified list.
I know the information I am wanting to check will be in the request and I can use
request.META['HTTP_USER_AGENT']
However where do I write any logic required and how could I apply this to any page the user tries to access.
Any help would really be appreciated
Ed
You'll want a Django middleware. It sits in front of all response handlers, so it can capture every request and take any action on it that you require.
Simple example (untested), a function that returns a function:
your_app/middleware.py
def restrict_middleware(get_response):
def middleware(request):
if request.META['HTTP_USER_AGENT'] == ...:
return redirect('/disallow/')
else:
return get_response(request)
return middleware
Then register your_app.middleware.restrict_middleware in the MIDDLEWARE array in your settings file.
Note that such browser/OS detection relies on what the browser/OS itself is claiming that it is, and can easily be spoofed on the client. So don't use this for any security purposes.

When are Flask Resources created?

I'm new to Flask. I have a resource class that handles POST requests. The request processing is quite elaborate, and can't be all in the post function. Do I get a new Resource instance for each request? Or are instances reused? Is it safe for me to do something like:
class MyResource(Resource):
def post(self):
self.var = 17
self.do_some_work()
return self.var * self.var
Does Flask guarantee that my resource instance will not be used for other transactions?
Resource objects are created at the time the request should be served and they are not persistent. Keep in mind that REST principles say that APIs must be stateless. If you want to store data between requests, you should use some kind of database.
The simplest method to prove what I said is to use a print (id(self)) in your get handler and trigger the request a few times. You will see that the object always changes.
Now, if you are interested about Flask internals, here we go.
The class Resource is part of Flask-RESTtful and the documentation states the following:
Resources are built on top of Flask pluggable views, giving you easy access to multiple HTTP methods just by defining methods on your resource.
Resources are added with the method Resource.add_resource() and it is simply registering the underlying View object.
if self.app is not None:
self._register_view(self.app, resource, *urls, **kwargs)
else:
self.resources.append((resource, urls, kwargs))
Resource._register_view() method does a lot of crazy stuff, but the most meaningful things are those two lines:
resource_func = self.output(resource.as_view(endpoint, *resource_class_args, **resource_class_kwargs))
...
self.blueprint_setup.add_url_rule(url, view_func=resource_func, **kwargs)
Here you can see that the view object provides a handler that will be associated with the URL path. This handler will be called every time a HTTP request is made to this route.
Finally, we arrived at the core, in the View.as_view() method, it creates a function on-the-fly and this function will represent the route handler.
def view(*args, **kwargs):
self = view.view_class(*class_args, **class_kwargs)
return self.dispatch_request(*args, **kwargs)
As you can see, this function will create a new object every time a request must be dispatched and as you already guessed, view_class is containing your custom class for handling the requests.

How do I replace a decorator at runtime?

I'm trying to adapt another StackOverflow answer on conditionally applying a decorator to only require login for a specific environment (eventually a staging environment, but development until I get this working). Toward that end, I started with the following
auth = HTTPDigestAuth()
def login_required(dec, condition):
def decorator(func):
if not condition:
return func
return dec(func)
return decorator
#bp.route('/auth')
#login_required(auth.login_required, current_app.config['ENV'] != 'development')
def auth_route():
return current_app.config['ENV']
When I launch the server, I get a RuntimeError: Working outside of application context error. After trying a few suggestions from an earlier version of this question, I got the RuntimeError to disappear, but the decorator still isn't being correctly applied when I want. Here's the current version:
def login_required(dec):
def decorator(func):
if not os.environ.get('ENV') != 'development':
return func
return dec(func)
return decorator
#bp.route('/auth')
#login_required(auth.login_required)
def auth_route():
return current_app.config['ENV']
This never returns the auth.login_reqired function. It always lets the browser in without authentication.
So, I tried changing the condition to
if not os.environ.get('ENV') is not None:
and then the authentication shows up.
Yes, I've done an export ENV=development in the shell and confirmed it with the env command. But even then it's not reading the environment variable as I would expect.
Perhaps this is simply the wrong way to go about it? My final goal is to require authentication on one particular environment. Is this possible with the path I'm on? Is it possible at all?
current_app is a context local proxy that only has meaning during a request. This means you can't use it before a request, i.e. as part of a decorator.
Using current_app is generally good practice because Flask allows multiple apps to be configured. In your specific case however, it isn't actually necessary. For instance, the following would work, because it uses the app object directly instead of the current_app proxy:
from yourpackage import app
#bp.route('/auth')
#login_required(auth.login_required, app.config['ENV'] != 'development')
def auth():
return current_app.config['ENV']
Let me paste something from documentation of Flask
Lifetime of the Context
The application context is created and destroyed as necessary. When a Flask application begins handling a request, it pushes an application context and a request context. When the request ends it pops the request context then the application context. Typically, an application context will have the same lifetime as a request.
Now let’s consider how decorators work. It is just a syntactic sugar see this answer.
So the login_required decorator is called while the module is loaded and the current app is not available yet because it’s not handling request.
I would do this way, move condition to the decorator function (relating to your example). It will be called while the request will be handled so you should have access to current_app.

Django: app level variables

I've created a Django-rest-framework app. It exposes some API which does some get/set operations in the MySQL DB.
I have a requirement of making an HTTP request to another server and piggyback this response along with the usual response. I'm trying to use a self-made HTTP connection pool to make HTTP requests instead of making new connections on each request.
What is the most appropriate place to keep this app level HTTP connection pool object?
I've looked around for it & there are multiple solutions each with some cons. Here are some:
To make a singleton class of the pool in a diff file, but this is not a good pythonic way to do things. There are various discussions over why not to use singleton design pattern.
Also, I don't know how intelligent it would be to pool a pooler? (:P)
To keep it in init.py of the app dir. The issue with that are as follows:
It should only contain imports & things related to that.
It will be difficult to unit test the code because the import would happen before mocking and it would actually try to hit the API.
To use sessions, but I guess that makes more sense if it was something user session specific, like a user specific number, etc
Also, the object needs to be serializable. I don't know how HTTP Connection pool can be serialized.
To keep it global in views.py but that also is discouraged.
What is the best place to store such app/global level variables?
This thread is a bit old but still could be googled. generally, if you want a component to be accessible among several apps in your Django project you can put it in a general or core app as a Util or whatever.
in terms of reusability and app-specific you can use a Factory with a cache mechanism something like:
class ConnectionPool:
pass
#dataclass
class ConnectionPoolFactory:
connection_pool_cache: dict[str: ConnectionPool] = field(default_factory=dict)
def get_connection(self, app_name: str) -> ConnectionPool:
if self.connection_pool_cache.get(app_name, None) is None:
self.connection_pool_cache[app_name] = ConnectionPool()
return self.connection_pool_cache[app_name]
A possible solution is to implement a custom Django middleware, as described in https://docs.djangoproject.com/ja/1.9/topics/http/middleware/.
You could initialize the HTTP connection pool in the middleware's __init__ method, which is only called once at the first request. Then, start the HTTP request during process_request and on process_response check it has finished (or wait for it) and append that response to the internal one.

action after sign in with google account

I'm new in google app engine, so my question may be kind of stupid or trivial, but anyway... For each user (for each google acc) I have an entity in my datastore. So when a new user logs in I want to add him to database.
I use:
- url: /.*
login: required
To make sure user is logged in with his google acc. The problem is when someone signs in from a subpage (blabla.appspot.com/something) then after log in he will be redirected to blabla.appspot.com/something and I have to verify in Request handler for "something" if the current user is already stored in database. How to do it without adding the same code to each request handler? Maybe it's possible to redirect to the main page after log in or something similar?
Here's a simple approach: If you create a subclass of webapp.RequestHandler that your handlers will then subclass, you can provide it with a convenience method for getting or creating a UserInfo object given users.get_current_user().user_id() (or .user_email(), if that's what you prefer to use as a key). Your handlers then call the convenience method in their get() and post() methods.
class MyRequestHandler(webapp.RequestHandler):
def setup_user_info(self):
# left as an exercise
self.user_info = user_info
and then
class MyHandler(MyRequestHandler):
def get(self):
self.setup_user_info()
...

Categories

Resources