diff --git a/x-pack/plugins/cloud/public/plugin.test.ts b/x-pack/plugins/cloud/public/plugin.test.ts index f7d71e7e1219a7..2215fbd64fe982 100644 --- a/x-pack/plugins/cloud/public/plugin.test.ts +++ b/x-pack/plugins/cloud/public/plugin.test.ts @@ -434,7 +434,7 @@ describe('Cloud Plugin', () => { const securityStart = securityMock.createStart(); securityStart.authc.getCurrentUser.mockResolvedValue( securityMock.createMockAuthenticatedUser({ - roles: ['superuser'], + elastic_cloud_user: true, }) ); @@ -446,14 +446,15 @@ describe('Cloud Plugin', () => { expect(securityStart.authc.getCurrentUser).not.toHaveBeenCalled(); }); - it('registers a custom nav link for superusers', async () => { + it('registers a custom nav link for cloud users', async () => { const { plugin } = startPlugin(); const coreStart = coreMock.createStart(); const securityStart = securityMock.createStart(); + securityStart.authc.getCurrentUser.mockResolvedValue( securityMock.createMockAuthenticatedUser({ - roles: ['superuser'], + elastic_cloud_user: true, }) ); plugin.start(coreStart, { security: securityStart }); @@ -494,14 +495,14 @@ describe('Cloud Plugin', () => { `); }); - it('does not register a custom nav link for non-superusers', async () => { + it('does not register a custom nav link for non-cloud users', async () => { const { plugin } = startPlugin(); const coreStart = coreMock.createStart(); const securityStart = securityMock.createStart(); securityStart.authc.getCurrentUser.mockResolvedValue( securityMock.createMockAuthenticatedUser({ - roles: ['not-a-superuser'], + elastic_cloud_user: false, }) ); plugin.start(coreStart, { security: securityStart }); @@ -511,14 +512,14 @@ describe('Cloud Plugin', () => { expect(coreStart.chrome.setCustomNavLink).not.toHaveBeenCalled(); }); - it('registers user profile links for superusers', async () => { + it('registers user profile links for cloud users', async () => { const { plugin } = startPlugin(); const coreStart = coreMock.createStart(); const securityStart = securityMock.createStart(); securityStart.authc.getCurrentUser.mockResolvedValue( securityMock.createMockAuthenticatedUser({ - roles: ['superuser'], + elastic_cloud_user: true, }) ); plugin.start(coreStart, { security: securityStart }); @@ -532,7 +533,7 @@ describe('Cloud Plugin', () => { Object { "href": "https://cloud.elastic.co/profile/alice", "iconType": "user", - "label": "Profile", + "label": "Edit profile", "order": 100, "setAsProfile": true, }, @@ -564,7 +565,7 @@ describe('Cloud Plugin', () => { Object { "href": "https://cloud.elastic.co/profile/alice", "iconType": "user", - "label": "Profile", + "label": "Edit profile", "order": 100, "setAsProfile": true, }, @@ -579,14 +580,14 @@ describe('Cloud Plugin', () => { `); }); - it('does not register profile links for non-superusers', async () => { + it('does not register profile links for non-cloud users', async () => { const { plugin } = startPlugin(); const coreStart = coreMock.createStart(); const securityStart = securityMock.createStart(); securityStart.authc.getCurrentUser.mockResolvedValue( securityMock.createMockAuthenticatedUser({ - roles: ['not-a-superuser'], + elastic_cloud_user: false, }) ); plugin.start(coreStart, { security: securityStart }); diff --git a/x-pack/plugins/cloud/public/plugin.tsx b/x-pack/plugins/cloud/public/plugin.tsx index 10c7b7165528e8..195e359436e28f 100644 --- a/x-pack/plugins/cloud/public/plugin.tsx +++ b/x-pack/plugins/cloud/public/plugin.tsx @@ -212,10 +212,12 @@ export class CloudPlugin implements Plugin { } // Security plugin is disabled if (!security) return true; - // Otherwise check roles. If user is not defined due to an unexpected error, then fail *open*. + + // Otherwise check if user is a cloud user. + // If user is not defined due to an unexpected error, then fail *open*. // Cloud admin console will always perform the actual authorization checks. const user = await security.authc.getCurrentUser().catch(() => null); - return user?.roles.includes('superuser') ?? true; + return user?.elastic_cloud_user ?? true; } /** diff --git a/x-pack/plugins/cloud/public/user_menu_links.ts b/x-pack/plugins/cloud/public/user_menu_links.ts index 55c41831cc18cc..e29736e215e0d5 100644 --- a/x-pack/plugins/cloud/public/user_menu_links.ts +++ b/x-pack/plugins/cloud/public/user_menu_links.ts @@ -17,7 +17,7 @@ export const createUserMenuLinks = (config: CloudConfigType): UserMenuLink[] => if (baseUrl && profileUrl) { userMenuLinks.push({ label: i18n.translate('xpack.cloud.userMenuLinks.profileLinkText', { - defaultMessage: 'Profile', + defaultMessage: 'Edit profile', }), iconType: 'user', href: getFullCloudUrl(baseUrl, profileUrl), diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx index c0c90c6421ab26..fdaeeed66fb632 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx @@ -6,6 +6,7 @@ */ import { act, renderHook } from '@testing-library/react-hooks'; +import { mount } from 'enzyme'; import type { FunctionComponent } from 'react'; import React from 'react'; @@ -14,10 +15,11 @@ import { coreMock, scopedHistoryMock, themeServiceMock } from '@kbn/core/public/ import { UserProfileAPIClient } from '..'; import type { UserData } from '../../../common'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; +import { UserAvatar } from '../../components'; import { UserAPIClient } from '../../management'; import { securityMock } from '../../mocks'; import { Providers } from '../account_management_app'; -import { useUserProfileForm } from './user_profile'; +import { UserProfile, useUserProfileForm } from './user_profile'; const user = mockAuthenticatedUser(); const coreStart = coreMock.createStart(); @@ -181,4 +183,52 @@ describe('useUserProfileForm', () => { expect(result.current.initialValues.user.full_name).toEqual('Another Name'); }); + + describe('User Avatar Form', () => { + it('should display if the User is not a cloud user', () => { + const data: UserData = {}; + + const nonCloudUser = mockAuthenticatedUser({ elastic_cloud_user: false }); + + const testWrapper = mount( + + + + ); + + expect(testWrapper.exists(UserAvatar)).toBeTruthy(); + }); + + it('should not display if the User is a cloud user', () => { + const data: UserData = {}; + + const cloudUser = mockAuthenticatedUser({ elastic_cloud_user: true }); + + const testWrapper = mount( + + + + ); + + expect(testWrapper.exists(UserAvatar)).toBeFalsy(); + }); + }); }); diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx index cc42f10da39b93..bc95fae5b1c68e 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx @@ -439,6 +439,8 @@ export const UserProfile: FunctionComponent = ({ user, data }) const canChangeDetails = canUserChangeDetails(user, services.application.capabilities); + const isCloudUser = user.elastic_cloud_user; + const rightSideItems = [ { title: ( @@ -559,7 +561,7 @@ export const UserProfile: FunctionComponent = ({ user, data }) >
- + {isCloudUser ? null : } setShowChangePasswordForm(true)} diff --git a/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx b/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx index 7b44fb08933620..298f71d01dd883 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_component.test.tsx @@ -173,10 +173,10 @@ describe('SecurityNavControl', () => { expect(wrapper.prop('isOpen')).toEqual(false); }); - it('should render additional user menu links registered by other plugins', async () => { + it('should render additional user menu links registered by other plugins and should render the default Edit Profile link as the first link when no custom profile link is provided', async () => { const wrapper = shallow( { "items": Array [ Object { "data-test-subj": "profileLink", - "href": "", + "href": "edit-profile-link", "icon": , "name": , "onClick": [Function], }, @@ -258,10 +254,10 @@ describe('SecurityNavControl', () => { `); }); - it('should render custom profile link registered by other plugins', async () => { + it('should render custom profile link registered by other plugins and not render default Edit Profile link', async () => { const wrapper = shallow( { />, "name": "link3", }, - Object { - "data-test-subj": "profileLink", - "href": "", - "icon": , - "name": , - "onClick": [Function], - }, Object { "data-test-subj": "logoutLink", "href": "", diff --git a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx index 2f462aaba4bc65..74d29fbb9f87bb 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx @@ -79,7 +79,6 @@ export const SecurityNavControl: FunctionComponent = ({ ); - const isAnonymous = currentUser.value ? isUserAnonymous(currentUser.value) : false; const items: EuiContextMenuPanelItemDescriptor[] = []; if (userMenuLinks.length) { const userMenuLinkMenuItems = userMenuLinks @@ -93,17 +92,18 @@ export const SecurityNavControl: FunctionComponent = ({ items.push(...userMenuLinkMenuItems); } - if (!isAnonymous) { - const hasCustomProfileLinks = userMenuLinks.some(({ setAsProfile }) => setAsProfile === true); + const isAnonymous = currentUser.value ? isUserAnonymous(currentUser.value) : false; + const hasCustomProfileLinks = userMenuLinks.some(({ setAsProfile }) => setAsProfile === true); + + if (!isAnonymous && !hasCustomProfileLinks) { const profileMenuItem: EuiContextMenuPanelItemDescriptor = { name: ( ), - icon: , + icon: , href: editProfileUrl, onClick: () => { setIsPopoverOpen(false); @@ -112,11 +112,7 @@ export const SecurityNavControl: FunctionComponent = ({ }; // Set this as the first link if there is no user-defined profile link - if (!hasCustomProfileLinks) { - items.unshift(profileMenuItem); - } else { - items.push(profileMenuItem); - } + items.unshift(profileMenuItem); } items.push({ diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index c2b568b87528ba..1ca0b1259d5881 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -24155,7 +24155,6 @@ "xpack.security.management.users.usersTitle": "Utilisateurs", "xpack.security.management.usersTitle": "Utilisateurs", "xpack.security.navControlComponent.accountMenuAriaLabel": "Menu Compte", - "xpack.security.navControlComponent.editProfileLinkText": "{profileOverridden, select, true{Préférences} other{Profil}}", "xpack.security.navControlComponent.loginLinkText": "Connexion", "xpack.security.navControlComponent.logoutLinkText": "Déconnexion", "xpack.security.overwrittenSession.continueAsUserText": "Continuer en tant que {username}", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 0ddcc7feee2d0e..b31e9da323327e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24140,7 +24140,6 @@ "xpack.security.management.users.usersTitle": "ユーザー", "xpack.security.management.usersTitle": "ユーザー", "xpack.security.navControlComponent.accountMenuAriaLabel": "アカウントメニュー", - "xpack.security.navControlComponent.editProfileLinkText": "{profileOverridden, select, true{設定} other{プロファイル}}", "xpack.security.navControlComponent.loginLinkText": "ログイン", "xpack.security.navControlComponent.logoutLinkText": "ログアウト", "xpack.security.overwrittenSession.continueAsUserText": "{username} として続行", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5ac4f914cbfe97..60e2ed23125554 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -24166,7 +24166,6 @@ "xpack.security.management.users.usersTitle": "用户", "xpack.security.management.usersTitle": "用户", "xpack.security.navControlComponent.accountMenuAriaLabel": "帐户菜单", - "xpack.security.navControlComponent.editProfileLinkText": "{profileOverridden, select, true{首选项} other{配置文件}}", "xpack.security.navControlComponent.loginLinkText": "登录", "xpack.security.navControlComponent.logoutLinkText": "注销", "xpack.security.overwrittenSession.continueAsUserText": "作为 {username} 继续",