Difference between with and without sudo() in Odoo - odoo

What is different between:
test = self.env['my.example'].sudo().create({'id':1, 'name': 'test'})
test = self.env['my.example'].create({'id':1, 'name': 'test'})
All example work, but what is the advantages when using sudo()?

Odoo 8–12
Calling sudo() (with no parameters) before calling create() will return the recordset with an updated environment with the admin (superuser) user ID set. This means that further method calls on your recordset will use the admin user and as a result bypass access rights/record rules checks [source]. sudo() also takes an optional parameter user which is the ID of the user (res.users) which will be used in the environment (SUPERUSER_ID is the default).
When not using sudo(), if the user who calls your method does not have create permissions on my.example model, then calling create will fail with an AccessError.
Because access rights/record rules are not applied for the superuser, sudo() should be used with caution. Also, it can have some undesired effects, eg. mixing records from different companies in multi-company environments, additional refetching due to cache invalidation (see section Environment swapping in Model Reference).
Odoo 13+
Starting with Odoo 13, calling sudo(flag) will return the recordset in a environment with superuser mode enabled or disabled, depending if flag is True or False, respectively. The superuser mode does not change the current user, and simply bypasses access rights checks. Use with_user(user) to actually switch users.

You can check the comments on sudo in Odoo code at odoo -> models.py -> def sudo().

Returns a new version of this recordset attached to the provided
user.
By default this returns a ``SUPERUSER`` recordset, where access
control and record rules are bypassed.
It is same as:
from odoo import api, SUPERUSER_ID
env = api.Environment(cr, SUPERUSER_ID, {})
In this example we pass SUPERUSER_ID in place of uid at the time of creating a Enviroment.
If you are not use Sudo() then the current user need permission to
create a given object.
.. note::
Using ``sudo`` could cause data access to cross the
boundaries of record rules, possibly mixing records that
are meant to be isolated (e.g. records from different
companies in multi-company environments).
It may lead to un-intuitive results in methods which select one
record among many - for example getting the default company, or
selecting a Bill of Materials.
.. note::
Because the record rules and access control will have to be
re-evaluated, the new recordset will not benefit from the current
environment's data cache, so later data access may incur extra
delays while re-fetching from the database.
The returned recordset has the same prefetch object as ``self``.

Related

Enable Impala Impersonation on Superset

Is there a way to make the logged user (on superset) to make the queries on impala?
I tried to enable the "Impersonate the logged on user" option on Databases but with no success because all the queries run on impala with superset user.
I'm trying to achieve the same! This will not completely answer this question since it does not still work but I want to share my research in order to maybe help another soul that is trying to use this instrument outside very basic use cases.
I went deep in the code and I found out that impersonation is not implemented for Impala. So you cannot achieve this from the UI. I found out this PR https://github.com/apache/superset/pull/4699 that for whatever reason was never merged into the codebase and tried to copy&paste code in my Superset version (1.1.0) but it didn't work. Adding some logs I can see that the configuration with the impersonation is updated, but then the actual Impala query is with the user I used to start the process.
As you can imagine, I am a complete noob at this. However I found out that the impersonation thing happens when you create a cursor and there is a constructor parameter in which you can pass the impersonation configuration.
I managed to correctly (at least to my understanding) implement impersonation for the SQL lab part.
In the sql_lab.py class you have to add in the execute_sql_statements method the following lines
with closing(engine.raw_connection()) as conn:
# closing the connection closes the cursor as well
cursor = conn.cursor(**database.cursor_kwargs)
where cursor_kwargs is defined in db_engine_specs/impala.py as the following
#classmethod
def get_configuration_for_impersonation(cls, uri, impersonate_user, username):
logger.info(
'Passing Impala execution_options.cursor_configuration for impersonation')
return {'execution_options': {
'cursor_configuration': {'impala.doas.user': username}}}
#classmethod
def get_cursor_configuration_for_impersonation(cls, uri, impersonate_user,
username):
logger.debug('Passing Impala cursor configuration for impersonation')
return {'configuration': {'impala.doas.user': username}}
Finally, in models/core.py you have to add the following bit in the get_sqla_engine def
params = extra.get("engine_params", {}) # that was already there just for you to find out the line
self.cursor_kwargs = self.db_engine_spec.get_cursor_configuration_for_impersonation(
str(url), self.impersonate_user, effective_username) # this is the line I added
...
params.update(self.get_encrypted_extra()) # already there
#new stuff
configuration = {}
configuration.update(
self.db_engine_spec.get_configuration_for_impersonation(
str(url),
self.impersonate_user,
effective_username))
if configuration:
params.update(configuration)
As you can see I just shamelessy pasted the code from the PR. However this kind of works only for the SQL lab as I already said. For the dashboards there is an entirely different way of querying Impala that I did not still find out.
This means that queries for the dashboards are handled in a different way and there isn't something like this
with closing(engine.raw_connection()) as conn:
# closing the connection closes the cursor as well
cursor = conn.cursor(**database.cursor_kwargs)
My gut (and debugging) feeling is that you need to first understand the sqlalchemy part and extend a new ImpalaEngine class that uses a custom cursor with the impersonation conf. Or something like that, however it is not simple (if we want to call this simple) as the sql_lab part. So, the trick is to find out where the query is executed and create a cursor with the impersonation configuration. Easy, isnt'it ?
I hope that this could shed some light to you and the others that have this issue. Let me know if you did find out another way to solve this issue, or if this comment was useful.
Update: something really useful
A colleague of mine succesfully implemented impersonation with impala without touching any superset related, but instead working directly with the impyla lib. A PR was open with the code to change. You can apply the patch directly in the impyla src used by superset. You have to edit both dbapi.py and hiveserver2.py.
As a reminder: we are still testing this and we do not know if it works with different accounts using the same superset instance.

Issues pulling change log using python

I am trying to query and pull changelog details using python.
The below code returns the list of issues in the project.
issued = jira.search_issues('project= proj_a', maxResults=5)
for issue in issued:
print(issue)
I am trying to pass values obtained in the issue above
issues = jira.issue(issue,expand='changelog')
changelog = issues.changelog
projects = jira.project(project)
I get the below error on trying the above:
JIRAError: JiraError HTTP 404 url: https://abc.atlassian.net/rest/api/2/issue/issue?expand=changelog
text: Issue does not exist or you do not have permission to see it.
Could anyone advise as to where am I going wrong or what permissions do I need.
Please note, if I pass a specific issue_id in the above code it works just fine but I am trying to pass a list of issue_id
You can already receive all the changelog data in the search_issues() method so you don't have to get the changelog by iterating over each issue and making another API call for each issue. Check out the code below for examples on how to work with the changelog.
issues = jira.search_issues('project= proj_a', maxResults=5, expand='changelog')
for issue in issues:
print(f"Changes from issue: {issue.key} {issue.fields.summary}")
print(f"Number of Changelog entries found: {issue.changelog.total}") # number of changelog entries (careful, each entry can have multiple field changes)
for history in issue.changelog.histories:
print(f"Author: {history.author}") # person who did the change
print(f"Timestamp: {history.created}") # when did the change happen?
print("\nListing all items that changed:")
for item in history.items:
print(f"Field name: {item.field}") # field to which the change happened
print(f"Changed to: {item.toString}") # new value, item.to might be better in some cases depending on your needs.
print(f"Changed from: {item.fromString}") # old value, item.from might be better in some cases depending on your needs.
print()
print()
Just to explain what you did wrong before when iterating over each issue: you have to use the issue.key, not the issue-resource itself. When you simply pass the issue, it won't be handled correctly as a parameter in jira.issue(). Instead, pass issue.key:
for issue in issues:
print(issue.key)
myIssue = jira.issue(issue.key, expand='changelog')

What is the use of logging menu in settings

