Commit Graph

495 Commits

Author SHA1 Message Date
Mateusz Mandera
9a043494fb sync_group: Fix incorrect reference to ldap_logger.
This is an obvious bug/typo introduced in
2cd6f07ffe.
2025-05-18 02:18:40 +02:00
Mateusz Mandera
53aaa0e918 sync_groups: Don't allow syncing of system groups.
Trying to sync system groups with this would easily lead to database
corruption and we should completely disallow it.
2025-05-15 14:02:57 -07:00
Mateusz Mandera
2cd6f07ffe ldap: Extract sync_groups function from sync_groups_from_ldap.
This extracts the core general group-syncing logic, which is independent
of any LDAP-specific concerns, to a separate function. This allows the
use of this core logic for group sync in other authentication backends.

The code also gets a bit cleaned up in the process to make it more
readable (with some readability tweaks to the log strings as well).
2025-05-15 10:20:28 -07:00
Mateusz Mandera
1eecbad381 ldap: Fix the syncing of user role via AUTH_LDAP_USER_FLAGS_BY_GROUP.
This was broken, due the mechanism simply using our
is_guest/is_realm_admin/etc. role setters, but failing to adjust system
group memberships - resulting in corrupted database state.
We need to ensure that change_user_role is called for setting user role.

There are two relevant codepaths that run the sync based on
AUTH_LDAP_USER_FLAGS_BY_GROUP and thus need to get this right:
1. manage.py sync_ldap_user_data
2. Just-in-time user creation when a user without a Zulip account logs
   in for the first using their ldap credentials. After
   get_or_build_user returns, django-auth-ldap sees that the user
   account has just been created, and proceeds to run ._populate_user().

Now that both user.save() and do_change_user_realm will be getting
called together, we need to ensure this always happens atomically.

This imposes the need to override _get_or_create_user to put it in a
transaction. The troublesome consequence is that this new
`atomic(savepoint=False)` causes the usual type of issue, where tests
testing error get their transaction rolled back and cannot continue
executing.

To get around that, we add a test helper
`artificial_transaction_savepoint` which allows these tests to wrap
their problematic blocks in an artificial transaction which provides a
savepoint, thus preventing the full test transaction rollback derailing
the rest of the test.
2025-04-28 17:44:56 -07:00
Mateusz Mandera
4dd1826532 ldap: Fix dev/test-specific bugs with AUTH_LDAP_USER_FLAGS_BY_GROUP.
Without these overrides, we cannot test the functionality in DEVELOPMENT
and TESTING.

There are two codepaths that we're covering here:
1. The sync which happens via `sync_ldap_user_data`.
2. The sync which happens during just-in-time user creation upon first
   login via ldap.

Both codepaths end up triggering ldap_user._get_or_create_user().
2025-04-28 17:44:56 -07:00
Tim Abbott
37b7a32eb4 backends: Fix exception with password lengths above 72.
Apparently, while we set our own maximum password length of 100
characters, zxcvbn had a hardcoded maximum length of 72 characters,
and threw an exception if that was exceeded.

The fact that we're discovering this now would suggest that nobody has
previously attempted a password between 72 and 100 characters in
length.
2025-03-25 09:44:52 -07:00
Anders Kaseorg
ec3d187659 backends: Fix type errors.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2025-02-18 22:04:43 -08:00
Anders Kaseorg
302b961ec1 backends: Add missing @override declarations.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2025-02-18 22:04:43 -08:00
Anders Kaseorg
4703aed86f backends: Rename SAMLAuthBackend.process_logout override.
Our method has an incompatible signature and cannot be validly typed
as an override.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2025-02-18 22:04:43 -08:00
Anders Kaseorg
f223251ffe requirements: Upgrade Python requirements.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2025-01-14 09:42:16 -08:00
Anders Kaseorg
3ec58fd3d5 requirements: Upgrade Python requirements.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-10-20 18:16:27 -07:00
Mateusz Mandera
2a1da859ea auth: Update AzureADAuthBackend to use the newer Microsoft API.
As detailed in the comment in the code:

The upstream implementation uses the outdated /oauth2/authorize
API (instead of the v2.0 API), which doesn't allow us to authenticate
users with just a personal Microsoft account. v2.0 API is required.
This requires us to override the default URLs to use it as well
as adjust the requested scopes, to match this new API.

