Skip to content

Commit

Permalink
Merge pull request #11588 from eduardoj/regenerate_token_string
Browse files Browse the repository at this point in the history
Regenerate the Token String in the UI
  • Loading branch information
dmarcoux committed Sep 13, 2021
2 parents c24135b + 8f4b738 commit 39ee292
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 45 deletions.
23 changes: 23 additions & 0 deletions src/api/app/components/copy_to_clipboard_input_component.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-# TODO: Relying on a form builder is a bit weird. We could perhaps directly use `text_field_tag` instead and pass an input value to the component.
= @form_builder.text_field(:string_readonly, id: 'copy-to-clipboard-readonly', class: 'form-control', readonly: true)
.input-group-append#copy-to-clipboard
%span.input-group-text
%i.fas.fa-clipboard

:javascript
function copyToClipboard() {
document.getElementById('copy-to-clipboard-readonly').select();
document.execCommand('copy');
}

- content_for :ready_function do
:plain
// Definition of the tooltip and click event handler for the "Copy to clipboard" icon
$('#copy-to-clipboard').tooltip({ title: 'Copy to clipboard' }).on('click', function () {
copyToClipboard();

// Shows "Copied!" tooltip. Later on shows the previous message 'Copy to clipboard' as tooltip.
$(this).tooltip('dispose').tooltip({ title: 'Copied!' }).tooltip('show').on('hidden.bs.tooltip', function () {
$(this).tooltip('dispose').tooltip({ title: 'Copy to clipboard' });
});
});
6 changes: 6 additions & 0 deletions src/api/app/components/copy_to_clipboard_input_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class CopyToClipboardInputComponent < ApplicationComponent
def initialize(form_builder:)
super
@form_builder = form_builder
end
end
28 changes: 21 additions & 7 deletions src/api/app/controllers/webui/users/tokens_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,28 @@ def edit
def update
authorize @token

new_scm_token = params.require(:token).permit(:scm_token)
respond_to do |format|
format.js do
if @token.regenerate_string
flash.now[:success] = "Token string successfully regenerated! Make sure you save it - you won't be able to access it again."
render partial: 'update', locals: { string: @token.string }
else
flash.now[:error] = 'Failed to regenerate Token string'
render partial: 'update'
end
end
format.html do
new_scm_token = params.require(:token).except(:string_readonly).permit(:scm_token)

if @token.update(new_scm_token)
flash[:success] = 'Token successfully updated'
else
flash[:error] = 'Failed to update Token'
end
if @token.update(new_scm_token)
flash[:success] = 'Token successfully updated'
else
flash[:error] = 'Failed to update Token'
end

redirect_to tokens_url
redirect_to tokens_url
end
end
end

def create
Expand Down Expand Up @@ -70,6 +83,7 @@ def set_params
@params = params.except(:project_name, :package_name).require(:token).except(:string_readonly).permit(:type, :scm_token).tap do |token_parameters|
token_parameters.require(:type)
end
@params = @params.except(:scm_token) unless @params[:type] == 'workflow'
@extra_params = params.slice(:project_name, :package_name).permit!
end

Expand Down
2 changes: 1 addition & 1 deletion src/api/app/policies/token_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def edit?
end

def update?
record.user == user && record.type == 'Token::Workflow' && Flipper.enabled?(:trigger_workflow, user)
record.user == user && Flipper.enabled?(:trigger_workflow, user)
end

def create?
Expand Down
4 changes: 2 additions & 2 deletions src/api/app/views/webui/users/tokens/_create.js.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
$('#flash').html("<%= escape_javascript(render(layout: false, partial: 'layouts/webui/flash', object: flash)) %>");
<% if flash[:error].blank? %>
$('#created-token').removeClass('d-none');
$('#token_string_readonly').val("<%= escape_javascript(string) %>");
$('.form-row :input').not('#token_string_readonly').prop('disabled', true);
$('#copy-to-clipboard-readonly').val("<%= escape_javascript(string) %>");
$('.form-row :input').not('#copy-to-clipboard-readonly').prop('disabled', true);
$('.actions').html("<%= escape_javascript(render partial: 'actions') %>");
<% end %>
10 changes: 10 additions & 0 deletions src/api/app/views/webui/users/tokens/_update.js.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
$('#flash').html("<%= escape_javascript(render(layout: false, partial: 'layouts/webui/flash', object: flash)) %>");
<% if flash[:error].blank? %>
$('#regenerate-token-modal').modal('hide');
$('#regenerate-token-group,#scm-token-group').addClass('d-none');
$('#copy-to-clipboard-readonly').val("<%= escape_javascript(string) %>");
$('.actions').html("<%= escape_javascript(render partial: 'actions') %>");
$('#created-token').removeClass('d-none').hide().slideDown('slow', function() {
$('#copy-to-clipboard-readonly').focus();
});
<% end %>
53 changes: 43 additions & 10 deletions src/api/app/views/webui/users/tokens/edit.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,55 @@

.row
.col-12.col-md-10.col-lg-8
-# "as: :token" to have a consistent name across all various token classes
= form_for(@token, as: :token, url: token_path, method: :put, local: true) do |f|
.form-row#edit-token
= form_with(url: token_path, scope: :token, method: :put, local: true) do |f|
.form-row.d-none#created-token
.col-12.col-md-10.col-lg-9
%fieldset.form-group
= f.label(:string_readonly, 'Your new OBS Personal Access Token Secret', class: 'col-form-label col-form-label-lg')
.input-group
= render CopyToClipboardInputComponent.new(form_builder: f)
%hr

