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

Refactor Custom Authenticator Settings Page #7499

Merged
merged 31 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
61d9720
Add a seperate component for custom authenticator settings page
Shenali-SJ Feb 1, 2025
5513007
Update types
Shenali-SJ Feb 4, 2025
acf30da
Merge upstream
Shenali-SJ Feb 4, 2025
1430b61
Add changeset
Shenali-SJ Feb 4, 2025
dabc3e6
Address comments
Shenali-SJ Feb 4, 2025
c63ef32
Remove unwanted conditions
Shenali-SJ Feb 4, 2025
0bb86d3
Reuse action util function and address comments
Shenali-SJ Feb 4, 2025
371702d
Remove unused imports
Shenali-SJ Feb 4, 2025
85aabe5
Merge upstream
Shenali-SJ Feb 6, 2025
037aad3
Update endpoint models
Shenali-SJ Feb 6, 2025
0e2610e
Remove t method param from actions
Shenali-SJ Feb 6, 2025
de57f1d
refactor actions validation utils
Shenali-SJ Feb 6, 2025
face1ee
Update conditions of settings form update
Shenali-SJ Feb 6, 2025
7d2fcf3
Fix lint issues
Shenali-SJ Feb 6, 2025
88a7344
Extract alert dispatch to util methods
Shenali-SJ Feb 6, 2025
3e1d501
Address comments
Shenali-SJ Feb 6, 2025
cb1b820
Merge remote-tracking branch 'upstream/master' into custom-auth-edit-…
Shenali-SJ Feb 6, 2025
f474fa5
features/admin.connections.v1/utils/connection-utils.ts
Shenali-SJ Feb 6, 2025
f5ae1cc
Merge remote-tracking branch 'upstream/master' into custom-auth-edit-…
Shenali-SJ Feb 7, 2025
54bf5fa
Update form validations
Shenali-SJ Feb 7, 2025
53060db
Extract alerts to util functions
Shenali-SJ Feb 7, 2025
4a8f032
Add changeset
Shenali-SJ Feb 7, 2025
9a2753a
update error conditions
Shenali-SJ Feb 7, 2025
e7b0772
Update wizard error clearing timeout
Shenali-SJ Feb 7, 2025
168ad35
Delete duplicated changeset
Shenali-SJ Feb 7, 2025
420f957
Update error messages
Shenali-SJ Feb 7, 2025
daa344d
Merge remote-tracking branch 'upstream/master' into custom-auth-edit-…
Shenali-SJ Feb 7, 2025
6c6e09b
Merge remote-tracking branch 'origin/custom-auth-edit-settings' into …
Shenali-SJ Feb 7, 2025
53b0cfe
Allow http protocol
Shenali-SJ Feb 7, 2025
d825a32
Fix formatting issues
Shenali-SJ Feb 7, 2025
07703d3
Fix lint issues
Shenali-SJ Feb 7, 2025
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
7 changes: 7 additions & 0 deletions .changeset/big-apples-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@wso2is/admin.connections.v1": patch
"@wso2is/admin.actions.v1": patch
"@wso2is/i18n": patch
---

Refactor Custom Authenticator Settings Page
5 changes: 5 additions & 0 deletions .changeset/swift-colts-judge.md
Shenali-SJ marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wso2is/admin.connections.v1": patch
---
Shenali-SJ marked this conversation as resolved.
Show resolved Hide resolved

Refactor Custom Authenticator Settings Page
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ const PreIssueAccessTokenActionConfigForm: FunctionComponent<PreIssueAccessToken
authenticationType: authenticationType,
isAuthenticationUpdateFormState: isAuthenticationUpdateFormState,
isCreateFormState: isCreateFormState
}, t);
});
};

const handleSubmit = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ const PreUpdatePasswordActionConfigForm: FunctionComponent<PreUpdatePasswordActi
authenticationType: authenticationType,
isAuthenticationUpdateFormState: isAuthenticationUpdateFormState,
isCreateFormState: isCreateFormState
}, t);
});

if (!values?.passwordSharing) {
customError.passwordSharing = t("actions:fields.passwordSharing.format.validations") as PasswordFormat;
Expand Down
107 changes: 75 additions & 32 deletions features/admin.actions.v1/util/form-field-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@
* under the License.
*/

import { URLUtils } from "@wso2is/core/utils";
import { I18n } from "@wso2is/i18n";
import { FormValidation } from "@wso2is/validation";
import { ActionsConstants } from "../constants/actions-constants";
import { ActionConfigFormPropertyInterface, AuthenticationType } from "../models/actions";
import {
ActionConfigFormPropertyInterface,
AuthenticationType,
EndpointConfigFormPropertyInterface
} from "../models/actions";

/**
* Validates the ActionConfig form fields.
*
* @param values - Form values to validate.
* @param options - Dynamic options for validation (e.g., flags and authenticationType).
* @param t - localization translations
* @returns A partial object containing validation errors.
*/
export const validateActionCommonFields = (
Expand All @@ -34,71 +38,110 @@ export const validateActionCommonFields = (
isCreateFormState: boolean;
isAuthenticationUpdateFormState: boolean;
authenticationType?: AuthenticationType;
},
t: (key: string, options?: any) => string
}
): Partial<ActionConfigFormPropertyInterface> => {
const { isCreateFormState, isAuthenticationUpdateFormState, authenticationType } = options;
const errors: Partial<ActionConfigFormPropertyInterface> = {};

if (!values?.name) {
errors.name = t("actions:fields.name.validations.empty");
errors.name = I18n.instance.t("actions:fields.name.validations.empty");
}

if (!ActionsConstants.ACTION_NAME_REGEX.test(values?.name)) {
errors.name = t("actions:fields.name.validations.invalid");
errors.name = I18n.instance.t("actions:fields.name.validations.invalid");
}

return {
...errors,
...validateActionEndpointFields(values, options)
};
};

