Skip to content

Commit

Permalink
Merge pull request #7179 from JayaShakthi97/feat-remote-userstore
Browse files Browse the repository at this point in the history
[feat] introduce new remote user store impl - show read groups field based on deployment config
  • Loading branch information
JayaShakthi97 authored Dec 5, 2024
2 parents 5c5e4f8 + aeeeed4 commit 783a47a
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 151 deletions.
6 changes: 6 additions & 0 deletions .changeset/pretty-houses-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@wso2is/admin.remote-userstores.v1": patch
"@wso2is/i18n": patch
---

[feat] introduce new remote user store impl - show read groups field based on deployment config

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import Grid from "@oxygen-ui/react/Grid";
import Stack from "@oxygen-ui/react/Stack";
import { Claim, IdentifiableComponentInterface } from "@wso2is/core/models";
import { FinalFormField, TextFieldAdapter } from "@wso2is/form/src";
import { FinalFormField, TextFieldAdapter } from "@wso2is/form";
import { EmptyPlaceholder, Heading, Text } from "@wso2is/react-components";
import React, { FunctionComponent, ReactElement } from "react";
import { useTranslation } from "react-i18next";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@

import FormLabel from "@oxygen-ui/react/FormLabel";
import Grid from "@oxygen-ui/react/Grid";
import { AppState } from "@wso2is/admin.core.v1/store";
import { RemoteUserStoreManagerType } from "@wso2is/admin.userstores.v1/constants/user-store-constants";
import { IdentifiableComponentInterface } from "@wso2is/core/models";
import { CheckboxFieldAdapter, FinalFormField, FormSpy, TextFieldAdapter } from "@wso2is/form/src";
import { FeatureAccessConfigInterface, IdentifiableComponentInterface } from "@wso2is/core/models";
import { CheckboxFieldAdapter, FinalFormField, FormSpy, TextFieldAdapter } from "@wso2is/form";
import { Heading, Hint } from "@wso2is/react-components";
import isEmpty from "lodash-es/isEmpty";
import React, { FunctionComponent, ReactElement } from "react";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
import { RemoteUserStoreConstants } from "../../../constants/remote-user-stores-constants";
import { getIsReadGroupsFeatureEnabled } from "../../../utils/ui-utils";

/**
* Interface for the group attributes section component props.
Expand Down Expand Up @@ -57,61 +60,83 @@ const GroupAttributesSection: FunctionComponent<GroupAttributesSectionPropsInter

const { t } = useTranslation();

const userStoreFeatureConfig: FeatureAccessConfigInterface = useSelector((state: AppState) =>
state?.config?.ui?.features?.userStores);

const isReadGroupsFeatureEnabled: boolean = getIsReadGroupsFeatureEnabled(userStoreFeatureConfig, userStoreManager);

const validateGroupnameField = (value: string, allValues: Record<string, unknown>) => {
if (!isReadGroupsFeatureEnabled && isEmpty(value)) {
return t("remoteUserStores:form.fields.groupnameMapping.validation.required");
}

const userStoreProperties: Record<string, string | boolean> =
allValues["userstore-properties"] as Record<string, string | boolean>;

if (userStoreProperties?.ReadGroups as boolean && isEmpty(value)) {
return t("remoteUserStores:form.fields.groupnameMapping.validation.required");
return t("remoteUserStores:form.fields.groupnameMapping.validation.readGroupsEnabled");
}

return undefined;
};

const validateGroupIdField = (value: string, allValues: Record<string, unknown>) => {
if (!isReadGroupsFeatureEnabled && isEmpty(value)) {
return t("remoteUserStores:form.fields.groupIdMapping.validation.required");
}

const userStoreProperties: Record<string, string | boolean> =
allValues["userstore-properties"] as Record<string, string | boolean>;

if (userStoreProperties?.ReadGroups as boolean && isEmpty(value)) {
return t("remoteUserStores:form.fields.groupIdMapping.validation.required");
return t("remoteUserStores:form.fields.groupIdMapping.validation.readGroupsEnabled");
}

return undefined;
};

if (!isReadGroupsFeatureEnabled
&& userStoreManager !== RemoteUserStoreManagerType.RemoteUserStoreManager) {
return null;
}

return (
<div>
<Heading as="h3">{ t("remoteUserStores:form.sections.groupAttributes") }</Heading>
<FormSpy subscription={ { values: true } }>
{ ({ values }: { values: Record<string, unknown> }) => {
const userStoreProperties: Record<string, unknown> =
values["userstore-properties"] as Record<string, unknown>;
const isReadGroupsEnabled: boolean = userStoreProperties?.ReadGroups as boolean;

const isReadGroupsEnabled: boolean = (userStoreProperties?.ReadGroups as boolean)
?? initialValues[RemoteUserStoreConstants.PROPERTY_NAME_READ_GROUPS] === "true";

return (
<Grid container spacing={ 2 } className="form-grid-container">
<Grid xs={ 12 }>
<FinalFormField
label={ t("remoteUserStores:form.fields.readGroups.label") }
name={
`userstore-properties.${RemoteUserStoreConstants.PROPERTY_NAME_READ_GROUPS}`
}
FormControlProps={ {
fullWidth: true,
margin: "dense"
} }
data-componentid={ `${componentId}-field-readGroups` }
component={ CheckboxFieldAdapter }
disabled={ isReadOnly }
hint={
(<Hint className="hint" compact>
{ t("remoteUserStores:form.fields.readGroups.helperText") }
</Hint>)
}
initialValue={ initialValues[
RemoteUserStoreConstants.PROPERTY_NAME_READ_GROUPS] === "true" }
/>
</Grid>
{ isReadGroupsFeatureEnabled && (
<Grid xs={ 12 }>
<FinalFormField
label={ t("remoteUserStores:form.fields.readGroups.label") }
name={
`userstore-properties.${RemoteUserStoreConstants.PROPERTY_NAME_READ_GROUPS}`
}
FormControlProps={ {
fullWidth: true,
margin: "dense"
} }
data-componentid={ `${componentId}-field-readGroups` }
component={ CheckboxFieldAdapter }
disabled={ isReadOnly }
hint={
(<Hint className="hint" compact>
{ t("remoteUserStores:form.fields.readGroups.helperText") }
</Hint>)
}
initialValue={ initialValues[
RemoteUserStoreConstants.PROPERTY_NAME_READ_GROUPS] === "true" }
/>
</Grid>
) }

{ userStoreManager === RemoteUserStoreManagerType.RemoteUserStoreManager && (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import Grid from "@oxygen-ui/react/Grid";
import Stack from "@oxygen-ui/react/Stack";
import { Claim, IdentifiableComponentInterface } from "@wso2is/core/models";
import { FinalFormField, TextFieldAdapter } from "@wso2is/form/src";
import { FinalFormField, TextFieldAdapter } from "@wso2is/form";
import { Heading, Text } from "@wso2is/react-components";
import React, { FunctionComponent, ReactElement } from "react";
import { useTranslation } from "react-i18next";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import FormLabel from "@oxygen-ui/react/FormLabel";
import Grid from "@oxygen-ui/react/Grid";
import { ClaimManagementConstants } from "@wso2is/admin.claims.v1/constants";
import { Claim, IdentifiableComponentInterface } from "@wso2is/core/models";
import { FinalFormField, TextFieldAdapter } from "@wso2is/form/src";
import { FinalFormField, TextFieldAdapter } from "@wso2is/form";
import { Heading, Hint } from "@wso2is/react-components";
import React, { FunctionComponent, ReactElement } from "react";
import { useTranslation } from "react-i18next";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {
Property
} from "@wso2is/core/models";
import { addAlert } from "@wso2is/core/store";
import { FinalForm, FormRenderProps } from "@wso2is/form/src";
import { FinalForm, FormRenderProps } from "@wso2is/form";
import {
DocumentationLink,
EmphasizedSegment,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
*/

/**
* Keys used in feature dictionry.
* Keys used in feature dictionary.
*/
export enum UserStoresFeatureDictionaryKeys {
ReadWriteUserStores = "USERSTORES_READ_WRITE_USERSTORES",
OptimizedUserStore = "OPTIMIZED_USERSTORE"
OptimizedUserStore = "OPTIMIZED_USERSTORE",
ClassicUserStoreReadGroups = "CLASSIC_USERSTORE_READ_GROUPS",
OptimizedUserStoreReadGroups = "OPTIMIZED_USERSTORE_READ_GROUPS"
}

/**
Expand Down Expand Up @@ -73,7 +75,9 @@ export class RemoteUserStoreConstants {
*/
public static readonly FEATURE_DICTIONARY: Map<string, string> = new Map<string, string>([
[ UserStoresFeatureDictionaryKeys.ReadWriteUserStores, "userStores.readWriteUserstores" ],
[ UserStoresFeatureDictionaryKeys.OptimizedUserStore, "userStores.optimizedUserstore" ]
[ UserStoresFeatureDictionaryKeys.OptimizedUserStore, "userStores.optimizedUserstore" ],
[ UserStoresFeatureDictionaryKeys.ClassicUserStoreReadGroups, "userStores.classicUserstore.readGroups" ],
[ UserStoresFeatureDictionaryKeys.OptimizedUserStoreReadGroups, "userStores.optimizedUserstore.readGroups" ]
]);

public static getPaths(): Map<string, string> {
Expand Down
52 changes: 52 additions & 0 deletions features/admin.remote-userstores.v1/utils/ui-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { FeatureAccessConfigInterface } from "@wso2is/access-control";
import { RemoteUserStoreManagerType } from "@wso2is/admin.userstores.v1/constants";
import { isFeatureEnabled } from "@wso2is/core/helpers";
import { RemoteUserStoreConstants, UserStoresFeatureDictionaryKeys } from "../constants/remote-user-stores-constants";

/**
* Checks if the read groups feature is enabled based on the feature config and the user store manager.
*
* @param userStoreFeatureConfig - User store feature config.
* @param userStoreManager - User store manager type.
*/
export const getIsReadGroupsFeatureEnabled = (
userStoreFeatureConfig: FeatureAccessConfigInterface,
userStoreManager: RemoteUserStoreManagerType
) => {
const isClassicUserStoreReadGroupsFeatureEnabled: boolean = isFeatureEnabled(
userStoreFeatureConfig,
RemoteUserStoreConstants.FEATURE_DICTIONARY
.get(UserStoresFeatureDictionaryKeys.ClassicUserStoreReadGroups)
);
const isOptimizedUserStoreReadGroupsFeatureEnabled: boolean = isFeatureEnabled(
userStoreFeatureConfig,
RemoteUserStoreConstants.FEATURE_DICTIONARY
.get(UserStoresFeatureDictionaryKeys.OptimizedUserStoreReadGroups)
);
const isReadGroupsFeatureEnabled: boolean = (
(userStoreManager === RemoteUserStoreManagerType.WSOutboundUserStoreManager
&& isClassicUserStoreReadGroupsFeatureEnabled)
|| (userStoreManager === RemoteUserStoreManagerType.RemoteUserStoreManager
&& isOptimizedUserStoreReadGroupsFeatureEnabled)
);

return isReadGroupsFeatureEnabled;
};
2 changes: 2 additions & 0 deletions modules/i18n/src/models/namespaces/remote-user-stores-ns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ export interface RemoteUserStoresNS {
placeholder: string;
validation: {
required: string;
readGroupsEnabled: string;
};
};
groupnameMapping: {
Expand All @@ -188,6 +189,7 @@ export interface RemoteUserStoresNS {
placeholder: string;
validation: {
required: string;
readGroupsEnabled: string;
};
};
name: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,17 @@ export const remoteUserStores: RemoteUserStoresNS = {
label: "Group ID",
placeholder: "e.g. groupId",
validation: {
required: "Group ID mapping is required when read groups is enabled"
readGroupsEnabled: "Group ID mapping is required when read groups is enabled",
required: "Group ID mapping is required"
}
},
groupnameMapping: {
helperText: "Specify the attribute from the user store that stores the group name.",
label: "Group Name",
placeholder: "e.g. groupName",
validation: {
required: "Group name mapping is required when read groups is enabled"
readGroupsEnabled: "Group name mapping is required when read groups is enabled",
required: "Group name mapping is required"
}
},
name: {
Expand Down

0 comments on commit 783a47a

Please sign in to comment.