-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[10.0][MIG] oauth_provider #889
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[10.0][MIG] oauth_provider #889
Conversation
e462403 to
91a367d
Compare
|
@sylvain-garancher - I'm going to change some things (detailed below), which will give us a full featured CRUD API. I was wondering, however, if we could relicense this to LGPL? The reasoning for this is that I need to design an opinionated API based on some proprietary vendor data structures & I will not be able to release the final product as Open Source due to contractual obligations. I'd love to use this module as a base, and have planned a nice Open Source REST API module to sit on top of this one, that my proprietary one will sit on top of. I'm between a rock and a hard place with this vendor though - they're pretty tight about their IP. Following are my plans for this module in order to support my requirements:
Some interesting design considerations that I'm not too sure of yet:
|
4473389 to
d55ba72
Compare
|
Hello, I'm not against a licence change, if it allows the module to be improved instead of writing a new one to replace it. Maybe the OCA has to give an opinion, as the module is in OCA's repository ? @pedrobaeza @gurneyalex The current version of the module only includes what my customer needed : login from a mobile application and retrieving some information about a subscription. Providing a full CRUD API is a great improvement idea :D About the create/read/write/delete fields, I wonder if it can be managed using For the scope validation, would it be possible to add a selection field to let the user define if it has to be valid after/before/both ? |
3fbf9f8 to
dd1216f
Compare
Easy enough, yeah.
Technically
Good idea - I'll add that the to
Sweet - I thought it looked like a lot of it was already hooked up, glad you confirmed. It's not totally required for my project, but I would prefer to deploy with those vs. legacy tokens. |
0055fff to
9378037
Compare
|
No problem in the license change if all the code authors agree and the target license is OSI. |
68b3114 to
d970605
Compare
|
Alright I've now made a dependent API & I would say that the structure of this is now finalized if anyone wants to take a look. I'll just be working on tests now 🚀 |
d970605 to
17b3016
Compare
I honestly don't even think that is necessary. We'll find out - #955 |
1201a71 to
4a09119
Compare
|
Good call @sylvain-garancher, looks like we have a 🍏 when based on #955. I tested too and it seems the logic in that module works while operating in the same cursor, so I'm not sure what the commit was about TBH |
|
I'm curious - how did you figure out which module combination was required to replicate this? |
|
First, I reproduced the issue on my system (basically by copy/pasting TravisCI's commands). Next step, while Odoo was blocked, I tried a Then, the hardest part should have been to find the module from the SQL query, but I had the luck to find the Finally, knowing the line in the python code which was blocked, I easily found what was the real issue. |
|
Thanks for the breakdown @sylvain-garancher - this will help next time I run into something similar. I'm a bit embarrassed how horrible my strategy was for this TBH |
4a09119 to
a398aac
Compare
ed3500f to
fcf8b7e
Compare
Garamotte
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some small comments inline.
The CRUD API seems OK, but not fully tested yet on my side. I'm not sure about the URLs format, waiting for opinions from someone else (although this can be an endless discussion).
| dictionary will be returned without nesting. | ||
| """ | ||
|
|
||
| if isinstance(record_ids, int): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to avoid variables with several allowed types for an argument when possible.
Removing the if will require to change some calls to this method (basically replace record.id by record.ids or record_id by [record_id]), but I think this is better.
|
|
||
| if record_ids is None: | ||
| record_ids = [] | ||
| elif isinstance(record_ids, int): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same remark as the get_data method about several allowed types for an argument.
| bool: Indicating whether the records are within scope. | ||
| """ | ||
|
|
||
| scoped_records = self.env[records._name].browse() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know calling browse() without any argument worked.
You can simply use scoped_records = self.env['records._name'] to get an empty recordset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm for some reason I thought there was a difference, but it seems you are correct:
>>> env['res.partner']
res.partner()
>>> env['res.partner'].browse()
res.partner()
|
|
||
| scoped_records = self.env[records._name].browse() | ||
| for scope in self: | ||
| scoped_records += scope._get_scoped_records( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer using the |= operator, as it removes duplicates. The += operator is not really helpful with Odoo's recordsets...
>>> partners = self.env['res.partner']
>>> partners += self.env['res.partner'].browse([1, 1])
>>> partners
res.partner(1, 1)
>>> partners |= self.env['res.partner'].browse([1, 1, 2])
>>> partners
res.partner(1, 2)
| bool: Whether the values are within the scope. | ||
| """ | ||
| scopes = self.filter_by_model(model) | ||
| field_names = scopes.mapped('field_ids').mapped('name') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can call mapped only once: field_names = scopes.mapped('field_ids.name')
|
I'm open to changing the URI schema if some good arguments are exposed. We haven't designed anything specific on top of the REST API yet, so it would be an easy change. You are correct that this could be an endless discussion though 😆 🚀 |
|
Comments handled @sylvain-garancher |
Garamotte
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I finally took time to carefully review the code, great work @lasley !
The docstrings are really good too, thanks for that (that's something I'm not yet good at).
Still not enough time to make some functional tests :(
| OauthProviderScope: Recordsets associated to the model. | ||
| """ | ||
| if isinstance(model, models.BaseModel): | ||
| model = model._name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it cleaner to avoid using isinstance when it's not needed. Here, we can force the argument to be a string.
When we have a BaseModel instance, then we can simply call filter_by_model(our_model._name).
| return data | ||
|
|
||
| @api.multi | ||
| def create_record(self, model_name, vals): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why this method is in the oauth.provider.scope model, instead of calling the three methods (_validate_scope_field, self.env['model_name'].create and _validate_scope_record) directly in the create_record method of oauth.provider.token ?
What's the benefit of having this method on the scope ?
Same question about write and delete, obviously.
|
|
||
| scoped_records = self.env[records._name] | ||
| for scope in self: | ||
| scoped_records |= scope._get_scoped_records( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this will not work if you have scopes using different models on the same token.
|
Thanks for the review @sylvain-garancher - I'll tend to the comments soon. I actually moved some of this code (the stuff to allow for proper receipt of JSON requests) into OCA/webhook#3 and will need to switch this code to use that. I think I'm about to make it a Python package instead though. Regarding functional tests - I haven't used the REST API provided here much, but we do have quite a few proprietary APIs built on top of it that have been working quite well for some time. It's really helped us for automation. |
* Bump version * Rename manifest * Change openerp references to odoo
90e3122 to
ca54f57
Compare
|
Hey @lasley, thank you for your Pull Request. It looks like some users haven't signed our Contributor License Agreement, yet.
Appreciation of efforts, |
|
Hello, what is the status on this module migration to newer versions? I see that it was in the 9.0 branch but not later? Any plans to have this oauth_provider in Odoo 13 or 14? Or are there other alternatives? |
|
There hasn't been any activity on this pull request in the past 4 months, so it has been marked as stale and it will be closed automatically if no further activity occurs in the next 30 days. |
Syncing from upstream OCA/server-tools (13.0)
This is a complete overhaul of the OAuth2 provider module, as well as a migration to v10. I added an abstract REST API as well, following are the docs:
REST API
This module also includes a basic REST-style CRUD interface backed by OAuth2 authentication.
List
Return allowed information from all records, with optional query.
Route: /api/rest/1.0/<string:model>
Accepts: GET
Args:
access_token (str): OAuth2 access token to utilize for the
operation.
model (str): Name of model to operate on. domain (array, optional): Domain to apply to the search, in the standard Odoo format. This will be appended to the scope’s pre-existing filter.
Returns:
array of objs: List of data mappings matching query.
Example:
Bash:
Python:
Read
Return allowed information from specific records.
Route: /api/rest/1.0/<string:model>/<int:record_id>
Accepts: GET
Args:
access_token (str): OAuth2 access token to utilize for the
operation.
model (str): Name of model to operate on. record_id (int): ID of record to get.
Returns:
obj: Record data.
Example:
Bash:
Python:
Create
Return allowed information from specific records.
Route: /api/rest/1.0/<string:model>
Accepts: POST
Args:
access_token (str): OAuth2 access token to utilize for the
operation.
model (str): Name of model to operate on. kwargs (mixed): All other named arguments are used as the data for record mutation.
Returns:
obj: Record data.
Example:
Bash: