Skip to content

Commit b86f733

Browse files
Merge pull request #13605 from jhadvig/standa_selfsubjects_frontend
OCPBUGS-29701: Use selfsubjectreview API from frontend
2 parents 05b4fc2 + ce45035 commit b86f733

33 files changed

Lines changed: 515 additions & 311 deletions

File tree

frontend/packages/console-app/console-extensions.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,47 @@
8787
}
8888
}
8989
},
90+
{
91+
"type": "console.flag/model",
92+
"properties": {
93+
"flag": "OPENSHIFT_OAUTH_API",
94+
"model": {
95+
"group": "config.openshift.io",
96+
"version": "v1",
97+
"kind": "OAuth"
98+
}
99+
}
100+
},
101+
{
102+
"type": "console.global-config",
103+
"properties": {
104+
"id": "oauth-config",
105+
"name": "oauth-config",
106+
"model": {
107+
"group": "config.openshift.io",
108+
"version": "v1",
109+
"kind": "OAuth"
110+
},
111+
"namespace": ""
112+
},
113+
"flags": {
114+
"required": ["OPENSHIFT_OAUTH_API"]
115+
}
116+
},
117+
{
118+
"type": "console.page/resource/details",
119+
"properties": {
120+
"model": {
121+
"group": "config.openshift.io",
122+
"version": "v1",
123+
"kind": "OAuth"
124+
},
125+
"component": { "$codeRef": "oauthConfigDetailsPage.default" }
126+
},
127+
"flags": {
128+
"required": ["OPENSHIFT_OAUTH_API"]
129+
}
130+
},
90131
{
91132
"type": "console.context-provider",
92133
"properties": {
@@ -279,6 +320,15 @@
279320
}
280321
},
281322

