Skip to content

Conversation

@everettbu
Copy link
Contributor

@everettbu everettbu commented Jul 26, 2025

Test 3

Summary by CodeRabbit

  • New Features

    • Introduced a system to block specific email addresses from account creation.
    • Enhanced email validation to check against blocked and restricted domains.
    • Improved error messages and feedback during account creation, including more detailed responses for failed registrations.
  • Bug Fixes

    • Fixed validation logic to ensure blocked or disallowed emails are immediately rejected.
  • Tests

    • Added comprehensive tests for email blocking and validation functionality.
  • Chores

    • Added new database support for managing blocked emails.

… many times each email address is blocked, and last time it was blocked. Move email validation out of User model and into EmailValidator. Signup form remembers which email addresses have failed and shows validation error on email field.
@coderabbitai
Copy link

coderabbitai bot commented Jul 26, 2025

Walkthrough

This change introduces a new system for blocking specific email addresses during user account creation. It adds a BlockedEmail model and database table, implements a custom email validator that checks domain restrictions and blocked emails, updates the user creation flow and error responses, and enhances frontend logic to remember and reject previously failed emails in the session. Corresponding tests and fabricators are also included.

Changes

Cohort / File(s) Change Summary
Frontend Email Rejection Handling
app/assets/javascripts/discourse/controllers/create_account_controller.js
Adds a rejectedEmails array to track emails rejected during account creation. Updates email validation logic to immediately fail for rejected emails and modifies account creation to add failed emails to this list.
User Creation Error Response
app/controllers/users_controller.rb
Enhances the JSON response on failed user creation to include structured error details and submitted values for name, username, and email.
BlockedEmail Model and Migration
app/models/blocked_email.rb, db/migrate/20130724201552_create_blocked_emails.rb
Introduces the BlockedEmail ActiveRecord model with presence and uniqueness validations, default action logic, and a method to check and update block status. Adds migration to create the blocked_emails table with relevant fields and a unique index on email.
User Email Validation Refactor
app/models/user.rb
Refactors email validation to use the new custom validator, removes domain restriction logic from the model, and simplifies validation declarations.
Custom Email Validator
lib/validators/email_validator.rb
Adds a new EmailValidator class to enforce domain whitelists/blacklists and block specific emails, integrating with the BlockedEmail model.
Localization
config/locales/server.en.yml
Adds a new translation string for blocked email error messaging.
Email Validator Tests
spec/components/validators/email_validator_spec.rb
Introduces RSpec tests for the EmailValidator, checking behavior for blocked and allowed emails.
BlockedEmail Fabricator
spec/fabricators/blocked_email_fabricator.rb
Adds a fabricator for generating test instances of blocked emails.
BlockedEmail Model Tests
spec/models/blocked_email_spec.rb
Adds RSpec tests for the BlockedEmail model, covering default values, block checks, and statistics updates.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Frontend
    participant Backend
    participant BlockedEmailModel
    participant EmailValidator

    User->>Frontend: Submit account creation with email
    Frontend->>Backend: POST /users with email
    Backend->>EmailValidator: Validate email
    EmailValidator->>BlockedEmailModel: should_block?(email)
    BlockedEmailModel-->>EmailValidator: Return block status
    EmailValidator-->>Backend: Validation result
    alt Email blocked or invalid
        Backend-->>Frontend: Respond with error, include email in error values
        Frontend->>Frontend: Add email to rejectedEmails
        Frontend-->>User: Show error, prevent reuse
    else Email valid
        Backend-->>Frontend: Respond with success
        Frontend-->>User: Proceed with account creation
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇
A hop, a skip, a block on spam,
Now emails checked, no more a sham!
With tables new and tests so bright,
We guard the warren day and night.
If rabbits coded, here’s their cheer—
“Your inbox safe, now have no fear!”
☁️📧✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch blocked-email-validation-post

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@everettbu
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jul 29, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
spec/components/validators/email_validator_spec.rb (1)

3-23: Good test coverage with minor style improvements needed.

The tests properly cover both blocked and unblocked email scenarios using appropriate stubs. However, there are some style issues to address:

 describe EmailValidator do
-
   let(:record) { Fabricate.build(:user, email: "bad@spamclub.com") }
   let(:validator) { described_class.new({attributes: :email}) }
   subject(:validate) { validator.validate_each(record,:email,record.email) }

   context "blocked email" do
     it "doesn't add an error when email doesn't match a blocked email" do
       BlockedEmail.stubs(:should_block?).with(record.email).returns(false)
       validate
       record.errors[:email].should_not be_present
     end

     it "adds an error when email matches a blocked email" do
       BlockedEmail.stubs(:should_block?).with(record.email).returns(true)
       validate
       record.errors[:email].should be_present
     end
   end
-
 end
spec/models/blocked_email_spec.rb (2)

1-49: Comprehensive test coverage with minor syntax improvements needed.

The test structure effectively covers the BlockedEmail model functionality. However, consider modernizing the RSpec syntax for better maintainability.

Apply this diff to modernize the RSpec syntax:

-    subject.should be_false
+    expect(subject).to be_falsey
-      it { should be_true }
+      it { is_expected.to be_truthy }
-      it { should be_false }
+      it { is_expected.to be_falsey }

4-5: Remove extra empty lines for consistent formatting.

The static analysis correctly identifies unnecessary empty lines at block boundaries.

Apply this diff to fix the formatting:

 describe BlockedEmail do
-
   let(:email) { 'block@spamfromhome.org' }
   end
-
 end

Also applies to: 47-48

lib/validators/email_validator.rb (1)

18-22: Consider enhancing domain matching robustness.

The current domain matching implementation works but could be made more robust to handle edge cases and improve security.

Apply this diff to improve the domain matching logic:

 def email_in_restriction_setting?(setting, value)
-  domains = setting.gsub('.', '\.')
-  regexp = Regexp.new("@(#{domains})", true)
-  value =~ regexp
+  return false if value.blank?
+  email_domain = value.split('@').last&.downcase
+  return false if email_domain.blank?
+  
+  allowed_domains = setting.split('|').map(&:strip).map(&:downcase)
+  allowed_domains.any? { |domain| email_domain == domain || email_domain.end_with?(".#{domain}") }
 end

This approach:

  • Handles subdomain matching more explicitly
  • Avoids regex injection risks
  • Provides clearer domain comparison logic
  • Handles edge cases like missing @ symbol
app/models/blocked_email.rb (2)

11-19: Consider performance and reliability improvements.

The should_block? method works correctly but could be optimized and made more robust.

Apply this diff to improve the method:

 def self.should_block?(email)
-  record = BlockedEmail.where(email: email).first
+  record = BlockedEmail.find_by(email: email)
   if record
     record.match_count += 1
     record.last_match_at = Time.zone.now
-    record.save
+    record.save!
   end
   record && record.action_type == actions[:block]
 end

Improvements:

  • find_by is more idiomatic than where().first
  • save! will raise an exception if the save fails, making debugging easier
  • Consider adding error handling if statistics updates should be non-blocking

11-19: Evaluate statistics tracking performance impact.

The current implementation updates match statistics on every call to should_block?, which could create database load if this method is called frequently. Consider if this level of tracking granularity is necessary.

For high-frequency validation scenarios, consider:

  • Batching statistics updates
  • Using background jobs for statistics
  • Caching frequently accessed blocked emails
  • Adding database indexes on the email column for performance
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e25638d and 5f8a130.

