mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Adding check to show/hide Avatar form based on whether the user is a … (#135743)
* Adding check to show/hide Avatar form based on whether the user is a cloud user or not * Adding tests to verify the avatar doesnt show up in the UserProfile if the AuthorizedUser is a cloud user * Changing the name of the link to 'Edit Profile' and making it available only for non-cloud users * Adding/Updating unit tests and fixing translation files * Removing unused values from FormattedText and related tests * Updating unit test to work with merge from main * Updating link to read Edit Profile to match wireframes per PR review * Reverting changes to translation files and changing the cloud Edit Profile link and unit tests * Changing capitalization of 'profile' so it follows the naming convention * Changing nav menu logic to only render the default Edit Profile link if there is no custom Edit Profile link passed in by another plugin * Updating logic to display custom nav links if user is a cloud user and related unit tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
46697d5ecd
commit
2f732ae0c3
10 changed files with 85 additions and 59 deletions
|
@ -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 });
|
||||
|
|
|
@ -212,10 +212,12 @@ export class CloudPlugin implements Plugin<CloudSetup> {
|
|||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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(
|
||||
<Providers
|
||||
services={coreStart}
|
||||
theme$={theme$}
|
||||
history={history}
|
||||
authc={authc}
|
||||
securityApiClients={{
|
||||
userProfiles: new UserProfileAPIClient(coreStart.http),
|
||||
users: new UserAPIClient(coreStart.http),
|
||||
}}
|
||||
>
|
||||
<UserProfile user={nonCloudUser} data={data} />
|
||||
</Providers>
|
||||
);
|
||||
|
||||
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(
|
||||
<Providers
|
||||
services={coreStart}
|
||||
theme$={theme$}
|
||||
history={history}
|
||||
authc={authc}
|
||||
securityApiClients={{
|
||||
userProfiles: new UserProfileAPIClient(coreStart.http),
|
||||
users: new UserAPIClient(coreStart.http),
|
||||
}}
|
||||
>
|
||||
<UserProfile user={cloudUser} data={data} />
|
||||
</Providers>
|
||||
);
|
||||
|
||||
expect(testWrapper.exists(UserAvatar)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -439,6 +439,8 @@ export const UserProfile: FunctionComponent<UserProfileProps> = ({ 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<UserProfileProps> = ({ user, data })
|
|||
>
|
||||
<Form aria-labelledby={titleId}>
|
||||
<UserDetailsEditor user={user} />
|
||||
<UserAvatarEditor user={user} formik={formik} />
|
||||
{isCloudUser ? null : <UserAvatarEditor user={user} formik={formik} />}
|
||||
<UserPasswordEditor
|
||||
user={user}
|
||||
onShowPasswordForm={() => setShowChangePasswordForm(true)}
|
||||
|
|
|
@ -173,10 +173,10 @@ describe('SecurityNavControl', () => {
|
|||
expect(wrapper.prop<boolean>('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(
|
||||
<SecurityNavControl
|
||||
editProfileUrl=""
|
||||
editProfileUrl="edit-profile-link"
|
||||
logoutUrl=""
|
||||
userMenuLinks$={
|
||||
new BehaviorSubject([
|
||||
|
@ -195,19 +195,15 @@ describe('SecurityNavControl', () => {
|
|||
"items": Array [
|
||||
Object {
|
||||
"data-test-subj": "profileLink",
|
||||
"href": "",
|
||||
"href": "edit-profile-link",
|
||||
"icon": <EuiIcon
|
||||
size="m"
|
||||
type="user"
|
||||
/>,
|
||||
"name": <FormattedMessage
|
||||
defaultMessage="{profileOverridden, select, true{Preferences} other{Profile}}"
|
||||
defaultMessage="Edit profile"
|
||||
id="xpack.security.navControlComponent.editProfileLinkText"
|
||||
values={
|
||||
Object {
|
||||
"profileOverridden": false,
|
||||
}
|
||||
}
|
||||
values={Object {}}
|
||||
/>,
|
||||
"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(
|
||||
<SecurityNavControl
|
||||
editProfileUrl=""
|
||||
editProfileUrl="edit-profile-link"
|
||||
logoutUrl=""
|
||||
userMenuLinks$={
|
||||
new BehaviorSubject([
|
||||
|
@ -311,24 +307,6 @@ describe('SecurityNavControl', () => {
|
|||
/>,
|
||||
"name": "link3",
|
||||
},
|
||||
Object {
|
||||
"data-test-subj": "profileLink",
|
||||
"href": "",
|
||||
"icon": <EuiIcon
|
||||
size="m"
|
||||
type="controlsHorizontal"
|
||||
/>,
|
||||
"name": <FormattedMessage
|
||||
defaultMessage="{profileOverridden, select, true{Preferences} other{Profile}}"
|
||||
id="xpack.security.navControlComponent.editProfileLinkText"
|
||||
values={
|
||||
Object {
|
||||
"profileOverridden": true,
|
||||
}
|
||||
}
|
||||
/>,
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-test-subj": "logoutLink",
|
||||
"href": "",
|
||||
|
|
|
@ -79,7 +79,6 @@ export const SecurityNavControl: FunctionComponent<SecurityNavControlProps> = ({
|
|||
</EuiHeaderSectionItemButton>
|
||||
);
|
||||
|
||||
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<SecurityNavControlProps> = ({
|
|||
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: (
|
||||
<FormattedMessage
|
||||
id="xpack.security.navControlComponent.editProfileLinkText"
|
||||
defaultMessage="{profileOverridden, select, true{Preferences} other{Profile}}"
|
||||
values={{ profileOverridden: hasCustomProfileLinks }}
|
||||
defaultMessage="Edit profile"
|
||||
/>
|
||||
),
|
||||
icon: <EuiIcon type={hasCustomProfileLinks ? 'controlsHorizontal' : 'user'} size="m" />,
|
||||
icon: <EuiIcon type="user" size="m" />,
|
||||
href: editProfileUrl,
|
||||
onClick: () => {
|
||||
setIsPopoverOpen(false);
|
||||
|
@ -112,11 +112,7 @@ export const SecurityNavControl: FunctionComponent<SecurityNavControlProps> = ({
|
|||
};
|
||||
|
||||
// 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({
|
||||
|
|
|
@ -24135,7 +24135,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}",
|
||||
|
|
|
@ -24120,7 +24120,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} として続行",
|
||||
|
|
|
@ -24146,7 +24146,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} 继续",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue