fixes https://github.com/TryGhost/Ghost/issues/14508
This change requires the frontend to send an explicit `emailType` when sending a magic link. We default to `subscribe` (`signin` for invite only sites) for now to remain compatible with the existing behaviour.
**Problem:**
When a member tries to login and that member doesn't exist, we created a new member in the past.
- This caused the creation of duplicate accounts when members were guessing the email address they used.
- This caused the creation of new accounts when using an old impersonation token, login link or email change link that was sent before member deletion.
**Fixed:**
- Trying to login with an email address that doesn't exist will throw an error now.
- Added new and separate rate limiting to login (to prevent user enumeration). This rate limiting has a higher default limit of 8. I think it needs a higher default limit (because it is rate limited on every call instead of per email address. And it should be configurable independent from administrator rate limiting. It also needs a lower lifetime value because it is never reset.
- Updated error responses in the `sendMagicLink` endpoint to use the default error encoding middleware.
- The type (`signin`, `signup`, `updateEmail` or `subscribe`) is now stored in the magic link. This is used to prevent signups with a sign in token.
**Notes:**
- Between tests, we truncate the database, but this is not enough for the rate limits to be truly reset. I had to add a method to the spam prevention service to reset all the instances between tests. Not resetting them caused random failures because every login in every test was hitting those spam prevention middlewares and somehow left a trace of that in those instances (even when the brute table is reset). Maybe those instances were doing some in memory caching.
refs https://github.com/TryGhost/Ghost/issues/14101
- migrated staff user controller to native class syntax
- removed use of `{{action}}` helper
- moved from custom components to native `<input>` and `<textarea>` for form fields
- added `{{select-on-click}}` modifier to cover the `<GhTextingInput @selectOnClick>` option behaviour for any input element
- added `submitForm()` test helper that finds closest `form` element and trigger's a `submit` event on it simulating <kbd>Enter</kbd> being pressed whilst a field has focus
fixes https://github.com/TryGhost/Ghost/issues/14508
This change requires the frontend to send an explicit `emailType` when sending a magic link. We default to `subscribe` (`signin` for invite only sites) for now to remain compatible with the existing behaviour.
**Problem:**
When a member tries to login and that member doesn't exist, we created a new member in the past.
- This caused the creation of duplicate accounts when members were guessing the email address they used.
- This caused the creation of new accounts when using an old impersonation token, login link or email change link that was sent before member deletion.
**Fixed:**
- Trying to login with an email address that doesn't exist will throw an error now.
- Added new and separate rate limiting to login (to prevent user enumeration). This rate limiting has a higher default limit of 8. I think it needs a higher default limit (because it is rate limited on every call instead of per email address. And it should be configurable independent from administrator rate limiting. It also needs a lower lifetime value because it is never reset.
- Updated error responses in the `sendMagicLink` endpoint to use the default error encoding middleware.
- The type (`signin`, `signup`, `updateEmail` or `subscribe`) is now stored in the magic link. This is used to prevent signups with a sign in token.
**Notes:**
- Between tests, we truncate the database, but this is not enough for the rate limits to be truly reset. I had to add a method to the spam prevention service to reset all the instances between tests. Not resetting them caused random failures because every login in every test was hitting those spam prevention middlewares and somehow left a trace of that in those instances (even when the brute table is reset). Maybe those instances were doing some in memory caching.
no issue
- All content-length snapshots should be using the same matcher for consistency - anyContentLength. It's more explicit about what the matcher is all about and might be useful to have content-length matchers in one place if it ever changes (the header value should be a damn digit after all, not a string!) (ref. https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2)
refs https://github.com/TryGhost/Team/issues/2024
Without validation it was possible to send a string of comma separated
email addresses to the endpoint, and an email would be sent to each
address, bypassing any rate limiting.
This bug does not allow for an authentication bypass exploit. It is purely a
spam email concern.
Credit: Sandip Maity <maitysandip925@gmail.com>
no issue
- The milliseconds configuration here is different to "seconds" used in the max-age header value itself and other middlewares (like CORS). It's not going to be fixed upstream, so whenever this piece of code is touched again would be smart to get our own converter from seconds to milliseconds going, or some other mechanism making max-age configuration uniform across codebase
refs https://github.com/TryGhost/Toolbox/issues/320
- The URL matcher is very likely to be reused in the future, so having it abstracted away gives two benefits:
1. Central place to document hacky behavior and easier future cleanup
2. The implementer of the e2e test does not have to see the "hacky note" and just concentrate on the implementation of the test
refs https://github.com/TryGhost/Toolbox/issues/320
- Header snapshot matching was missing from webhook e2e tests. With a bumped version of webhook-mock-receiver it's now possible to record and match webhook request headers.
refs https://github.com/TryGhost/Toolbox/issues/426
- with the existing `ship` command, it publishes to npm before pushing
to GitHub
- I fear we're likely to run into a scenario where the HEAD of `main` is
behind, so the git push fails, and then rebasing the HEAD will break
the tags that we created
- in this scenario, I think it's better to hard reset back, git pull and
then try again
- in that case, we need to publish to npm after GitHub
- this commit should implement that
refs https://github.com/TryGhost/Toolbox/issues/426
- now Portal is in the monorepo, the dev script doesn't need to wipe the
console output nor print instructions as that should be handled with
setting environment variables
refs https://github.com/TryGhost/Toolbox/issues/426
- this adds support for `--portal` in `yarn dev` to support the upcoming
migration of Portal into the monorepo
- also adds the Portal folder to the ignorelist for nodemon so Ghost
doesn't bootloop
- these changes were needed ahead of the upcoming bump to react-scripts,
which bumps eslint to v8, and therefore we can update
eslint-plugin-ghost, which introduces some new linting rules
refs e0430b4efc
- acceptance test had mixed up name/slug fields so was testing for incorrect values now that cmd+s inside the slug field is working as really expected
no issue
- swapped from route actions triggered by shortcuts mixin to explicit `{{on-key}}` actions
- when saved via keyboard, blur any focused element to trigger it's on-blur action and schedule the save to run after those actions
refs https://github.com/TryGhost/Ghost/issues/14101
Twitter/facebook URL validation doesn't follow our typical validation and was duplicated across multiple screens making the controllers unnecessarily complex.
- extracted url input fields and their validation into separate components
- uses tracked scratch values so that the input field values can reset to the saved value on save
- twitter/facebook URL inputs are different to our other inputs because invalid values won't prevent saving, instead they are reset to their previous value on save
- added `this.validate()` call after a successful save in `settings` service so the service and underlying model validations are both in sync (fixes validation error sticking around after saving with invalid twitter/facebook values that have been reset)
no issue
- reset logic was incorrect meaning that if you focused a twitter/facebook URL field then moved to a different field without inputting anything an error would be thrown from trying to use `.match()` on `null`
refs https://github.com/TryGhost/Team/issues/2019
- the anchor chart keeps showing the loading spinner for a site that has no paid tiers and the source attribution flag switched on.
- this was because it tries to load the the MRR chart by default, which doesn't has any data when paid tiers are disabled.
- updated the chart to use `total members` data when paid tiers is disabled
refs https://github.com/TryGhost/Team/issues/2019
- the anchor chart keeps showing the loading spinner for a site that has no paid tiers and the source attribution flag switched on.
- this was because it tries to load the the MRR chart by default, which doesn't has any data when paid tiers are disabled.
- updated the chart to use `total members` data when paid tiers is disabled
* 🐛 Fixed broken `close` button on modals
no issue
Some modals' close elements containing `{{on "click" @close}}` and `href=""` would not take the 'on cl
ick' function into account, meaning the whole page would get redirected to "/" (back to the root of the dashboard) instead of just closing the modal.