Fix access for IDP links tab. (#29428)
* Fix access for IDP links tab. Signed-off-by: Stan Silvert <ssilvert@redhat.com> * Fix tests. Signed-off-by: Stan Silvert <ssilvert@redhat.com> --------- Signed-off-by: Stan Silvert <ssilvert@redhat.com>
This commit is contained in:
parent
b4d231fd40
commit
f14f4805d6
4 changed files with 116 additions and 93 deletions
|
@ -60,7 +60,7 @@ export default class IdentityProviderLinksTab {
|
|||
public assertNoIdentityProvidersLinkedMessageExist(exist: boolean) {
|
||||
cy.get(this.#linkedProvidersSection).should(
|
||||
(exist ? "" : "not.") + "contain.text",
|
||||
"No identity providers linked. Choose one from the list below.",
|
||||
"No identity providers linked.",
|
||||
);
|
||||
|
||||
return this;
|
||||
|
|
|
@ -2105,7 +2105,7 @@ duplicateEmailsHelpText=Allow multiple users to have the same email address. Cha
|
|||
importOverwritten_zero=No records overwritten.
|
||||
usermodel.realmRoleMapping.rolePrefix.label=Realm Role prefix
|
||||
eventTypes.GRANT_CONSENT.name=Grant consent
|
||||
noProvidersLinked=No identity providers linked. Choose one from the list below.
|
||||
noProvidersLinked=No identity providers linked.
|
||||
testConnectionSuccess=Success\! SMTP connection successful. E-mail was sent\!
|
||||
samlSettings=SAML settings
|
||||
userFedDisableConfirm=If you disable this user federation provider, it will not be considered for queries and imported users will be disabled and read-only until the provider is enabled again.
|
||||
|
@ -2852,7 +2852,7 @@ syncModeOverrideHelp=Overrides the default sync mode of the IDP for this mapper.
|
|||
eventTypes.TOKEN_EXCHANGE_ERROR.description=Token exchange error
|
||||
strictTransportSecurityHelp=The Strict-Transport-Security HTTP header tells browsers to always use HTTPS. Once a browser sees this header, it will only visit the site over HTTPS for the time specified (1 year) at max-age, including the subdomains. <1>Learn more</1>
|
||||
authenticationExplain=Authentication is the area where you can configure and manage different credential types.
|
||||
passwordPoliciesHelp.hashIterations=The number of times a password is hashed before storage or verification. Default\: -1 in case argon2 is used as the hashing algorithm; 210,000 in case pbkdf2-sha512 is used as the hashing algorithm; 600,000 if the pbkdf2-sha256 algorithm is used as the hashing algorithm; 1,300,000 if the pbkdf2 algorithm is used as the hashing algorithm.
|
||||
passwordPoliciesHelp.hashIterations=The number of times a password is hashed before storage or verification. Default\: -1 in case argon2 is used as the hashing algorithm; 210,000 in case pbkdf2-sha512 is used as the hashing algorithm; 600,000 if the pbkdf2-sha256 algorithm is used as the hashing algorithm; 1,300,000 if the pbkdf2 algorithm is used as the hashing algorithm.
|
||||
dropNonexistingGroupsDuringSync=Drop non-existing groups during sync
|
||||
clientAssertionSigningAlgHelp=Signature algorithm to create JWT assertion as client authentication. In the case of JWT signed with private key or JWT signed with client secret, it is required. If no algorithm is specified, the following algorithm is adapted. RS256 is adapted in the case of JWT signed with private key. HS256 is adapted in the case of JWT signed with client secret.
|
||||
jwtX509HeadersEnabledHelp=If enabled, the x5t (X.509 Certificate SHA-1 Thumbprint) header will be added to the JWT to reference the certificate used to sign it. Otherwise, the kid (Key ID) header will be used instead.
|
||||
|
|
|
@ -368,17 +368,15 @@ export default function EditUser() {
|
|||
>
|
||||
<UserConsents />
|
||||
</Tab>
|
||||
{hasAccess("view-identity-providers") && (
|
||||
<Tab
|
||||
data-testid="identity-provider-links-tab"
|
||||
title={
|
||||
<TabTitleText>{t("identityProviderLinks")}</TabTitleText>
|
||||
}
|
||||
{...identityProviderLinksTab}
|
||||
>
|
||||
<UserIdentityProviderLinks userId={user.id!} />
|
||||
</Tab>
|
||||
)}
|
||||
<Tab
|
||||
data-testid="identity-provider-links-tab"
|
||||
title={
|
||||
<TabTitleText>{t("identityProviderLinks")}</TabTitleText>
|
||||
}
|
||||
{...identityProviderLinksTab}
|
||||
>
|
||||
<UserIdentityProviderLinks userId={user.id!} />
|
||||
</Tab>
|
||||
<Tab
|
||||
data-testid="user-sessions-tab"
|
||||
title={<TabTitleText>{t("sessions")}</TabTitleText>}
|
||||
|
|
|
@ -24,6 +24,7 @@ import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
|||
import { toIdentityProvider } from "../identity-providers/routes/IdentityProvider";
|
||||
import { emptyFormatter, upperCaseFormatter } from "../util";
|
||||
import { UserIdpModal } from "./UserIdPModal";
|
||||
import { useAccess } from "../context/access/Access";
|
||||
|
||||
type UserIdentityProviderLinksProps = {
|
||||
userId: string;
|
||||
|
@ -41,6 +42,12 @@ export const UserIdentityProviderLinks = ({
|
|||
const { realm } = useRealm();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const { t } = useTranslation();
|
||||
const { hasAccess, hasSomeAccess } = useAccess();
|
||||
|
||||
const canQueryIDPDetails = hasSomeAccess(
|
||||
"manage-identity-providers",
|
||||
"view-identity-providers",
|
||||
);
|
||||
|
||||
const refresh = () => setKey(new Date().getTime());
|
||||
|
||||
|
@ -51,15 +58,17 @@ export const UserIdentityProviderLinks = ({
|
|||
const identityProviders = useServerInfo().identityProviders;
|
||||
|
||||
const getFederatedIdentities = async () => {
|
||||
const allProviders = await adminClient.identityProviders.find();
|
||||
|
||||
const allFedIds = (await adminClient.users.listFederatedIdentities({
|
||||
id: userId,
|
||||
})) as WithProviderId[];
|
||||
for (const element of allFedIds) {
|
||||
element.providerId = allProviders.find(
|
||||
(item) => item.alias === element.identityProvider,
|
||||
)?.providerId!;
|
||||
|
||||
if (canQueryIDPDetails) {
|
||||
const allProviders = await adminClient.identityProviders.find();
|
||||
for (const element of allFedIds) {
|
||||
element.providerId = allProviders.find(
|
||||
(item) => item.alias === element.identityProvider,
|
||||
)?.providerId!;
|
||||
}
|
||||
}
|
||||
|
||||
return allFedIds;
|
||||
|
@ -107,6 +116,9 @@ export const UserIdentityProviderLinks = ({
|
|||
});
|
||||
|
||||
const idpLinkRenderer = (idp: WithProviderId) => {
|
||||
if (!canQueryIDPDetails)
|
||||
return <span>{capitalize(idp.identityProvider)}</span>;
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={toIdentityProvider({
|
||||
|
@ -148,6 +160,8 @@ export const UserIdentityProviderLinks = ({
|
|||
};
|
||||
|
||||
const unlinkRenderer = (fedIdentity: FederatedIdentityRepresentation) => {
|
||||
if (!hasAccess("manage-users")) return <span />;
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="link"
|
||||
|
@ -175,6 +189,48 @@ export const UserIdentityProviderLinks = ({
|
|||
);
|
||||
};
|
||||
|
||||
const linkedIdpColumns = () => {
|
||||
const columns = [
|
||||
{
|
||||
name: "identityProvider",
|
||||
displayKey: "name",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
cellRenderer: idpLinkRenderer,
|
||||
transforms: [cellWidth(20)],
|
||||
},
|
||||
|
||||
{
|
||||
name: "userId",
|
||||
displayKey: "userID",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
transforms: [cellWidth(30)],
|
||||
},
|
||||
{
|
||||
name: "userName",
|
||||
displayKey: "username",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
transforms: [cellWidth(20)],
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
cellRenderer: unlinkRenderer,
|
||||
transforms: [cellWidth(20)],
|
||||
},
|
||||
];
|
||||
|
||||
if (canQueryIDPDetails)
|
||||
columns.splice(1, 0, {
|
||||
name: "type",
|
||||
displayKey: "type",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
cellRenderer: badgeRenderer1,
|
||||
transforms: [cellWidth(10)],
|
||||
});
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLinkIdPModalOpen && (
|
||||
|
@ -199,40 +255,7 @@ export const UserIdentityProviderLinks = ({
|
|||
isPaginated={false}
|
||||
ariaLabelKey="LinkedIdPs"
|
||||
className="kc-linked-IdPs-table"
|
||||
columns={[
|
||||
{
|
||||
name: "identityProvider",
|
||||
displayKey: "name",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
cellRenderer: idpLinkRenderer,
|
||||
transforms: [cellWidth(20)],
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
displayKey: "type",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
cellRenderer: badgeRenderer1,
|
||||
transforms: [cellWidth(10)],
|
||||
},
|
||||
{
|
||||
name: "userId",
|
||||
displayKey: "userID",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
transforms: [cellWidth(30)],
|
||||
},
|
||||
{
|
||||
name: "userName",
|
||||
displayKey: "username",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
transforms: [cellWidth(20)],
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
cellRenderer: unlinkRenderer,
|
||||
transforms: [cellWidth(20)],
|
||||
},
|
||||
]}
|
||||
columns={linkedIdpColumns()}
|
||||
emptyState={
|
||||
<TextContent className="kc-no-providers-text">
|
||||
<Text>{t("noProvidersLinked")}</Text>
|
||||
|
@ -240,45 +263,47 @@ export const UserIdentityProviderLinks = ({
|
|||
}
|
||||
/>
|
||||
</FormPanel>
|
||||
<FormPanel className="kc-available-idps" title={t("availableIdPs")}>
|
||||
<TextContent>
|
||||
<Text className="kc-available-idps-text">
|
||||
{t("availableIdPsText")}
|
||||
</Text>
|
||||
</TextContent>
|
||||
<KeycloakDataTable
|
||||
loader={availableIdPsLoader}
|
||||
key={key}
|
||||
isPaginated={false}
|
||||
ariaLabelKey="LinkedIdPs"
|
||||
className="kc-linked-IdPs-table"
|
||||
columns={[
|
||||
{
|
||||
name: "alias",
|
||||
displayKey: "name",
|
||||
cellFormatters: [emptyFormatter(), upperCaseFormatter()],
|
||||
transforms: [cellWidth(20)],
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
displayKey: "type",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
cellRenderer: badgeRenderer2,
|
||||
transforms: [cellWidth(60)],
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
cellRenderer: linkRenderer,
|
||||
},
|
||||
]}
|
||||
emptyState={
|
||||
<TextContent className="kc-no-providers-text">
|
||||
<Text>{t("noAvailableIdentityProviders")}</Text>
|
||||
</TextContent>
|
||||
}
|
||||
/>
|
||||
</FormPanel>
|
||||
{hasAccess("manage-users") && canQueryIDPDetails && (
|
||||
<FormPanel className="kc-available-idps" title={t("availableIdPs")}>
|
||||
<TextContent>
|
||||
<Text className="kc-available-idps-text">
|
||||
{t("availableIdPsText")}
|
||||
</Text>
|
||||
</TextContent>
|
||||
<KeycloakDataTable
|
||||
loader={availableIdPsLoader}
|
||||
key={key}
|
||||
isPaginated={false}
|
||||
ariaLabelKey="LinkedIdPs"
|
||||
className="kc-linked-IdPs-table"
|
||||
columns={[
|
||||
{
|
||||
name: "alias",
|
||||
displayKey: "name",
|
||||
cellFormatters: [emptyFormatter(), upperCaseFormatter()],
|
||||
transforms: [cellWidth(20)],
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
displayKey: "type",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
cellRenderer: badgeRenderer2,
|
||||
transforms: [cellWidth(60)],
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
cellFormatters: [emptyFormatter()],
|
||||
cellRenderer: linkRenderer,
|
||||
},
|
||||
]}
|
||||
emptyState={
|
||||
<TextContent className="kc-no-providers-text">
|
||||
<Text>{t("noAvailableIdentityProviders")}</Text>
|
||||
</TextContent>
|
||||
}
|
||||
/>
|
||||
</FormPanel>
|
||||
)}
|
||||
</PageSection>
|
||||
</>
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue