summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean McGivern <sean@gitlab.com>2016-09-27 12:46:09 +0200
committerSean McGivern <sean@gitlab.com>2016-09-27 17:59:45 +0200
commit4650d5bdbfd8619979db413759de873d55b8a3c7 (patch)
tree590ed82c0db9799bb097b50978cc27ee6a99663c
parent3820ca5876ecb550d4d7eba0db98296f467125d8 (diff)
downloadgitlab-ce-4650d5bdbfd8619979db413759de873d55b8a3c7.tar.gz
Hide private token by default
Only show the private token on the profile page after the user enters their current password. Works nicer with JS, but also works without.
-rw-r--r--CHANGELOG1
-rw-r--r--app/assets/javascripts/dispatcher.js3
-rw-r--r--app/assets/javascripts/private_token_viewer.js.es646
-rw-r--r--app/assets/stylesheets/behaviors.scss4
-rw-r--r--app/controllers/profiles/accounts_controller.rb31
-rw-r--r--app/views/profiles/accounts/show.html.haml28
-rw-r--r--config/routes.rb1
-rw-r--r--spec/features/profiles/private_token_spec.rb46
8 files changed, 150 insertions, 10 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 28c5d23d604..542b7602809 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.13.0 (unreleased)
- Speed-up group milestones show page
- Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison)
+ - Hide API token by default on profile page
- Revoke button in Applications Settings underlines on hover.
- Add organization field to user profile
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index ddf11ecf34c..58b4e9c258d 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -193,6 +193,9 @@
case 'projects:cycle_analytics:show':
new gl.CycleAnalytics();
break;
+ case 'profiles:accounts:show':
+ new gl.PrivateTokenViewer();
+ break;
}
switch (path.first()) {
case 'admin':
diff --git a/app/assets/javascripts/private_token_viewer.js.es6 b/app/assets/javascripts/private_token_viewer.js.es6
new file mode 100644
index 00000000000..6c7cd436e37
--- /dev/null
+++ b/app/assets/javascripts/private_token_viewer.js.es6
@@ -0,0 +1,46 @@
+((global) => {
+ gl.PrivateTokenViewer = class PrivateTokenViewer {
+ constructor() {
+ this.$show = $('#private-token-show');
+ this.$error = $('#private-token-error');
+ this.$request = $('#private-token-request');
+ this.$requestForm = this.$request.find('form');
+
+ this.initialSetup();
+ }
+
+ initialSetup() {
+ this.$show.addClass('hidden');
+ this.$error.addClass('hidden');
+
+ this.$requestForm.on('submit', this.submitPassword.bind(this));
+ }
+
+ submitPassword(event) {
+ $.ajax({
+ url: this.$requestForm.attr('action'),
+ method: 'POST',
+ dataType: 'json',
+ contentType: 'application/json',
+ data: JSON.stringify({
+ current_password: this.$requestForm.find('#current_password').val()
+ })
+ }).done((data) => {
+ this.$show.find('#token').val(data.private_token);
+
+ this.$show.removeClass('hidden');
+ this.$error.addClass('hidden');
+ this.$request.addClass('hidden');
+ }).error((request) => {
+ this.$error.text(request.responseJSON.message);
+
+ this.$show.addClass('hidden');
+ this.$error.removeClass('hidden');
+ this.$request.removeClass('hidden');
+ this.$requestForm.find('[type="submit"]').enable();
+ });
+
+ event.preventDefault();
+ }
+ };
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss
index 897bc49e7df..f76396b0a02 100644
--- a/app/assets/stylesheets/behaviors.scss
+++ b/app/assets/stylesheets/behaviors.scss
@@ -21,6 +21,10 @@
}
}
+.hidden {
+ display: none;
+}
+
// Hide element if Vue is still working on rendering it fully.
[v-cloak="true"] {
display: none !important;
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index 69959fe3687..99a06fbe702 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -1,6 +1,29 @@
class Profiles::AccountsController < Profiles::ApplicationController
+ before_action :set_user, only: [:show, :private_token]
+
def show
- @user = current_user
+ end
+
+ def private_token
+ if params[:current_password]
+ if @user.valid_password?(params[:current_password])
+ @private_token = @user.private_token
+ else
+ @private_token_error = 'The password you entered is incorrect. Please try again.'
+ end
+ end
+
+ respond_to do |format|
+ format.html { render 'show' }
+
+ format.json do
+ if @private_token
+ render json: { private_token: @private_token }
+ else
+ render status: :bad_request, json: { message: @private_token_error }
+ end
+ end
+ end
end
def unlink
@@ -8,4 +31,10 @@ class Profiles::AccountsController < Profiles::ApplicationController
current_user.identities.find_by(provider: provider).destroy unless provider.to_s == 'saml'
redirect_to profile_account_path
end
+
+ private
+
+ def set_user
+ @user = current_user
+ end
end
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index c80f22457b4..cf1ec9a21ce 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -12,16 +12,26 @@
%p
Your private token is used to access application resources without authentication.
.col-lg-9
- = form_for @user, url: reset_private_token_profile_path, method: :put, html: { class: "private-token" } do |f|
- %p.cgray
- - if current_user.private_token
+ %p.cgray
+ - if current_user.private_token
+ #private-token-show{class: @private_token.blank? && 'hidden'}
= label_tag "token", "Private token", class: "label-light"
- = text_field_tag "token", current_user.private_token, class: "form-control"
- - else
- %span You don`t have one yet. Click generate to fix it.
- %p.help-block
- It can be used for atom feeds or the API. Keep it secret!
- .prepend-top-default
+ = text_field_tag "token", @private_token, class: "form-control"
+ #private-token-request{class: @private_token.present? && 'hidden'}
+ = form_tag private_token_profile_account_path, method: 'POST' do
+ .form-group
+ = label_tag :current_password, 'To view your private token, enter your current password', class: 'label-light'
+ = password_field_tag :current_password, nil, required: true, class: 'form-control'
+ #private-token-error.prepend-top-default.error-message{class: @private_token_error.blank? && 'hidden'}
+ = @private_token_error
+ .prepend-top-default
+ = submit_tag 'Show private token', class: 'btn btn-default'
+ - else
+ %span You don't have a private token yet. Click generate to create one.
+ %p.help-block
+ This is used for Atom feeds and the API, and gives full access to your account. Keep it secret!
+ .prepend-top-default
+ = form_for @user, url: reset_private_token_profile_path, method: :put, html: { class: "private-token" } do |f|
- if current_user.private_token
= f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default"
- else
diff --git a/config/routes.rb b/config/routes.rb
index 4d6ec699cbd..3edd77f6ee1 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -351,6 +351,7 @@ Rails.application.routes.draw do
resource :account, only: [:show] do
member do
delete :unlink
+ post :private_token
end
end
resource :notifications, only: [:show, :update]
diff --git a/spec/features/profiles/private_token_spec.rb b/spec/features/profiles/private_token_spec.rb
new file mode 100644
index 00000000000..eb16f47065a
--- /dev/null
+++ b/spec/features/profiles/private_token_spec.rb
@@ -0,0 +1,46 @@
+require 'rails_helper'
+
+describe 'Profile > Private tokens', feature: true do
+ let(:user) { create(:user) }
+
+ shared_examples 'private token viewer' do
+ context 'when entering an invalid password' do
+ before do
+ login_as(user)
+ visit profile_account_path
+ fill_in 'current_password', with: user.password.succ
+ click_on 'Show private token'
+ end
+
+ it 'shows an error and the form' do
+ expect(find('#private-token-error')[:class]).not_to include('hidden')
+ expect(find('#private-token-request')[:class]).not_to include('hidden')
+
+ expect(find('#private-token-show', visible: false)[:class]).to include('hidden')
+ end
+
+ context 'when entering a valid password' do
+ before do
+ fill_in 'current_password', with: user.password
+ click_on 'Show private token'
+ end
+
+ it 'shows only the private token' do
+ expect(find('#private-token-show')[:class]).not_to include('hidden')
+ expect(find('#token').value).to eq(user.private_token)
+
+ expect(find('#private-token-error', visible: false)[:class]).to include('hidden')
+ expect(find('#private-token-request', visible: false)[:class]).to include('hidden')
+ end
+ end
+ end
+ end
+
+ context 'with JavaScript enabled', js: true do
+ it_behaves_like 'private token viewer'
+ end
+
+ context 'with JavaScript disabled' do
+ it_behaves_like 'private token viewer'
+ end
+end