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

Convert account screen to React/EUI #30977

Merged
merged 27 commits into from
Apr 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
34989bc
WIP account management redesign
legrego Feb 6, 2019
5c58c97
style updates
legrego Feb 6, 2019
970421d
start implementing change password logic
legrego Feb 7, 2019
6fd7007
restyle
legrego Feb 12, 2019
1397cef
remove api key management section
legrego Feb 12, 2019
bd5d965
improved change password validation
legrego Feb 12, 2019
6002e73
first round of design edits
legrego Feb 13, 2019
8c3b297
cleanup and testing
legrego Feb 13, 2019
bc7ada1
fix import
legrego Feb 14, 2019
9176a7b
fix translations
legrego Feb 14, 2019
5cf6324
Merge branch 'master' into security/reactify-account-screen
legrego Feb 15, 2019
aff0851
fix error handling on user management page
legrego Feb 15, 2019
5df3f96
consolidate password change logic
legrego Feb 15, 2019
b290ecb
fix tests
legrego Feb 15, 2019
08877ae
happy linter, happy life
legrego Feb 15, 2019
b02f489
Merge branch 'master' into security/reactify-account-screen
legrego Apr 2, 2019
962d363
finish change password test
legrego Apr 2, 2019
3ce6fa7
removes unused translations
legrego Apr 2, 2019
8a5e256
fix typo in test
legrego Apr 2, 2019
fff9fe4
fix change password functional test
legrego Apr 2, 2019
57f1ed4
Design edits (#19)
cchaos Apr 5, 2019
01b2da6
clear password form on success
legrego Apr 5, 2019
64629fe
Merge branch 'master' into security/reactify-account-screen
legrego Apr 8, 2019
fa06efc
copy edits
legrego Apr 8, 2019
670a28a
Merge branch 'master' into security/reactify-account-screen
legrego Apr 12, 2019
08b0ac5
fix handling of Change Password button
legrego Apr 15, 2019
a03183d
use encodeURIComponent for user supplied data
legrego Apr 15, 2019
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
1 change: 0 additions & 1 deletion x-pack/plugins/security/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@

export const GLOBAL_RESOURCE = '*';
export const IGNORED_TYPES = ['space'];
export const REALMS_ELIGIBLE_FOR_PASSWORD_CHANGE = ['reserved', 'native'];
export const APPLICATION_PREFIX = 'kibana-';
export const RESERVED_PRIVILEGES_APPLICATION_WILDCARD = 'kibana-*';
62 changes: 62 additions & 0 deletions x-pack/plugins/security/common/model/user.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { canUserChangePassword, getUserDisplayName, User } from './user';

describe('#getUserDisplayName', () => {
it(`uses the full name when available`, () => {
expect(
getUserDisplayName({
full_name: 'my full name',
username: 'foo',
} as User)
).toEqual('my full name');
});

it(`uses the username when full name is not available`, () => {
expect(
getUserDisplayName({
username: 'foo',
} as User)
).toEqual('foo');
});
});

describe('#canUserChangePassword', () => {
['reserved', 'native'].forEach(realm => {
it(`returns true for users in the ${realm} realm`, () => {
expect(
canUserChangePassword({
username: 'foo',
authentication_realm: {
name: 'the realm name',
type: realm,
},
} as User)
).toEqual(true);
});
});

it(`returns true when no realm is provided`, () => {
expect(
canUserChangePassword({
username: 'foo',
} as User)
).toEqual(true);
});

it(`returns false for all other realms`, () => {
expect(
canUserChangePassword({
username: 'foo',
authentication_realm: {
name: 'the realm name',
type: 'does not matter',
},
} as User)
).toEqual(false);
});
});
37 changes: 37 additions & 0 deletions x-pack/plugins/security/common/model/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export interface User {
username: string;
email: string;
full_name: string;
roles: string[];
enabled: boolean;
authentication_realm?: {
name: string;
type: string;
};
lookup_realm?: {
name: string;
type: string;
};
}

const REALMS_ELIGIBLE_FOR_PASSWORD_CHANGE = ['reserved', 'native'];

export function getUserDisplayName(user: User): string {
return user.full_name || user.username;
}

export function canUserChangePassword(user: User): boolean {
const { authentication_realm: authenticationRealm } = user;

if (!authenticationRealm) {
return true;
}

return REALMS_ELIGIBLE_FOR_PASSWORD_CHANGE.includes(authenticationRealm.type);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
jest.mock('../../../lib/api', () => {
return {
UserAPIClient: {
changePassword: jest.fn(),
},
};
});
import { EuiFieldText } from '@elastic/eui';
import { ReactWrapper } from 'enzyme';
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { User } from '../../../../common/model/user';
import { UserAPIClient } from '../../../lib/api';
import { ChangePasswordForm } from './change_password_form';

function getCurrentPasswordField(wrapper: ReactWrapper<any>) {
return wrapper.find(EuiFieldText).filter('[data-test-subj="currentPassword"]');
}

function getNewPasswordField(wrapper: ReactWrapper<any>) {
return wrapper.find(EuiFieldText).filter('[data-test-subj="newPassword"]');
}

function getConfirmPasswordField(wrapper: ReactWrapper<any>) {
return wrapper.find(EuiFieldText).filter('[data-test-subj="confirmNewPassword"]');
}

describe('<ChangePasswordForm>', () => {
describe('for the current user', () => {
it('shows fields for current and new passwords', () => {
const user: User = {
username: 'user',
full_name: 'john smith',
email: '[email protected]',
enabled: true,
roles: [],
};

const wrapper = mountWithIntl(
<ChangePasswordForm user={user} isUserChangingOwnPassword={true} />
);

expect(getCurrentPasswordField(wrapper)).toHaveLength(1);
expect(getNewPasswordField(wrapper)).toHaveLength(1);
expect(getConfirmPasswordField(wrapper)).toHaveLength(1);
});

it('allows a password to be changed', () => {
const user: User = {
username: 'user',
full_name: 'john smith',
email: '[email protected]',
enabled: true,
roles: [],
};

const callback = jest.fn();

const wrapper = mountWithIntl(
<ChangePasswordForm
user={user}
isUserChangingOwnPassword={true}
onChangePassword={callback}
/>
);

const currentPassword = getCurrentPasswordField(wrapper);
currentPassword.props().onChange!({ target: { value: 'myCurrentPassword' } } as any);

const newPassword = getNewPasswordField(wrapper);
newPassword.props().onChange!({ target: { value: 'myNewPassword' } } as any);

const confirmPassword = getConfirmPasswordField(wrapper);
confirmPassword.props().onChange!({ target: { value: 'myNewPassword' } } as any);

wrapper.find('button[data-test-subj="changePasswordButton"]').simulate('click');

expect(UserAPIClient.changePassword).toHaveBeenCalledTimes(1);
expect(UserAPIClient.changePassword).toHaveBeenCalledWith(
'user',
'myNewPassword',
'myCurrentPassword'
);
});
});

describe('for another user', () => {
it('shows fields for new password only', () => {
const user: User = {
username: 'user',
full_name: 'john smith',
email: '[email protected]',
enabled: true,
roles: [],
};

const wrapper = mountWithIntl(
<ChangePasswordForm user={user} isUserChangingOwnPassword={false} />
);

expect(getCurrentPasswordField(wrapper)).toHaveLength(0);
expect(getNewPasswordField(wrapper)).toHaveLength(1);
expect(getConfirmPasswordField(wrapper)).toHaveLength(1);
});
});
});
Loading