.form-row
.col-12.col-md-10.col-lg-9
.form-group
= f.label(:id, 'Id:')
= @token.id
.form-group
= f.label(:type, 'Operation:')
= @token.class.token_name.capitalize
.form-group
= f.label(:scm_token) do
SCM token:
%abbr.text-danger{ title: 'required' } *
.input-group
= f.password_field(:scm_token, size: 80, class: 'form-control', placeholder: 'Please enter your new SCM token', required: true)
- if @token.package.present?
.form-group
= f.label(:type, 'Package:')
= link_to("#{@token.package.project.name}/#{@token.package.name}",
package_show_path(project: @token.package.project, package: @token.package))
.form-group#regenerate-token-group
= link_to('Regenerate Token', '#', title: 'Regenerate Token', class: 'btn btn-outline-danger float-right ml-3 px-4',
data: { toggle: 'modal', target: '#regenerate-token-modal' })
If your token has been lost, forgotten or compromised, you can regenerate it.
Don't forget to replace the string in any scripts or applications using this token.
- if @token.type == 'Token::Workflow'
.form-group#scm-token-group
= f.label(:scm_token) do
SCM token:
%abbr.text-danger{ title: 'required' } *
.input-group
= f.password_field(:scm_token, size: 80, class: 'form-control', placeholder: 'Please enter your new SCM token', required: true)
.actions
= link_to('Cancel', tokens_path, title: 'Cancel', class: 'btn btn-outline-secondary px-4 mr-3 mt-3 mt-sm-0')
= f.submit('Update', class: 'btn btn-primary px-4 mt-3 mt-sm-0')
- if @token.type == 'Token::Workflow'
= f.submit('Update', class: 'btn btn-primary px-4 mt-3 mt-sm-0')

.modal.fade#regenerate-token-modal{ tabindex: -1, role: 'dialog', aria: { labelledby: 'regenerate-token-modal-label', hidden: true } }
.modal-dialog.modal-dialog-centered{ role: 'document' }
.modal-content
.modal-header
%h5.modal-title#regenerate-token-modal-label Do you really want to regenerate this token string?
.modal-body
%p
Please confirm that you want to regenerate this token string.
= form_with(url: token_path, scope: :token, method: :put, local: false) do |f|
.modal-footer
%a.btn.btn-sm.btn-outline-secondary.px-4{ data: { dismiss: 'modal' } }
Cancel
= f.submit('Regenerate', class: 'btn btn-sm btn-danger px-4', data: { disable: { with: 'Regenerate' } })
21 changes: 1 addition & 20 deletions src/api/app/views/webui/users/tokens/new.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
%fieldset.form-group
= f.label(:string_readonly, 'Your new OBS Personal Access Token Secret', class: 'col-form-label col-form-label-lg')
.input-group
= f.text_field(:string_readonly, class: 'form-control', readonly: true)
.input-group-append#copy-to-clipboard
%span.input-group-text
%i.fas.fa-clipboard
= render CopyToClipboardInputComponent.new(form_builder: f)
%hr

.form-row
Expand Down Expand Up @@ -51,12 +48,6 @@
= link_to('Cancel', tokens_path, title: 'Cancel', class: 'btn btn-outline-secondary px-4 mr-3 mt-3 mt-sm-0')
= f.submit('Create', class: 'btn btn-primary px-4 mt-3 mt-sm-0')

:javascript
function copyToClipboard(elementId) {
document.getElementById(elementId).select();
document.execCommand('copy');
}

- content_for :ready_function do
:plain
// Default type value: 'runservice'
Expand All @@ -75,13 +66,3 @@
$('#token_scm_token').attr('required', false);
}
});

// Definition of the tooltip and click event handler for the "Copy to clipboard" icon
$('#copy-to-clipboard').tooltip({ title: 'Copy to clipboard' }).on('click', function () {
copyToClipboard('token_string_readonly');

// Shows "Copied!" tooltip. Later on shows the previous message 'Copy to clipboard' as tooltip.
$(this).tooltip('dispose').tooltip({ title: 'Copied!' }).tooltip('show').on('hidden.bs.tooltip', function () {
$(this).tooltip('dispose').tooltip({ title: 'Copy to clipboard' });
});
});
12 changes: 7 additions & 5 deletions src/api/spec/controllers/webui/users/tokens_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,16 @@
it { expect { subject }.to change { token.reload.scm_token }.from('something').to('something_else') }
end

context 'does not update a non-workflow token belonging to the logged-in user' do
context 'updates the token string of a token belonging to the logged-in user' do
let(:token) { create(:service_token, user: user) }
let(:update_parameters) { { id: token.id, token: { scm_token: 'something_else' } } }
let(:update_parameters) { { id: token.id } }

include_examples 'check for flashing an error'
subject { put :update, params: update_parameters, xhr: true }

it { is_expected.to redirect_to(root_path) }
it { expect { subject }.not_to change(token, :scm_token) }
include_examples 'check for flashing a success'

it { expect { subject }.to(change { token.reload.string }) }
it { expect { subject }.not_to(change { token.reload.scm_token }) }
end

context 'redirects to index when passing a non-existent token' do
Expand Down

0 comments on commit 39ee292

Please sign in to comment.