📒 Files selected for processing (10)
  • app/assets/javascripts/discourse/controllers/create_account_controller.js (4 hunks)
  • app/controllers/users_controller.rb (1 hunks)
  • app/models/blocked_email.rb (1 hunks)
  • app/models/user.rb (1 hunks)
  • config/locales/server.en.yml (1 hunks)
  • db/migrate/20130724201552_create_blocked_emails.rb (1 hunks)
  • lib/validators/email_validator.rb (1 hunks)
  • spec/components/validators/email_validator_spec.rb (1 hunks)
  • spec/fabricators/blocked_email_fabricator.rb (1 hunks)
  • spec/models/blocked_email_spec.rb (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
spec/fabricators/blocked_email_fabricator.rb (1)
app/models/blocked_email.rb (1)
  • actions (7-9)
app/controllers/users_controller.rb (1)
app/serializers/user_serializer.rb (1)
  • attributes (1-76)
spec/components/validators/email_validator_spec.rb (2)
spec/spec_helper.rb (1)
  • build (130-132)
lib/validators/email_validator.rb (2)
  • validate_each (1-24)
  • validate_each (3-16)
lib/validators/email_validator.rb (1)
app/models/blocked_email.rb (1)
  • should_block? (11-19)
🪛 RuboCop (1.76.1)
spec/components/validators/email_validator_spec.rb

[convention] 4-5: Extra empty line detected at block body beginning.

(Layout/EmptyLinesAroundBlockBody)


[convention] 22-23: Extra empty line detected at block body end.

(Layout/EmptyLinesAroundBlockBody)

spec/models/blocked_email_spec.rb

[convention] 4-5: Extra empty line detected at block body beginning.

(Layout/EmptyLinesAroundBlockBody)


[convention] 47-48: Extra empty line detected at block body end.

(Layout/EmptyLinesAroundBlockBody)

🔇 Additional comments (11)
config/locales/server.en.yml (1)

734-734: LGTM!

The new locale string is appropriately placed and follows the existing naming conventions. The message "is not allowed." is clear and consistent with the existing email validation messages.

spec/fabricators/blocked_email_fabricator.rb (1)

1-4: LGTM!

The fabricator is well-designed with unique sequential email generation and appropriate test data. The email format clearly indicates test nature and the action_type correctly references the BlockedEmail enum.

db/migrate/20130724201552_create_blocked_emails.rb (1)

1-12: LGTM!

The migration is well-designed with appropriate column types, constraints, and indexing. The unique index on email prevents duplicates, and the match_count/last_match_at columns provide useful tracking capabilities.

app/controllers/users_controller.rb (1)

197-199: Excellent enhancement to error response structure.

The addition of structured errors hash and values hash greatly improves the user experience by:

  • Providing detailed validation errors for programmatic handling
  • Preserving submitted values so users don't need to re-enter valid fields
  • Maintaining backward compatibility with the existing message field
lib/validators/email_validator.rb (1)

1-16: Well-structured email validator with proper error handling.

The validator correctly implements the validation flow: whitelist → blacklist → blocked email check. The conditional logic ensures appropriate precedence and the integration with BlockedEmail is well-designed.

app/assets/javascripts/discourse/controllers/create_account_controller.js (4)

17-17: Good addition of client-side email tracking.

The rejectedEmails array properly uses Ember.A() for reactive array functionality.


69-75: Verify the validation flow order for user experience.

The rejected email check occurs before other email validations, which provides immediate feedback. However, consider whether this might confuse users if the email format is invalid but they see a "rejected" message instead of "invalid format".

The current order prioritizes rejected email feedback over format validation. Consider if this provides the best user experience or if format validation should come first.


96-96: Correct computed property dependency.

The addition of rejectedEmails.@each as a dependency ensures the validation updates when emails are added to the rejected list.


274-276: Effective error handling integration.

The server response parsing correctly identifies email validation failures and captures the submitted email for future rejection. This creates a good feedback loop between server-side validation and client-side prevention.

app/models/blocked_email.rb (1)

1-9: Well-structured model with proper validations.

The BlockedEmail model follows Rails conventions with appropriate validations and the enum-like actions pattern provides good type safety.

app/models/user.rb (1)

46-47: Excellent refactoring to use dedicated email validator.

The migration from custom validation methods to Rails standard validations with the new EmailValidator improves code organization and maintainability. The conditional validation if: :email_changed? appropriately prevents unnecessary validation runs.

This change successfully:

  • Separates email validation concerns into a dedicated validator
  • Maintains existing validation requirements (presence, uniqueness)
  • Integrates domain restrictions and blocked email checking
  • Follows Rails conventions for cleaner, more maintainable code

@github-actions
Copy link

This pull request has been automatically marked as stale because it has been open for 60 days with no activity. To keep it open, remove the stale tag, push code, or add a comment. Otherwise, it will be closed in 14 days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants