Skip to content

Commit

Permalink
Merge pull request wso2#6543 from Thumimku/impersonation
Browse files Browse the repository at this point in the history
[UI] [Impersonation] Subject token configuration and Impersonation configuration
  • Loading branch information
Thumimku authored Jul 12, 2024
2 parents 3f14d36 + ad98b12 commit 997c70b
Show file tree
Hide file tree
Showing 40 changed files with 1,609 additions and 40 deletions.
9 changes: 9 additions & 0 deletions .changeset/clean-bags-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@wso2is/admin.server-configurations.v1": minor
"@wso2is/admin.application-roles.v1": minor
"@wso2is/admin.impersonation.v1": minor
"@wso2is/admin.core.v1": minor
"@wso2is/i18n": minor
---

Add Impersonation support
5 changes: 5 additions & 0 deletions .changeset/rude-guests-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wso2is/console": patch
---

add impersonation page routes
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ module.exports = {
],
root: true,
rules: {
"@typescript-eslint/no-unsafe-optional-chaining": "off",
"array-bracket-spacing": [ 1, "always" ],
"comma-dangle": [ "warn", "never" ],
"eol-last": "error",
Expand Down
1 change: 1 addition & 0 deletions apps/console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"@wso2is/admin.organizations.v1": "^2.20.57",
"@wso2is/admin.private-key-jwt.v1": "^2.20.57",
"@wso2is/admin.remote-repository-configuration.v1": "^2.20.57",
"@wso2is/admin.impersonation.v1": "^1.0.0",
"@wso2is/admin.roles.v1": "^2.20.57",
"@wso2is/admin.roles.v2": "^2.20.57",
"@wso2is/admin.saml2-configuration.v1": "^2.20.57",
Expand Down
9 changes: 9 additions & 0 deletions apps/console/src/configs/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ export const getAppViewRoutes = (): RouteInterface[] => {
protected: true,
showOnSidePanel: false
},
{
component: lazy(() => import("@wso2is/admin.impersonation.v1/pages/impersonation-configuration")),
exact: true,
id: "impersonationConfiguration",
name: "console:impersonationConfig.title",
path: AppConstants.getPaths().get("IMPERSONATION"),
protected: true,
showOnSidePanel: false
},
{
component: lazy(() =>
import(
Expand Down
162 changes: 162 additions & 0 deletions features/admin.applications.v1/components/forms/inbound-oidc-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Chip from "@oxygen-ui/react/Chip";
import { AppState, ConfigReducerStateInterface } from "@wso2is/admin.core.v1";
import useGlobalVariables from "@wso2is/admin.core.v1/hooks/use-global-variables";
import { applicationConfig } from "@wso2is/admin.extensions.v1";
import { ImpersonationConfigConstants } from "@wso2is/admin.impersonation.v1/constants/impersonation-configuration";
import { getSharedOrganizations } from "@wso2is/admin.organizations.v1/api";
import { OrganizationType } from "@wso2is/admin.organizations.v1/constants";
import { OrganizationInterface, OrganizationResponseInterface } from "@wso2is/admin.organizations.v1/models";
Expand Down Expand Up @@ -241,6 +242,8 @@ export const InboundOIDCForm: FunctionComponent<InboundOIDCFormPropsInterface> =
isRefreshTokenWithoutAllowedGrantType,
setRefreshTokenWithoutAlllowdGrantType
] = useState<boolean>(false);
const [ isSubjectTokenEnabled, setIsSubjectTokenEnabled ] = useState<boolean>(false);
const [ isSubjectTokenFeatureAvailable, setIsSubjectTokenFeatureAvailable ] = useState<boolean>(false);
const config: ConfigReducerStateInterface = useSelector((state: AppState) => state.config);

const clientSecret: MutableRefObject<HTMLElement> = useRef<HTMLElement>();
Expand Down Expand Up @@ -278,6 +281,8 @@ export const InboundOIDCForm: FunctionComponent<InboundOIDCFormPropsInterface> =
const requestObjectSigningAlg: MutableRefObject<HTMLElement> = useRef<HTMLElement>();
const requestObjectEncryptionAlgorithm: MutableRefObject<HTMLElement> = useRef<HTMLElement>();
const requestObjectEncryptionMethod: MutableRefObject<HTMLElement> = useRef<HTMLElement>();
const subjectToken: MutableRefObject<HTMLElement> = useRef<HTMLElement>();
const applicationSubjectTokenExpiryInSeconds: MutableRefObject<HTMLElement> = useRef<HTMLElement>();

const [ isSPAApplication, setSPAApplication ] = useState<boolean>(false);
const [ isOIDCWebApplication, setOIDCWebApplication ] = useState<boolean>(false);
Expand Down Expand Up @@ -579,6 +584,26 @@ export const InboundOIDCForm: FunctionComponent<InboundOIDCFormPropsInterface> =

}, [ initialValues ]);

/**
* Sets if subject token is enabled.
*/
useEffect(() => {
setIsSubjectTokenEnabled(initialValues?.subjectToken ? initialValues?.subjectToken?.enable : false);
setIsSubjectTokenFeatureAvailable(initialValues?.subjectToken ? true : false);
}, [ initialValues ]);

useEffect(() => {
if (isGrantChanged) {
if (!selectedGrantTypes?.includes(ApplicationManagementConstants.OAUTH2_TOKEN_EXCHANGE)) {
setIsSubjectTokenEnabled(false);
}

if (initialValues?.subjectToken) {
setIsSubjectTokenEnabled(initialValues?.subjectToken?.enable);
}
}
}, [ selectedGrantTypes, isGrantChanged ]);

/**
* Set the certificate type.
*/
Expand Down Expand Up @@ -1189,6 +1214,12 @@ export const InboundOIDCForm: FunctionComponent<InboundOIDCFormPropsInterface> =
renewRefreshToken: values.get("RefreshToken")?.length > 0
},
scopeValidators: values.get("scopeValidator"),
subjectToken: {
applicationSubjectTokenExpiryInSeconds : values.get("applicationSubjectTokenExpiryInSeconds")
? parseInt(values.get("applicationSubjectTokenExpiryInSeconds"), 10)
: ImpersonationConfigConstants.DEFAULT_SUBJECT_TOKEN_EXPIRY_TIME,
enable : values.get("SubjectToken")?.length > 0
},
validateRequestObjectSignature: values.get("enableRequestObjectSignatureValidation")?.length > 0
};

Expand Down Expand Up @@ -1379,6 +1410,13 @@ export const InboundOIDCForm: FunctionComponent<InboundOIDCFormPropsInterface> =
? parseInt(values.get("expiryInSeconds"), 10)
: Number(metadata?.defaultRefreshTokenExpiryTime),
renewRefreshToken: values.get("RefreshToken")?.length > 0
},
subjectToken: {
applicationSubjectTokenExpiryInSeconds : values.get("applicationSubjectTokenExpiryInSeconds")
? parseInt(values.get("applicationSubjectTokenExpiryInSeconds"), 10)
: ImpersonationConfigConstants.DEFAULT_SUBJECT_TOKEN_EXPIRY_TIME,
enable : values.get("SubjectToken")?.length > 0

}
};

Expand Down Expand Up @@ -1563,6 +1601,14 @@ export const InboundOIDCForm: FunctionComponent<InboundOIDCFormPropsInterface> =
case "expiryInSeconds":
expiryInSeconds.current.scrollIntoView(options);

break;
case "subjectToken":
subjectToken.current.scrollIntoView(options);

break;
case "applicationSubjectTokenExpiryInSeconds":
applicationSubjectTokenExpiryInSeconds.current.scrollIntoView(options);

break;
case "audience":
audience.current.scrollIntoView(options);
Expand Down Expand Up @@ -2928,7 +2974,122 @@ export const InboundOIDCForm: FunctionComponent<InboundOIDCFormPropsInterface> =
</>
)
}
{ /* Subject Token */ }
{ selectedGrantTypes?.includes(ApplicationManagementConstants.OAUTH2_TOKEN_EXCHANGE)
&& !isSystemApplication
&& !isDefaultApplication
&& isSubjectTokenFeatureAvailable
&& (
<>
<Grid.Row columns={ 2 }>
<Grid.Column mobile={ 16 }>
<Divider />
<Divider hidden />
</Grid.Column>
<Grid.Column mobile={ 16 }>
<Heading as="h4">
{ t("applications:forms.inboundOIDC.sections.subjectToken.heading") }
</Heading>
<Field
ref={ subjectToken }
name="SubjectToken"
label=""
required={ false }
requiredErrorMessage={
t("applications:forms.inboundOIDC.sections" +
".subjectToken.fields.enable.validations.empty")
}
type="checkbox"
listen={ (values: Map<string, FormValue>): void => {
const isSubjectTokenEnabled: boolean = values?.get("SubjectToken")
?.includes("subjectToken");

setIsSubjectTokenEnabled(isSubjectTokenEnabled);
} }
value={
initialValues?.subjectToken?.enable
? [ "subjectToken" ]
: []
}
children={ [
{
label: t("applications:forms.inboundOIDC" +
".sections.subjectToken.fields.enable.label"),
value: "subjectToken"
}
] }
readOnly={ readOnly }
data-componentid={ `${ testId }-subject-token-checkbox` }
/>
<Hint>
<Trans
i18nKey={
"applications:forms.inboundOIDC.sections" +
".subjectToken.fields.enable.hint"
}
>
Select to enable the subject token response type for this application
to be used in the impersonation flow.
</Trans>
</Hint>
</Grid.Column>
</Grid.Row>
{ isSubjectTokenEnabled
&& (
<Grid.Row columns={ 1 }>
<Grid.Column mobile={ 16 } tablet={ 16 } computer={ 16 }>
<Field
ref={ applicationSubjectTokenExpiryInSeconds }
name="applicationSubjectTokenExpiryInSeconds"
label={
t("applications:forms.inboundOIDC.sections" +
".subjectToken.fields.expiry.label")
}
required={ true }
requiredErrorMessage={
t("applications:forms.inboundOIDC.sections" +
".subjectToken.fields.expiry.validations.empty")
}
placeholder={
t("applications:forms.inboundOIDC.sections" +
".subjectToken.fields.expiry.placeholder")
}
validation={ (value: FormValue, validation: Validation) => {
if (!isValidExpiryTime(value.toString())) {
validation.isValid = false;
validation.errorMessages.push(
t("applications:forms.inboundOIDC" +
".sections.subjectToken.fields.expiry.validations.invalid")
);
}
} }
value={ initialValues?.subjectToken
? initialValues.subjectToken
.applicationSubjectTokenExpiryInSeconds.toString()
: ImpersonationConfigConstants.DEFAULT_SUBJECT_TOKEN_EXPIRY_TIME }
type="number"
readOnly={ readOnly }
min={ 1 }
data-testid={ `${ testId }-subject-token-expiry-time-input` }
/>
<Hint>
<Trans
i18nKey={
"applications:forms.inboundOIDC.sections" +
".subjectToken.fields.expiry.hint"
}
>
Specify the validity period of the <Code withBackground>subject_token</Code>
in seconds.
</Trans>
</Hint>
</Grid.Column>
</Grid.Row>
)
}
</>
)
}
{ /* ID Token */ }
{
!isM2MApplication
Expand Down Expand Up @@ -4148,6 +4309,7 @@ InboundOIDCForm.defaultProps = {
refreshToken: undefined,
scopeValidators: [],
state: undefined,
subjectToken: undefined,
validateRequestObjectSignature: undefined
}
};
9 changes: 9 additions & 0 deletions features/admin.applications.v1/models/application-inbound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ interface RefreshTokenConfigurationInterface {
renewRefreshToken?: boolean;
}

/**
* Interface for Subject Token Configuration.
*/
interface SubjectTokenConfigurationInterface {
applicationSubjectTokenExpiryInSeconds?: number;
enable?: boolean;
}

interface IdTokenEncryptionConfigurationInterface {
enabled?: boolean;
algorithm?: string;
Expand Down Expand Up @@ -175,6 +183,7 @@ export interface OIDCDataInterface {
requestObject?: RequestObjectConfigurationInterface;
accessToken?: AccessTokenConfigurationInterface;
refreshToken?: RefreshTokenConfigurationInterface;
subjectToken?: SubjectTokenConfigurationInterface;
idToken?: IdTokenConfigurationInterface;
logout?: OIDCLogoutConfigurationInterface;
validateRequestObjectSignature?: boolean;
Expand Down
1 change: 1 addition & 0 deletions features/admin.applications.v1/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@wso2is/admin.organizations.v1": "^2.20.57",
"@wso2is/admin.roles.v2": "^2.20.57",
"@wso2is/admin.secrets.v1": "^2.20.57",
"@wso2is/admin.impersonation.v1": "^1.0.0",
"@wso2is/admin.server-configurations.v1": "^2.20.57",
"@wso2is/admin.userstores.v1": "^2.20.57",
"@wso2is/admin.wsfed-configuration.v1": "^2.20.57",
Expand Down
1 change: 1 addition & 0 deletions features/admin.core.v1/configs/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export class Config {
I18nConstants.USERSTORES_NAMESPACE,
I18nConstants.VALIDATION_NAMESPACE,
I18nConstants.JWT_PRIVATE_KEY_CONFIGURATION_NAMESPACE,
I18nConstants.IMPERSONATION_CONFIGURATION_NAMESPACE,
I18nConstants.TRANSFER_LIST_NAMESPACE,
I18nConstants.USER_NAMESPACE,
I18nConstants.USERS_NAMESPACE,
Expand Down
4 changes: 3 additions & 1 deletion features/admin.core.v1/constants/app-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,9 @@ export class AppConstants {
[ "INTERNAL_NOTIFICATION_SENDING",
`${AppConstants.getAdminViewBasePath()}/server/internal-notification-sending` ],
[ "OUTBOUND_PROVISIONING_SETTINGS",
`${AppConstants.getAdminViewBasePath()}/outbound-provisioning-settings` ]
`${AppConstants.getAdminViewBasePath()}/outbound-provisioning-settings` ],
[ "IMPERSONATION", `${AppConstants.getAdminViewBasePath()}/login-and-registration/impersonation` ]

]);

return paths;
Expand Down
9 changes: 8 additions & 1 deletion features/admin.core.v1/constants/i18n-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ export class I18nConstants {
public static readonly JWT_PRIVATE_KEY_CONFIGURATION_NAMESPACE: string =
I18nModuleConstants.JWT_PRIVATE_KEY_CONFIGURATION_NAMESPACE;

/**
* JWT private key configuration namespace.
*/
public static readonly IMPERSONATION_CONFIGURATION_NAMESPACE: string =
I18nModuleConstants.IMPERSONATION_NAMESPACE;

/**
* transferList namespace.
*/
Expand Down Expand Up @@ -290,7 +296,8 @@ export class I18nConstants {
[ I18nConstants.APPLICATIONS_NAMESPACE, "portals" ],
[ I18nConstants.IDP_NAMESPACE, "portals" ],
[ I18nConstants.API_RESOURCES_NAMESPACE, "portals" ],
[ I18nConstants.AI_NAMESPACE, "portals" ]
[ I18nConstants.AI_NAMESPACE, "portals" ],
[ I18nConstants.IMPERSONATION_CONFIGURATION_NAMESPACE, "portals" ]
]);

/**
Expand Down
1 change: 1 addition & 0 deletions features/admin.core.v1/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@wso2is/admin.organization-discovery.v1": "^2.20.57",
"@wso2is/admin.organizations.v1": "^2.20.57",
"@wso2is/admin.private-key-jwt.v1": "^2.20.57",
"@wso2is/admin.impersonation.v1": "^1.0.0",
"@wso2is/admin.remote-repository-configuration.v1": "^2.20.57",
"@wso2is/admin.roles.v1": "^2.20.57",
"@wso2is/admin.roles.v2": "^2.20.57",
Expand Down
1 change: 1 addition & 0 deletions features/admin.core.v1/store/reducers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export const commonConfigReducerInitialState: CommonConfigReducerStateInterface<
guests: "",
guestsList: "",
identityProviders: "",
impersonationConfigurations: "",
jwtAuthenticationServiceMgt: "",
localAuthenticators: "",
localClaims: "",
Expand Down
1 change: 1 addition & 0 deletions features/admin.core.v1/utils/route-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ export class RouteUtils {
AppConstants.getPaths().get("ALTERNATIVE_LOGIN_IDENTIFIER_EDIT"),
AppConstants.getPaths().get("MULTI_ATTRIBUTE_LOGIN"),
AppConstants.getPaths().get("VALIDATION_CONFIG_EDIT"),
AppConstants.getPaths().get("IMPERSONATION"),
AppConstants.getPaths().get("ORGANIZATION_DISCOVERY_DOMAINS"),
AppConstants.getPaths().get("OUTBOUND_PROVISIONING_SETTINGS"),
AppConstants.getPaths().get("PRIVATE_KEY_JWT_CONFIG_EDIT")
Expand Down
Loading

0 comments on commit 997c70b

Please sign in to comment.