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

Pull in rhecosystem changes (need FEO support) #1635

Merged
merged 78 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
61316d1
Modified the user page to add activate user or deactivate user feature
yzhao583 Jun 1, 2023
e206f71
Merge pull request #1 from yzhao583/RHBKAAS-65-Modify-the-user-page-o…
yzhao583 Jun 1, 2023
52ebfda
use is_active to active or deactive user
yzhao583 Jun 1, 2023
d000e8d
Added the invite users modal without api call
yzhao583 Jun 6, 2023
b6b8620
Modified the activate/deactivate user feature, and added the invite u…
yzhao583 Jun 9, 2023
81b2c44
Modified the invite user modal to fetch users after invitation works
yzhao583 Jun 10, 2023
24c0128
remove orgid from body when call add users api
yzhao583 Jun 12, 2023
c4d5c73
remove unnecessary import
yzhao583 Jun 12, 2023
9669252
Merge pull request #2 from yzhao583/RHBKAAS-70-Modify-the-RBAC-consol…
yzhao583 Jun 12, 2023
d9ea4c2
fix conflict
yzhao583 Jun 12, 2023
69a182f
move from react router v4 to v6
yzhao583 Jun 13, 2023
e56b362
added route for invite user modal
yzhao583 Jun 13, 2023
f4b6508
fetch token from local storage
yzhao583 Jun 13, 2023
40ac903
Change the way of getting token
yzhao583 Jun 14, 2023
b5809d4
remove no reffer header
yzhao583 Jun 14, 2023
3cfdb41
Merge pull request #3 from yzhao583/RHBKAAS-89-fix-cors-issue
yzhao583 Jun 16, 2023
f6dbacb
Merge remote-tracking branch 'upstream/master'
yzhao583 Jun 16, 2023
d990296
Remove the description of the users page for FEDRAMP.
yzhao583 Jun 16, 2023
bcc60f2
Merge pull request #4 from yzhao583/RHBKAAS-91-remove-reference-to-UGC
yzhao583 Jun 16, 2023
34bf8d4
Added a switch to allow user to change user is_org_admin status
yzhao583 Jun 19, 2023
3ed7c06
small fix
yzhao583 Jun 19, 2023
7e4e65c
modified the change user admin status switch to use dropdown component
yzhao583 Jun 21, 2023
c7bfc35
Merge pull request #5 from yzhao583/RHBKAAS-92-Add-switch-to-change-t…
yzhao583 Jun 21, 2023
0ecff6f
Merge remote-tracking branch 'upstream/master'
yzhao583 Jun 21, 2023
43052a5
change id to external_source_id
yzhao583 Jun 21, 2023
d756e8d
fix issue for non-admin user
yzhao583 Jun 23, 2023
50ee39b
Merge pull request #6 from yzhao583/RHBKAAS-79-Hide-pages-when-user-i…
yzhao583 Jun 23, 2023
748ff13
add ability to avoid current user to deactivate themselves
yzhao583 Jun 30, 2023
6fe0896
Merge pull request #7 from RHEcosystemAppEng/RHBKAAS-139-add-ability-…
yzhao583 Jun 30, 2023
b777686
Small fix
yzhao583 Jun 30, 2023
0f37dd2
Merge pull request #8 from RHEcosystemAppEng/RHBKAAS-139-add-ability-…
yzhao583 Jun 30, 2023
28273b5
Disable checkbox for the current user
yzhao583 Jun 30, 2023
9cf7713
check user for external-idp
tchughesiv Jun 30, 2023
6902b4e
Merge pull request #9 from tchughesiv/RHBKAAS-135
yzhao583 Jun 30, 2023
23d478d
modified the page to avoid the selection for the current logged in us…
yzhao583 Jul 5, 2023
301d713
Merge branch 'master' into RHBKAAS-139-add-ability-to-avoid-current-u…
yzhao583 Jul 5, 2023
0e2909f
Merge pull request #10 from RHEcosystemAppEng/RHBKAAS-139-add-ability…
yzhao583 Jul 5, 2023
3408ac5
Merge remote-tracking branch 'upstream/master'
yzhao583 Jul 5, 2023
b061a29
Merge pull request #11 from RHEcosystemAppEng/sync-upstream
yzhao583 Jul 5, 2023
a0a49ce
Fixed import issue
yzhao583 Jul 5, 2023
de0d39b
Merge pull request #12 from RHEcosystemAppEng/fix-issue-on-add-group-…
yzhao583 Jul 5, 2023
f56feb7
Modified description text on add group page
yzhao583 Jul 5, 2023
120dad5
Merge pull request #13 from RHEcosystemAppEng/Modify-text-on-the-add-…
yzhao583 Jul 5, 2023
831b6ba
throw error msg if the response code is 206 when invite user
yzhao583 Jul 17, 2023
1e55cc6
Merge pull request #14 from RHEcosystemAppEng/RHBKAAS-126-add-handlin…
yzhao583 Jul 17, 2023
195e8de
Display warming instead of error when partial inviting users succeed
yzhao583 Jul 19, 2023
6122c28
Merge pull request #15 from RHEcosystemAppEng/RHBKAAS-126-add-handlin…
yzhao583 Jul 19, 2023
def82e1
Add api url based on env
aneelac22 Jul 20, 2023
dab080a
Update scr env api url
aneelac22 Jul 26, 2023
c82db49
add a modal to allow admin to confirm when deactivate users
yzhao583 Jul 28, 2023
a41a530
Merge pull request #17 from RHEcosystemAppEng/RHBKAAS-297-add-a-modal…
yzhao583 Jul 28, 2023
1a2d0cf
Merge pull request #16 from aneelac22/feature/env-config
yzhao583 Aug 3, 2023
88b29df
FIxed add users to group issue
yzhao583 Aug 4, 2023
576310c
Merge pull request #18 from RHEcosystemAppEng/Fix-add-users-to-group-…
yzhao583 Aug 4, 2023
73a0cf1
Fix user access admin view issue
yzhao583 Aug 4, 2023
a6c1e97
Merge pull request #19 from RHEcosystemAppEng/Fix-add-users-to-group-…
yzhao583 Aug 4, 2023
e23081d
hide checkbox for non-admin user who has user admin access permission
yzhao583 Aug 10, 2023
dbedb1e
Merge pull request #20 from RHEcosystemAppEng/Hide-checkbox-for-non-a…
yzhao583 Aug 10, 2023
72660d9
Fixed selectable issue for user table
yzhao583 Aug 10, 2023
3116539
Merge pull request #21 from RHEcosystemAppEng/Hide-checkbox-for-non-a…
yzhao583 Aug 10, 2023
11b6b71
Updates for stage env
aneelac22 Nov 21, 2023
14212d8
Merge pull request #22 from RHEcosystemAppEng/stage-updates
aneelac22 Nov 21, 2023
943f4f1
Update prod env variables
aneelac22 Nov 27, 2023
ec79448
Merge pull request #23 from RHEcosystemAppEng/prod-updates
aneelac22 Nov 27, 2023
75ff5b9
update build command
aneelac22 Nov 27, 2023
3a496ef
Merge pull request #24 from RHEcosystemAppEng/prod-updates
aneelac22 Nov 27, 2023
2ea365b
Add beta build alias
aneelac22 Nov 27, 2023
7632c43
Merge pull request #26 from RHEcosystemAppEng/prod-updates
aneelac22 Nov 27, 2023
80e48dd
Fix getBaseUrl not working
aneelac22 Nov 28, 2023
153bd5f
Merge pull request #28 from RHEcosystemAppEng/prod-sso
aneelac22 Nov 28, 2023
c3db149
Fix typo in get api url
aneelac22 Nov 28, 2023
2caddd0
Merge pull request #30 from RHEcosystemAppEng/prod-sso
aneelac22 Nov 28, 2023
66b9f80
fix user-helper url bug
aneelac22 Nov 29, 2023
42bf310
Merge pull request #31 from RHEcosystemAppEng/prod-itless
aneelac22 Nov 29, 2023
a4974bf
update build commands
aneelac22 Jan 15, 2024
663a88b
Merge pull request #33 from RHEcosystemAppEng/build-updates
aneelac22 Jan 15, 2024
b6f0e35
Merge remote-tracking branch 'rhecosystem/master' into pull-in-rhecos…
florkbr Jul 17, 2024
d354f56
Code review comments for invite users logic
florkbr Jul 22, 2024
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
8 changes: 8 additions & 0 deletions config/setupTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,11 @@ global.matchMedia =
};