/**
* Validate endpoint fields of the ActionConfig form.
*
* @param values - Form values to validate.
* @param options - Dynamic options for validation (e.g., flags and authenticationType).
* @returns A partial object containing validation errors.
*/
export const validateActionEndpointFields = (
Shenali-SJ marked this conversation as resolved.
Show resolved Hide resolved
values: EndpointConfigFormPropertyInterface,
options: {
isCreateFormState?: boolean;
isAuthenticationUpdateFormState: boolean;
authenticationType?: AuthenticationType;
}
): Partial<EndpointConfigFormPropertyInterface> => {
const { isCreateFormState, isAuthenticationUpdateFormState, authenticationType } = options;
const errors: Partial<EndpointConfigFormPropertyInterface> = {};

if (!values?.endpointUri) {
errors.endpointUri = t("actions:fields.endpoint.validations.empty");
errors.endpointUri = I18n.instance.t("actions:fields.endpoint.validations.empty");
}
if (URLUtils.isURLValid(values?.endpointUri)) {
if (!(URLUtils.isHttpsUrl(values?.endpointUri))) {
errors.endpointUri = t("actions:fields.endpoint.validations.notHttps");
}
} else {
errors.endpointUri = t("actions:fields.endpoint.validations.invalidUrl");

if (
!FormValidation.url(values?.endpointUri, {
domain: {
allowUnicode: true,
minDomainSegments: 1,
tlds: false
},
scheme: [ "https" ]
})
) {
errors.endpointUri = I18n.instance.t("actions:fields.endpoint.validations.invalidUrl");
}

if (!values?.authenticationType) {
errors.authenticationType = t("actions:fields.authenticationType.validations.empty");
errors.authenticationType = I18n.instance.t("actions:fields.authenticationType.validations.empty");
}

switch (authenticationType) {
case AuthenticationType.BASIC:
if(isCreateFormState || isAuthenticationUpdateFormState ||
values?.usernameAuthProperty || values?.passwordAuthProperty) {
if (
isCreateFormState || isAuthenticationUpdateFormState || values?.usernameAuthProperty ||
values?.passwordAuthProperty
) {
if (!values?.usernameAuthProperty) {
errors.usernameAuthProperty = t("actions:fields.authentication." +
"types.basic.properties.username.validations.empty");
errors.usernameAuthProperty = I18n.instance.t(
"actions:fields.authentication.types.basic.properties.username.validations.empty"
);
}
if (!values?.passwordAuthProperty) {
errors.passwordAuthProperty = t("actions:fields.authentication." +
"types.basic.properties.password.validations.empty");
errors.passwordAuthProperty = I18n.instance.t(
"actions:fields.authentication.types.basic.properties.password.validations.empty"
);
}
}

break;
case AuthenticationType.BEARER:
if (isCreateFormState || isAuthenticationUpdateFormState) {
if (!values?.accessTokenAuthProperty) {
errors.accessTokenAuthProperty = t("actions:fields.authentication." +
"types.bearer.properties.accessToken.validations.empty");
errors.accessTokenAuthProperty = I18n.instance.t(
"actions:fields.authentication.types.bearer.properties.accessToken.validations.empty"
);
}
}

break;
case AuthenticationType.API_KEY:
if (isCreateFormState || isAuthenticationUpdateFormState ||
values?.headerAuthProperty || values?.valueAuthProperty) {
if (isCreateFormState || isAuthenticationUpdateFormState || values?.headerAuthProperty ||
values?.valueAuthProperty
) {
if (!values?.headerAuthProperty) {
errors.headerAuthProperty = t("actions:fields.authentication." +
"types.apiKey.properties.header.validations.empty");
errors.headerAuthProperty = I18n.instance.t(
"actions:fields.authentication.types.apiKey.properties.header.validations.empty"
);
}
if (!ActionsConstants.API_HEADER_REGEX.test(values?.headerAuthProperty)) {
errors.headerAuthProperty = t("actions:fields.authentication." +
"types.apiKey.properties.header.validations.invalid");
errors.headerAuthProperty = I18n.instance.t(
"actions:fields.authentication.types.apiKey.properties.header.validations.invalid"
);
}
if (!values?.valueAuthProperty) {
errors.valueAuthProperty = t("actions:fields.authentication." +
"types.apiKey.properties.value.validations.empty");
errors.valueAuthProperty = I18n.instance.t(
"actions:fields.authentication.types.apiKey.properties.value.validations.empty"
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion features/admin.connections.v1/api/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
*/

import { AsgardeoSPAClient, HttpClientInstance } from "@asgardeo/auth-react";
import { store } from "@wso2is/admin.core.v1/store";
import useRequest, {
RequestConfigInterface,
RequestErrorInterface,
RequestResultInterface
} from "@wso2is/admin.core.v1/hooks/use-request";
import useResourceEndpoints from "@wso2is/admin.core.v1/hooks/use-resource-endpoints";
import { store } from "@wso2is/admin.core.v1/store";
Shenali-SJ marked this conversation as resolved.
Show resolved Hide resolved
import { IdentityAppsApiException } from "@wso2is/core/exceptions";
import { HttpMethods } from "@wso2is/core/models";
import { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,70 @@ const CustomAuthenticationCreateWizard: FunctionComponent<CustomAuthenticationCr
}
};

/**
* This method handles custom authenticator create errors.
*
* @param error - error object.
*/
const handleCustomAuthenticatorCreateErrors = (error: AxiosError): void => {
const identityAppsError: IdentityAppsError = ConnectionUIConstants.ERROR_CREATE_LIMIT_REACHED;

if (
error?.response?.status === 403 &&
error?.response?.data?.code === identityAppsError.getErrorCode()
) {
Shenali-SJ marked this conversation as resolved.
Show resolved Hide resolved
setAlert({
code: identityAppsError.getErrorCode(),
description: t(identityAppsError.getErrorDescription()),
level: AlertLevels.ERROR,
message: t(identityAppsError.getErrorMessage()),
traceId: identityAppsError.getErrorTraceId()
});
setTimeout(() => setAlert(undefined), 4000);

return;
}

if (error?.response?.status === 500 && error?.response?.data?.code === "IDP-65002") {
setAlert({
description: t(
"customAuthentication:notifications.addCustomAuthenticator.genericError.description"
),
level: AlertLevels.ERROR,
message: t(
"customAuthentication:notifications.addCustomAuthenticator.genericError.message"
)
});
setTimeout(() => setAlert(undefined), 8000);

return;
}

if (error?.response?.data?.description) {
setAlert({
description: t(
"customAuthentication:notifications.addCustomAuthenticator.error.description",
{
description: error.response.data.description
}
),
level: AlertLevels.ERROR,
message: t("customAuthentication:notifications.addCustomAuthenticator.error.message")
});
setTimeout(() => setAlert(undefined), 4000);

return;
}
setAlert({
description: t(
"customAuthentication:notifications.addCustomAuthenticator.genericError.description"
),
level: AlertLevels.ERROR,
message: t("customAuthentication:notifications.addCustomAuthenticator.genericError.message")
});
setTimeout(() => setAlert(undefined), 4000);
};

/**
* This method creates an external authenticator.
*
Expand All @@ -547,9 +611,11 @@ const CustomAuthenticationCreateWizard: FunctionComponent<CustomAuthenticationCr
});
dispatch(
addAlert({
description: t("authenticationProvider:notifications.addIDP.success.description"),
description: t("customAuthentication:notifications.addCustomAuthenticator.success.description"),
level: AlertLevels.SUCCESS,
message: t("authenticationProvider:notifications.addIDP.success.message")
message: t(
"customAuthentication:notifications.addCustomAuthenticator.success.message"
)
})
);

Expand All @@ -564,50 +630,7 @@ const CustomAuthenticationCreateWizard: FunctionComponent<CustomAuthenticationCr
onIDPCreate();
})
.catch((error: AxiosError) => {
const identityAppsError: IdentityAppsError = ConnectionUIConstants.ERROR_CREATE_LIMIT_REACHED;

if (error.response.status === 403 && error?.response?.data?.code === identityAppsError.getErrorCode()) {
setAlert({
code: identityAppsError.getErrorCode(),
description: t(identityAppsError.getErrorDescription()),
level: AlertLevels.ERROR,
message: t(identityAppsError.getErrorMessage()),
traceId: identityAppsError.getErrorTraceId()
});
setTimeout(() => setAlert(undefined), 4000);

return;
}

if (error?.response.status === 500 && error.response?.data.code === "IDP-65002") {
setAlert({
description: t("authenticationProvider:notifications.addIDP.serverError.description"),
level: AlertLevels.ERROR,
message: t("authenticationProvider:notifications.addIDP.serverError.message")
});
setTimeout(() => setAlert(undefined), 8000);

return;
}

if (error.response && error.response.data && error.response.data.description) {
setAlert({
description: t("authenticationProvider:notifications.addIDP.error.description", {
description: error.response.data.description
}),
level: AlertLevels.ERROR,
message: t("authenticationProvider:notifications.addIDP.error.message")
});
setTimeout(() => setAlert(undefined), 4000);

return;
}
setAlert({
description: t("authenticationProvider:notifications.addIDP.genericError.description"),
level: AlertLevels.ERROR,
message: t("authenticationProvider:notifications.addIDP.genericError.message")
});
setTimeout(() => setAlert(undefined), 4000);
handleCustomAuthenticatorCreateErrors(error);
})
.finally(() => {
setIsSubmitting(false);
Expand Down Expand Up @@ -644,50 +667,7 @@ const CustomAuthenticationCreateWizard: FunctionComponent<CustomAuthenticationCr
handleSuccessfulAuthenticatorCreate();
})
.catch((error: AxiosError) => {
const identityAppsError: IdentityAppsError = ConnectionUIConstants.ERROR_CREATE_LIMIT_REACHED;

if (error.response.status === 403 && error?.response?.data?.code === identityAppsError.getErrorCode()) {
setAlert({
code: identityAppsError.getErrorCode(),
description: t(identityAppsError.getErrorDescription()),
level: AlertLevels.ERROR,
message: t(identityAppsError.getErrorMessage()),
traceId: identityAppsError.getErrorTraceId()
});
setTimeout(() => setAlert(undefined), 4000);

return;
}

if (error?.response.status === 500 && error.response?.data.code === "IDP-65002") {
setAlert({
description: t("authenticationProvider:notifications.addIDP.serverError.description"),
level: AlertLevels.ERROR,
message: t("authenticationProvider:notifications.addIDP.serverError.message")
});
setTimeout(() => setAlert(undefined), 8000);

return;
}

if (error.response && error.response.data && error.response.data.description) {
setAlert({
description: t("authenticationProvider:notifications.addIDP.error.description", {
description: error.response.data.description
}),
level: AlertLevels.ERROR,
message: t("authenticationProvider:notifications.addIDP.error.message")
});
setTimeout(() => setAlert(undefined), 4000);

return;
}
setAlert({
description: t("authenticationProvider:notifications.addIDP.genericError.description"),
level: AlertLevels.ERROR,
message: t("authenticationProvider:notifications.addIDP.genericError.message")
});
setTimeout(() => setAlert(undefined), 4000);
handleCustomAuthenticatorCreateErrors(error);
})
.finally(() => {
setIsSubmitting(false);
Expand Down
Loading
Loading