Use the common way to define permissions on the API.
Note: we keep here the notion of "public" contacts,
even if the API does not really allows that. The use
case is not clear for that, but we allow contact w/o
owner to be displayed.
To improve code readability, I propose to rename
the contact field `override`. This comes along
with the fact a contact should not not always
override another (it's the case were I only want
to create some personal contacts).
We make the full name mandatory and add a field to
allow user to store personnal notes on the contact.
This also make the "base" contact not mandatory because
user may want to create new contacts out of the blue.
To improve readability and code sharing we group all
APIs into the same "api" module for each application.
Next submodules might be "scim",
"resource_server_scim", ...
The only shared module is the "permissions" one for now.
We don't want every Service Provider to be able to request
every endpoint if those are not implementing a filtering on
the data returned. To prevent any data leak we enforce the
developers to manually "whitelist" each endpoint and add
the proper filtering when needed.
This allows, on a per user basis, the display of
features.
The main goal here is to allow Team admin or owner
to see the management views.
We also added the same for the two other features
(mailboxes and contacts)
This will be improved later if needed :)
After some reflexion, the use of a slug field raises to many
problems without being really needed.
One problem is the slug is made from the group name, but we
don't have unicity on this, so a user might be blocked without
any clue.
We also want to allow group names to be reused (which is already
allowed except for the automatic slug).
The unique ID that will be shared with Service Providers will be
the PK/UUID.
We introduce the Organization model has a "hat" for all
users and team.
Each User must have a "default" organization.
Each Team must have an organization.
When a User creates a new Team, the team is linked to their
default Organization.
For now the Organization should not be visible to end users
this is a purely technical aspect as it.
The models are also adding a permission to allow User to edit
an Organization, but for now there are no endpoints for that.
Next steps:
- Add an Organization to each User and Team on all environments
to mark Organization as mandatory in database.
- Add scope to Organization to list the Service Provider list
allowed for a User in an Organization.
- Add endpoints + frontend to manage Organization's scopes
Pylint 3.2.0 introduced a new check `possibly-used-before-assignment`, which
ensures variables are defined regardless of conditional statements.
Some if/else branches were missing defaults. These have been fixed.
The email field on the user is renamed to "admin_email" for clarity. The
"email" and "name" fields of user's main identity are made available on
the user model so it is easier to access it.
Important ordering fields for TeamAccess depend on user's
identities data. User and identities has a one-to-many relationship,
which forced us to prefetch the user-related data when listing
team's accesses.
Prefetch get data from the database using two SQL queries, and
join data in Python. User's data were not available in the first
SQL query.
Without annotating the query set with user main identities data,
we could not use default OrderingFilter backend code, which order_by()
the queryset.
Enhance list capabilities, by adding the OrderingFilter as filter backend,
to the TeamAccess viewset.
API response can be ordered by TeamAccess role. More supported ordering
fields will be supported later on.
Our Pagination class inherits from the PageNumberPagination Django class.
However, this base class as not ordering attribute. Thus, setting a
default value wont have any effect on the code.
Why did we end up passing a value to this non-existing attribute? Becasue
we copy/pasted some code sources from Joanie, and Joanie also has this
attribute set to a default value.
If you take a look at DRF pagination style documentation, the only three
attributes they set on the child class are 'page_size', 'max_page_size'
'page_size_query_param'. 'ordering' is not mentionned in the attributes
you may override. However, the CursorPagination class offers the latter
attribute, which may explain why we did end up setting this non-existing
attribute in Joanie.
Compute Trigram similarity on user's name, and sum it up
with existing one based on user's email.
This approach is inspired by Contact search feature, which
computes a Trigram similarity score on first name and last
name, to sum up their scores.
With a similarity score influenced by both email and name,
API results would reflect both email and name user's attributes.
As we sum up similarities, I increased the similarity threshold.
Its value is empirical, and was finetuned to avoid breaking
existing tests. Please note, the updated value is closer to the
threshold used to search contacts.
Email or Name can be None. Summing two similarity scores with
one of them None, results in a None total score. To mitigate
this issue, I added a default empty string value, to replace
None values. Thus, the similarity score on this default empty
string value is equal to 0 and not to None anymore.
Nest invitation router below team router and add create endpoints for
authenticated administrators/owners to invite new members to their team,
list valid and expired invitations or delete invite altogether.
Update will not be handled for now. Delete and recreate if needed.
Break copy/pasted comment from Joanie in several inline
comments, that are more specific and easy to read.
Hopefully, it will help future myself understanding this
queryset and explaining it.
To compute accesses's abilities, we need to determine
which is the user's role in the team.
We opted for a subquery, which retrieves the user's role
within the team and annotate queryset's results.
The current subquery was broken, and retrieved other
users than the request's user. It led to compute accesses'
abilities based on a randomly picked user.
We recently updated Ruff from 0.2.2 to v0.3, which introduced
Ruff 2024.2 style. This new style updated Ruff formatter's behavior,
making our make lint command fails.
Ruff 2024.2 style add a blank line after the module docstring.
Please take a look at Ruff ChangeLog to get more info.
Add serializers to return basic user info when listing /team/<id>/accesses/
endpoint. This will allow front-end to retrieve members info without having
to query API for each user.id.
Integrate 'mozilla-django-oidc' dependency, to support
Authorization Code flow, which is required by Agent Connect.
Thus, we provide a secure back channel OIDC flow, and return
to the client only a session cookie.
Done:
- Replace JWT authentication by Session based authentication in DRF
- Update Django settings to make OIDC configurations easily editable
- Add 'mozilla-django-oidc' routes to our router
- Implement a custom Django Authentication class to adapt
'mozilla-django-oidc' to our needs
'mozilla-django-oidc' routes added are:
- /authenticate
- /callback (the redirect_uri called back by the Idp)
- /logout
Create invitation model, factory and related tests to prepare back-end
for invitation endpoints. We chose to use a separate dedicated model
for separation of concerns, see
https://github.com/numerique-gouv/people/issues/25
* ✨(api) search users by email
The front end should be able to search users by email.
To that goal, we added a list method to the users viewset
thus creating the /users/ endpoint.
Results are filtered based on similarity with the query,
based on what preexisted for the /contacts/ endpoint.
* ✅(api) test list users by email
Test search when complete, partial query,
accentuated and capital.
Also, lower similarity threshold for user search by email
as it was too high for some tests to pass.
* 💡(api) improve documentation and test comments
Improve user viewset documentation
and comments describing tests sections
Co-authored-by: aleb_the_flash <45729124+lebaudantoine@users.noreply.github.com>
Co-authored-by: Anthony LC <anthony.le-courric@mail.numerique.gouv.fr>
* 🛂(api) set isAuthenticated as base requirements
Instead of checking permissions or adding decorators
to every viewset, isAuthenticated is set as base requirement.
* 🛂(api) define throttle limits in settings
Use of Djando Rest Framework's throttle options, now set globally
to avoid duplicate code.
* 🩹(api) add email to user serializer
email field added to serializer. Tests modified accordingly.
I added the email field as "read only" to pass tests, but we need to discuss
that point in review.
* 🧱(api) move search logic to queryset
User viewset "list" method was overridden to allow search by email.
This removed the pagination. Instead of manually re-adding pagination at
the end of this method, I moved the search/filter logic to get_queryset,
to leave DRF handle pagination.
* ✅(api) test throttle protection
Test that throttle protection succesfully blocks too many requests.
* 📝(tests) improve tests comment
Fix typos on comments and clarify which setting are tested on test_throttle test
(setting import required disabling pylint false positive error)
Co-authored-by: aleb_the_flash <45729124+lebaudantoine@users.noreply.github.com>
---------
Co-authored-by: aleb_the_flash <45729124+lebaudantoine@users.noreply.github.com>
Co-authored-by: Anthony LC <anthony.le-courric@mail.numerique.gouv.fr>
Used https://github.com/openfun/joanie as boilerplate, ran a few
transformations with ChapGPT and adapted models and endpoints to
fit to my current vision of the project.