The backend in its previous state was only able to authenticate users
that were tied to an organizational directory, even if the application
settings in Azure were set up to also allow personal accounts. Users
trying to use a personal account would face an error from Microsoft:

AADSTS500200: User account 'xxxx@example.com' is a
personal Microsoft account. Personal Microsoft accounts are not
supported for this application unless explicitly invited to an
organization

https://github.com/python-social-auth/social-core/issues/723 is a
related upstream issue.
2024-08-21 16:16:30 -07:00
Mateusz Mandera
7e1f468f04 saml: Fix exception when syncing missing value to custom profile field.
There was a bug here that would trigger an exception inside
`sync_user_profile_custom_fields`, causing it to get logged with
logging.warning, when an attribute configured for SAML custom profile
field sync was missing from a SAMLResponse or had an empty value.
`sync_user_profile_custom_fields` expects valid values, and None is not
valid.

We could consider a slightly different behavior here instead - when an
attribute is sent with no value in the SAMLResponse, that means the attr
has no value in the IdP's user directory - so perhaps a better behavior
would be to also remove the custom profile field value in Zulip. However
there are two issues with that:

1. It's not necessarily the best behavior, because an organization might
want the "user doesn't have this attribute set at the IdP level" state
to just mean that the user should be free to set the value manually in
Zulip if they wish. And having that value get reset on every login would
then be an issue. The implementation in this commit is consistent with
this philosophy.

2. There's some implementation difficulty - upstream
`self.get_attr(...)`, which we use for reading the attr value from the
SAMLResponse, doesn't distinguish between an attribute being sent with
no value and the attribute not being sent at all - in both cases it
returns None. So we'd need some extra work here with parsing the
SAMLResponse properly, to be able to know when the custom profile field
should get cleared.
2024-08-20 13:57:30 -07:00
Mateusz Mandera
833dce8a13 saml: Add support for syncing user role.
Replace the SOCIAL_AUTH_SYNC_CUSTOM_ATTRS_DICT with
SOCIAL_AUTH_SYNC_ATTRS_DICT, designed to support also regular user attrs
like role or full name (in the future).

Custom attributes can stay configured as they were and will get merged
into SOCIAL_AUTH_SYNC_ATTRS_DICT in computed_settings, or can be
specified in SOCIAL_AUTH_SYNC_ATTRS_DICT directly with "custom__"
prefix.

The role sync is plumbed through to user creation, so users can
immediately be created with their intended role as provided by the IdP
when they're creating their account, even when doing this flow without
an invitiation.
2024-08-20 11:53:24 -07:00
Anders Kaseorg
0fa5e7f629 ruff: Fix UP035 Import from collections.abc, typing instead.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-07-13 22:28:22 -07:00
Anders Kaseorg
531b34cb4c ruff: Fix UP007 Use X | Y for type annotations.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-07-13 22:28:22 -07:00
Anders Kaseorg
e08a24e47f ruff: Fix UP006 Use list instead of List for type annotation.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-07-13 22:28:22 -07:00
Alex Vandiver
08b24484d1 upload: Remove redundant acting_user_profile argument.
This argument, effectively added in 9eb47f108c, was never actually
used.
2024-06-26 16:43:11 -07:00
Alex Vandiver
b36ad31f0e backends: Early return in sync_avatar_from_ldap. 2024-06-20 23:06:08 -04:00
Vector73
8ab526a25a models: Replace realm.uri with realm.url.
In #23380, we are changing all occurrences of uri with url in order to
follow the latest URL standard. Previous PRs #25038 and #25045 has
replaced the occurences of uri that has no direct relation with realm.

This commit changes just the model property, which has no API
compatibility concerns.
2024-05-08 11:12:43 -07:00
Sahil Batra
27558315a2 settings: Use named_user_group field to access name.
This commit updates code to access name from named_user_group
field which points to the "NamedUserGroup" instead of directly
accessing name from "UserGroup", since name field will only
be present on NamedUserGroup objects in further commits.
2024-04-26 17:03:09 -07:00
Sahil Batra
0ff9aacb96 audit_logs: Set modified_user_group field to NamedUserGroup. 2024-04-26 17:03:09 -07:00
Anders Kaseorg
d8ebb2db95 auth: Avoid deprecated django.contrib.auth.views.logout_then_login.
It’s removed in Django 5.0.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-04-04 16:27:58 -07:00
roanster007
c7a08f3b77 settings: Add permission to enforce unique names in realm.
Previously, users were allowed to signup or change their names to
those which already existed in the realm.