What is the use / purpose of logging menu in the
Settings -> Technical -> Database structure -> Logging
It seems that you can store all the log messages in that view (model ir.logging) as long as you use the parameter --log-db your_database_name when executing odoo-bin in the command line (or add log_db = your_database_name in your Odoo config file).
Check out Odoo 11 info about command line parameters: https://www.odoo.com/documentation/11.0/reference/cmdline.html
--log-db
logs to the ir.logging model (ir_logging table) of the specified database. The database can be the name of a database in the “current” PostgreSQL, or a PostgreSQL URI for e.g. log aggregation
This is the theory, but honestly, I was not able to make it work, and I did not waste much time in trying to know why.
EDIT
As #CZoellner says, it seems that log messages stored in ir_logging table (log messages you see clicking on the menuitem Settings -> Technical -> Database structure -> Logging) come only from scheduled actions. If you create an scheduled action which executes some Python code, you have the following available variables to use in your method code:
env: Odoo Environment on which the action is triggered.
model: Odoo Model of the record on which the action is triggered; is a void recordset.
record: record on which the action is triggered; may be void.
records: recordset of all records on which the action is triggered in multi-mode; may be void.
time, datetime, dateutil, timezone: useful Python libraries.
log: log(message, level='info'): logging function to record debug information in ir.logging table.
Warning: Warning Exception to use with raise To return an action, assign: action = {...}.
If you use the log one, for example:
log('This message will be stored in ir_logging table', level='critical')
That log message and its details will be stored in ir_logging table each time the scheduled action is executed (automatically or manually). This answers your question, but now I am wondering what is the parameter --log-db, as I have tested it and these log messages are stored in ir_logging no matter this parameter is set or not.

Tasks in Kanban View: card rearrangement error when Project/User is only authorized to modify his own tasks

This happens when a Project/User drops his task unto a destination stage containing tasks not owned by him.
Apparently, Odoo remembers the stack ordering of tasks within a stage via project.task.sequence, and updates all the task cards' sequence fields when the Project/User completes the drop action. But since the Project/User is not authorized to modify other users' tasks (of project.task object type). The Odoo server raises the exception shown below.
Access restriction is implemented via the following record rule for Project/User:
Name: Project/Task: only assignee and creator can modify task
Object: Task (project.task)
Apply for: Write
Domain filter: ['|',('user_id','=',user.id),('create_uid','=',user.id)]
Group name: Project/User
Is there any workaround to this problem?
At time of writing, the error can be reproduced at http://demo.odoo.com currently running Odoo version 8.saas~6.
Note that by default Human Resources / Employees are allowed to modify tasks not assigned to them, so the write and delete access of record rule "Project/Task: employees: public, portal, employee or (followers and following)" must first be removed.
Couldn't think of a better solution, so I just did the following hack instead:
Add to top portion of _write function of openerp.models.BaseModel in the file ODOO_ROOT/models.py:
def _write(self, cr, user, ids, vals, context=None):
# use admin if just writing to 'sequence' field of model 'project.task'
if self._name == 'project.task' and vals.keys() == ['sequence']:
user = SUPERUSER_ID
This is probably OK since the sequence field is not really a very important field to protect from casual modification by non-owners.

Using variable values within different scenarios in a feature, capybara

I have a feature with 4 scenarios. I would like to use the value of 1 variable I set in scenario 1 across different steps and in Scenario 2.
I use $ but this is not set. I am assuming $ value remains the same across a feature
When(/^the user goes to manageusers, picks one of the secondary users$/) do
click_link "Admin"
click_link "Manage Users"
emailofuser=ENV["email"].to_s
atpos = emailofuser.index('#')
emailofuser = emailofuser[0,atpos]
page.body.to_s.scan(/<td>(.*?)#ABC.com<\/td>/).flatten().each do |w|
if "#{w}" != emailofuser
$secondaryUserEmail = "#{w}" + "#ABC.com"
break
end
end
end
When(/^the secondary user logs in with password "([^"]*)"$/) do |arg|
if getURL != URI.parse(current_url)
visit getURL
end
find(:xpath,"//input[#id='user_email']").set($secondaryUserEmail )
fill_in "user_password", :with => arg
click_button "Sign in"
end
In the Above Step, the steps are in 1 scenario in a feature file and I also have the same step secondary user in Scenario 2 within a feature. the variable $secondaryuserEmail some times does not get set and login as a secondary user fails.
Whats the best way for me to declare variables that I can access across steps within a scenario and across scenarios within a feature.
You should find out why $secondaryuserEmail does not get set. That sounds like a bug somewhere in the app you're testing. If it's not a bug, you should try to handle the exception.
To your original question, it might be a good idea to set variables in helper methods, then call these methods to access them using instance variables. Most people generally recommend against sharing variables across scenarios but I've used helper methods in order to store variables throughout my specs that normally go unchanged.