323+
{
324+
"type": "console.page/route",
325+
"properties": {
326+
"exact": true,
327+
"path": ["/cluster-configuration", "/cluster-configuration/:group"],
328+
"component": { "$codeRef": "clusterConfiguration.ClusterConfigurationPage" }
329+
}
330+
},
331+
282332
{
283333
"type": "console.flag/hookProvider",
284334
"properties": {

frontend/packages/console-app/locales/en/console-app.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,24 @@
439439
"Certificate approval required": "Certificate approval required",
440440
"An error occurred. Please try again": "An error occurred. Please try again",
441441
"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.",
442+
"Identity providers": "Identity providers",
443+
"Mapping method": "Mapping method",
444+
"Basic Authentication": "Basic Authentication",
445+
"GitHub": "GitHub",
446+
"GitLab": "GitLab",
447+
"Google": "Google",
448+
"HTPasswd": "HTPasswd",
449+
"Keystone": "Keystone",
450+
"LDAP": "LDAP",
451+
"OpenID Connect": "OpenID Connect",
452+
"Request Header": "Request Header",
453+
"OAuth details": "OAuth details",
454+
"Access token max age": "Access token max age",
455+
"Identity providers determine how users log into the cluster.": "Identity providers determine how users log into the cluster.",
456+
"New identity provider added.": "New identity provider added.",
457+
"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.",
458+
"View authentication conditions for reconfiguration status.": "View authentication conditions for reconfiguration status.",
459+
"Add": "Add",
442460
"Min available {{minAvailable}} of {{count}} pod_one": "Min available {{minAvailable}} of {{count}} pod",
443461
"Min available {{minAvailable}} of {{count}} pod_other": "Min available {{minAvailable}} of {{count}} pods",
444462
"Max unavailable {{maxUnavailable}} of {{count}} pod_one": "Max unavailable {{maxUnavailable}} of {{count}} pod",

frontend/packages/console-app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"storageProvisioners": "src/components/storage/StorageClassProviders",
4949
"storageProvisionerDocs": "src/components/storage/Documentation",
5050
"nodeStatus": "src/components/nodes/status",
51-
"nodeActions": "src/components/nodes/menu-actions.tsx"
51+
"nodeActions": "src/components/nodes/menu-actions.tsx",
52+
"oauthConfigDetailsPage": "src/components/oauth-config/OAuthConfigDetailsPage.tsx"
5253
}
5354
}
5455
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as React from 'react';
2+
import * as _ from 'lodash';
3+
import { useTranslation } from 'react-i18next';
4+
import { EmptyBox } from '@console/internal/components/utils';
5+
import { IdentityProvider } from '@console/internal/module/k8s';
6+
7+
export const IdentityProviders: React.FC<IdentityProvidersProps> = ({ identityProviders }) => {
8+
const { t } = useTranslation();
9+
return _.isEmpty(identityProviders) ? (
10+
<EmptyBox label={t('console-app~Identity providers')} />
11+
) : (
12+
<div className="co-table-container">
13+
<table className="pf-v5-c-table pf-m-compact pf-m-border-rows">
14+
<thead className="pf-v5-c-table__thead">
15+
<tr className="pf-v5-c-table__tr">
16+
<th className="pf-v5-c-table__th">{t('console-app~Name')}</th>
17+
<th className="pf-v5-c-table__th">{t('console-app~Type')}</th>
18+
<th className="pf-v5-c-table__th">{t('console-app~Mapping method')}</th>
19+
</tr>
20+
</thead>
21+
<tbody className="pf-v5-c-table__tbody">
22+
{_.map(identityProviders, (idp) => (
23+
<tr className="pf-v5-c-table__tr" key={idp.name}>
24+
<td className="pf-v5-c-table__td" data-test-idp-name={idp.name}>
25+
{idp.name}
26+
</td>
27+
<td className="pf-v5-c-table__td" data-test-idp-type-for={idp.name}>
28+
{idp.type}
29+
</td>
30+
<td className="pf-v5-c-table__td" data-test-idp-mapping-for={idp.name}>
31+
{idp.mappingMethod || 'claim'}
32+
</td>
33+
</tr>
34+
))}
35+
</tbody>
36+
</table>
37+
</div>
38+
);
39+
};
40+
41+
type IdentityProvidersProps = {
42+
identityProviders: IdentityProvider[];
43+
};
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import * as React from 'react';
2+
import { formatPrometheusDuration } from '@openshift-console/plugin-shared/src/datetime/prometheus';
3+
import { Alert } from '@patternfly/react-core';
4+
import {
5+
Dropdown as DropdownDeprecated,
6+
DropdownItem as DropdownItemDeprecated,
7+
DropdownToggle as DropdownToggleDeprecated,
8+
} from '@patternfly/react-core/deprecated';
9+
import { CaretDownIcon } from '@patternfly/react-icons/dist/esm/icons/caret-down-icon';
10+
import * as _ from 'lodash';
11+
import { useTranslation } from 'react-i18next';
12+
import { Link, useNavigate } from 'react-router-dom-v5-compat';
13+
import {
14+
resourcePathFromModel,
15+
ResourceSummary,
16+
SectionHeading,
17+
} from '@console/internal/components/utils';
18+
import { ClusterOperatorModel } from '@console/internal/models';
19+
import { OAuthKind } from '@console/internal/module/k8s';
20+
import { IDP_TYPES } from '@console/shared/src/constants/auth';
21+
import { useQueryParams } from '@console/shared/src/hooks/useQueryParams';
22+
import { IdentityProviders } from './IdentityProviders';
23+
24+
// Convert to ms for formatPrometheusDuration
25+
const tokenDuration = (seconds: number) =>
26+
_.isNil(seconds) ? '-' : formatPrometheusDuration(seconds * 1000);
27+
28+
export const OAuthConfigDetails: React.FC<OAuthDetailsProps> = ({ obj }: { obj: OAuthKind }) => {
29+
const navigate = useNavigate();
30+
const [isIDPOpen, setIDPOpen] = React.useState(false);
31+
const { identityProviders, tokenConfig } = obj.spec;
32+
const { t } = useTranslation();
33+
const queryParams = useQueryParams();
34+
const idpAdded = queryParams.get('idpAdded');
35+
36+
const getAddIDPItemLabels = (type: string) => {
37+
switch (type) {
38+
case 'Basic Authentication':
39+
return t('console-app~Basic Authentication');
40+
case 'GitHub':
41+
return t('console-app~GitHub');
42+
case 'GitLab':
43+
return t('console-app~GitLab');
44+
case 'Google':
45+
return t('console-app~Google');
46+
case 'HTPasswd':
47+
return t('console-app~HTPasswd');
48+
case 'Keystone':
49+
return t('console-app~Keystone');
50+
case 'LDAP':
51+
return t('console-app~LDAP');
52+
case 'OpenID Connect':
53+
return t('console-app~OpenID Connect');
54+
case 'Request Header':
55+
return t('console-app~Request Header');
56+
default:
57+
return type;
58+
}
59+
};
60+
61+
const IDPDropdownItems = Object.entries(IDP_TYPES).map((idp) => {
62+
const [key, value] = idp;
63+
64+
return (
65+
<DropdownItemDeprecated
66+
key={`idp-${key}`}
67+
component="button"
68+
id={key}
69+
data-test-id={key}
70+
onClick={(e) => navigate(`/settings/idp/${e.currentTarget.id}`)}
71+
>
72+
{getAddIDPItemLabels(value)}
73+
</DropdownItemDeprecated>
74+
);
75+
});
76+
77+
return (
78+
<>
79+
<div className="co-m-pane__body">
80+
<SectionHeading text={t('console-app~OAuth details')} />
81+
<div className="row">
82+
<div className="col-md-6">
83+
<ResourceSummary resource={obj}>
84+
{tokenConfig && (
85+
<>
86+
<dt>{t('console-app~Access token max age')}</dt>
87+
<dd>{tokenDuration(tokenConfig.accessTokenMaxAgeSeconds)}</dd>
88+
</>
89+
)}
90+
</ResourceSummary>
91+
</div>
92+
</div>
93+
</div>
94+
<div className="co-m-pane__body">
95+
<SectionHeading text={t('console-app~Identity providers')} />
96+
<p className="co-m-pane__explanation co-m-pane__explanation--alt">
97+
{t('console-app~Identity providers determine how users log into the cluster.')}
98+
</p>
99+
{idpAdded === 'true' && (
100+
<Alert
101+
isInline
102+
className="co-alert"
103+
variant="info"
104+
title={t('console-app~New identity provider added.')}
105+
>
106+
<>
107+
{t(
108+
'console-app~Authentication is being reconfigured. The new identity provider will be available once reconfiguration is complete.',
109+
)}{' '}
110+
<Link to={resourcePathFromModel(ClusterOperatorModel, 'authentication')}>
111+
{t('console-app~View authentication conditions for reconfiguration status.')}
112+
</Link>
113+
</>
114+
</Alert>
115+
)}
116+
<DropdownDeprecated
117+
className="co-m-pane__dropdown"
118+
toggle={
119+
<DropdownToggleDeprecated
120+
id="idp-dropdown"
121+
onToggle={() => setIDPOpen(!isIDPOpen)}
122+
toggleIndicator={CaretDownIcon}
123+
data-test-id="dropdown-button"
124+
>
125+
{t('console-app~Add')}
126+
</DropdownToggleDeprecated>
127+
}
128+
isOpen={isIDPOpen}
129+
dropdownItems={IDPDropdownItems}
130+
onSelect={() => setIDPOpen(false)}
131+
id="idp"
132+
/>
133+
<IdentityProviders identityProviders={identityProviders} />
134+
</div>
135+
</>
136+
);
137+
};
138+
139+
type OAuthDetailsProps = {
140+
obj: OAuthKind;
141+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as React from 'react';
2+
import { DetailsPage } from '@console/internal/components/factory';
3+
import { Kebab, navFactory } from '@console/internal/components/utils';
4+
import { OAuthModel } from '@console/internal/models';
5+
import { referenceForModel } from '@console/internal/module/k8s';
6+
import { OAuthConfigDetails } from './OAuthConfigDetails';
7+
8+
const { common } = Kebab.factory;
9+
const menuActions = [...Kebab.getExtensionsActionsForKind(OAuthModel), ...common];
10+
const oAuthReference = referenceForModel(OAuthModel);
11+
12+
const OAuthConfigDetailsPage: React.FC<React.ComponentProps<typeof DetailsPage>> = (props) => (
13+
<DetailsPage
14+
{...props}
15+
kind={oAuthReference}
16+
menuActions={menuActions}
17+
pages={[navFactory.details(OAuthConfigDetails), navFactory.editYaml()]}
18+
/>
19+
);
20+
21+
export default OAuthConfigDetailsPage;

frontend/packages/console-dynamic-plugin-sdk/src/app/core/actions/core.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { action, ActionType as Action } from 'typesafe-actions';
2-
import { UserKind } from '../../redux-types';
2+
import { UserInfo } from '../../../extensions';
33

44
export enum ActionType {
55
SetUser = 'setUser',
@@ -8,7 +8,7 @@ export enum ActionType {
88
SetActiveCluster = 'setActiveCluster',
99
}
1010

11-
export const setUser = (user: UserKind) => action(ActionType.SetUser, { user });
11+
export const setUser = (userInfo: UserInfo) => action(ActionType.SetUser, { userInfo });
1212
export const beginImpersonate = (kind: string, name: string, subprotocols: string[]) =>
1313
action(ActionType.BeginImpersonate, { kind, name, subprotocols });
1414
export const endImpersonate = () => action(ActionType.EndImpersonate);

frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/__tests__/core.spec.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,7 @@ describe('Core Reducer', () => {
88

99
it('set user', () => {
1010
const mockUser = {
11-
apiVersion: 'user.openshift.io/v1',
12-
kind: 'User',
13-
identities: [],
14-
metadata: {
15-
name: 'kube:admin',
16-
},
11+
username: 'kube:admin',
1712
};
1813
reducerTest(coreReducer, state, setUser(mockUser)).expectVal({
1914
user: mockUser,

frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/core.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ import { ActionType, CoreAction } from '../actions/core';
1010
* @see CoreAction
1111
* @returns The the updated state.
1212
*/
13-
export const coreReducer = (
14-
state: CoreState = { user: { identities: [] } },
15-
action: CoreAction,
16-
): CoreState => {
13+
export const coreReducer = (state: CoreState = { user: {} }, action: CoreAction): CoreState => {
1714
switch (action.type) {
1815
case ActionType.BeginImpersonate:
1916
return {
@@ -41,7 +38,7 @@ export const coreReducer = (
4138
case ActionType.SetUser:
4239
return {
4340
...state,
44-
user: action.payload.user,
41+
user: action.payload.userInfo,
4542
};
4643

4744
default:

frontend/packages/console-dynamic-plugin-sdk/src/app/core/reducers/coreSelectors.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { ImpersonateKind, SDKStoreState, UserKind } from '../../redux-types';
1+
import { UserInfo } from '../../../extensions';
2+
import { ImpersonateKind, SDKStoreState } from '../../redux-types';
23

34
type GetImpersonate = (state: SDKStoreState) => ImpersonateKind;
4-
type GetUser = (state: SDKStoreState) => UserKind;
5+
type GetUser = (state: SDKStoreState) => UserInfo;
56

67
/**
78
* It provides impersonation details from the redux store.

0 commit comments

Comments
 (0)