This commit adds an Organization Permission, that shall enforce
users to use unique names while signing up or changing their
names. If a same or normalized full name is found in realm,
then a validation error is thrown.

Fixes #7830.
2024-04-02 14:55:59 -07:00
Mateusz Mandera
da9e4e6e54 backends: Implementation of restricting certain backends by plan.
Only affects zulipchat, by being based on the BILLING_ENABLED setting.

The restricted backends in this commit are
- AzureAD - restricted to Standard plan
- SAML - restricted to Plus plan, although it was already practically
  restricted due to requiring server-side configuration to be done by us

This restriction is placed upon **enabling** a backend - so
organizations that already have a backend enabled, will continue to be
able to use it. This allows us to make exceptions and enable a backend
for an org manually via the shell, and to grandfather organizations into
keeping the backend they have been relying on.
2024-03-05 11:48:58 -08:00
Mateusz Mandera
fdbdf8c620 backends: Add ZulipAuthMixin inheritance to ZulipRemoteUserBackend.
By convention, all our backends inherit form ZulipAuthMixin. This is
helpful to have a single place if we want to add some class attributes
that all the backends should have.
2024-03-05 11:48:58 -08:00
Anders Kaseorg
869d9d9a79 ruff: Fix RUF025 Unnecessary dict comprehension for iterable.
This is a preview rule, not yet enabled by default.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-03-01 09:30:04 -08:00
Mateusz Mandera
80f5963bbc auth: Add a configurable wrapper around authenticate calls. 2024-01-15 12:18:48 -08:00
Anders Kaseorg
27c0b507af models: Extract zerver.models.custom_profile_fields.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-12-16 22:08:44 -08:00
Anders Kaseorg
cd96193768 models: Extract zerver.models.realms.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-12-16 22:08:44 -08:00
Anders Kaseorg
45bb8d2580 models: Extract zerver.models.users.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-12-16 22:08:44 -08:00
Mateusz Mandera
2a65183991 tests: Add test for nocoverage userAccountControl case in ldap auth.
This logic has been #nocoverage since its implementation, but since this
is an authentication codepath, it seems important for it to have a test.
2023-11-24 13:38:23 -08:00
Mateusz Mandera
1800b2c797 ldap: Tweak AUTH_LDAP_ADVANCED_REALM_ACCESS_CONTROL behavior.
The original behavior of this setting was to disable LDAP
authentication for any realms not configured to use it. This was an
arbitrary choice, and its only value was to potentially help catch
typos for users who are lazy about testing their configuration.

Since it makes it a very inconvenient to potentially host multiple
organizations with different LDAP configurations, remove that
behavior.
2023-11-17 14:40:26 -08:00
Anders Kaseorg
a50eb2e809 mypy: Enable new error explicit-override.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-10-12 12:28:41 -07:00
Simon Michalke
b6a25840a1 zerver: Implement ldap group synchronization.
Fixes #9957.

Co-authored-by: Mateusz Mandera <mateusz.mandera@zulip.com>
2023-10-05 16:12:52 -07:00
Mateusz Mandera
a095c34503 saml: Add OneLogin_Saml2_ValidationError to caught parsing exceptions.
This is an exception that we should be generally catching like the
others, which will give our standard /login/ redirect and proper logging
- as opposed to a 500 if we don't catch.

Addresses directly a bug we occurred in the wild, where a SAMLResponse
was submitted without issuers specified in a valid way, causing this
exception. The added test tests this specific type of scenario.
2023-09-27 10:32:18 -07:00
Anders Kaseorg
3b09197fdf ruff: Fix RUF015 Prefer next(...) over single element slice.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-07-23 15:20:53 -07:00
Alex Vandiver
0dbe111ab3 test_helpers: Switch add/remove_ratelimit to a contextmanager.
Failing to remove all of the rules which were added causes action at a
distance with other tests.  The two methods were also only used by
test code, making their existence in zerver.lib.rate_limiter clearly
misplaced.

