Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #38108 - As a user I want to invalidate tokens for self(UI) #10405

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,16 @@ def impersonate
def invalidate_jwt
@user = find_resource(:edit_users)
@user.jwt_secret&.destroy
process_success(
:success_msg => _('Successfully invalidated JWTs for %s.') % @user.login
)
respond_to do |format|
format.html do
process_success(
:success_msg => _('Successfully invalidated JWTs for %s.') % @user.login
)
end
format.json do
render :json => {}, :status => :ok
end
end
end

def stop_impersonation
Expand Down
5 changes: 5 additions & 0 deletions app/views/jwt_tokens/_jwt_tokens_tab.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="tab-pane" id="jwt_tokens">
<%= react_component('JwtTokens', {
userId: @user.id,
}) %>
</div>
7 changes: 7 additions & 0 deletions app/views/users/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
<% if @editing_self || (@user.persisted? && authorized_for(hash_for_api_user_personal_access_tokens_path(user_id: @user))) %>
<li><a href='#personal_access_tokens' data-toggle='tab'><%= _('Personal Access Tokens') %></a></li>
<% end %>
<% if @editing_self %>
<li><a href='#jwt_tokens' data-toggle='tab'><%= _('JWT Tokens') %></a></li>
<% end %>
<%= render_tab_header_for(:main_tabs, :subject => @user, :form => f) %>
</ul>

Expand Down Expand Up @@ -97,6 +100,10 @@
<%= render 'personal_access_tokens/personal_access_tokens_tab', :f => f, :user => @user %>
<% end %>

<% if @editing_self %>
<%= render 'jwt_tokens/jwt_tokens_tab', :f => f, :user => @user %>
<% end %>

<div class='tab-pane' id='roles'>
<% caption = @user.inherited_admin? ? _('Admin rights are currently inherited from a user group') : '' %>
<%= checkbox_f f, :admin, help_block: caption if User.current.can_change_admin_flag? %>
Expand Down
9 changes: 9 additions & 0 deletions test/controllers/users_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ class UsersControllerTest < ActionController::TestCase
assert_nil user.jwt_secret
end

test "User should be able to invalidate jwt for self" do
User.current = users(:one)
user = User.current
FactoryBot.build(:jwt_secret, token: 'test_jwt_secret', user: user)
patch :invalidate_jwt, params: { :id => user.id }
user.reload
assert_nil user.jwt_secret
end

test 'user with edit users permission should be able to invalidate jwt for another user' do
User.current = setup_user "edit", "users"
user = users(:two)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import LabelIcon from './common/LabelIcon';
import { WelcomeAuthSource } from './AuthSource/Welcome';
import { WelcomeConfigReports } from './ConfigReports/Welcome';
import { WelcomeArchitecture } from './Architectures/Welcome';
import JwtTokens from './users/JwtTokens/JwtTokens';

const componentRegistry = {
registry: forceSingleton('component_registry', () => ({})),
Expand Down Expand Up @@ -142,6 +143,7 @@ const coreComponents = [
{ name: 'SettingsTable', type: SettingsTable },
{ name: 'SettingUpdateModal', type: SettingUpdateModal },
{ name: 'PersonalAccessTokens', type: PersonalAccessTokens },
{ name: 'JwtTokens', type: JwtTokens },
{ name: 'ClipboardCopy', type: ClipboardCopy },
{ name: 'LabelIcon', type: LabelIcon },
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, { Fragment } from 'react';
import { Button } from '@patternfly/react-core';
import { KeyIcon } from '@patternfly/react-icons';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import { APIActions } from '../../../redux/API';
import { openConfirmModal } from '../../ConfirmModal';
import { translate as __ } from '../../../common/I18n';

const JwtTokens = ({ userId }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the userID is not set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user is logged in, we always have the @user object set and we are sending it from here so I don't think that would happen

const dispatch = useDispatch();
return (
<Fragment>
<table className="table table-bordered table-striped table-hover table-fixed">
stejskalleos marked this conversation as resolved.
Show resolved Hide resolved
<tbody>
<tr>
<td className="blank-slate-pf">
<div className="blank-slate-pf-icon">
<KeyIcon type="fa" name="key" color="#9c9c9c" />
</div>
<h1>{__('JWT Tokens')}</h1>
<p>
{__(
'By invalidating your JSON Web Tokens (JWTs), you will no longer be able to register hosts by using your existing JWTs.'
)}
</p>
<Button
ouiaId="invalidate-jwt-token-button"
variant="primary"
isSmall
onClick={() =>
dispatch(
openConfirmModal({
isWarning: true,
title: __('Invalidate tokens for self?'),
confirmButtonText: __('Confirm'),
onConfirm: () =>
dispatch(
APIActions.patch({
url: `/users/${userId}/invalidate_jwt`,
key: `INVALIDATE-JWT`,
successToast: () =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the call is not successful?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will not happen, Like we discussed in the first story PR, there will be no case when invalidation won't work. I can add a safety failed toast, what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if user was logged out meanwhile? The Rails will respond with 501

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are sending :ok status at all times, ref
I think I can add a safety error toast too, what do u think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are sending :ok status at all time

That's not true. You do in>your< controller, but what if there is a problem on the network, DNS, or somewhere else? How do you handle that error?

__('Successfully Invalidated JWTs'),
})
),
})
)
}
>
{__('Invalidate JWTs')}
</Button>
</td>
</tr>
</tbody>
</table>
</Fragment>
);
};

JwtTokens.propTypes = {
userId: PropTypes.string.isRequired,
};

export default JwtTokens;
Loading