Commit 011e561b authored by tiagonbotelho's avatar tiagonbotelho

implements reset incoming email token on issues modal and account page,

reactivates all tests and writes more tests for it
parent 9d514213
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
Issuable.initSearch(); Issuable.initSearch();
Issuable.initChecks(); Issuable.initChecks();
Issuable.initResetFilters(); Issuable.initResetFilters();
Issuable.resetIncomingEmailToken();
return Issuable.initLabelFilterRemove(); return Issuable.initLabelFilterRemove();
}, },
initTemplates: function() { initTemplates: function() {
...@@ -154,6 +155,27 @@ ...@@ -154,6 +155,27 @@
this.issuableBulkActions.willUpdateLabels = false; this.issuableBulkActions.willUpdateLabels = false;
} }
return true; return true;
},
resetIncomingEmailToken: function() {
$('.incoming-email-token-reset').on('click', function(e) {
e.preventDefault();
$.ajax({
type: 'PUT',
url: $('.incoming-email-token-reset').attr('href'),
dataType: 'json',
success: function(response) {
$('#issue_email').val(response.new_issue_address).focus();
},
beforeSend: function() {
$('.incoming-email-token-reset').text('resetting...');
},
complete: function() {
$('.incoming-email-token-reset').text('reset it');
}
});
});
} }
}; };
......
...@@ -23,6 +23,10 @@ ...@@ -23,6 +23,10 @@
color: $md-link-color; color: $md-link-color;
} }
.private-tokens-reset div.reset-action:not(:first-child) {
padding-top: 15px;
}
.oauth-buttons { .oauth-buttons {
.btn-group { .btn-group {
margin-right: 10px; margin-right: 10px;
......
...@@ -26,7 +26,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -26,7 +26,7 @@ class ProfilesController < Profiles::ApplicationController
def reset_private_token def reset_private_token
if current_user.reset_authentication_token! if current_user.reset_authentication_token!
flash[:notice] = "Private token was successfully updated" flash[:notice] = "Private token was successfully reset"
end end
redirect_to profile_account_path redirect_to profile_account_path
...@@ -34,7 +34,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -34,7 +34,7 @@ class ProfilesController < Profiles::ApplicationController
def reset_incoming_email_token def reset_incoming_email_token
if current_user.reset_incoming_email_token! if current_user.reset_incoming_email_token!
flash[:notice] = "Incoming email token was successfully updated" flash[:notice] = "Incoming email token was successfully reset"
end end
redirect_to profile_account_path redirect_to profile_account_path
......
...@@ -160,6 +160,13 @@ class ProjectsController < Projects::ApplicationController ...@@ -160,6 +160,13 @@ class ProjectsController < Projects::ApplicationController
end end
end end
def new_issue_address
return render_404 unless Gitlab::IncomingEmail.supports_issue_creation?
current_user.reset_incoming_email_token!
render json: { new_issue_address: @project.new_issue_address(current_user) }
end
def archive def archive
return access_denied! unless can?(current_user, :archive_project, @project) return access_denied! unless can?(current_user, :archive_project, @project)
......
module AccountsHelper
def incoming_email_token_enabled?
current_user.incoming_email_token && Gitlab::IncomingEmail.supports_issue_creation?
end
end
...@@ -624,7 +624,7 @@ class Project < ActiveRecord::Base ...@@ -624,7 +624,7 @@ class Project < ActiveRecord::Base
end end
def new_issue_address(author) def new_issue_address(author)
return unless Gitlab::IncomingEmail.enabled? && author return unless Gitlab::IncomingEmail.supports_issue_creation? && author
author.ensure_incoming_email_token! author.ensure_incoming_email_token!
......
...@@ -951,7 +951,7 @@ class User < ActiveRecord::Base ...@@ -951,7 +951,7 @@ class User < ActiveRecord::Base
def generate_token(token_field) def generate_token(token_field)
if token_field == :incoming_email_token if token_field == :incoming_email_token
# Needs to be all lowercase and alphanumeric because it's gonna be used in an email address. # Needs to be all lowercase and alphanumeric because it's gonna be used in an email address.
SecureRandom.hex SecureRandom.hex.to_i(16).to_s(36)
else else
super super
end end
......
...@@ -8,28 +8,35 @@ ...@@ -8,28 +8,35 @@
.row.prepend-top-default .row.prepend-top-default
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
%h4.prepend-top-0 %h4.prepend-top-0
Private Tokens = incoming_email_token_enabled? ? "Private Tokens" : "Private Token"
%p %p
Your private token is used to access the API and Atom feeds without Keep
username/password authentication. = incoming_email_token_enabled? ? "these tokens" : "this token"
%p secret, anyone with access to them can interact with GitLab as if they were you.
Your incoming email token is used to create new issues by email, and is .col-lg-9.private-tokens-reset
included in your project-specific email addresses. .reset-action
.col-lg-9
%p.cgray %p.cgray
- if current_user.private_token - if current_user.private_token
= label_tag "token", "Private token", class: "label-light" = label_tag "private-token", "Private token", class: "label-light"
= text_field_tag "token", current_user.private_token, class: "form-control" = text_field_tag "private-token", current_user.private_token, class: "form-control", readonly: true, onclick: "this.select()"
- else - else
%span You don`t have one yet. Click generate to fix it. %span You don't have one yet. Click generate to fix it.
%p.help-block %p.help-block
Keep this token secret, anyone with access to it can interact with the GitLab API as if they were you. Your private token is used to access the API and Atom feeds without username/password authentication.
.prepend-top-default .prepend-top-default
- if current_user.private_token - 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" = link_to 'Reset private token', reset_private_token_profile_path, method: :put, data: { confirm: "Are you sure?" }, class: "btn btn-default private-token"
- else - else
= f.submit 'Generate', class: "btn btn-default" = f.submit 'Generate', class: "btn btn-default"
= link_to 'Reset incoming email token', reset_incoming_email_token_profile_path, method: :put, data: { confirm: "Are you sure?" }, class: "btn btn-default" - 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"
%hr %hr
.row.prepend-top-default .row.prepend-top-default
......
...@@ -12,16 +12,23 @@ ...@@ -12,16 +12,23 @@
Create new issue by email Create new issue by email
.modal-body .modal-body
%p %p
Write an email to the below email address. (This is a private email address, so keep it secret.) You can create a new issue inside this project by sending an email to the following email address:
.email-modal-input-group.input-group .email-modal-input-group.input-group
= text_field_tag :issue_email, email, class: "monospace js-select-on-focus form-control", readonly: true = text_field_tag :issue_email, email, class: "monospace js-select-on-focus form-control", readonly: true
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#issue_email') = clipboard_button(clipboard_target: '#issue_email')
%p %p
Send an email to this address to create an issue. The subject will be used as the title of the new issue, and the message will be the description.
%p
Use the subject line as the title of your issue. = link_to 'Slash commands', help_page_path('user/project/slash_commands'), target: '_blank', tabindex: -1
and styling with
= link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1
are supported.
%p %p
Use the message as the body of your issue (feel free to include some nice This is a private email address, generated just for you.
= succeed ")." do
= link_to "Markdown", help_page_path('markdown', 'markdown') Anyone who gets ahold of it can create issues as if they were you.
You should
= link_to 'reset it', new_issue_address_namespace_project_path(@project.namespace, @project), class: 'incoming-email-token-reset'
if that ever happens.
---
title: Use separate email-token for incoming email and revert back the inactive feature
merge_request: 5914
author:
...@@ -18,6 +18,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: ...@@ -18,6 +18,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
get :autocomplete_sources get :autocomplete_sources
get :activity get :activity
get :refs get :refs
put :new_issue_address
end end
scope module: :projects do scope module: :projects do
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160819232256) do ActiveRecord::Schema.define(version: 20161103171205) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
......
...@@ -59,11 +59,6 @@ Feature: Profile ...@@ -59,11 +59,6 @@ Feature: Profile
When I unsuccessfully change my password When I unsuccessfully change my password
Then I should see a password error message Then I should see a password error message
Scenario: I reset my token
Given I visit profile account page
Then I reset my token
And I should see new token
Scenario: I visit history tab Scenario: I visit history tab
Given I have activity Given I have activity
When I visit Audit Log page When I visit Audit Log page
......
...@@ -104,18 +104,6 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -104,18 +104,6 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end end
end end
step 'I reset my token' do
page.within '.private-token' do
@old_token = @user.private_token
click_button "Reset private token"
end
end
step 'I should see new token' do
expect(find("#token").value).not_to eq @old_token
expect(find("#token").value).to eq @user.reload.private_token
end
step 'I have activity' do step 'I have activity' do
create(:closed_issue_event, author: current_user) create(:closed_issue_event, author: current_user)
end end
......
...@@ -4,8 +4,7 @@ require 'gitlab/email/handler/create_issue_handler' ...@@ -4,8 +4,7 @@ require 'gitlab/email/handler/create_issue_handler'
module Gitlab module Gitlab
module Email module Email
module Handler module Handler
# The `CreateIssueHandler` feature is disabled for the time being. HANDLERS = [CreateNoteHandler, CreateIssueHandler]
HANDLERS = [CreateNoteHandler]
def self.for(mail, mail_key) def self.for(mail, mail_key)
HANDLERS.find do |klass| HANDLERS.find do |klass|
......
module Gitlab module Gitlab
module IncomingEmail module IncomingEmail
WILDCARD_PLACEHOLDER = '%{key}'.freeze
class << self class << self
FALLBACK_MESSAGE_ID_REGEX = /\Areply\-(.+)@#{Gitlab.config.gitlab.host}\Z/.freeze FALLBACK_MESSAGE_ID_REGEX = /\Areply\-(.+)@#{Gitlab.config.gitlab.host}\Z/.freeze
...@@ -7,8 +9,16 @@ module Gitlab ...@@ -7,8 +9,16 @@ module Gitlab
config.enabled && config.address config.enabled && config.address
end end
def supports_wildcard?
config.address && config.address.include?(WILDCARD_PLACEHOLDER)
end
def supports_issue_creation?
enabled? && supports_wildcard?
end
def reply_address(key) def reply_address(key)
config.address.gsub('%{key}', key) config.address.gsub(WILDCARD_PLACEHOLDER, key)
end end
def key_from_address(address) def key_from_address(address)
......
...@@ -264,6 +264,33 @@ describe ProjectsController do ...@@ -264,6 +264,33 @@ describe ProjectsController do
end end
end end
describe 'PUT #new_issue_address' do
subject do
put :new_issue_address,
namespace_id: project.namespace.to_param,
id: project.to_param
user.reload
end
before do
sign_in(user)
project.team << [user, :developer]
allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
end
it 'has http status 200' do
expect(response).to have_http_status(200)
end
it 'changes the user incoming email token' do
expect { subject }.to change { user.incoming_email_token }
end
it 'changes projects new issue address' do
expect { subject }.to change { project.new_issue_address(user) }
end
end
describe "POST #toggle_star" do describe "POST #toggle_star" do
it "toggles star if user is signed in" do it "toggles star if user is signed in" do
sign_in(user) sign_in(user)
......
...@@ -3,6 +3,7 @@ require 'spec_helper' ...@@ -3,6 +3,7 @@ require 'spec_helper'
describe 'Issues', feature: true do describe 'Issues', feature: true do
include IssueHelpers include IssueHelpers
include SortingHelper include SortingHelper
include WaitForAjax
let(:project) { create(:project) } let(:project) { create(:project) }
...@@ -368,6 +369,26 @@ describe 'Issues', feature: true do ...@@ -368,6 +369,26 @@ describe 'Issues', feature: true do
end end
end end
describe 'when I want to reset my incoming email token' do
let(:project1) { create(:project, namespace: @user.namespace) }
before do
allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
project1.team << [@user, :master]
visit namespace_project_issues_path(@user.namespace, project1)
end
it 'changes incoming email address token', js: true do
find('.issue-email-modal-btn').click
previous_token = find('input#issue_email').value
find('.incoming-email-token-reset').click
wait_for_ajax
expect(find('input#issue_email').value).not_to eq(previous_token)
end
end
describe 'update labels from issue#show', js: true do describe 'update labels from issue#show', js: true do
let(:issue) { create(:issue, project: project, author: @user, assignee: @user) } let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
let!(:label) { create(:label, project: project) } let!(:label) { create(:label, project: project) }
...@@ -553,7 +574,7 @@ describe 'Issues', feature: true do ...@@ -553,7 +574,7 @@ describe 'Issues', feature: true do
end end
end end
xdescribe 'new issue by email' do describe 'new issue by email' do
shared_examples 'show the email in the modal' do shared_examples 'show the email in the modal' do
before do before do
stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab") stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
......
...@@ -32,4 +32,33 @@ describe 'Profile account page', feature: true do ...@@ -32,4 +32,33 @@ describe 'Profile account page', feature: true do
expect(current_path).to eq(profile_account_path) expect(current_path).to eq(profile_account_path)
end end
end end
describe 'when I reset private token' do
before do
visit profile_account_path
end
it 'resets private token' do
previous_token = find("#private-token").value
click_link('Reset private token')
expect(find('#private-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)
visit profile_account_path
end
it 'resets incoming email token' do
previous_token = find('#incoming-email-token').value
click_link('Reset incoming email token')
expect(find('#incoming-email-token').value).not_to eq(previous_token)
end
end
end end
require 'spec_helper' require 'spec_helper'
require_relative '../email_shared_blocks' require_relative '../email_shared_blocks'
xdescribe Gitlab::Email::Handler::CreateIssueHandler, lib: true do describe Gitlab::Email::Handler::CreateIssueHandler, lib: true do
include_context :email_shared_context include_context :email_shared_context
it_behaves_like :email_shared_examples it_behaves_like :email_shared_examples
......
...@@ -295,7 +295,7 @@ describe Project, models: true do ...@@ -295,7 +295,7 @@ describe Project, models: true do
end end
end end
xdescribe "#new_issue_address" do describe "#new_issue_address" do
let(:project) { create(:empty_project, path: "somewhere") } let(:project) { create(:empty_project, path: "somewhere") }
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -305,8 +305,7 @@ describe Project, models: true do ...@@ -305,8 +305,7 @@ describe Project, models: true do
end end
it 'returns the address to create a new issue' do it 'returns the address to create a new issue' do
token = user.authentication_token address = "p+#{project.path_with_namespace}+#{user.incoming_email_token}@gl.ab"
address = "p+#{project.namespace.path}/#{project.path}+#{token}@gl.ab"
expect(project.new_issue_address(user)).to eq(address) expect(project.new_issue_address(user)).to eq(address)
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