Merge branch 'feature/rss-scoped-token' into 'master'
Use rss token for atom access See merge request !11647
This commit is contained in:
commit
ec2130bea8
38 changed files with 280 additions and 99 deletions
|
@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base
|
|||
include EnforcesTwoFactorAuthentication
|
||||
|
||||
before_action :authenticate_user_from_private_token!
|
||||
before_action :authenticate_user_from_rss_token!
|
||||
before_action :authenticate_user!
|
||||
before_action :validate_user_service_ticket!
|
||||
before_action :check_password_expiration
|
||||
|
@ -72,13 +73,20 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
user = User.find_by_authentication_token(token) || User.find_by_personal_access_token(token)
|
||||
|
||||
if user && can?(user, :log_in)
|
||||
# Notice we are passing store false, so the user is not
|
||||
# actually stored in the session and a token is needed
|
||||
# for every request. If you want the token to work as a
|
||||
# sign in token, you can simply remove store: false.
|
||||
sign_in user, store: false
|
||||
end
|
||||
sessionless_sign_in(user)
|
||||
end
|
||||
|
||||
# This filter handles authentication for atom request with an rss_token
|
||||
def authenticate_user_from_rss_token!
|
||||
return unless request.format.atom?
|
||||
|
||||
token = params[:rss_token].presence
|
||||
|
||||
return unless token.present?
|
||||
|
||||
user = User.find_by_rss_token(token)
|
||||
|
||||
sessionless_sign_in(user)
|
||||
end
|
||||
|
||||
def log_exception(exception)
|
||||
|
@ -282,4 +290,14 @@ class ApplicationController < ActionController::Base
|
|||
ensure
|
||||
Gitlab::I18n.reset_locale
|
||||
end
|
||||
|
||||
def sessionless_sign_in(user)
|
||||
if user && can?(user, :log_in)
|
||||
# Notice we are passing store false, so the user is not
|
||||
# actually stored in the session and a token is needed
|
||||
# for every request. If you want the token to work as a
|
||||
# sign in token, you can simply remove store: false.
|
||||
sign_in user, store: false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,6 +40,14 @@ class ProfilesController < Profiles::ApplicationController
|
|||
redirect_to profile_account_path
|
||||
end
|
||||
|
||||
def reset_rss_token
|
||||
if current_user.reset_rss_token!
|
||||
flash[:notice] = "RSS token was successfully reset"
|
||||
end
|
||||
|
||||
redirect_to profile_account_path
|
||||
end
|
||||
|
||||
def audit_log
|
||||
@events = AuditEvent.where(entity_type: "User", entity_id: current_user.id).
|
||||
order("created_at DESC").
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module RssHelper
|
||||
def rss_url_options
|
||||
{ format: :atom, private_token: current_user.try(:private_token) }
|
||||
{ format: :atom, rss_token: current_user.try(:rss_token) }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,7 @@ class User < ActiveRecord::Base
|
|||
|
||||
add_authentication_token_field :authentication_token
|
||||
add_authentication_token_field :incoming_email_token
|
||||
add_authentication_token_field :rss_token
|
||||
|
||||
default_value_for :admin, false
|
||||
default_value_for(:external) { current_application_settings.user_default_external }
|
||||
|
@ -1004,6 +1005,13 @@ class User < ActiveRecord::Base
|
|||
save
|
||||
end
|
||||
|
||||
# each existing user needs to have an `rss_token`.
|
||||
# we do this on read since migrating all existing users is not a feasible
|
||||
# solution.
|
||||
def rss_token
|
||||
ensure_rss_token!
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# override, from Devise::Validatable
|
||||
|
|
11
app/views/profiles/accounts/_reset_token.html.haml
Normal file
11
app/views/profiles/accounts/_reset_token.html.haml
Normal file
|
@ -0,0 +1,11 @@
|
|||
- 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'
|
|
@ -8,35 +8,17 @@
|
|||
.row.prepend-top-default
|
||||
.col-lg-3.profile-settings-sidebar
|
||||
%h4.prepend-top-0
|
||||
= incoming_email_token_enabled? ? "Private Tokens" : "Private Token"
|
||||
Private Tokens
|
||||
%p
|
||||
Keep
|
||||
= incoming_email_token_enabled? ? "these tokens" : "this token"
|
||||
secret, anyone with access to them can interact with GitLab as if they were you.
|
||||
Keep these tokens secret, anyone with access to them can interact with
|
||||
GitLab as if they were you.
|
||||
.col-lg-9.private-tokens-reset
|
||||
.reset-action
|
||||
%p.cgray
|
||||
- if current_user.private_token
|
||||
= label_tag "private-token", "Private token", class: "label-light"
|
||||
= text_field_tag "private-token", current_user.private_token, class: "form-control", readonly: true, onclick: "this.select()"
|
||||
- else
|
||||
%span You don't have one yet. Click generate to fix it.
|
||||
%p.help-block
|
||||
Your private token is used to access the API and Atom feeds without username/password authentication.
|
||||
.prepend-top-default
|
||||
- if current_user.private_token
|
||||
= link_to 'Reset private token', reset_private_token_profile_path, method: :put, data: { confirm: "Are you sure?" }, class: "btn btn-default private-token"
|
||||
- else
|
||||
= f.submit 'Generate', class: "btn btn-default"
|
||||
= 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?
|
||||
.reset-action
|
||||
%p.cgray
|
||||
= 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
|
||||
Your incoming email token is used to create new issues by email, and is included in your project-specific email addresses.
|
||||
.prepend-top-default
|
||||
= link_to 'Reset incoming email token', reset_incoming_email_token_profile_path, method: :put, data: { confirm: "Are you sure?" }, class: "btn btn-default incoming-email-token"
|
||||
= 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
|
||||
|
|
4
changelogs/unreleased/feature-rss-scoped-token.yml
Normal file
4
changelogs/unreleased/feature-rss-scoped-token.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Expose atom links with an RSS token instead of using the private token
|
||||
merge_request: 11647
|
||||
author: Alexis Reigel
|
|
@ -65,6 +65,7 @@ module Gitlab
|
|||
hook
|
||||
import_url
|
||||
incoming_email_token
|
||||
rss_token
|
||||
key
|
||||
otp_attempt
|
||||
password
|
||||
|
|
|
@ -5,6 +5,7 @@ resource :profile, only: [:show, :update] do
|
|||
|
||||
put :reset_private_token
|
||||
put :reset_incoming_email_token
|
||||
put :reset_rss_token
|
||||
put :update_username
|
||||
end
|
||||
|
||||
|
|
19
db/migrate/20170523091700_add_rss_token_to_users.rb
Normal file
19
db/migrate/20170523091700_add_rss_token_to_users.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
class AddRssTokenToUsers < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_column :users, :rss_token, :string
|
||||
|
||||
add_concurrent_index :users, :rss_token
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index :users, :rss_token if index_exists? :users, :rss_token
|
||||
|
||||
remove_column :users, :rss_token
|
||||
end
|
||||
end
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20170521184006) do
|
||||
ActiveRecord::Schema.define(version: 20170523091700) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -1362,6 +1362,7 @@ ActiveRecord::Schema.define(version: 20170521184006) do
|
|||
t.date "last_activity_on"
|
||||
t.boolean "notified_of_own_activity"
|
||||
t.string "preferred_language"
|
||||
t.string "rss_token"
|
||||
end
|
||||
|
||||
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
|
||||
|
@ -1375,6 +1376,7 @@ ActiveRecord::Schema.define(version: 20170521184006) do
|
|||
add_index "users", ["name"], name: "index_users_on_name", using: :btree
|
||||
add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
|
||||
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
|
||||
add_index "users", ["rss_token"], name: "index_users_on_rss_token", using: :btree
|
||||
add_index "users", ["state"], name: "index_users_on_state", using: :btree
|
||||
add_index "users", ["username"], name: "index_users_on_username", using: :btree
|
||||
add_index "users", ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"}
|
||||
|
|
|
@ -11,6 +11,11 @@ namespace :tokens do
|
|||
reset_all_users_token(:reset_incoming_email_token!)
|
||||
end
|
||||
|
||||
desc "Reset all GitLab RSS tokens"
|
||||
task reset_all_rss: :environment do
|
||||
reset_all_users_token(:reset_rss_token!)
|
||||
end
|
||||
|
||||
def reset_all_users_token(reset_token_method)
|
||||
TmpUser.find_in_batches do |batch|
|
||||
puts "Processing batch starting with user ID: #{batch.first.id}"
|
||||
|
@ -35,4 +40,9 @@ class TmpUser < ActiveRecord::Base
|
|||
write_new_token(:incoming_email_token)
|
||||
save!(validate: false)
|
||||
end
|
||||
|
||||
def reset_rss_token!
|
||||
write_new_token(:rss_token)
|
||||
save!(validate: false)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -99,6 +99,42 @@ describe ApplicationController do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#authenticate_user_from_rss_token' do
|
||||
describe "authenticating a user from an RSS token" do
|
||||
controller(described_class) do
|
||||
def index
|
||||
render text: 'authenticated'
|
||||
end
|
||||
end
|
||||
|
||||
context "when the 'rss_token' param is populated with the RSS token" do
|
||||
context 'when the request format is atom' do
|
||||
it "logs the user in" do
|
||||
get :index, rss_token: user.rss_token, format: :atom
|
||||
expect(response).to have_http_status 200
|
||||
expect(response.body).to eq 'authenticated'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the request format is not atom' do
|
||||
it "doesn't log the user in" do
|
||||
get :index, rss_token: user.rss_token
|
||||
expect(response.status).not_to have_http_status 200
|
||||
expect(response.body).not_to eq 'authenticated'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the 'rss_token' param is populated with an invalid RSS token" do
|
||||
it "doesn't log the user" do
|
||||
get :index, rss_token: "token"
|
||||
expect(response.status).not_to eq 200
|
||||
expect(response.body).not_to eq 'authenticated'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#route_not_found' do
|
||||
it 'renders 404 if authenticated' do
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
|
|
|
@ -8,6 +8,10 @@ FactoryGirl.define do
|
|||
confirmation_token { nil }
|
||||
can_create_group true
|
||||
|
||||
before(:create) do |user|
|
||||
user.ensure_rss_token
|
||||
end
|
||||
|
||||
trait :admin do
|
||||
admin true
|
||||
end
|
||||
|
|
|
@ -20,13 +20,20 @@ describe "Dashboard Issues Feed", feature: true do
|
|||
expect(body).to have_selector('title', text: "#{user.name} issues")
|
||||
end
|
||||
|
||||
it "renders atom feed via RSS token" do
|
||||
visit issues_dashboard_path(:atom, rss_token: user.rss_token)
|
||||
|
||||
expect(response_headers['Content-Type']).to have_content('application/atom+xml')
|
||||
expect(body).to have_selector('title', text: "#{user.name} issues")
|
||||
end
|
||||
|
||||
it "renders atom feed with url parameters" do
|
||||
visit issues_dashboard_path(:atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
|
||||
visit issues_dashboard_path(:atom, rss_token: user.rss_token, state: 'opened', assignee_id: user.id)
|
||||
|
||||
link = find('link[type="application/atom+xml"]')
|
||||
params = CGI.parse(URI.parse(link[:href]).query)
|
||||
|
||||
expect(params).to include('private_token' => [user.private_token])
|
||||
expect(params).to include('rss_token' => [user.rss_token])
|
||||
expect(params).to include('state' => ['opened'])
|
||||
expect(params).to include('assignee_id' => [user.id.to_s])
|
||||
end
|
||||
|
@ -35,7 +42,7 @@ describe "Dashboard Issues Feed", feature: true do
|
|||
let!(:issue2) { create(:issue, author: user, assignees: [assignee], project: project2, description: 'test desc') }
|
||||
|
||||
it "renders issue fields" do
|
||||
visit issues_dashboard_path(:atom, private_token: user.private_token)
|
||||
visit issues_dashboard_path(:atom, rss_token: user.rss_token)
|
||||
|
||||
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]")
|
||||
|
||||
|
@ -58,7 +65,7 @@ describe "Dashboard Issues Feed", feature: true do
|
|||
end
|
||||
|
||||
it "renders issue label and milestone info" do
|
||||
visit issues_dashboard_path(:atom, private_token: user.private_token)
|
||||
visit issues_dashboard_path(:atom, rss_token: user.rss_token)
|
||||
|
||||
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]")
|
||||
|
||||
|
|
|
@ -11,6 +11,13 @@ describe "Dashboard Feed", feature: true do
|
|||
end
|
||||
end
|
||||
|
||||
context "projects atom feed via RSS token" do
|
||||
it "renders projects atom feed" do
|
||||
visit dashboard_projects_path(:atom, rss_token: user.rss_token)
|
||||
expect(body).to have_selector('feed title')
|
||||
end
|
||||
end
|
||||
|
||||
context 'feed content' do
|
||||
let(:project) { create(:project) }
|
||||
let(:issue) { create(:issue, project: project, author: user, description: '') }
|
||||
|
@ -20,7 +27,7 @@ describe "Dashboard Feed", feature: true do
|
|||
project.team << [user, :master]
|
||||
issue_event(issue, user)
|
||||
note_event(note, user)
|
||||
visit dashboard_projects_path(:atom, private_token: user.private_token)
|
||||
visit dashboard_projects_path(:atom, rss_token: user.rss_token)
|
||||
end
|
||||
|
||||
it "has issue opened event" do
|
||||
|
|
|
@ -43,25 +43,40 @@ describe 'Issues Feed', feature: true do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when authenticated via RSS token' do
|
||||
it 'renders atom feed' do
|
||||
visit namespace_project_issues_path(project.namespace, project, :atom,
|
||||
rss_token: user.rss_token)
|
||||
|
||||
expect(response_headers['Content-Type']).
|
||||
to have_content('application/atom+xml')
|
||||
expect(body).to have_selector('title', text: "#{project.name} issues")
|
||||
expect(body).to have_selector('author email', text: issue.author_public_email)
|
||||
expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email)
|
||||
expect(body).to have_selector('assignee email', text: issue.assignees.first.public_email)
|
||||
expect(body).to have_selector('entry summary', text: issue.title)
|
||||
end
|
||||
end
|
||||
|
||||
it "renders atom feed with url parameters for project issues" do
|
||||
visit namespace_project_issues_path(project.namespace, project,
|
||||
:atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
|
||||
:atom, rss_token: user.rss_token, state: 'opened', assignee_id: user.id)
|
||||
|
||||
link = find('link[type="application/atom+xml"]')
|
||||
params = CGI.parse(URI.parse(link[:href]).query)
|
||||
|
||||
expect(params).to include('private_token' => [user.private_token])
|
||||
expect(params).to include('rss_token' => [user.rss_token])
|
||||
expect(params).to include('state' => ['opened'])
|
||||
expect(params).to include('assignee_id' => [user.id.to_s])
|
||||
end
|
||||
|
||||
it "renders atom feed with url parameters for group issues" do
|
||||
visit issues_group_path(group, :atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
|
||||
visit issues_group_path(group, :atom, rss_token: user.rss_token, state: 'opened', assignee_id: user.id)
|
||||
|
||||
link = find('link[type="application/atom+xml"]')
|
||||
params = CGI.parse(URI.parse(link[:href]).query)
|
||||
|
||||
expect(params).to include('private_token' => [user.private_token])
|
||||
expect(params).to include('rss_token' => [user.rss_token])
|
||||
expect(params).to include('state' => ['opened'])
|
||||
expect(params).to include('assignee_id' => [user.id.to_s])
|
||||
end
|
||||
|
|
|
@ -11,6 +11,13 @@ describe "User Feed", feature: true do
|
|||
end
|
||||
end
|
||||
|
||||
context 'user atom feed via RSS token' do
|
||||
it "renders user atom feed" do
|
||||
visit user_path(user, :atom, rss_token: user.rss_token)
|
||||
expect(body).to have_selector('feed title')
|
||||
end
|
||||
end
|
||||
|
||||
context 'feed content' do
|
||||
let(:project) { create(:project) }
|
||||
let(:issue) do
|
||||
|
@ -40,7 +47,7 @@ describe "User Feed", feature: true do
|
|||
issue_event(issue, user)
|
||||
note_event(note, user)
|
||||
merge_request_event(merge_request, user)
|
||||
visit user_path(user, :atom, private_token: user.private_token)
|
||||
visit user_path(user, :atom, rss_token: user.rss_token)
|
||||
end
|
||||
|
||||
it 'has issue opened event' do
|
||||
|
|
|
@ -5,7 +5,7 @@ RSpec.describe 'Dashboard Activity', feature: true do
|
|||
login_as(create :user)
|
||||
visit activity_dashboard_path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button with current_user's private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
|
||||
|
||||
it_behaves_like "it has an RSS button with current_user's RSS token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
|
||||
end
|
||||
|
|
|
@ -62,6 +62,6 @@ RSpec.describe 'Dashboard Issues', feature: true do
|
|||
expect(page).to have_content(other_issue.title)
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button with current_user's private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
|
||||
it_behaves_like "it has an RSS button with current_user's RSS token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
|
||||
end
|
||||
|
|
|
@ -31,5 +31,5 @@ RSpec.describe 'Dashboard Projects', feature: true do
|
|||
end
|
||||
end
|
||||
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
|
||||
end
|
||||
|
|
|
@ -53,10 +53,10 @@ describe "Dashboard Issues filtering", feature: true, js: true do
|
|||
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
|
||||
auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query)
|
||||
|
||||
expect(params).to include('private_token' => [user.private_token])
|
||||
expect(params).to include('rss_token' => [user.rss_token])
|
||||
expect(params).to include('milestone_title' => [''])
|
||||
expect(params).to include('assignee_id' => [user.id.to_s])
|
||||
expect(auto_discovery_params).to include('private_token' => [user.private_token])
|
||||
expect(auto_discovery_params).to include('rss_token' => [user.rss_token])
|
||||
expect(auto_discovery_params).to include('milestone_title' => [''])
|
||||
expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
|
||||
end
|
||||
|
|
|
@ -11,8 +11,8 @@ feature 'Group activity page', feature: true do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button with current_user's private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
|
||||
it_behaves_like "it has an RSS button with current_user's RSS token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
|
@ -20,7 +20,7 @@ feature 'Group activity page', feature: true do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button without a private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without a private token"
|
||||
it_behaves_like "it has an RSS button without an RSS token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without an RSS token"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,15 +12,15 @@ feature 'Group issues page', feature: true do
|
|||
context 'when signed in' do
|
||||
let(:user) { user_in_group }
|
||||
|
||||
it_behaves_like "it has an RSS button with current_user's private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
|
||||
it_behaves_like "it has an RSS button with current_user's RSS token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
let(:user) { nil }
|
||||
|
||||
it_behaves_like "it has an RSS button without a private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without a private token"
|
||||
it_behaves_like "it has an RSS button without an RSS token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without an RSS token"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ feature 'Group show page', feature: true do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
|
@ -19,6 +19,6 @@ feature 'Group show page', feature: true do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "an autodiscoverable RSS feed without a private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without an RSS token"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -810,10 +810,10 @@ describe 'Filter issues', js: true, feature: true do
|
|||
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
|
||||
auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query)
|
||||
|
||||
expect(params).to include('private_token' => [user.private_token])
|
||||
expect(params).to include('rss_token' => [user.rss_token])
|
||||
expect(params).to include('milestone_title' => [milestone.title])
|
||||
expect(params).to include('assignee_id' => [user.id.to_s])
|
||||
expect(auto_discovery_params).to include('private_token' => [user.private_token])
|
||||
expect(auto_discovery_params).to include('rss_token' => [user.rss_token])
|
||||
expect(auto_discovery_params).to include('milestone_title' => [milestone.title])
|
||||
expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
|
||||
end
|
||||
|
@ -825,10 +825,10 @@ describe 'Filter issues', js: true, feature: true do
|
|||
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
|
||||
auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query)
|
||||
|
||||
expect(params).to include('private_token' => [user.private_token])
|
||||
expect(params).to include('rss_token' => [user.rss_token])
|
||||
expect(params).to include('milestone_title' => [milestone.title])
|
||||
expect(params).to include('assignee_id' => [user.id.to_s])
|
||||
expect(auto_discovery_params).to include('private_token' => [user.private_token])
|
||||
expect(auto_discovery_params).to include('rss_token' => [user.rss_token])
|
||||
expect(auto_discovery_params).to include('milestone_title' => [milestone.title])
|
||||
expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
|
||||
end
|
||||
|
|
|
@ -47,6 +47,21 @@ describe 'Profile account page', feature: true do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'when I reset RSS token' do
|
||||
before do
|
||||
visit profile_account_path
|
||||
end
|
||||
|
||||
it 'resets RSS token' do
|
||||
previous_token = find("#rss-token").value
|
||||
|
||||
click_link('Reset RSS token')
|
||||
|
||||
expect(page).to have_content 'RSS token was successfully reset'
|
||||
expect(find('#rss-token').value).not_to eq(previous_token)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when I reset incoming email token' do
|
||||
before do
|
||||
allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
|
||||
|
|
|
@ -16,7 +16,7 @@ feature 'Project Activity RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button with current_user's private token"
|
||||
it_behaves_like "it has an RSS button with current_user's RSS token"
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
|
@ -24,6 +24,6 @@ feature 'Project Activity RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button without a private token"
|
||||
it_behaves_like "it has an RSS button without an RSS token"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,8 +12,8 @@ feature 'Project Commits RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button with current_user's private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
|
||||
it_behaves_like "it has an RSS button with current_user's RSS token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
|
@ -21,7 +21,7 @@ feature 'Project Commits RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button without a private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without a private token"
|
||||
it_behaves_like "it has an RSS button without an RSS token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without an RSS token"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,8 +16,8 @@ feature 'Project Issues RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button with current_user's private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
|
||||
it_behaves_like "it has an RSS button with current_user's RSS token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
|
@ -25,7 +25,7 @@ feature 'Project Issues RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button without a private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without a private token"
|
||||
it_behaves_like "it has an RSS button without an RSS token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without an RSS token"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ feature 'Project RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
|
@ -20,6 +20,6 @@ feature 'Project RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "an autodiscoverable RSS feed without a private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without an RSS token"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ feature 'Project Tree RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
|
@ -20,6 +20,6 @@ feature 'Project Tree RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "an autodiscoverable RSS feed without a private token"
|
||||
it_behaves_like "an autodiscoverable RSS feed without an RSS token"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ feature 'User RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button with current_user's private token"
|
||||
it_behaves_like "it has an RSS button with current_user's RSS token"
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
|
@ -17,6 +17,6 @@ feature 'User RSS' do
|
|||
visit path
|
||||
end
|
||||
|
||||
it_behaves_like "it has an RSS button without a private token"
|
||||
it_behaves_like "it has an RSS button without an RSS token"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,17 +3,17 @@ require 'spec_helper'
|
|||
describe RssHelper do
|
||||
describe '#rss_url_options' do
|
||||
context 'when signed in' do
|
||||
it "includes the current_user's private_token" do
|
||||
it "includes the current_user's rss_token" do
|
||||
current_user = create(:user)
|
||||
allow(helper).to receive(:current_user).and_return(current_user)
|
||||
expect(helper.rss_url_options).to include private_token: current_user.private_token
|
||||
expect(helper.rss_url_options).to include rss_token: current_user.rss_token
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
it "does not have a private_token" do
|
||||
it "does not have an rss_token" do
|
||||
allow(helper).to receive(:current_user).and_return(nil)
|
||||
expect(helper.rss_url_options[:private_token]).to be_nil
|
||||
expect(helper.rss_url_options[:rss_token]).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -440,6 +440,22 @@ describe User, models: true do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'ensure incoming email token' do
|
||||
it 'has incoming email token' do
|
||||
user = create(:user)
|
||||
expect(user.incoming_email_token).not_to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
describe 'rss token' do
|
||||
it 'ensures an rss token on read' do
|
||||
user = create(:user, rss_token: nil)
|
||||
rss_token = user.rss_token
|
||||
expect(rss_token).not_to be_blank
|
||||
expect(user.reload.rss_token).to eq rss_token
|
||||
end
|
||||
end
|
||||
|
||||
describe '#recently_sent_password_reset?' do
|
||||
it 'is false when reset_password_sent_at is nil' do
|
||||
user = build_stubbed(:user, reset_password_sent_at: nil)
|
||||
|
|
|
@ -151,6 +151,10 @@ describe ProfilesController, "routing" do
|
|||
expect(put("/profile/reset_private_token")).to route_to('profiles#reset_private_token')
|
||||
end
|
||||
|
||||
it "to #reset_rss_token" do
|
||||
expect(put("/profile/reset_rss_token")).to route_to('profiles#reset_rss_token')
|
||||
end
|
||||
|
||||
it "to #show" do
|
||||
expect(get("/profile")).to route_to('profiles#show')
|
||||
end
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
shared_examples "an autodiscoverable RSS feed with current_user's private token" do
|
||||
it "has an RSS autodiscovery link tag with current_user's private token" do
|
||||
expect(page).to have_css("link[type*='atom+xml'][href*='private_token=#{Thread.current[:current_user].private_token}']", visible: false)
|
||||
shared_examples "an autodiscoverable RSS feed with current_user's RSS token" do
|
||||
it "has an RSS autodiscovery link tag with current_user's RSS token" do
|
||||
expect(page).to have_css("link[type*='atom+xml'][href*='rss_token=#{Thread.current[:current_user].rss_token}']", visible: false)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "it has an RSS button with current_user's private token" do
|
||||
it "shows the RSS button with current_user's private token" do
|
||||
expect(page).to have_css("a:has(.fa-rss)[href*='private_token=#{Thread.current[:current_user].private_token}']")
|
||||
shared_examples "it has an RSS button with current_user's RSS token" do
|
||||
it "shows the RSS button with current_user's RSS token" do
|
||||
expect(page).to have_css("a:has(.fa-rss)[href*='rss_token=#{Thread.current[:current_user].rss_token}']")
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "an autodiscoverable RSS feed without a private token" do
|
||||
it "has an RSS autodiscovery link tag without a private token" do
|
||||
expect(page).to have_css("link[type*='atom+xml']:not([href*='private_token'])", visible: false)
|
||||
shared_examples "an autodiscoverable RSS feed without an RSS token" do
|
||||
it "has an RSS autodiscovery link tag without an RSS token" do
|
||||
expect(page).to have_css("link[type*='atom+xml']:not([href*='rss_token'])", visible: false)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "it has an RSS button without a private token" do
|
||||
it "shows the RSS button without a private token" do
|
||||
expect(page).to have_css("a:has(.fa-rss):not([href*='private_token'])")
|
||||
shared_examples "it has an RSS button without an RSS token" do
|
||||
it "shows the RSS button without an RSS token" do
|
||||
expect(page).to have_css("a:has(.fa-rss):not([href*='rss_token'])")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,4 +18,10 @@ describe 'tokens rake tasks' do
|
|||
expect { run_rake_task('tokens:reset_all_email') }.to change { user.reload.incoming_email_token }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'reset_all_rss task' do
|
||||
it 'invokes create_hooks task' do
|
||||
expect { run_rake_task('tokens:reset_all_rss') }.to change { user.reload.rss_token }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue