Rapid concurrent SSO attempts is something that happens quite frequently
in the wild at large enough scale.
When this happens conditions such as adding a user to a group could possibly
fire concurrently causing a user to be added to the same group twice and
erroring out.
To avoid all concurrency issues here we protect with a coarse distributed
mutex. This heavily mitigates the risk around concurrent group additions and
concurrent updates to user related records.
Previously we were only updating group membership when a user record was
first created in an SSO setting.
This corrects it so we also update it if SSO changes the email
* DEV: Replace User.unstage and User#unstage API with User#unstage!
Quoting @SamSaffron:
> User.unstage mixes concerns of both unstaging users and updating params which is fragile/surprising.
> u.unstage destroys notifications and raises a user_unstaged event prior to the user becoming unstaged and the user object being saved.
User#unstage! no longer updates user attributes and saves the object before triggering the `user_unstaged` event.
* Update one more spec
* Assign attributes after unstaging
This is a bottom up rewrite of Discourse cache to support faster performance
and a limited surface area.
ActiveSupport::Cache::Store accepts many options we do not use, this partial
implementation only picks the bits out that we do use and want to support.
Additionally params are named which avoids typos such as "expires_at" vs "expires_in"
This also moves a few spots in Discourse to use Discourse.cache over setex
Performance of setex and Discourse.cache.write is similar.
Zeitwerk simplifies working with dependencies in dev and makes it easier reloading class chains.
We no longer need to use Rails "require_dependency" anywhere and instead can just use standard
Ruby patterns to require files.
This is a far reaching change and we expect some followups here.
We now treat any external_id of blank string (" " or " " or "", etc) or a
invalid word (none, nil, blank, null) - case insensitive - as invalid.
In this case the client will see "please contact admin" the logs will explain
the reason clearly.
This reduces chances of errors where consumers of strings mutate inputs
and reduces memory usage of the app.
Test suite passes now, but there may be some stuff left, so we will run
a few sites on a branch prior to merging
`Upload#url` is more likely and can change from time to time. When it
does changes, we don't want to have to look through multiple tables to
ensure that the URLs are all up to date. Instead, we simply associate
uploads properly to `UserProfile` so that it does not have to replicate
the URLs in the table.
Use:
locale
locale_force_update
To force user locale on users where SiteSetting.allow_user_locale is enabled
Note: If an invalid locale is specified no action will occur
* When two SSO requests containing the same email in the payload are
sent at the same time, it would sometimes result in two users
being created but one without an email record. Investigations
points to ActiveRecord not generating the right statements but
we have no figured out the reproduction steps yet. We should review
this after upgrading to Rails 5.2.
In some cases add_groups and remove_groups is too much work, some sites
may wish to simply synchronize group membership based on a list.
When sso_overrides_groups is on all not automatic group membership is
sourced from SSO. Note if you omit to specify groups, they will be cleared
out.
Setting a user's default groups based on their email address should only be done once, ie. when they confirm their email address.
Previously we were doing this everytime we'd save a user record 🤷
Use: add_groups with a comma delimited list to ensure a user is in groups (using group names)
Use: remove_groups with a comma delimited list to ensure a user is removed from groups (using group names)