Skip to content

Commit

Permalink
Merge pull request #7514 from Shenali-SJ/local-authenticator-image-url
Browse files Browse the repository at this point in the history
Enhance Custom Local Authenticator Icon Resolution with Customised Icon Support
  • Loading branch information
ashanthamara authored Feb 6, 2025
2 parents 841aa9f + 8d42235 commit b9cc103
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 63 deletions.
6 changes: 6 additions & 0 deletions .changeset/tall-foxes-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@wso2is/admin.connections.v1": patch
"@wso2is/i18n": patch
---

Enhance Custom Local Authenticator Icon Resolution with Customised Icon Support
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ import {
getConnectedApps
} from "../api/connections";
import { getConnectionIcons } from "../configs/ui";
import { ConnectionUIConstants } from "../constants/connection-ui-constants";
import { AuthenticatorMeta } from "../meta/authenticator-meta";
import {
AuthenticatorCategories,
Expand Down Expand Up @@ -484,10 +483,7 @@ export const AuthenticatorGrid: FunctionComponent<AuthenticatorGridPropsInterfac
}

if (ConnectionsManagementUtils.IsCustomAuthenticator(connection)) {
return ConnectionsManagementUtils.resolveConnectionResourcePath(
connectionResourcesUrl,
ConnectionUIConstants.CUSTOM_LOCAL_AUTHENTICATOR_IMAGE_URI
);
return connection?.image || AuthenticatorMeta.getCustomAuthenticatorIcon();
}

