Commit Graph

78 Commits

Author SHA1 Message Date
Sabrina Demagny
017f52a0dc (api) add RELEASE version on config endpoint
Add release version deployed to config endpoint
in order to display release info in La Régie footer.
2024-10-14 14:57:28 +02:00
Quentin BEY
d635c484ae 🛂(backend) do not duplicate user when disabled
When a user is disabled and tries to login, we
don't want the user to be duplicated,
the user should not be able to login.

Fixes https://github.com/numerique-gouv/people/issues/455
2024-10-11 16:19:18 +02:00
Marie PUPO JEAMMET
0e48bc0f90 🛂(backend) match email if no existing user matches the sub
Some OIDC identity providers may provide a random value in the "sub"
field instead of an identifying ID. In this case, it may be a good
idea to fallback to matching the user on its email field.
2024-10-11 15:24:53 +02:00
Marie PUPO JEAMMET
ae05b430db 🚨(pylint) fix linting error introduced by new pylint version
pylint version v3.3.1 added a new error and broke our CI
2024-09-30 13:06:39 +02:00
Marie PUPO JEAMMET
4b47f80cab 🚨(tests) fix obsolete code warnings
- in docker compose, remove obsolete 'version' field
- in django, replace obsolete CheckConstraints 'check' field by 'condition'
2024-09-05 14:57:32 +02:00
lebaudantoine
78818ba541 🩹(backend) enable resource server authentication if properly configured
Tests are missing, let's ship it, I'll open an issue.

Without such protection, the whole app would crash if the resource server is
not configured. The fallback backend would return an appropriate error to
the client if the resource server is improperly configured.
2024-08-29 11:39:08 +02:00
lebaudantoine
f1a2b7c603 (backend) authenticate requests using an access token issued by AC
Overload mozilla-django-oidc class to support an authentication method
with the resource server backend.

This enables any route of the API to be called with an access token
issued by Agent Connect.
2024-08-29 11:39:08 +02:00
lebaudantoine
5634a7f390 (backend) add resource server backend
Why:

Many services in La Suite rely on Agent Connect to authenticate their users.
Delegating  authentication to Agent Connect is highly beneficial. With a central
party (Agent Connect)  handling user authentication, our services can seamlessly
communicate with each other.  Our backend must be able to receive and verify
access tokens issued by Agent Connect.

Additionally, it should ensure that the resource owner has granted permission
for our  data to the service provider transmitting the access token.

How:

Our backend needs to verify access tokens by introspecting them. This involves
requesting the Authorization Server to validate the access token received in
the authentication header. The Authorization Server validates the token's
integrity, provides authentication and authorization information about
the user currently logged into the service provider requesting data from
the resource server.

The data returned by the Authorization Server to the resource server
is encrypted and signed. To encrypt the introspection token, the Authorization
Server retrieves the resource server's public key from
the new ‘/jwks’ endpoint.

Encryption parameters, such as algorithm and encoding, are configured on
the resource server. Ensure that these parameters match between
the Authorization Server and the resource server.

The resource server verifies the token signature using the Authorization
Server's public key, exposed through its `/jwks` endpoint. Make sure
the signature algorithms match between both servers. Finally, introspection
token claims are verified to adhere to good  practices for handling JWTs,
including checks on issuer, audience, and expiration time.

The introspection token contains a subject (`sub`). The resource server uses
this subject to retrieve the requested database user, compatible
with both pairwise and public subjects.

Important:

Agent Connect does not follow RFC 7662 but uses a draft RFC that adds security
(signing/encryption) to the initial specification. Refer to the "References"
section for more information.

References:

The initial RFC describing token introspection is RFC 7662 "OAuth 2.0 Token
Introspection". However, this RFC specifies that the introspection
response is a plain JSON object.

In eGovernment applications, our resource server requires stronger assurance
that the Authorization Server issued the token introspection response.

France Connect's team implemented a stronger version of the spec, returning
a signed and encrypted token  introspection response. This version is still
a draft, available under:

"draft-ietf-oauth-jwt-introspection-response".
2024-08-29 11:39:08 +02:00
lebaudantoine
9c05167d80 (backend) introduce an authorization server client
In OAuth 2.0, the Authorization Server is equivalent to the OIDC provider.

The Authorization Server exposes endpoints for token introspection and JWKS.
I’ve created a client to easily interact with the Authorization Server,
while doing the token introspection in our resource server.

Token introspection will be explained in upcoming commits.

The current OIDC library used in the project doesn’t offer token introspection,
leading to redundancy in the code handling some OIDC/OAuth2 flows.

This overlap makes the code bulky. My goal is to quickly deliver a working
PoC for the resource server, with plans to refactor in the longer run.

Please feel free to provide feedback on the class design.
2024-08-29 11:39:08 +02:00
lebaudantoine
21371dbd1b (backend) add a '/jwks' endpoint
Introduce a new endpoint, /jwks, which returns a JSON Web Key Set (JWKS).
This set of public crypto keys will be used by external parties to encrypt
data intended for our backend. In the context of the resource server, this key
will be used by the authorization server to encrypt the introspection response.

The current implementation exposes a single public key, with the private key
configurable in the app settings. The private key is represented as a string.
For enhanced security, we might prefer to store this data in a .pem file
excluded from version control.

A few parameters for this key, such as its type and encoding, are configurable
in the settings.

A critique of the current design is its lack of extensibility.
If we decide to offer more than one encryption method, this view will require
refactoring.

Additionally, the current implementation is tightly coupled with joserfc.

This lays the foundation for further improvements.

Please note, this endpoint only public components of the key, there is no
chance for any secret leaking.
2024-08-29 11:39:08 +02:00
lebaudantoine
b40aefc505 ✏️(backend) fix minor typo
Found and fixed a minor typo. Nit-picking!
2024-08-29 11:39:08 +02:00
lebaudantoine
591b3eedff 🏗️(backend) create a new python package for the resource server
Encapsulate all Resource Server (RS) sources in a dedicated python package.

Resource server belongs to the Oauth2 ecosystem, please find informations
here https://www.oauth.com/oauth2-servers/the-resource-server/
2024-08-29 11:39:08 +02:00
Sabrina Demagny
ba46d7de54 (users) improve user display on admin users list
If user email exists, display it instead of sub to identify
users in admin view list.
2024-08-28 11:00:34 +02:00
Sabrina Demagny
b79b4b1853 (domains) manage domain roles on user admin view
Allow to manage mail domain roles on user admin interface
2024-08-28 11:00:34 +02:00
Laurent Bossavit
e3f8633931 (test) fix flaky search test
By making this email address invariant, we remove failures due to FactoryBoy's
random address being considered as a match to our test query
(and hence returning unexpected number of matches).
2024-08-28 10:47:19 +02:00
Anthony LC
03bfef6061 (backend) add public endpoint /api/v1.0/config/
Add public endpoint /api/v1.0/config/ to
share some public configuration values
with the frontend.
2024-08-21 15:13:12 +02:00
Marie PUPO JEAMMET
87e7d3e0b1 🚚(swagger) move swagger under /api/
Swagger was under /v1.0/swagger.
I just wanna move it under /api/ where the rest of the API is.
2024-08-05 16:49:27 +02:00
Sabrina Demagny
ab54d5af8f (backend) allow to filter member on team access endpoint
Filter member by name...
2024-07-31 16:01:32 +02:00
Sabrina Demagny
8d7614c512 (models) add TeamAccess models on admin view
Declare TeamAccessAdmin
2024-07-10 16:40:48 +02:00
Marie PUPO JEAMMET
66300aca66 🧑‍💻(models) improve user str representation
Improve user model str representation to display name or email
if provided. Otherwise, returns sub as last resort.
2024-07-03 17:16:22 +02:00
Samuel Paccoud - DINUM
2ec292bb91 ♻️(models) remove multiple identities
Multiple identities were complicating this project's code.
We moved the management of multiple identities to our
OIDC provider.
2024-06-27 17:45:23 +02:00
Anthony LC
4b80b288f9 ♻️(mails) link email from current site
The link in the email was pointing on the
staging website. We now use a variable to
target the current site setup in the database.
2024-06-05 09:50:09 +02:00
antoine lebaud
e0739689e6 🚨(backend) handle new checks introduced in Pylint v3.2.0
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.
2024-05-31 12:53:11 +02:00
Lebaud Antoine
7a26f377e3 (backend) support Agent Connect Logout flow
The default Logout view provided by Mozilla Django OIDC is not suitable
for the Agent Connect Logout flow.