Object.assign(global, { TextDecoder, TextEncoder });

// TODO several tests do not wrap components in a flag provider and assume this returns false
jest.mock('@unleash/proxy-client-react', () => {
return {
__esModule: true,
useFlag: () => false,
};
});
107 changes: 107 additions & 0 deletions src/Messages.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,93 @@
import { defineMessages } from 'react-intl';

export default defineMessages({
inviteUsers: {
id: 'inviteUsers',
description: 'Invite users',
defaultMessage: 'Invite users',
},
inviteUsersTitle: {
id: 'inviteUsersTitle',
description: 'Invite users modal title',
defaultMessage: 'Invite New Users',
},
inviteUsersDescription: {
id: 'inviteUsersDescription',
description: 'Invite users modal description',
defaultMessage:
'Invite users to create a Red Hat login with your organization. Your e-mail address will be included in the invite as a point of contact.',
},
inviteUsersFormIsAdminFieldTitle: {
id: 'inviteUsersFormIsAdminFieldTitle',
description: 'Invite users form is admin field title',
defaultMessage: 'Organization Administrators',
},
inviteUsersFormIsAdminFieldDescription: {
id: 'inviteUsersFormIsAdminFieldDescription',
description: 'Invite users form is admin field description',
defaultMessage:
'The organization administrator role is the highest permission level with full access to content and features. This is the only role that can manage users.',
},
inviteUsersFormEmailsFieldTitle: {
id: 'inviteUsersFormEmailsFieldTitle',
description: 'Invite users form emails field title',
defaultMessage: 'Enter the e-mail addresses of the users you would like to invite',
},
inviteUsersFormEmailsFieldDescription: {
id: 'inviteUsersFormEmailsFieldDescription',
description: 'Invite users form emails field description',
defaultMessage: 'Enter up to 50 e-mail addresses separated by commas or returns.',
},
inviteUsersCancelled: {
id: 'inviteUsersCancelled',
description: 'Invite users cancelled notification description',
defaultMessage: 'Invite users process was canceled by the user.',
},
inviteUsersButton: {
id: 'inviteUsersButton',
description: 'Invite users button text',
defaultMessage: 'Invite new users',
},
inviteUsersErrorTitle: {
id: 'inviteUsersErrorTitle',
description: 'Invite users error notification title',
defaultMessage: 'Failed inviting all users',
},
inviteUsersErrorDescription: {
id: 'inviteUsersErrorDescription',
description: 'Invite users error notification description',
defaultMessage: 'Failed inviting users.',
},
activateUsersButton: {
id: 'activateUsersButton',
description: 'activate users button text',
defaultMessage: 'Activate users',
},
deactivateUsersButton: {
id: 'deactivateUsersButton',
description: 'deactivate users button text',
defaultMessage: 'Deactivate users',
},
deactivateUsersConfirmationModalTitle: {
id: 'deactivateUsersConfirmationModalTitle',
description: 'deactivate users confirmation modal title text',
defaultMessage: 'Deactivate users',
},
deactivateUsersConfirmationModalDescription: {
id: 'deactivateUsersConfirmationModalDescription',
description: 'deactivate users confirmation modal description text',
defaultMessage: 'Are you sure you want to deactivate the user(s) below from your Red Hat organization?',
},
deactivateUsersConfirmationModalCheckboxText: {
id: 'deactivateUsersConfirmationModalCheckboxText',
description: 'deactivate users confirmation modal checkbox text',
defaultMessage: 'Yes, I confirm that I want to remove these users',
},
deactivateUsersConfirmationButton: {
id: 'deactivateUsersConfirmationButton',
description: 'deactivate users confirmation button text',
defaultMessage: 'Deactivate user(s)',
},
notApplicable: {
id: 'notApplicable',
description: 'Not applicable text for resource definitions',
Expand Down Expand Up @@ -222,6 +309,26 @@ export default defineMessages({
description: 'Edit group error notification description',
defaultMessage: 'The group was not updated successfuly.',
},
editUserSuccessTitle: {
id: 'editUserSuccessTitle',
description: 'Edit user success notification title',
defaultMessage: 'Success updating user',
},
editUserSuccessDescription: {
id: 'editUserSuccessDescription',
description: 'Edit user success notification description',
defaultMessage: 'The user was updated successfully.',
},
editUserErrorTitle: {
id: 'editUserErrorTitle',
description: 'Edit user error notification title',
defaultMessage: 'Failed updating user',
},
editUserErrorDescription: {
id: 'editUserErrorDescription',
description: 'Edit user error notification description',
defaultMessage: 'The user was not updated successfuly.',
},
removeGroupSuccess: {
id: 'removeGroupSuccess',
description: 'Remove group success notification title',
Expand Down
11 changes: 8 additions & 3 deletions src/Routing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const Overview = lazy(() => import('./smart-components/overview/overview'));
const Users = lazy(() => import('./smart-components/user/users'));
const UserDetail = lazy(() => import('./smart-components/user/user'));
const AddUserToGroup = lazy(() => import('./smart-components/user/add-user-to-group/add-user-to-group'));
const InviteUsersModal = lazy(() => import('./smart-components/user/invite-users/invite-users-modal'));

const Roles = lazy(() => import('./smart-components/role/roles'));
const Role = lazy(() => import('./smart-components/role/role'));
Expand All @@ -37,7 +38,7 @@ const AddGroupServiceAccounts = lazy(() => import('./smart-components/group/serv
const RemoveServiceAccountFromGroup = lazy(() => import('./smart-components/group/service-account/remove-group-service-accounts'));
const QuickstartsTest = lazy(() => import('./smart-components/quickstarts/quickstarts-test'));

const getRoutes = ({ enableServiceAccounts }: Record<string, boolean>) => [
const getRoutes = ({ enableServiceAccounts, isITLess }: Record<string, boolean>) => [
{
path: pathnames.overview.path,
element: Overview,
Expand All @@ -60,7 +61,10 @@ const getRoutes = ({ enableServiceAccounts }: Record<string, boolean>) => [
path: pathnames.users.path,
element: Users,
},

isITLess && {
path: pathnames['invite-users'].path,
element: InviteUsersModal,
},
{
path: pathnames['role-detail'].path,
element: Role,
Expand Down Expand Up @@ -233,6 +237,7 @@ const renderRoutes = (routes: RouteType[] = []) =>
const Routing = () => {
const location = useLocation();
const { updateDocumentTitle, isBeta } = useChrome();
const isITLess = useFlag('platform.rbac.itless');
const enableServiceAccounts =
(isBeta() && useFlag('platform.rbac.group-service-accounts')) || (!isBeta() && useFlag('platform.rbac.group-service-accounts.stable'));

Expand All @@ -252,7 +257,7 @@ const Routing = () => {
}
}, [location.pathname, updateDocumentTitle]);

const routes = getRoutes({ enableServiceAccounts });
const routes = getRoutes({ enableServiceAccounts, isITLess });
const renderedRoutes = useMemo(() => renderRoutes(routes as never), [routes]);
return (
<Suspense fallback={<AppPlaceholder />}>
Expand Down
16 changes: 16 additions & 0 deletions src/helpers/shared/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,19 @@ export const getDateFormat = (date) => {
const monthAgo = new Date(Date.now());
return Date.parse(date) < monthAgo.setMonth(monthAgo.getMonth() - 1) ? 'onlyDate' : 'relative';
};

export const isExternalIdp = (token = '') => {
let roles = [''];
let tokenArray = token.split('.');
if (tokenArray.length > 1) {
let token1 = window.atob(tokenArray[1]);
if (token1) {
roles = JSON.parse(token1)?.realm_access?.roles;
if (roles.includes('external-idp')) {
return true;
}
}
}

return false;
};
112 changes: 112 additions & 0 deletions src/helpers/user/user-helper.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getLastPageOffset, isOffsetValid } from '../shared/pagination';
import { getPrincipalApi } from '../shared/user-login';
import { isInt, isStage, isITLessProd } from '../../itLessConfig';

const principalApi = getPrincipalApi();

Expand All @@ -8,6 +9,117 @@ const principalStatusApiMap = {
Inactive: 'disabled',
All: 'all',
};

const getBaseUrl = (url) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Note. We should be feeding it from chrome service and configure a env variable in the OC namespace. @florkbr can you create a ticket for this?

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 (isInt) {
return url.int;
} else if (isStage) {
return url.stage;
} else if (isITLessProd) {
return url.prod;
} else {
return '';
}
};

async function fetchBaseUrl() {
try {
// TODO move to env var defined in cluster surfaced through chrome service
const response = await fetch('/apps/rbac/env.json');
const jsonData = await response.json();
florkbr marked this conversation as resolved.
Show resolved Hide resolved
return jsonData;
} catch (error) {
console.log(error);
}
}

const headers = {
'Content-Type': 'application/json',
Accept: 'application/json',
};

function handleResponse(response, resolve, reject) {
if (response.ok && response.status !== 206) {
resolve(response);
} else if (response.ok && response.status === 206) {
response.json().then((body) => {
reject(body);
});
} else {
reject(response);
}
}

function handleError(error, reject) {
reject(new Error(error.message));
}

export async function addUsers(usersData = { emails: [], isAdmin: undefined }) {
const requestOpts = {
method: 'POST',
headers,
body: JSON.stringify({
emails: usersData.emails,
isAdmin: usersData.isAdmin,
}),
};
const url = await fetchBaseUrl();
const baseUrl = getBaseUrl(url);
let promise = new Promise((resolve, reject) => {
return fetch(`${baseUrl}/user/invite`, requestOpts)
Hyperkid123 marked this conversation as resolved.
Show resolved Hide resolved
.then(
(response) => handleResponse(response, resolve, reject),
(error) => handleError(error, reject)
)
.catch((error) => handleError(error, reject));
});

return promise;
}

export async function updateUserIsOrgAdminStatus(user) {
let requestOpts = {
method: 'PUT',
headers,
};

const url = await fetchBaseUrl();
const baseUrl = getBaseUrl(url);

let promise = new Promise((resolve, reject) => {
return fetch(`${baseUrl}/user/${user.id}/admin/${user.is_org_admin}`, requestOpts)
.then(
(response) => handleResponse(response, resolve, reject),
(error) => handleError(error, reject)
)
.catch((error) => handleError(error, reject));
});

return promise;
}

export async function updateUsers(users) {
let requestOpts = {
method: 'PUT',
headers,
body: JSON.stringify({ users: users }),
};

const url = await fetchBaseUrl();
const baseUrl = getBaseUrl(url);

let promise = new Promise((resolve, reject) => {
return fetch(`${baseUrl}/change-users-status`, requestOpts)
.then(
(response) => handleResponse(response, resolve, reject),
(error) => handleError(error, reject)
)
.catch((error) => handleError(error, reject));
});

return promise;
}

export async function fetchUsers({ limit, offset = 0, orderBy, filters = {}, usesMetaInURL, matchCriteria = 'partial' }) {
const { username, email, status = [] } = filters;
const sortOrder = orderBy === '-username' ? 'desc' : 'asc';
Expand Down
4 changes: 4 additions & 0 deletions src/itLessConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const isEphem = insights.chrome.getEnvironment() === 'ephem';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a bit different from our chrome.isFedramp helper as we will use this to lookup the correct invite API base URL (from a static file in each env).

export const isInt = insights.chrome.getEnvironment() === 'int';
export const isStage = insights.chrome.getEnvironment() === 'frhStage';
export const isITLessProd = insights.chrome.getEnvironment() === 'frh';
4 changes: 3 additions & 1 deletion src/presentational-components/shared/ActiveUsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import PermissionsContext from '../../utilities/permissions-context';
import messages from '../../Messages';
import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome';
import { useFlag } from '@unleash/proxy-client-react';

const ActiveUser = ({ linkDescription, linkTitle }) => {
const intl = useIntl();
const chrome = useChrome();
const env = chrome.getEnvironment();
const prefix = chrome.isProd() ? '' : `${env}.`;
const { orgAdmin } = useContext(PermissionsContext);
return orgAdmin ? (
const isITLess = useFlag('platform.rbac.itless');
return !isITLess && orgAdmin ? (
<Text className="pf-v5-u-mt-0" component={TextVariants.h7}>
{`${intl.formatMessage(messages.usersDescription)} `}
{linkDescription}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { Fragment } from 'react';
import { useIntl } from 'react-intl';
import propTypes from 'prop-types';
import messages from '../../Messages';
import { useFlag } from '@unleash/proxy-client-react';
import { TableVariant } from '@patternfly/react-table';
import { Table, TableHeader, TableBody } from '@patternfly/react-table/deprecated';
import TableToolbar from '@redhat-cloud-services/frontend-components/TableToolbar';
Expand Down Expand Up @@ -59,8 +60,10 @@ export const TableToolbarView = ({
tableId,
containerRef,
textFilterRef,
toolbarChildren,
}) => {
const intl = useIntl();
const isITLess = useFlag('platform.rbac.itless');
const renderEmpty = () => ({
title: (
<EmptyWithAction
Expand Down Expand Up @@ -130,6 +133,7 @@ export const TableToolbarView = ({
tableId={tableId}
containerRef={containerRef}
textFilterRef={textFilterRef}
toolbarChildren={toolbarChildren}
/>
{isLoading ? (
<SkeletonTable
Expand All @@ -146,7 +150,7 @@ export const TableToolbarView = ({
{...(isSelectable &&
rows?.length > 0 && {
onSelect: (_e, isSelected, _idx, { uuid, cells: [name], requires }) =>
setCheckedItems(selectedRows([{ uuid, name, requires }], isSelected)),
setCheckedItems(selectedRows([{ uuid, name, requires, ...(isITLess && { username: data[_idx]?.username }) }], isSelected)),
})}
{...(isExpandable && { onExpand })}
rows={rows?.length > 0 ? rows : [{ fullWidth: true, cells: [renderEmpty()] }]}
Expand Down Expand Up @@ -234,6 +238,7 @@ TableToolbarView.propTypes = {
noDataDescription: propTypes.arrayOf(propTypes.node),
filters: propTypes.array,
tableId: propTypes.string.isRequired,
toolbarChildren: propTypes.func,
};

TableToolbarView.defaultProps = {
Expand All @@ -245,4 +250,5 @@ TableToolbarView.defaultProps = {
hideFilterChips: false,
checkedRows: [],
hideHeader: false,
toolbarChildren: () => null,
};
Loading