return AuthenticatorMeta.getAuthenticatorIcon(connection?.id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import { createConnection, createCustomAuthentication, useGetConnectionTemplate
import { getConnectionWizardStepIcons } from "../../configs/ui";
import { CommonAuthenticatorConstants } from "../../constants/common-authenticator-constants";
import { ConnectionUIConstants } from "../../constants/connection-ui-constants";
import { AuthenticatorMeta } from "../../meta/authenticator-meta";
import {
AuthenticationTypeDropdownOption,
AvailableCustomAuthentications,
Expand Down Expand Up @@ -741,7 +742,6 @@ const CustomAuthenticationCreateWizard: FunctionComponent<CustomAuthenticationCr
customAuthenticator.name = prefixedIdentifier;
customAuthenticator.displayName = values?.displayName?.toString();
customAuthenticator.description = connectionTemplate.customLocalAuthenticator.description;
// TODO: Provide support to add local file URI as the image.

customAuthenticator.endpoint.authentication.type = endpointAuthType;
customAuthenticator.endpoint.uri = values?.endpointUri.toString();
Expand All @@ -758,7 +758,6 @@ const CustomAuthenticationCreateWizard: FunctionComponent<CustomAuthenticationCr
setIsSubmitting(true);
createCustomLocalAuthenticator(customAuthenticator);
}

};

const wizardCommonFirstPage = () => (
Expand Down Expand Up @@ -1076,10 +1075,7 @@ const CustomAuthenticationCreateWizard: FunctionComponent<CustomAuthenticationCr
data-componentid={ `${_componentId}-modal-header` }>
<div className={ "display-flex" }>
<GenericIcon
icon={ ConnectionsManagementUtils.resolveConnectionResourcePath(
"",
"assets/images/logos/custom-authentication.svg"
) }
icon={ AuthenticatorMeta.getCustomAuthenticatorIcon() }
size="x30"
transparent
spaced={ "right" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { getLocalAuthenticator } from "../../../api/authenticators";
import { getFederatedAuthenticatorDetails } from "../../../api/connections";
import { CommonAuthenticatorConstants } from "../../../constants/common-authenticator-constants";
import { ConnectionUIConstants } from "../../../constants/connection-ui-constants";
import { AuthenticatorMeta } from "../../../meta/authenticator-meta";
import {
ConnectionInterface,
ConnectionListResponseInterface,
Expand All @@ -38,6 +39,7 @@ import {
ExternalEndpoint,
FederatedAuthenticatorListItemInterface
} from "../../../models/connection";
import { ConnectionsManagementUtils } from "../../../utils/connection-utils";

/**
* Proptypes for the custom authenticator general details form component.
Expand Down Expand Up @@ -110,12 +112,11 @@ export const CustomAuthGeneralDetailsForm: FunctionComponent<CustomAuthGeneralDe
}: CustomAuthGeneralDetailsFormPopsInterface): ReactElement => {

const dispatch: Dispatch = useDispatch();
const { t } = useTranslation();

const [ isCustomLocalAuth, setIsCustomLocalAuth ] = useState<boolean>(undefined);
const [ authenticatorEndpoint, setAuthenticatorEndpoint ] = useState<ExternalEndpoint>(null);

const { t } = useTranslation();

const { CONNECTION_TEMPLATE_IDS: ConnectionTemplateIds } = CommonAuthenticatorConstants;

useEffect(() => {
Expand Down Expand Up @@ -152,7 +153,7 @@ export const CustomAuthGeneralDetailsForm: FunctionComponent<CustomAuthGeneralDe
description: error.response.data.description
}),
level: AlertLevels.ERROR,
message: t("authenticationProvider:" + "notifications.getIDP.error.message")
message: t("authenticationProvider:notifications.getIDP.error.message")
})
);

Expand All @@ -161,9 +162,9 @@ export const CustomAuthGeneralDetailsForm: FunctionComponent<CustomAuthGeneralDe

dispatch(
addAlert({
description: t("authenticationProvider:" + "notifications.getIDP.genericError.description"),
description: t("authenticationProvider:notifications.getIDP.genericError.description"),
level: AlertLevels.ERROR,
message: t("authenticationProvider:" + "notifications.getIDP.genericError.message")
message: t("authenticationProvider:notifications.getIDP.genericError.message")
})
);
});
Expand All @@ -181,7 +182,7 @@ export const CustomAuthGeneralDetailsForm: FunctionComponent<CustomAuthGeneralDe
description: error.response.data.description
}),
level: AlertLevels.ERROR,
message: t("authenticationProvider:" + "notifications.getIDP.error.message")
message: t("authenticationProvider:notifications.getIDP.error.message")
})
);

Expand All @@ -190,9 +191,9 @@ export const CustomAuthGeneralDetailsForm: FunctionComponent<CustomAuthGeneralDe

dispatch(
addAlert({
description: t("authenticationProvider:" + "notifications.getIDP.genericError.description"),
description: t("authenticationProvider:notifications.getIDP.genericError.description"),
level: AlertLevels.ERROR,
message: t("authenticationProvider:" + "notifications.getIDP.genericError.message")
message: t("authenticationProvider:notifications.getIDP.genericError.message")
})
);
});
Expand Down Expand Up @@ -256,25 +257,45 @@ export const CustomAuthGeneralDetailsForm: FunctionComponent<CustomAuthGeneralDe
};

/**
* This method validates the general settings fields.
*
* @param values - values to be validated.
* @returns - errors object.
*/
* Resolve the authenticator image.
*
* Custom federated authenticator stores either external image URL or relative image URI in the database.
* But in custom local authenticator, the image URI is persisted only if an external image URL is not provided.
* Therefore, when it is a custom local authenticator image URL needs to be resolved from the local file
* system if the image URI is not provided.
*
* @returns - Resolved image URL.
*/
const resolveAuthenticatorImage = (): string => {
if (isCustomLocalAuth && !editingIDP?.image) {
return ConnectionsManagementUtils.resolveConnectionRelativePath(
AuthenticatorMeta.getCustomAuthenticatorIcon()
);
} else {
return editingIDP.image;
};
};

/**
* This method validates the general settings fields.
*
* @param values - values to be validated.
* @returns - errors object.
*/
const validateGeneralSettingsField = (
values: CustomAuthenticationCreateWizardGeneralFormValuesInterface
): Partial<CustomAuthenticationCreateWizardGeneralFormValuesInterface> => {
const errors: Partial<CustomAuthenticationCreateWizardGeneralFormValuesInterface> = {};

if (!CommonAuthenticatorConstants.IDENTIFIER_REGEX.test(values?.identifier)) {
errors.identifier = t(
"customAuthentication:fields.createWizard.generalSettingsStep." + "identifier.validations.invalid"
"customAuthentication:fields.createWizard.generalSettingsStep.identifier.validations.invalid"
);
}

if (!CommonAuthenticatorConstants.DISPLAY_NAME_REGEX.test(values?.displayName)) {
errors.displayName = t(
"customAuthentication:fields.createWizard.generalSettingsStep." + "displayName.validations.invalid"
"customAuthentication:fields.createWizard.generalSettingsStep.displayName.validations.invalid"
);
}

Expand Down Expand Up @@ -305,12 +326,12 @@ export const CustomAuthGeneralDetailsForm: FunctionComponent<CustomAuthGeneralDe
inputType="text"
name="identifier"
label={ t("customAuthentication:fields.createWizard.generalSettingsStep.identifier.label") }
placeholder={ t("customAuthentication:fields.createWizard.generalSettingsStep." +
"identifier.placeholder") }
placeholder={ t(
"customAuthentication:fields.createWizard.generalSettingsStep.identifier.placeholder"
) }
maxLength={ 100 }
minLength={ 3 }
data-componentid={ `${_componentId}-form-wizard-identifier` }
width={ 15 }
hint={ t(
"customAuthentication:fields.createWizard.generalSettingsStep.helpPanel." +
"identifier.description"
Expand All @@ -329,40 +350,29 @@ export const CustomAuthGeneralDetailsForm: FunctionComponent<CustomAuthGeneralDe
maxLength={ 100 }
minLength={ 3 }
data-componentid={ `${_componentId}-form-wizard-display-name` }
width={ 15 }
/>
<Field.Input
name="image"
ariaLabel="image"
inputType="url"
label={ "Icon URL" }
required={ false }
placeholder={ t("authenticationProvider:" +
"forms.generalDetails.image." +
"placeholder") }
value={ editingIDP.image }
data-componentid={ `${ _componentId }-idp-image` }
maxLength={
ConnectionUIConstants
.GENERAL_FORM_CONSTRAINTS.IMAGE_URL_MAX_LENGTH as number
}
minLength={
ConnectionUIConstants
.GENERAL_FORM_CONSTRAINTS.IMAGE_URL_MIN_LENGTH as number
}
hint="Logo to display in login pages."
placeholder={ t("authenticationProvider:forms.generalDetails.image.placeholder") }
value={ resolveAuthenticatorImage() }
data-componentid={ `${_componentId}-idp-image` }
maxLength={ ConnectionUIConstants.GENERAL_FORM_CONSTRAINTS.IMAGE_URL_MAX_LENGTH as number }
minLength={ ConnectionUIConstants.GENERAL_FORM_CONSTRAINTS.IMAGE_URL_MIN_LENGTH as number }
hint={ t("customAuthentication:fields.editPage.generalTab.iconUrl.hint") }
readOnly={ isReadOnly }
/>
<Field.Textarea
name="description"
ariaLabel="description"
label={ t("authenticationProvider:forms." +
"generalDetails.description.label") }
label={ t("authenticationProvider:forms.generalDetails.description.label") }
required={ false }
placeholder={ t("authenticationProvider:forms." +
"generalDetails.description.placeholder") }
placeholder={ t("authenticationProvider:forms.generalDetails.description.placeholder") }
value={ editingIDP.description }
data-componentid={ `${ _componentId }-idp-description` }
data-componentid={ `${_componentId}-idp-description` }
maxLength={ ConnectionUIConstants.IDP_NAME_LENGTH.max }
minLength={ ConnectionUIConstants.IDP_NAME_LENGTH.min }
hint="A text description of the authenticator."
Expand Down
2 changes: 2 additions & 0 deletions features/admin.connections.v1/configs/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

import BasicAuthIcon from "../../themes/default/assets/images/authenticators/basic-auth.png";
import CustomAuthenticator from "../../themes/default/assets/images/authenticators/custom-authenticator.svg";
import FIDOIcon from "../../themes/default/assets/images/authenticators/fido-passkey-black.svg";
import SalesforceLogo from "../../themes/default/assets/images/connectors/salesforce.png";
import SCIMLogo from "../../themes/default/assets/images/connectors/scim.png";
Expand Down Expand Up @@ -86,6 +87,7 @@ export const getConnectionIcons = (): any => {
apple: AppleLogo,
basic: BasicAuthIcon,
basicAuthenticator: BasicAuthIcon,
customAuthenticator: CustomAuthenticator,
default: DefaultConnectionIcon,
"email-otp-authenticator": EmailOTPIcon,
emailOTP: EmailOTPIcon,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,4 @@ export class ConnectionUIConstants {
value: EndpointAuthenticationType.API_KEY
}
];

public static readonly CUSTOM_LOCAL_AUTHENTICATOR_IMAGE_URI: string =
"assets/images/logos/custom-authentication.svg";
}
13 changes: 13 additions & 0 deletions features/admin.connections.v1/meta/authenticator-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,19 @@ export class AuthenticatorMeta {
return icon ?? getConnectionIcons().default;
}

/**
* Get Custom Authenticator Icon.
*
* Currently authenticator id is being used to fetch the respective authenticator icon.
* Existing function could not be used since the id and the name of
* custom authenticators are not pre defined.
*
* @returns Custom Authenticator Icon.
*/
public static getCustomAuthenticatorIcon(): string {
return getConnectionIcons()?.customAuthenticator;
}

/**
* Get Authenticator Type display name.
*
Expand Down
12 changes: 1 addition & 11 deletions features/admin.connections.v1/pages/authenticator-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { FeatureAccessConfigInterface, useRequiredScopes } from "@wso2is/access-
import { ApplicationTemplateConstants } from "@wso2is/admin.application-templates.v1/constants/templates";
import { AppConstants } from "@wso2is/admin.core.v1/constants/app-constants";
import { history } from "@wso2is/admin.core.v1/helpers/history";
import useUIConfig from "@wso2is/admin.core.v1/hooks/use-ui-configs";
import { FeatureConfigInterface } from "@wso2is/admin.core.v1/models/config";
import { AppState } from "@wso2is/admin.core.v1/store";
import { identityProviderConfig } from "@wso2is/admin.extensions.v1/configs";
Expand All @@ -47,11 +46,9 @@ import { getLocalAuthenticator } from "../api/authenticators";
import { useGetConnectionTemplate } from "../api/connections";
import { EditConnection } from "../components/edit/connection-edit";
import { CommonAuthenticatorConstants } from "../constants/common-authenticator-constants";
import { ConnectionUIConstants } from "../constants/connection-ui-constants";
import { AuthenticatorMeta } from "../meta/authenticator-meta";
import { AuthenticatorLabels } from "../models/authenticators";
import { CustomAuthConnectionInterface } from "../models/connection";
import { ConnectionsManagementUtils } from "../utils/connection-utils";

/**
* Proptypes for the Custom Local Authenticator edit page component.
Expand Down Expand Up @@ -97,10 +94,6 @@ export const AuthenticatorEditPage: FunctionComponent<AuthenticatorEditPageProps

const { data: template } = useGetConnectionTemplate(templateId, shouldFetchConnectionTemplate);

const { UIConfig } = useUIConfig();

const connectionResourcesUrl: string = UIConfig?.connectionResourcesUrl;

useEffect(() => {
if (!connector) {
return;
Expand Down Expand Up @@ -215,10 +208,7 @@ export const AuthenticatorEditPage: FunctionComponent<AuthenticatorEditPageProps
<AppAvatar
hoverable={ false }
name={ connector?.displayName }
image={ ConnectionsManagementUtils.resolveConnectionResourcePath(
connectionResourcesUrl,
ConnectionUIConstants.CUSTOM_LOCAL_AUTHENTICATOR_IMAGE_URI
) }
image={ connector?.image || AuthenticatorMeta.getCustomAuthenticatorIcon() }
size="tiny"
/>
);
Expand Down
17 changes: 17 additions & 0 deletions features/admin.connections.v1/utils/connection-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,23 @@ export class ConnectionsManagementUtils {
}
}

/**
* Util to resolve the resource path relative to assets folder.
*
* @param path - Resource path.
* @returns - Relative path.
*/
public static resolveConnectionRelativePath(path: string): string {
const assetsFolder: string = "assets";
const index: number = path.indexOf(assetsFolder);

if (index === -1) {
return path;
}

return path.slice(index);
}

/**
* Util to resolve connection doc links.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,12 @@ export interface customAuthenticationNS {
}
}
};
editPage: {
generalTab: {
iconUrl: {
hint: string;
}
}
}
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ export const customAuthentication: customAuthenticationNS = {
},
subTitle: "Register externally implemented authentication service.",
title: "Custom Authentication"
},
editPage: {
generalTab: {
iconUrl: {
hint: "A URL for the image of the connection for display purposes. If not provided a generated thumbnail" +
"will be displayed. Recommended size is 200x200 pixels."
}
}
}
}
};

0 comments on commit b9cc103

Please sign in to comment.