Previously, when a user was logging-out, only its Django session was ended.
However, its session in the OIDC provider was still active.

Agent Connect implements a 'session/end' endpoint, that allows services to
end user session when they logout.

Agent Connect logout triggers cannot work with the default views implemented
by the dependency Mozilla Django OIDC. In their implementation, they decided
to end Django Session before redirecting to the OIDC provider.

The Django session needs to be retained during the logout process.

An OIDC state is saved to the request session, pass to Agent Connect Logout
endpoint, and verified when the backend receives the Logout callback from Agent
Connect. It seems to follow OIDC specifications.

If for any reason, the Logout flow cannot be initiated with Agent Connect,
(missing ID token in cache, unauthenticated user, etc), the user is redirected
to the final URL, without interacting with Agent Connect.
2024-05-31 12:14:58 +02:00
Lebaud Antoine
05d9a09d63 🚚(backend) create a dedicated authentication package
Prepare adding advanced authentication features. Create a dedicated
authentication Python package within the core app.

This code organization will be more extensible.
2024-05-31 12:14:58 +02:00
Marie PUPO JEAMMET
df24c24da1 (api) add CRUD for mailbox manager MailDomain models
Add create,list,retrieve and delete actions for MailDomain model.
2024-04-19 18:45:50 +02:00
Lebaud Antoine
2f1805b721 🩹(backend) address linter flakiness on Email tests
Pylint was randomly failing due to a warning while unpacking emails.
The W0632 (Possible unbalanced tuple unpacking) was triggered.

Replace tuple unpacking by an explicitly accessing the first element of
the array using index.
2024-04-08 15:35:12 +02:00
Lebaud Antoine
54386fcdd3 🩹(backend) address test flakiness while sorting Team accesses
Previously, there was a difference between Django's `order_by`
behavior and Python's `sorted` function, leading to test failures
under specific conditions. For example, entries such as 'Jose Smith'
and 'Joseph Walker' were not consistently sorted in the same order
between the two methods.

To resolve this issue, we've ensured that sorting the expected
results in the TeamAccess tests are both case-insensitive and
space-insensitive. This adjustment fix tests flakiness
2024-04-08 15:07:58 +02:00
Marie PUPO JEAMMET
ebf58f42c9 (webhook) add webhook logic and synchronization utils
adding webhooks logic to send serialized team memberships data
to a designated serie of webhooks.
2024-04-05 16:06:09 +02:00
Samuel Paccoud - DINUM
7ea6342a01 ♻️(models) refactor user email fields
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.
2024-04-05 16:06:09 +02:00
Sabrina Demagny
775b32ff45 (backend) enhance search users to add in a team
Exclude from the result all users already members of the current team
2024-04-02 11:12:08 +02:00
Lebaud Antoine
522914b47a (backend) email invitation to new users
When generating an Invitation object within the database, our intention
is to promptly notify the user via email. We send them an invitation
to join Desk.

This code is inspired by Joanie successful order flow.

Johann's design was missing a link to Desk, I simply added a button which
redirect to the staging url. This url is hardcoded, we should refactor it
when we will deploy Desk in pre-prod or prod environments.

Johann's design relied on Marianne font. I implemented a simpler version,
which uses a google font. That's not important for MVP.

Look and feel of this first invitation template is enough to make our PoC
functionnal, which is the more important.
2024-03-22 13:42:22 +01:00
Lebaud Antoine
1919dce3a9 🧑‍💻(views) render email's template
THis feature is inspired by Joanie. Add two new urls to render Emails
HTML and Text templates.

Developpers can render the email template they are working on. When necessary,
run make mails-build, and reload `_debug__/mail/hello_html`, it will re-render
the updated email template.

Also, I have copy/pasted one template extra tags from Joanie, which loads
bas64 string from static images. This code is necessary to render the dummy
template `hello.html`.
2024-03-22 13:42:22 +01:00
Lebaud Antoine
0141aa220f 🎨(models) extract invitation converter in a proper method
Improved code readability, by extracting this well-scoped unit of
logic in a dedicated method. Also, rename active_invitations to match
'valid' vocabulary used elsewhere in the doc. If no valid invitation
exists, early return to avoid nesting.
2024-03-22 13:31:24 +01:00
Lebaud Antoine
97752e1d5f 🩹(factory) handle email uniqueness
When generating a batch of users using Faker, there's a possibility of
generating multiple users with the same email address. This breaches
the uniqueness constraint set on the email field, leading to flaky
tests that may fail when random behavior coincides unfavorably.

Implemented a method inspired by Identity's model to ensure unique
email addresses when creating user batches with Faker.
Updated relevant tests for improved stability.
2024-03-22 08:28:30 +01:00
Lebaud Antoine
99cee241f9 (api) support TeamAccess ordering on user-based fields
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.
2024-03-22 08:28:30 +01:00
Lebaud Antoine
6de0d013c3 (api) support TeamAccess ordering on their role
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.
2024-03-22 08:28:30 +01:00
Lebaud Antoine
1de743e18a (pagination) add few tests on page's size
We created a custom Pagination class, were max_page_size is overriden.
I decided to add bare minimum tests to make sure we can set the maximum
page size using the 'page_size' query parameter.
2024-03-22 08:28:30 +01:00
Lebaud Antoine
756867da19 🔥(pagination) remove unused ordering field
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.
2024-03-22 08:28:30 +01:00
Marie PUPO JEAMMET
340ddf8b1a 🐛(dependencies) modify expected details on 404 responses
djangorestframework released version 3.15.0, which includes modifications of
details upon returning 404 errors (see related issue
https://github.com/encode/django-rest-framework/pull/8051).

This commit changes the expected details of 404 responses in our tests,
to match DRF 3.15.0.
2024-03-21 15:46:42 +01:00
Marie PUPO JEAMMET
7ef67037c3 (backend) convert invitations to accesses
Convert related invitations to accesses upon creating a new identity.
2024-03-21 12:14:10 +01:00
Lebaud Antoine
7d65de1938 (backend) search user on her email and name
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.
2024-03-11 20:23:05 +01:00
Lebaud Antoine
b2d68df646 (backend) mock identities' name when searching a user
When testing user search, we generated few identities
with mocked emails.

Name attribute was introduced on Identity model. Currently
names are freely and randomly generated by the factory.

To make this mocked data more realist, mock also identities'
names to match their email.

It should not break existing tests, and will make them more
predictable when introducing advanced search features.
2024-03-11 20:23:05 +01:00
Marie PUPO JEAMMET
fa88f70cee 🐛(admin) prevent updating of invitations
Invitations cannot be updated for now. To reflect api behaviour,
we disable update in django admin as well.
2024-03-11 11:39:02 +01:00
Marie PUPO JEAMMET
b2956e42d3 🛂(abilities) fix anonymous and unrelated users accessing resources
The function computing abilities return "True" for method get,
even if role of request user was None.
2024-03-11 11:39:02 +01:00
Marie PUPO JEAMMET
18971a10e0 🚨(tests) fix back-end tests warnings
Fixes a warnings in back-end tests suite:
- post_generation hooks save
- ordering for invitation and user viewsets
2024-03-11 11:39:02 +01:00
Marie PUPO JEAMMET
62758763df (api) add invitations CRUD
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.
2024-03-11 11:39:02 +01:00
Lebaud Antoine
b5ce19a28e 📝(backend) clarify how team accesses are queried
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.
2024-03-07 19:55:53 +01:00
Lebaud Antoine
163f987132 🐛(backend) fix team accesses abilities
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.
2024-03-07 19:55:53 +01:00
Lebaud Antoine
e9482a985f (backend) enhance tests when listing team accesses
Abilities on team accesses are computed based on request user role.
Thus, members' roles in relation with user's role matters a lot, to
ensure the abilities were correctly computed.

Complexified the test that lists team accesses while being authenticated.
More members are added to the team with privileged roles. The user
is added last to the less with the less privileged role, "member".

Order matters, because when computing the sub query to determine
user's role within the team, code use the first result value to set the
role to compute abilities.
2024-03-07 19:55:53 +01:00