Skip to content

Conversation

@jlledom
Copy link
Contributor

@jlledom jlledom commented Jan 9, 2026

Note
This PR includes all changes from #4194. To make it easier to review, jlledom#3 includes only the actual diff to make strong passwords mandatory.

What this PR does / why we need it:

Currently, a provider can enforce strong password for the developer portal, and weak passwords are accepted by default. About admin portal, there's no option to enable strong passwords, weak passwords are always accepted.

I think both situations make no sense. I don't think it's acceptable to allow users decide whether they enforce strong passwords or not, as long as strong passwords are possible, that should be the default. And same thing about admin portal.

In this PR, I remove any option to accept weak passwords in both admin or developer portals. Existing passwords will continue to work, but new passwords will be enforced to be strong. Also, I increased the minimal number of characters to 16.

This affects multiple screens, but also API endpoints, this is the complete list:

  • All scenarios with a password
    • Admin portal
      • UI
        • User invitation signup form
        • Personal details
        • Edit user
        • Forgot password
      • API
        • POST /master/api/providers.xml
        • POST /admin/api/users.xml
        • PUT /admin/api/users/{id}.xml
    • Developer portal
      • UI
        • New buyer signup form
        • Existing buyer user invitation signup form
        • Personal details
        • Edit user
        • Forgot password
      • API
        • POST /admin/api/signup.xml
        • POST /admin/api/accounts/{id}/users.xml
        • PUT /admin/api/accounts/{id}/users/{id}.xml

Besides, while doing this, I found a few UI errors that I told Claude to fix. This are the affected screens:

  • Admin portal
    • User invitation signup form
      • JS validation:
        • Before: min 6 characters
        • Now: Same restrictions as in backend
      • Error messages formatting
        • "\n" was literally printed instead of a new line.
    • Personal Details
      • Password input in clear text
    • Edit user
      • Password inputs visible for users without password (e.g. OAuth)
    • Create new buyer
      • Password input in clear text
    • Forgot password
      • Pass JS Validation: Sames as above

Another thing I noticed is strong passwords, even when enabled, were not being enforced for users not created by a human. For instance, the default admin user created when a buyer is created was always accepting weak passwords no matter the setting. I also fixed this.

Which issue(s) this PR fixes

https://issues.redhat.com/browse/THREESCALE-8916

Verification steps

You can go through any (ideally all) screens above an try to set a weak password. Also tests should pass.

Implemented 9 years ago, transparently migrated on login. At this point
I think the migration is already completed.
@jlledom jlledom changed the title Threescale 8916 admin strong paswords THREESCALE-8916: Make strong passwords mandatory Jan 9, 2026
@jlledom jlledom force-pushed the THREESCALE-8916-admin-strong-paswords branch from da18ea7 to 5b00c7f Compare January 9, 2026 14:14
Comment on lines +8 to +17
const RE_STRONG_PASSWORD = new RegExp(
'^' +
'(?=.*\\d)' + // at least one digit
'(?=.*[a-z])' + // at least one lowercase
'(?=.*[A-Z])' + // at least one uppercase
`(?=.*[${STRONG_PASSWORD_SPECIAL_CHARS.replace(/[[\]\\]/g, '\\$&')}])` + // at least one special char
'(?!.*\\s)' + // no whitespace
`.{${STRONG_PASSWORD_MIN_SIZE},}` + // minimum length
'$'
)
Copy link
Contributor

Choose a reason for hiding this comment

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

According to NIST guidelines, passwords have to be long, not contain funny characters. I think we should have a default password setting of maybe 12 characters and allow customers to set their own REGEX. This will also allow us to keep short passwords in dev mode so we don't have to type funny stuff to login local server when developing 😎

We better read about current best practices though.

Copy link
Contributor

Choose a reason for hiding this comment

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

So currently NIST seems to care about password length and expiration is not recommended. I think standard was 14 chars at the moment without character enforcement. Maybe we should have a setting to allow customers setting minimal password length. idk if we should just keep what we had before and have standard 14 chars, and when strong password chosen, then go for 16 chars. Adding a new field for a custom length is probably an overkill. Lets discuss further.

