Restore Bitbucket Cloud API-token imports without OAuth provider

What does this MR do and why?

API-token Bitbucket Cloud imports fail at the first Sidekiq stage on any instance that has no bitbucket entry under omniauth.providers. This is a regression introduced in GitLab 19.2.

Import::BitbucketImport::ClientFactory.for (lib/import/bitbucket_import/client_factory.rb) calls Gitlab::Auth::OAuth::Provider.config_for('bitbucket') unconditionally and then reads provider.app_id / provider.app_secret. When no provider is configured, config_for returns nil, and every import stage worker raises:

NoMethodError: undefined method 'app_id' for nil

This regressed in !241902 (merged), which extracted the Bitbucket Cloud client into the gitlab-bitbucket gem. Before that MR, the provider lookup lived inside Bitbucket::OauthConnection#client and only ran lazily, when an OAuth token needed to be refreshed. API-token imports never reached that code path, so they never depended on the provider being configured.

Imports started through the REST API (POST /api/v4/import/bitbucket) use bitbucket_email + bitbucket_api_token, not OAuth. Import::BitbucketService handles the initial request without the provider and succeeds, so the project gets created. The import then fails once the Sidekiq stage workers build the client through ClientFactory.for. Affected callers include:

  • app/workers/gitlab/bitbucket_import/stage/import_repository_worker.rb
  • lib/gitlab/bitbucket_import/parallel_scheduling.rb
  • lib/gitlab/bitbucket_import/importers/repository_importer.rb
  • lib/gitlab/bitbucket_import/importers/users_importer.rb

This affects self-managed and GitLab Dedicated instances that do not configure Bitbucket as an OAuth 2.0 provider. GitLab.com is unaffected because the provider is configured there. It was reported by a GitLab Dedicated customer on 19.2.4 whose migration tooling (Congregate) uses the API endpoint. The OAuth-based UI import flow (Import::BitbucketController) always required the provider and is unchanged by this fix.

Fix

Bitbucket::Connection already selects Bitbucket::ApiConnection when the credentials contain email and api_token. That connection never refreshes a token and never reads app_id / app_secret. The factory now only merges app_id / app_secret into the client options when the provider is actually configured:

 def self.for(project)
-  provider = Gitlab::Auth::OAuth::Provider.config_for('bitbucket')
-
-  params = project.import_data.credentials.merge(
-    logger: Gitlab::BitbucketImport::Logger,
-    # Required to refresh the OAuth token during import (see TokenRefreshStrategy).
-    app_id: provider.app_id,
-    app_secret: provider.app_secret
-  )
+  params = project.import_data.credentials.merge(logger: Gitlab::BitbucketImport::Logger)
+
+  # Only OAuth imports refresh tokens (see TokenRefreshStrategy). API-token imports
+  # must keep working on instances with no Bitbucket OAuth provider configured.
+  provider = Gitlab::Auth::OAuth::Provider.config_for('bitbucket')
+  params.merge!(app_id: provider.app_id, app_secret: provider.app_secret) if provider

OAuth imports are unchanged when the provider is configured. OAuth imports with no provider configured now raise KeyError from options.fetch(:app_id) when the client is first used, instead of NoMethodError when the token is refreshed. Both are "broken without provider" outcomes, same as before 19.2, but the new one fails earlier with a clearer error.

Backports

The regression shipped in 19.2 and is present in 19.3. Affected Dedicated tenants are on 19.2.x, so this needs backport MRs to 19-2-stable-ee and 19-3-stable-ee once this MR merges.

References

Screenshots or screen recordings

Not applicable. This is a backend-only change with no UI impact.

How to set up and validate locally

  1. Make sure GDK has no bitbucket provider under omniauth.providers (the default GDK config has none). Confirm in a Rails console:
    Gitlab::Auth::OAuth::Provider.config_for('bitbucket') # => nil
  2. Create a Bitbucket Cloud API token (Atlassian account settings, API tokens with scopes) for a Bitbucket account that owns a test repository.
  3. Start an import through the API:
    curl --request POST --url "http://gdk.test:3000/api/v4/import/bitbucket" \
      --header "content-type: application/json" \
      --header "PRIVATE-TOKEN: <gitlab-pat>" \
      --data '{ "bitbucket_email": "<atlassian-email>", "bitbucket_api_token": "<bitbucket-api-token>", "repo_path": "<workspace>/<repo>", "target_namespace": "<group>", "new_name": "bitbucket-api-import" }'
  4. On master, the project is created but the import fails. log/importer.log (or the Sidekiq logs) shows:
    NoMethodError: undefined method 'app_id' for nil
    from Import::BitbucketImport::ClientFactory.for. With this branch, the import completes.
  5. Run the spec:
    bin/rspec spec/lib/import/bitbucket_import/client_factory_spec.rb

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

  • Tests added: spec/lib/import/bitbucket_import/client_factory_spec.rb now covers OAuth credentials (unchanged behaviour) and API-token credentials with no provider configured (new). The new example was run against the pre-fix code and fails with the same NoMethodError seen in production.
  • Changelog trailer: Changelog: fixed.
  • No database or migration changes.
  • No UI changes.
  • No feature flag needed: this restores the previous (pre-19.2) behaviour rather than adding new behaviour.
  • No documentation changes needed.

Merge request reports

Loading
Loading