Commit 56dccc2e authored by Sean McGivern's avatar Sean McGivern
Browse files

Merge branch 'dm-remove-private-token' into 'master'

Remove Private Tokens

Closes #38595 and #38447

See merge request gitlab-org/gitlab-ce!14838
......@@ -44,7 +44,7 @@ def impersonation_token_params
end
def set_index_vars
@scopes = Gitlab::Auth::API_SCOPES
@scopes = Gitlab::Auth.available_scopes(current_user)
@impersonation_token ||= finder.build
@inactive_impersonation_tokens = finder(state: 'inactive').execute
......
......@@ -11,7 +11,7 @@ class ApplicationController < ActionController::Base
include EnforcesTwoFactorAuthentication
include WithPerformanceBar
before_action :authenticate_user_from_private_token!
before_action :authenticate_user_from_personal_access_token!
before_action :authenticate_user_from_rss_token!
before_action :authenticate_user!
before_action :validate_user_service_ticket!
......@@ -100,13 +100,12 @@ def auth_user
return try(:authenticated_user)
end
# This filter handles both private tokens and personal access tokens
def authenticate_user_from_private_token!
def authenticate_user_from_personal_access_token!
token = params[:private_token].presence || request.headers['PRIVATE-TOKEN'].presence
return unless token.present?
user = User.find_by_authentication_token(token) || User.find_by_personal_access_token(token)
user = User.find_by_personal_access_token(token)
sessionless_sign_in(user)
end
......
......@@ -30,11 +30,11 @@ def authenticate_project_or_user
render_unauthorized
end
end
rescue Gitlab::Auth::MissingPersonalTokenError
render_missing_personal_token
rescue Gitlab::Auth::MissingPersonalAccessTokenError
render_missing_personal_access_token
end
def render_missing_personal_token
def render_missing_personal_access_token
render json: {
errors: [
{ code: 'UNAUTHORIZED',
......
......@@ -39,7 +39,7 @@ def personal_access_token_params
end
def set_index_vars
@scopes = Gitlab::Auth.available_scopes
@scopes = Gitlab::Auth.available_scopes(current_user)
@inactive_personal_access_tokens = finder(state: 'inactive').execute
@active_personal_access_tokens = finder(state: 'active').execute.order(:expires_at)
......
......@@ -24,16 +24,6 @@ def update
end
end
def reset_private_token
Users::UpdateService.new(current_user, user: @user).execute! do |user|
user.reset_authentication_token!
end
flash[:notice] = "Private token was successfully reset"
redirect_to profile_account_path
end
def reset_incoming_email_token
Users::UpdateService.new(current_user, user: @user).execute! do |user|
user.reset_incoming_email_token!
......@@ -41,7 +31,7 @@ def reset_incoming_email_token
flash[:notice] = "Incoming email token was successfully reset"
redirect_to profile_account_path
redirect_to profile_personal_access_tokens_path
end
def reset_rss_token
......@@ -51,7 +41,7 @@ def reset_rss_token
flash[:notice] = "RSS token was successfully reset"
redirect_to profile_account_path
redirect_to profile_personal_access_tokens_path
end
def audit_log
......
......@@ -53,8 +53,8 @@ def authenticate_user
send_challenges
render plain: "HTTP Basic: Access denied\n", status: 401
rescue Gitlab::Auth::MissingPersonalTokenError
render_missing_personal_token
rescue Gitlab::Auth::MissingPersonalAccessTokenError
render_missing_personal_access_token
end
def basic_auth_provided?
......@@ -78,7 +78,7 @@ def parse_repo_path
@project, @wiki, @redirected_path = Gitlab::RepoPath.parse("#{params[:namespace_id]}/#{params[:project_id]}")
end
def render_missing_personal_token
def render_missing_personal_access_token
render plain: "HTTP Basic: Access denied\n" \
"You must use a personal access token with 'api' scope for Git over HTTP.\n" \
"You can generate one at #{profile_personal_access_tokens_url}",
......
......@@ -2,5 +2,13 @@ class OauthAccessToken < Doorkeeper::AccessToken
belongs_to :resource_owner, class_name: 'User'
belongs_to :application, class_name: 'Doorkeeper::Application'
alias_method :user, :resource_owner
alias_attribute :user, :resource_owner
def scopes=(value)
if value.is_a?(Array)
super(Doorkeeper::OAuth::Scopes.from_array(value).to_s)
else
super
end
end
end
......@@ -21,8 +21,8 @@ class User < ActiveRecord::Base
ignore_column :external_email
ignore_column :email_provider
ignore_column :authentication_token
add_authentication_token_field :authentication_token
add_authentication_token_field :incoming_email_token
add_authentication_token_field :rss_token
......@@ -163,7 +163,7 @@ def update_tracked_fields!(request)
before_validation :sanitize_attrs
before_validation :set_notification_email, if: :email_changed?
before_validation :set_public_email, if: :public_email_changed?
before_save :ensure_authentication_token, :ensure_incoming_email_token
before_save :ensure_incoming_email_token
before_save :ensure_user_rights_and_limits, if: :external_changed?
before_save :skip_reconfirmation!, if: ->(user) { user.email_changed? && user.read_only_attribute?(:email) }
before_save :check_for_verified_email, if: ->(user) { user.email_changed? && !user.new_record? }
......@@ -185,8 +185,6 @@ def update_tracked_fields!(request)
# Note: When adding an option, it MUST go on the end of the array.
enum project_view: [:readme, :activity, :files]
alias_attribute :private_token, :authentication_token
delegate :path, to: :namespace, allow_nil: true, prefix: true
state_machine :state, initial: :active do
......
......@@ -39,11 +39,8 @@ def include_any_scope?(required_scopes)
token_scopes = token.scopes.map(&:to_sym)
required_scopes.any? do |scope|
if scope.respond_to?(:sufficient?)
scope.sufficient?(token_scopes, request)
else
API::Scope.new(scope).sufficient?(token_scopes, request)
end
scope = API::Scope.new(scope) unless scope.is_a?(API::Scope)
scope.sufficient?(token_scopes, request)
end
end
end
......
- name = label.parameterize
- attribute = name.underscore
.reset-action
%p.cgray
= label_tag name, label, class: "label-light"
= text_field_tag name, current_user.send(attribute), class: 'form-control', readonly: true, onclick: 'this.select()'
%p.help-block
= help_text
.prepend-top-default
= link_to button_label, [:reset, attribute, :profile], method: :put, data: { confirm: 'Are you sure?' }, class: 'btn btn-default private-token'
......@@ -6,22 +6,6 @@
.alert.alert-info
Some options are unavailable for LDAP accounts
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
Private Tokens
%p
Keep these tokens secret, anyone with access to them can interact with
GitLab as if they were you.
.col-lg-8.private-tokens-reset
= render partial: 'reset_token', locals: { label: 'Private token', button_label: 'Reset private token', help_text: 'Your private token is used to access the API and Atom feeds without username/password authentication.' }
= render partial: 'reset_token', locals: { label: 'RSS token', button_label: 'Reset RSS token', help_text: 'Your RSS token is used to create urls for personalized RSS feeds.' }
- if incoming_email_token_enabled?
= render partial: 'reset_token', locals: { label: 'Incoming email token', button_label: 'Reset incoming email token', help_text: 'Your incoming email token is used to create new issues by email, and is included in your project-specific email addresses.' }
%hr
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
......
......@@ -30,3 +30,40 @@
= render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes
= render "shared/personal_access_tokens_table", impersonation: false, active_tokens: @active_personal_access_tokens, inactive_tokens: @inactive_personal_access_tokens
%hr
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
RSS token
%p
Your RSS token is used to authenticate you when your RSS reader loads a personalized RSS feed, and is included in your personal RSS feed URLs.
%p
It cannot be used to access any other data.
.col-lg-8.rss-token-reset
= label_tag :rss_token, 'RSS token', class: "label-light"
= text_field_tag :rss_token, current_user.rss_token, class: 'form-control', readonly: true, onclick: 'this.select()'
%p.help-block
Keep this token secret. Anyone who gets ahold of it can read activity and issue RSS feeds as if they were you.
You should
= link_to 'reset it', [:reset, :rss_token, :profile], method: :put, data: { confirm: 'Are you sure? Any RSS URLs currently in use will stop working.' }
if that ever happens.
- if incoming_email_token_enabled?
%hr
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
Incoming email token
%p
Your incoming email token is used to authenticate you when you create a new issue by email, and is included in your personal project-specific email addresses.
%p
It cannot be used to access any other data.
.col-lg-8.incoming-email-token-reset
= label_tag :incoming_email_token, 'Incoming email token', class: "label-light"
= text_field_tag :incoming_email_token, current_user.incoming_email_token, class: 'form-control', readonly: true, onclick: 'this.select()'
%p.help-block
Keep this token secret. Anyone who gets ahold of it can create issues as if they were you.
You should
= link_to 'reset it', [:reset, :incoming_email_token, :profile], method: :put, data: { confirm: 'Are you sure? Any issue email addresses currently in use will stop working.' }
if that ever happens.
---
title: Add sudo scope for OAuth and Personal Access Tokens to be used by admins to
impersonate other users on the API
merge_request:
author:
type: added
---
title: Convert private tokens to Personal Access Tokens with sudo scope
merge_request:
author:
type: security
---
title: Remove private tokens from web interface and API
merge_request:
author:
type: security
---
title: Remove Session API now that private tokens are removed from user API endpoints
merge_request:
author:
type: removed
......@@ -58,9 +58,10 @@ en:
expired: "The access token expired"
unknown: "The access token is invalid"
scopes:
api: Access your API
read_user: Read user information
api: Access the authenticated user's API
read_user: Read the authenticated user's personal information
openid: Authenticate using OpenID Connect
sudo: Perform API actions as any user in the system (if the authenticated user is an admin)
flash:
applications:
......
......@@ -6,7 +6,6 @@
get :audit_log
get :applications, to: 'oauth/applications#index'
put :reset_private_token
put :reset_incoming_email_token
put :reset_rss_token
put :update_username
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class MigrateUserAuthenticationTokenToPersonalAccessToken < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# disable_ddl_transaction!
TOKEN_NAME = 'Private Token'.freeze
def up
execute <<~SQL
INSERT INTO personal_access_tokens (user_id, token, name, created_at, updated_at, scopes)
SELECT id, authentication_token, '#{TOKEN_NAME}', NOW(), NOW(), '#{%w[api].to_yaml}'
FROM users
WHERE authentication_token IS NOT NULL
AND admin = FALSE
AND NOT EXISTS (
SELECT true
FROM personal_access_tokens
WHERE user_id = users.id
AND token = users.authentication_token
)
SQL
# Admins also need the `sudo` scope
execute <<~SQL
INSERT INTO personal_access_tokens (user_id, token, name, created_at, updated_at, scopes)
SELECT id, authentication_token, '#{TOKEN_NAME}', NOW(), NOW(), '#{%w[api sudo].to_yaml}'
FROM users
WHERE authentication_token IS NOT NULL
AND admin = TRUE
AND NOT EXISTS (
SELECT true
FROM personal_access_tokens
WHERE user_id = users.id
AND token = users.authentication_token
)
SQL
end
def down
if Gitlab::Database.postgresql?
execute <<~SQL
UPDATE users
SET authentication_token = pats.token
FROM (
SELECT user_id, token
FROM personal_access_tokens
WHERE name = '#{TOKEN_NAME}'
) AS pats
WHERE id = pats.user_id
SQL
else
execute <<~SQL
UPDATE users
INNER JOIN personal_access_tokens AS pats
ON users.id = pats.user_id
SET authentication_token = pats.token
WHERE pats.name = '#{TOKEN_NAME}'
SQL
end
execute <<~SQL
DELETE FROM personal_access_tokens
WHERE name = '#{TOKEN_NAME}'
AND EXISTS (
SELECT true
FROM users
WHERE id = personal_access_tokens.user_id
AND authentication_token = personal_access_tokens.token
)
SQL
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveUserAuthenticationToken < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
remove_column :users, :authentication_token
end
def down
add_column :users, :authentication_token, :string
add_concurrent_index :users, :authentication_token, unique: true
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment