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

OCPBUGS-29914: [release-4.15] Use selfsubjectreview API from frontend #13636

Merged
merged 5 commits into from Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 50 additions & 0 deletions frontend/packages/console-app/console-extensions.json
Expand Up @@ -87,6 +87,47 @@
}
}
},
{
"type": "console.flag/model",
"properties": {
"flag": "OPENSHIFT_OAUTH_API",
"model": {
"group": "config.openshift.io",
"version": "v1",
"kind": "OAuth"
}
}
},
{
"type": "console.global-config",
"properties": {
"id": "oauth-config",
"name": "oauth-config",
"model": {
"group": "config.openshift.io",
"version": "v1",
"kind": "OAuth"
},
"namespace": ""
},
"flags": {
"required": ["OPENSHIFT_OAUTH_API"]
}
},
{
"type": "console.page/resource/details",
"properties": {
"model": {
"group": "config.openshift.io",
"version": "v1",
"kind": "OAuth"
},
"component": { "$codeRef": "oauthConfigDetailsPage.default" }
},
"flags": {
"required": ["OPENSHIFT_OAUTH_API"]
}
},
{
"type": "console.context-provider",
"properties": {
Expand Down Expand Up @@ -279,6 +320,15 @@
}
},

{
"type": "console.page/route",
"properties": {
"exact": true,
"path": ["/cluster-configuration", "/cluster-configuration/:group"],
"component": { "$codeRef": "clusterConfiguration.ClusterConfigurationPage" }
}
},

{
"type": "console.flag/hookProvider",
"properties": {
Expand Down
18 changes: 18 additions & 0 deletions frontend/packages/console-app/locales/en/console-app.json
Expand Up @@ -431,6 +431,24 @@
"Scheduling disabled": "Scheduling disabled",
"No new Pods or workloads will be placed on this Node until it's marked as schedulable.": "No new Pods or workloads will be placed on this Node until it's marked as schedulable.",
"Mark as schedulable": "Mark as schedulable",
"Identity providers": "Identity providers",
"Mapping method": "Mapping method",
"Basic Authentication": "Basic Authentication",
"GitHub": "GitHub",
"GitLab": "GitLab",
"Google": "Google",
"HTPasswd": "HTPasswd",
"Keystone": "Keystone",
"LDAP": "LDAP",
"OpenID Connect": "OpenID Connect",
"Request Header": "Request Header",
"OAuth details": "OAuth details",
"Access token max age": "Access token max age",
"Identity providers determine how users log into the cluster.": "Identity providers determine how users log into the cluster.",
"New identity provider added.": "New identity provider added.",
"Authentication is being reconfigured. The new identity provider will be available once reconfiguration is complete.": "Authentication is being reconfigured. The new identity provider will be available once reconfiguration is complete.",
"View authentication conditions for reconfiguration status.": "View authentication conditions for reconfiguration status.",
"Add": "Add",
"Min available {{minAvailable}} of {{count}} pod_one": "Min available {{minAvailable}} of {{count}} pod",
"Min available {{minAvailable}} of {{count}} pod_other": "Min available {{minAvailable}} of {{count}} pods",
"Max unavailable {{maxUnavailable}} of {{count}} pod_one": "Max unavailable {{maxUnavailable}} of {{count}} pod",
Expand Down
5 changes: 4 additions & 1 deletion frontend/packages/console-app/package.json
Expand Up @@ -46,7 +46,10 @@
"perspectiveConfiguration": "src/components/detect-perspective/PerspectiveConfiguration.tsx",
"dynamicPluginsHealthResource": "src/components/dashboards-page/dynamic-plugins-health-resource",
"storageProvisioners": "src/components/storage/StorageClassProviders",
"storageProvisionerDocs": "src/components/storage/Documentation"
"storageProvisionerDocs": "src/components/storage/Documentation",
"nodeStatus": "src/components/nodes/status",
"nodeActions": "src/components/nodes/menu-actions.tsx",
"oauthConfigDetailsPage": "src/components/oauth-config/OAuthConfigDetailsPage.tsx"
}
}
}
@@ -0,0 +1,43 @@
import * as React from 'react';
import * as _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { EmptyBox } from '@console/internal/components/utils';
import { IdentityProvider } from '@console/internal/module/k8s';

export const IdentityProviders: React.FC<IdentityProvidersProps> = ({ identityProviders }) => {
const { t } = useTranslation();
return _.isEmpty(identityProviders) ? (
<EmptyBox label={t('console-app~Identity providers')} />
) : (
<div className="co-table-container">
<table className="pf-v5-c-table pf-m-compact pf-m-border-rows">
<thead className="pf-v5-c-table__thead">
<tr className="pf-v5-c-table__tr">
<th className="pf-v5-c-table__th">{t('console-app~Name')}</th>
<th className="pf-v5-c-table__th">{t('console-app~Type')}</th>
<th className="pf-v5-c-table__th">{t('console-app~Mapping method')}</th>
</tr>
</thead>
<tbody className="pf-v5-c-table__tbody">
{_.map(identityProviders, (idp) => (
<tr className="pf-v5-c-table__tr" key={idp.name}>
<td className="pf-v5-c-table__td" data-test-idp-name={idp.name}>
{idp.name}
</td>
<td className="pf-v5-c-table__td" data-test-idp-type-for={idp.name}>
{idp.type}
</td>
<td className="pf-v5-c-table__td" data-test-idp-mapping-for={idp.name}>
{idp.mappingMethod || 'claim'}
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};

type IdentityProvidersProps = {
identityProviders: IdentityProvider[];
};
@@ -0,0 +1,141 @@
import * as React from 'react';
import { formatPrometheusDuration } from '@openshift-console/plugin-shared/src/datetime/prometheus';
import { Alert } from '@patternfly/react-core';
import {
Dropdown as DropdownDeprecated,
DropdownItem as DropdownItemDeprecated,
DropdownToggle as DropdownToggleDeprecated,
} from '@patternfly/react-core/deprecated';
import { CaretDownIcon } from '@patternfly/react-icons/dist/esm/icons/caret-down-icon';
import * as _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { Link, useNavigate } from 'react-router-dom-v5-compat';
import {
resourcePathFromModel,
ResourceSummary,
SectionHeading,
} from '@console/internal/components/utils';
import { ClusterOperatorModel } from '@console/internal/models';
import { OAuthKind } from '@console/internal/module/k8s';
import { IDP_TYPES } from '@console/shared/src/constants/auth';
import { useQueryParams } from '@console/shared/src/hooks/useQueryParams';
import { IdentityProviders } from './IdentityProviders';

// Convert to ms for formatPrometheusDuration
const tokenDuration = (seconds: number) =>
_.isNil(seconds) ? '-' : formatPrometheusDuration(seconds * 1000);

export const OAuthConfigDetails: React.FC<OAuthDetailsProps> = ({ obj }: { obj: OAuthKind }) => {
const navigate = useNavigate();
const [isIDPOpen, setIDPOpen] = React.useState(false);
const { identityProviders, tokenConfig } = obj.spec;
const { t } = useTranslation();
const queryParams = useQueryParams();
const idpAdded = queryParams.get('idpAdded');

const getAddIDPItemLabels = (type: string) => {
switch (type) {
case 'Basic Authentication':
return t('console-app~Basic Authentication');
case 'GitHub':
return t('console-app~GitHub');
case 'GitLab':
return t('console-app~GitLab');
case 'Google':
return t('console-app~Google');
case 'HTPasswd':
return t('console-app~HTPasswd');
case 'Keystone':
return t('console-app~Keystone');
case 'LDAP':
return t('console-app~LDAP');
case 'OpenID Connect':
return t('console-app~OpenID Connect');
case 'Request Header':
return t('console-app~Request Header');
default:
return type;
}
};

const IDPDropdownItems = Object.entries(IDP_TYPES).map((idp) => {
const [key, value] = idp;

return (
<DropdownItemDeprecated
key={`idp-${key}`}
component="button"
id={key}
data-test-id={key}
onClick={(e) => navigate(`/settings/idp/${e.currentTarget.id}`)}
>
{getAddIDPItemLabels(value)}
</DropdownItemDeprecated>
);
});

return (
<>
<div className="co-m-pane__body">
<SectionHeading text={t('console-app~OAuth details')} />
<div className="row">
<div className="col-md-6">
<ResourceSummary resource={obj}>
{tokenConfig && (
<>
<dt>{t('console-app~Access token max age')}</dt>
<dd>{tokenDuration(tokenConfig.accessTokenMaxAgeSeconds)}</dd>
</>
)}
</ResourceSummary>
</div>
</div>
</div>
<div className="co-m-pane__body">
<SectionHeading text={t('console-app~Identity providers')} />
<p className="co-m-pane__explanation co-m-pane__explanation--alt">
{t('console-app~Identity providers determine how users log into the cluster.')}
</p>
{idpAdded === 'true' && (
<Alert
isInline
className="co-alert"
variant="info"
title={t('console-app~New identity provider added.')}
>
<>
{t(
'console-app~Authentication is being reconfigured. The new identity provider will be available once reconfiguration is complete.',
)}{' '}
<Link to={resourcePathFromModel(ClusterOperatorModel, 'authentication')}>
{t('console-app~View authentication conditions for reconfiguration status.')}
</Link>
</>
</Alert>
)}
<DropdownDeprecated
className="co-m-pane__dropdown"
toggle={
<DropdownToggleDeprecated
id="idp-dropdown"
onToggle={() => setIDPOpen(!isIDPOpen)}
toggleIndicator={CaretDownIcon}
data-test-id="dropdown-button"
>
{t('console-app~Add')}
</DropdownToggleDeprecated>
}
isOpen={isIDPOpen}
dropdownItems={IDPDropdownItems}
onSelect={() => setIDPOpen(false)}
id="idp"
/>
<IdentityProviders identityProviders={identityProviders} />
</div>
</>
);
};

type OAuthDetailsProps = {
obj: OAuthKind;
};
@@ -0,0 +1,21 @@
import * as React from 'react';
import { DetailsPage } from '@console/internal/components/factory';
import { Kebab, navFactory } from '@console/internal/components/utils';
import { OAuthModel } from '@console/internal/models';
import { referenceForModel } from '@console/internal/module/k8s';
import { OAuthConfigDetails } from './OAuthConfigDetails';

const { common } = Kebab.factory;
const menuActions = [...Kebab.getExtensionsActionsForKind(OAuthModel), ...common];
const oAuthReference = referenceForModel(OAuthModel);

const OAuthConfigDetailsPage: React.FC<React.ComponentProps<typeof DetailsPage>> = (props) => (
<DetailsPage
{...props}
kind={oAuthReference}
menuActions={menuActions}
pages={[navFactory.details(OAuthConfigDetails), navFactory.editYaml()]}
/>
);

export default OAuthConfigDetailsPage;
@@ -1,5 +1,5 @@
import { action, ActionType as Action } from 'typesafe-actions';
import { UserKind } from '../../redux-types';
import { UserInfo } from '../../../extensions';

export enum ActionType {
SetUser = 'setUser',
Expand All @@ -8,7 +8,7 @@ export enum ActionType {
SetActiveCluster = 'setActiveCluster',
}

export const setUser = (user: UserKind) => action(ActionType.SetUser, { user });
export const setUser = (userInfo: UserInfo) => action(ActionType.SetUser, { userInfo });
export const beginImpersonate = (kind: string, name: string, subprotocols: string[]) =>
action(ActionType.BeginImpersonate, { kind, name, subprotocols });
export const endImpersonate = () => action(ActionType.EndImpersonate);
Expand Down
Expand Up @@ -8,12 +8,7 @@ describe('Core Reducer', () => {

it('set user', () => {
const mockUser = {
apiVersion: 'user.openshift.io/v1',
kind: 'User',
identities: [],
metadata: {
name: 'kube:admin',
},
username: 'kube:admin',
};
reducerTest(coreReducer, state, setUser(mockUser)).expectVal({
user: mockUser,
Expand Down
Expand Up @@ -10,10 +10,7 @@ import { ActionType, CoreAction } from '../actions/core';
* @see CoreAction
* @returns The the updated state.
*/
export const coreReducer = (
state: CoreState = { user: { identities: [] } },
action: CoreAction,
): CoreState => {
export const coreReducer = (state: CoreState = { user: {} }, action: CoreAction): CoreState => {
switch (action.type) {
case ActionType.BeginImpersonate:
return {
Expand Down Expand Up @@ -41,7 +38,7 @@ export const coreReducer = (
case ActionType.SetUser:
return {
...state,
user: action.payload.user,
user: action.payload.userInfo,
};

default:
Expand Down
@@ -1,7 +1,8 @@
import { ImpersonateKind, SDKStoreState, UserKind } from '../../redux-types';
import { UserInfo } from '../../../extensions';
import { ImpersonateKind, SDKStoreState } from '../../redux-types';

type GetImpersonate = (state: SDKStoreState) => ImpersonateKind;
type GetUser = (state: SDKStoreState) => UserKind;
type GetUser = (state: SDKStoreState) => UserInfo;

/**
* It provides impersonation details from the redux store.
Expand Down
@@ -1,21 +1,16 @@
import { Map as ImmutableMap } from 'immutable';
import { K8sResourceCommon } from '../extensions/console-types';
import { UserInfo } from '../extensions/console-types';

export type K8sState = ImmutableMap<string, any>;

export type UserKind = {
fullName?: string;
identities: string[];
} & K8sResourceCommon;

export type ImpersonateKind = {
kind: string;
name: string;
subprotocols: string[];
};

export type CoreState = {
user?: UserKind;
user?: UserInfo;
impersonate?: ImpersonateKind;
};

Expand Down