diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 78020fbb6c5..170333146fd 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -96,9 +96,13 @@ 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
- )
+ unless editing_self?
+ process_success(
+ :success_msg => _('Successfully invalidated JWTs for %s.') % @user.login
+ )
+ return
+ end
+ render :json => {}, :status => :ok
end
def stop_impersonation
diff --git a/app/models/user.rb b/app/models/user.rb
index a8ca2a1aed2..352cdd32663 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -463,7 +463,7 @@ def can_change_admin_flag?
def editing_self?(options = {})
options[:controller].to_s == 'users' &&
- options[:action] =~ /edit|update/ &&
+ options[:action] =~ /edit|update|invalidate_jwt/ &&
options[:id].to_i == id ||
options[:controller].to_s =~ /\Aapi\/v\d+\/users\Z/ &&
options[:action] =~ /show|update/ &&
diff --git a/app/views/jwt_tokens/_jwt_tokens_tab.html.erb b/app/views/jwt_tokens/_jwt_tokens_tab.html.erb
new file mode 100644
index 00000000000..212031b2fa4
--- /dev/null
+++ b/app/views/jwt_tokens/_jwt_tokens_tab.html.erb
@@ -0,0 +1,5 @@
+
<% 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? %>
diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb
index 6b1a4bdae92..c2631f59069 100644
--- a/test/controllers/users_controller_test.rb
+++ b/test/controllers/users_controller_test.rb
@@ -162,16 +162,25 @@ class UsersControllerTest < ActionController::TestCase
User.current = users(:admin)
user = users(:two)
FactoryBot.build(:jwt_secret, token: 'test_jwt_secret', user: user)
- patch :invalidate_jwt, params: { :id => user.id }
+ patch :invalidate_jwt, params: { :id => user.id }, session: set_session_user(User.current)
user.reload
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.create(:jwt_secret, token: 'test_jwt_secret', user: user)
+ patch :invalidate_jwt, params: { :id => user.id }, session: set_session_user(User.current)
+ user.reload
+ assert_response :success
+ 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)
- FactoryBot.build(:jwt_secret, token: 'test_jwt_secret', user: user)
- patch :invalidate_jwt, params: { :id => user.id }
+ user = users(:scoped)
+ FactoryBot.create(:jwt_secret, token: 'test_jwt_secret', user: user)
+ patch :invalidate_jwt, params: { :id => user.id }, session: set_session_user(User.current)
user.reload
assert_nil user.jwt_secret
end
@@ -179,8 +188,8 @@ class UsersControllerTest < ActionController::TestCase
test 'user without edit users permission should not be able to invalidate jwt for another user' do
User.current = users(:one)
user = users(:two)
- FactoryBot.build(:jwt_secret, token: 'test_jwt_secret', user: user)
- patch :invalidate_jwt, params: { :id => user.id }
+ FactoryBot.create(:jwt_secret, token: 'test_jwt_secret', user: user)
+ patch :invalidate_jwt, params: { :id => user.id }, session: set_session_user(User.current)
assert_response :forbidden
end
diff --git a/webpack/assets/javascripts/react_app/components/componentRegistry.js b/webpack/assets/javascripts/react_app/components/componentRegistry.js
index 5e8c9a509a7..2e5b8e66c4d 100644
--- a/webpack/assets/javascripts/react_app/components/componentRegistry.js
+++ b/webpack/assets/javascripts/react_app/components/componentRegistry.js
@@ -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', () => ({})),
@@ -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 },
{
diff --git a/webpack/assets/javascripts/react_app/components/users/JwtTokens/JwtTokens.js b/webpack/assets/javascripts/react_app/components/users/JwtTokens/JwtTokens.js
new file mode 100644
index 00000000000..1037e3b7e37
--- /dev/null
+++ b/webpack/assets/javascripts/react_app/components/users/JwtTokens/JwtTokens.js
@@ -0,0 +1,65 @@
+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 { openConfirmModal } from '../../ConfirmModal';
+import { APIActions } from '../../../redux/API';
+import { translate as __ } from '../../../common/I18n';
+
+const JwtTokens = ({ userId }) => {
+ const dispatch = useDispatch();
+ return (
+
+
+
+
+
+
+
+
+ {__('JWT Tokens')}
+
+ {__(
+ 'By invalidating your JSON Web Tokens (JWTs), you will no longer be able to register hosts by using your existing JWTs.'
+ )}
+
+
+ |
+
+
+
+
+ );
+};
+
+JwtTokens.propTypes = {
+ userId: PropTypes.string.isRequired,
+};
+
+export default JwtTokens;