Copy link
Contributor

@akostadinov akostadinov Jan 9, 2026

Choose a reason for hiding this comment

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

We should follow NIST SP 800-63B-4 Date Published: July 2025
https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63B-4.pdf

See section 3.1.1.2. Password Verifiers

Maybe we don't even need the strong password setting.

self.password = unencrypted_password
ThreeScale::Analytics.user_tracking(self).track('Migrated to BCrypt')
update_columns(password_digest: password_digest, salt: nil, crypted_password: nil)
authenticate(unencrypted_password)
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel uneasy about this whole file. What is it doing? Why is it wrapping? What was super?


def password_required?
signup.by_user? && super
(signup.by_user? || using_password?) && super
Copy link
Contributor

Choose a reason for hiding this comment

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

can you explain why do we need this change?

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we rename this method? What is super? I wonder if we should separate usage of password on signup and for validation.

Copy link
Contributor

@akostadinov akostadinov Jan 9, 2026

Choose a reason for hiding this comment

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

I asked AI whether the method can be split for checking whether password fields should be shown and whether password field has to be validated. Idk if the analysis makes sense but here it is:

● You're absolutely right - this is a confusing design! The same password_required? method is being used for two different purposes:

  1. Model validation - "Should we validate password presence when saving?"
  2. UI rendering - "Should we show password fields in forms?"

  And worse, it's got that triple-level super chain that's hard to follow.

  Better Design

  This could be much clearer with separate methods:

  For Validation:

  # In User model
  def validate_password_presence?
    signup.by_user? && password_digest_requires_validation? && legacy_password_requires_validation?
  end

  validates_presence_of :password, if: :validate_password_presence?

  For UI:

  # In User model  
  def needs_password_field?
    signup.by_user?  # Or even more specific: !using_external_auth?
  end

  # In templates
  {% if user.needs_password_field? %}
    <input name="account[user][password]" type="password">
  {% endif %}

  Why the current design exists

  Looking at the code, this seems to be a result of:
  1. Legacy migration - Moving from crypted_password to password_digest (bcrypt)
  2. Monkey-patching Rails - Overriding has_secure_password's password_required?
  3. No refactoring - Just kept adding super chains instead of cleaning it up

  The method does too many things and the name doesn't clearly indicate which concern it addresses.

public_search account_plans_ui_visible change_account_plan_permission service_plans_ui_visible
change_service_plan_permission enforce_sso
useraccountarea_enabled hide_service signups_enabled account_approval_required public_search
account_plans_ui_visible change_account_plan_permission service_plans_ui_visiblechange_service_plan_permission
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
account_plans_ui_visible change_account_plan_permission service_plans_ui_visiblechange_service_plan_permission
account_plans_ui_visible change_account_plan_permission service_plans_ui_visible change_service_plan_permission

@mayorova
Copy link
Contributor

To be honest, I find it quite annoying to not being able to set a "weak" password, especially it's uncomfortable in the development environment, where I'm not interested in security.
Also, customers can also have their test accounts, and it could be annoying to be obliged to use a 16-characters password... 😒

So, ideally, I'd prefer to keep the optional "strong password" setting. Whoever cares about security can enable it 😬

As for the admin portal,

About admin portal, there's no option to enable strong passwords, weak passwords are always accepted.

I think you can enable strong password on the Master portal, and this setting will be applied to the admin portal passwords. Unless this doesn't work as expected:
image

jlledom and others added 11 commits January 12, 2026 16:57
For both provider and buyer users
Co-authored-by: Claude <noreply@anthropic.com>
It was actually showing "\n" in flash errors

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
It's fine to leave the password nil when there's an open_id set
Use strong passwords in tests
@jlledom jlledom force-pushed the THREESCALE-8916-admin-strong-paswords branch from 33fe198 to 25f4a95 Compare January 13, 2026 09:43
@jlledom jlledom self-assigned this Jan 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants