I want to override setup_db method for my add-on because in current situation you can't take argument from url with db name, and if the user has more than 1 database I can`t run my login link from incognito.
I don't want the user to go to /web/database/selector first.
I was thinking about the user going to /web/login?db=example_db_name and then somehow redirect to my login link.
("somehow" because if you type it, it redirects you to /web/login, so i cant add redirect from login page).
I'm doing that assuming that in odoo.conf user has
db_name = False, dbfilter = .
If you faced the same problem, here is my solution. It`s overriding the default method which is kinda bad usually, but in our situation there isnt much we can do.
from odoo import http
class Rooting(http.Root):
def setup_db(self, httprequest):
db = httprequest.session.db
# Check if session.db is legit
if db:
if db not in http.db_filter([db], httprequest=httprequest):
httprequest.session.logout()
db = None
if not db:
if 'db' in httprequest.args:
db = httprequest.args['db']
httprequest.session.db = db
if not db:
httprequest.session.db = http.db_monodb(httprequest)
http.Root.setup_db = Rooting.setup_db
Related
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
what is the use of load_user(user_id) method ? and when will it called ? In documentation it is mentioned that it is used to reload the user object from the user ID stored in the session . I don't understand from where session gets the user_id and where the method reloads the user object.And also why it needs to write in models.py file ?
During the logging we pass the user object in login_user() method so does this method add the user object in session and sets this user to current_user ?
When a logined user returns to your website, their browser will send cookie related to your website to the server. Like other modern web framework, flask will not store the user's credential inside the cookie, but store a session ID instead. Then flask will use this session ID to lookup the database and find the correct user information and send it back to the browser. So here is the load_userfunction kicks in. load_user is a callback function used by the flask-login login manager. When flask needs to look up and load the user related to a specific session ID, it will call this function. This is also why it is in the model.py since it is directly related to the database.
I have an application running in production that I've built for a single client that I want to convert to support multiple "tenants".
Currently I am using a Postgres database where all my data resides in a single database in the default public schema. I would like to isolate each tenant to a separate Postgres schema. Ideally, my application's UI would make a call to my API using the tenant's subdomain. In before_request I would somehow be able to set all database queries during the current request context to only query that tenant's schema, is this possible?
I envisage an ideal solution to be something similar to this contrived example:
from flask import Flask, request, jsonify
from pony.orm import Database, Required
app = Flask(__name__)
db = Database(**{<db_connection_dict>})
class User(db.Entity):
email = Required(str)
password = Required(str)
#classmethod
def login(cls, email: str, password: str) -> str:
user = cls.get(lambda u: u.email.lower() == email.lower())
if not user:
return None
password_is_valid = <method_to_check_hashed_pasword>
if not password_is_valid:
return None
return <method_to_generate_jwt>
db.generate_mapping()
#app.before_request
def set_tenant():
tenant_subdomain = request.host.split(".")[0]
// MISSING STEP.. set_schema is a fictitous method, does something similar to this exist?
db.set_schema(schema=tenant_subdomain)??
#app.route("auth/login", methods=["POST"]
def login_route():
data = request.get_json()
jwt = User.login(data["email"], data["password"])
if not jwt:
return make_response({}, 403)
return make_response(jsonify(data=jwt), 200)
I've come across an interesting/simple example using SQLAlchemy. If not possible with PonyORM I may consider porting my models over to SQLAlchemy but would miss the simplicity of Pony :(
I thought about possibly using the Database.on_connect method to do something as such but not sure if if anyone has any other ideas or if this would even work properly in production. I suspect not because if I had two separate tenants querying the database they would overwrite the search path..
#db.on_connect()
def set_request_context_tenant_schema(db, connection) -> None:
subdomain = request.host.split(".")[0]
cursor = connection.cursor()
cursor.execute(f"SET search_path TO {subdomain}, public;")
For my site for auth I'm using https://flask-httpauth.readthedocs.io/en/latest/ . Now I'm trying to make it that it's using data from database. To do that i created database named Users and created columns named username and password.
To get data from this table after defining its class as model I've made get_user functions which looks like it:
#staticmethod
def get_user():
query = (Users
.select())
user = []
for s in query:
user.append(s)
return user
(I'm not sure if it's correct)
Next I had to modify get_pw function but I also wasn't sure how to modify it so I made it look like it:
#auth.get_password
def get_pw(username):
if username in Users.get_user():
return users.get(Users.get_user())
return None
Now after running the site I get prompt to give login and password but those that I set up in my database doesn't seem to work so there must be a problem with get_pw function. Also I'm using peewee SQL to manage database : http://docs.peewee-orm.com/en/latest/peewee/querying.html
You can get rid of your get_user method since you are issuing a very large select query that fetches all records from user table. The get_pw can be redefined as:
def get_pw(username):
user = Users.get(Users.name == username) #assuming you have a username field in model
return user.password #assuming a password field
Also, its a good practice to define your model class as a singular noun rather than plural. So, its better to call it User rather than Users.
This'll help you get started in no time: http://docs.peewee-orm.com/en/latest/peewee/quickstart.html#quickstart
I am working on a google app engine (gae) project in python which has the following structure:
class LoginHandler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class SignupHandler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class Site1Handler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class Site2Handler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class ...
application = webapp2.WSGIApplication([('/login', LoginHandler),
('/signup',SignupHandler),
('/site1', Site1Handler),
('/site2', Site2Handler),
...,
],
debug=True)
Every user who wants to use this application has to be logged in.
Therefore on the login-site and the signup-site a cookie value with an user_id is set.
So lets imagine this app has 100 URLs and the corresponding 100 Site...Handlers() implemented.
Than for every get()/post() call I first get the user_id from the cookie and check in the database if this user exists and if it is valid.
So if the user clicks on 20 sites the app accesses 20 times the db to validate the user.
I am sure there is a better way and I would be glad if someone could show me how to do this.
I have already seen someone inherited his own Handler from webapp2.RequestHandler
which would than look like:
class MyHandler(webapp2.RequestHandler):
def initialize(self, *a, **kw):
webapp2.RequestHandler.initialize(self, *a, **kw)
uid = self.request.cookies.get('user_id')
self.user = uid and User.all().filter('userid =', uid).get()
class LoginHandler(MyHandler):
def get(self):
...#if self.user is valid -> OK
def post():
...#if self.user is valid -> OK
...
And here it is getting confusing for me.
Consider two or more people accessing the application concurrently. Will then User1 see data of User2 because self.user is initialized with data from User2?
I also concidered using a global variable to save the current user. But here the same problem if two users access the app concurrent.
I also found the webapp2.registry functionality which seemed to me the same like a global dictionary. And here also the problem of two or more users accessing the app at the same time.
Could someone please show me how to do it right? I am very new to gae and very happy for every hint in the right direction.
(Maybe Memcached is the solution. But I am more interested in a review of this check if user is valid pattern. So what would be best practice to do this?)
Assuming that you are using NDB and validating your user by getting a User object via a key/id - it will be automatically cached in memcache as well as in current local instance's memory, so your route handlers won't be calling Datastore with every single request, this is all done automatically, no extra coding required. If for validation / getting the user object you are using a query - the result won't be automatically cached but you can always manually cache it and verify the user via cache first and if the cache doesn't exist only then query Datastore, caching the results for the next request.
See more here.
If you are using webapp2's Sessions with signed/secure cookies then the data in those cookies, including the fact that the user is validated (which you previously set when when validating the user the first time) can be trusted, as long as you use long and randomly generated secret_key, that is kept secret and thus, just like with cache, you first check whether the user is validated in the cookie and if not, you ask Datastore and save the result in the session cookie for the next request. See more here.
Either way, you don't have to repeat your validation code in every single handler like you are showing in your example. One way of fixing it would be using decorators which would make your validation reuse as simple as placing #login_required before your get method. See more info here and take a look at the webapp2_extras.appengine.users file to get an idea how to write your own, simmilar decorator.
Absolutely losing my brain over this. I can't figure out why this is happening. Each time I run this test, the object gets saved to the normal, non-test database. However, both assertions at the end of the test fail anyway, saying they can't find ANY users in the database, even though each time the test runs I have to go into the admin to delete the objects it's created on localhost. I'm using SQLITE3 in my settings, and I understand that SQLITE tests are supposed to run in memory, rather than hitting the database. I've searched and searched and can't find any useful information on the web. Here's the test function:
import time
import datetime
from django.test import TestCase, LiveServerTestCase
from django.core.urlresolvers import resolve
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from django.contrib.auth.models import User
from apps.registration.forms import RegistrationForm
class NewVisitorTest(LiveServerTestCase):
def setUp(self):
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3)
def tearDown(self):
self.browser.quit()
def test_registration_process(self):
# Goes to registration page
self.browser.get('http://localhost:8000/register/')
# User can find sign up form
registration_form = self.browser.find_element_by_id('id_registration_form')
# User can fill out sign up form
first_name_input = self.browser.find_element_by_id('id_first_name')
first_name_input.send_keys('Jim')
last_name_input = self.browser.find_element_by_id('id_last_name')
last_name_input.send_keys('Barrow')
date = datetime.date.today()
date_input = self.browser.find_element_by_id('id_birthday')
date_input.send_keys(str(date))
username_input = self.browser.find_element_by_id('id_username')
username_input.send_keys('jim_barrow')
password_input = self.browser.find_element_by_id('id_password')
password_input.send_keys('kittensarecute')
password_1_input = self.browser.find_element_by_id('id_password1')
password_1_input.send_keys('kittensarecute')
email_input = self.browser.find_element_by_id('id_email')
email_input.send_keys('jim_barrow#gmail.com')
# User can submit sign up form
registration_form.submit()
# User is now registered as a user object
users = User.objects.all()
self.assertEqual(len(users), 1)
# User is now registered as a person object
persons = Person.objects.all()
self.assertEqual(len(persons), 1)
if __name__ == '__main__':
unittest.main()
If there's any other context I can provide, I'll happily show you. This is practically a blank project, so there aren't any strange or unusual settings in settings.py which might confuse things. Any help would be greatly appreciated.
According to the LiveServerTestCase docs, the live server is on port 8081 by default. However you are fetching the page from port 8000 instead.
I expect you are running the dev server on port 8000 and your tests are connecting to it, so your new objects appear in the non-test database. You need to change your code to fetch the page from port 8081 instead.
Quick Update:
As of Django 1.11, the server setup by LiveServerTestCase uses any free port assigned by the localhost, not simply 8081.
You can access the live server URL and port using self.live_server_url, as per docs