This fixes one instance of a mis-balanced add/remove, which caused
tests to start failing if run non-parallel and one more anonymous
request was added within a rate-limit-enabled block.
2023-06-12 12:55:27 -07:00
Anders Kaseorg
b7909db987 ruff: Fix PLC0208 Use a sequence type when iterating over values.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-06-06 14:58:11 -07:00
Mateusz Mandera
45e3626bd2 saml: Clean up additional session vars if authentication fails.
This doesn't have any obvious security implications right now, but
nonetheless such information is not meant to stick around in the session
if authentication didn't succeed and not cleaning up would be a bug.
2023-05-23 13:01:15 -07:00
Mateusz Mandera
c9fb55dd20 saml: Improve the comments about the SAMLDocument processing logic. 2023-05-23 13:01:15 -07:00
Mateusz Mandera
8fb0fe96c6 saml: Save SessionIndex in session and use when making a LogoutRequest.
This is a useful improvement in general for making correct
LogoutRequests to Idps and a necessary one to make SP-initiated logout
fully work properly in the desktop application. During desktop auth
flow, the user goes through the browser, where they log in through their
IdP. This gives them a logged in  browser session at the IdP. However,
SAML SP-initiated logout is fully conducted within the desktop
application. This means that proper information needs to be given to the
the IdP in the LogoutRequest to let it associate the LogoutRequest with
that logged in session that was established in the browser. SessionIndex
is exactly the tool for that in the SAML spec.
2023-05-23 13:01:15 -07:00
Mateusz Mandera
5dd4dcdebb saml: Make SP-initiated SLO work in the desktop application. 2023-05-23 13:01:15 -07:00
Mateusz Mandera
3f55c10685 saml: Rework SP-initiated logout config to support IdP-level config.
This gives more flexibility on a server with multiple organizations and
SAML IdPs. Such a server can have some organizations handled by IdPs
with SLO set up, and some without it set up. In such a scenario, having
a generic True/False server-wide setting is insufficient and instead
being able to specify the IdPs/orgs for SLO is needed.
2023-05-23 13:01:15 -07:00
Mateusz Mandera
0bb0220ebb saml: Implement SP-initiated Logout.
Closes #20084

This is the flow that this implements:
1. A logged-in user clicks "Logout".
2. If they didn't auth via SAML, just do normal logout. Otherwise:
3. Form a LogoutRequest and redirect the user to
https://idp.example.com/slo-endpoint?SAMLRequest=<LogoutRequest here>
4. The IdP validates the LogoutRequest, terminates its own user session
and redirects the user to
https://thezuliporg.example.com/complete/saml/?SAMLRequest=<LogoutResponse>
with the appropriate LogoutResponse. In case of failure, the
LogoutResponse is expected to express that.
5. Zulip validates the LogoutResponse and if the response is a success
response, it executes the regular Zulip logout and the full flow is
finished.
2023-05-23 13:01:15 -07:00
Mateusz Mandera
01498add9b auth: Rename authentication_method session var. 2023-05-23 13:01:15 -07:00
Sahil Batra
7f01b3fb63 users: Set tos_version to -1 for users who have not logged-in yet.
We now set tos_version to "-1" for imported users and the ones
created using API or using other methods like LDAP, SCIM and
management commands. This value will help us to allow users to
change email address visibility setting during first login.
2023-05-16 13:52:56 -07:00
Mateusz Mandera
254ea4b0c8 social_auth: Save authentication method information in the session.
The immediate application of this will be for SAML SP-initiated logout,
where information about which IdP was used for authenticating the
session needs to be accessed. Aside of that, this seems like generally
valuable session information to keep that other features may benefit
from in the future.
2023-05-12 16:21:26 -07:00
Mateusz Mandera
eb4fc7568c auth_enabled_helper: Add realm_authentication_methods argument.
This allows removing pointless db queries when calling
*_auth_enabled(realm) repeatedly.
2023-04-18 09:22:56 -07:00
Mateusz Mandera
72d56d5d59 auth: Remove Realm.AUTHENTICATION_FLAGS class attribute.
With the removal of the authentication_methods bitfield this is now
useless and just duplicates AUTH_BACKEND_NAME_MAP keys.
2023-04-18 09:22:56 -07:00