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

Add the option to modify the primary user store domain name in console and myaccount apps #7239

Merged
merged 11 commits into from
Jan 7, 2025
22 changes: 22 additions & 0 deletions .changeset/rare-moons-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"@wso2is/admin.application-roles.v1": patch
"@wso2is/admin.console-settings.v1": patch
"@wso2is/admin.administrators.v1": patch
"@wso2is/admin.organizations.v1": patch
"@wso2is/admin.applications.v1": patch
"@wso2is/admin.connections.v1": patch
"@wso2is/admin.extensions.v1": patch
"@wso2is/admin.tenants.v1": patch
"@wso2is/admin.claims.v1": patch
"@wso2is/admin.groups.v1": patch
"@wso2is/react-components": patch
"@wso2is/admin.roles.v1": patch
"@wso2is/admin.roles.v2": patch
"@wso2is/admin.users.v1": patch
"@wso2is/admin.core.v1": patch
"@wso2is/myaccount": patch
"@wso2is/console": patch
"@wso2is/core": patch
---

Add the option to modify the primary user store domain name in console and myaccount apps
4 changes: 2 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2022-2023, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2022-2025, 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
Expand Down Expand Up @@ -72,7 +72,7 @@ const getLicenseHeaderPattern = () => {
const LICENSE_HEADER_DEFAULT_PATTERN = [
"*",
{
pattern: " Copyright \\(c\\) \\b(2019|202[0-4])(?:-(202[0-4]))?, WSO2 LLC. \\(https://www.wso2.com\\).$",
pattern: " Copyright \\(c\\) \\b(2019|202[0-5])(?:-(202[0-5]))?, WSO2 LLC. \\(https://www.wso2.com\\).$",
template: " * Copyright (c) {{year}}, WSO2 LLC. (https://www.wso2.com)."
},
" *",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1787,6 +1787,9 @@
{% endfor %}
{% endif %}
],
{% if user_store.properties.DomainName is defined %}
pavinduLakshan marked this conversation as resolved.
Show resolved Hide resolved
"primaryUserStoreDomainName": "{{ user_store.properties.DomainName }}",
{% endif %}
"hiddenUserStores": [
{% if console.ui.hidden_user_stores is defined %}
{% for value in console.ui.hidden_user_stores %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@
{% endfor %}
{% endif %}
},
{% if user_store.properties.DomainName is defined %}
"primaryUserStoreDomainName": "{{ user_store.properties.DomainName }}",
{% endif %}
{% if identity_mgt.user_claim_update.enable_multiple_emails_and_mobile_numbers is defined %}
"isMultipleEmailsAndMobileNumbersEnabled": {{ identity_mgt.user_claim_update.enable_multiple_emails_and_mobile_numbers }},
{% endif %}
Expand Down
5 changes: 3 additions & 2 deletions apps/myaccount/src/components/profile/profile.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2024, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2019-2025, 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
Expand Down Expand Up @@ -165,6 +165,7 @@ export const Profile: FunctionComponent<ProfileProps> = (props: ProfileProps): R

const allowedScopes: string = useSelector((state: AppState) => state?.authenticationInformation?.scope);
const isMultipleEmailsAndMobileConfigEnabled: boolean = config?.ui?.isMultipleEmailsAndMobileNumbersEnabled;
const primaryUserStoreDomainName: string = config?.ui?.primaryUserStoreDomainName;

const [ isMobileVerificationEnabled, setIsMobileVerificationEnabled ] = useState<boolean>(false);
const [ isEmailVerificationEnabled, setIsEmailVerificationEnabled ] = useState<boolean>(false);
Expand Down Expand Up @@ -328,7 +329,7 @@ export const Profile: FunctionComponent<ProfileProps> = (props: ProfileProps): R
const username: string = profileDetails?.profileInfo["userName"];

if (!username) return;
const userStoreDomain: string = resolveUserstore(username)?.toUpperCase();
const userStoreDomain: string = resolveUserstore(username, primaryUserStoreDomainName)?.toUpperCase();
// Check each required attribute exists and domain is not excluded in the excluded user store list.
const attributeCheck: boolean = multipleEmailsAndMobileFeatureRelatedAttributes.every(
(attribute: string) => {
Expand Down
5 changes: 4 additions & 1 deletion apps/myaccount/src/configs/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2022, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2022-2025, 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
Expand All @@ -18,6 +18,7 @@

import { I18nModuleInitOptions, I18nModuleOptionsInterface, MetaI18N, generateBackendPaths } from "@wso2is/i18n";
import { I18nConstants } from "../constants";
import { AppConstants } from "../constants/app-constants";
// Keep statement as this to avoid cyclic dependency. Do not import from config index.
import { SCIMConfigs } from "../extensions/configs/scim";
import { AppUtils } from "../init/app-utils";
Expand Down Expand Up @@ -163,6 +164,8 @@ export class Config {
window["AppUtils"]?.getConfig()?.ui?.isMultipleEmailsAndMobileNumbersEnabled,
isPasswordInputValidationEnabled: window["AppUtils"]?.getConfig()?.ui?.isPasswordInputValidationEnabled,
isProfileUsernameReadonly: window["AppUtils"]?.getConfig()?.ui?.isProfileUsernameReadonly,
primaryUserStoreDomainName: window[ "AppUtils" ]?.getConfig()?.ui?.primaryUserStoreDomainName?.toUpperCase()
pavinduLakshan marked this conversation as resolved.
Show resolved Hide resolved
?? AppConstants.PRIMARY_USER_STORE_IDENTIFIER,
privacyPolicyConfigs: window["AppUtils"]?.getConfig()?.ui?.privacyPolicyConfigs,
productName: window["AppUtils"]?.getConfig()?.ui?.productName,
productVersionConfig: window["AppUtils"]?.getConfig()?.ui?.productVersionConfig,
Expand Down
31 changes: 19 additions & 12 deletions features/admin.administrators.v1/components/all-users-list.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2024-2025, 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
Expand All @@ -16,6 +16,7 @@
* under the License.
*/

import { useRequiredScopes } from "@wso2is/access-control";
import {
AppState,
FeatureConfigInterface,
Expand All @@ -29,7 +30,7 @@ import { RealmConfigInterface } from "@wso2is/admin.server-configurations.v1";
import { UserAccountTypes, UserManagementConstants } from "@wso2is/admin.users.v1/constants/user-management-constants";
import { UserBasicInterface, UserListInterface } from "@wso2is/admin.users.v1/models";
import { UserManagementUtils } from "@wso2is/admin.users.v1/utils";
import { getUserNameWithoutDomain, hasRequiredScopes, isFeatureEnabled, resolveUserstore } from "@wso2is/core/helpers";
import { getUserNameWithoutDomain, isFeatureEnabled, resolveUserstore } from "@wso2is/core/helpers";
import {
LoadableComponentInterface,
SBACInterface,
Expand All @@ -50,7 +51,7 @@ import React, { ReactElement, ReactNode, SyntheticEvent, useEffect, useState } f
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
import { Header, Icon, ListItemProps, SemanticICONS } from "semantic-ui-react";
import { AdministratorConstants } from "../constants";
import { AdministratorConstants } from "../constants/users";

/**
* Prop types for the all users list component.
Expand Down Expand Up @@ -170,6 +171,11 @@ export const AllUsersList: React.FunctionComponent<AllUsersListProps> = (props:
const [ loading, setLoading ] = useState(false);

const authenticatedUser: string = useSelector((state: AppState) => state?.auth?.username);
const primaryUserStoreDomainName: string = useSelector((state: AppState) =>
state?.config?.ui?.primaryUserStoreDomainName);

const hasUserUpdatePermission: boolean = useRequiredScopes(featureConfig?.users?.scopes?.update);
const hasUserDeteletPermission: boolean = useRequiredScopes(featureConfig?.users?.scopes?.delete);

/**
* Set tenant admin.
Expand All @@ -193,10 +199,8 @@ export const AllUsersList: React.FunctionComponent<AllUsersListProps> = (props:
setUsersList(allUsersList);
}, [ allUsersList ]);

const allowedScopes: string = useSelector((state: AppState) => state?.auth?.allowedScopes);

const handleUserEdit = (user: UserBasicInterface) => {
if (resolveUserstore(user.userName) === userstoresConfig.primaryUserstoreName) {
if (resolveUserstore(user.userName, primaryUserStoreDomainName) === userstoresConfig.primaryUserstoreName) {
history.push(AdministratorConstants.getPaths().get("CUSTOMER_USER_EDIT_PATH").replace(":id", user.id));
} else {
history.push(AdministratorConstants.getPaths().get("COLLABORATOR_USER_EDIT_PATH").replace(":id", user.id));
Expand Down Expand Up @@ -289,7 +293,8 @@ export const AllUsersList: React.FunctionComponent<AllUsersListProps> = (props:
if (user.userName === tenantAdmin) {
return "Owner";
}
if (resolveUserstore(user.userName) === userstoresConfig.primaryUserstoreName) {
if (resolveUserstore(user.userName, primaryUserStoreDomainName)
=== userstoresConfig.primaryUserstoreName) {
return UserAccountTypes.USER;
} else {
return administratorConfig.adminRoleName;
Expand Down Expand Up @@ -453,7 +458,7 @@ export const AllUsersList: React.FunctionComponent<AllUsersListProps> = (props:
? user?.userName?.split("/")[0]
: AdministratorConstants.ASGARDEO_USERSTORE;

return !hasRequiredScopes(featureConfig?.users, featureConfig?.users?.scopes?.update, allowedScopes)
return !hasUserUpdatePermission
|| !isFeatureEnabled(featureConfig?.users,
UserManagementConstants.FEATURE_DICTIONARY.get("USER_UPDATE"))
|| readOnlyUserStores?.includes(userStore.toString())
Expand All @@ -467,7 +472,7 @@ export const AllUsersList: React.FunctionComponent<AllUsersListProps> = (props:
? user?.userName?.split("/")[0]
: AdministratorConstants.ASGARDEO_USERSTORE;

return !hasRequiredScopes(featureConfig?.users, featureConfig?.users?.scopes?.update, allowedScopes)
return !hasUserUpdatePermission
|| !isFeatureEnabled(featureConfig?.users,
UserManagementConstants.FEATURE_DICTIONARY.get("USER_UPDATE"))
|| readOnlyUserStores?.includes(userStore.toString())
Expand All @@ -487,7 +492,7 @@ export const AllUsersList: React.FunctionComponent<AllUsersListProps> = (props:

return !isFeatureEnabled(featureConfig?.users,
UserManagementConstants.FEATURE_DICTIONARY.get("USER_DELETE"))
|| !hasRequiredScopes(featureConfig?.users, featureConfig?.users?.scopes?.delete, allowedScopes)
|| !hasUserDeteletPermission
|| readOnlyUserStores?.includes(userStore.toString())
|| user.userName === realmConfigs?.adminUser || authenticatedUser.includes(user.userName);
},
Expand Down Expand Up @@ -599,14 +604,16 @@ export const AllUsersList: React.FunctionComponent<AllUsersListProps> = (props:
attached
negative
>
{ resolveUserstore(deletingUser.userName) === userstoresConfig.primaryUserstoreName
{ resolveUserstore(deletingUser.userName, primaryUserStoreDomainName)
=== userstoresConfig.primaryUserstoreName
? t("user:deleteUser.confirmationModal.message")
: t("extensions:manage.guest.deleteUser.confirmationModal.message")
}
</ConfirmationModal.Message>
<ConfirmationModal.Content data-testid={ `${ testId }-confirmation-modal-content` }>
<div className="modal-alert-wrapper"> { alert && alertComponent }</div>
{ resolveUserstore(deletingUser.userName) === userstoresConfig.primaryUserstoreName
{ resolveUserstore(deletingUser.userName, primaryUserStoreDomainName)
=== userstoresConfig.primaryUserstoreName
? (
deletingUser[SCIMConfigs.scim.enterpriseSchema]?.userSourceId
? t("user:deleteJITUser.confirmationModal.content")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2024-2025, 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
Expand All @@ -16,6 +16,7 @@
* under the License.
*/

import { useRequiredScopes } from "@wso2is/access-control";
import { FeatureConfigInterface } from "@wso2is/admin.core.v1/models";
import { AppState } from "@wso2is/admin.core.v1/store";
import { administratorConfig } from "@wso2is/admin.extensions.v1/configs/administrator";
Expand All @@ -26,8 +27,7 @@ import { UserRolesList } from "@wso2is/admin.users.v1/components/user-roles-list
import { UserSessions } from "@wso2is/admin.users.v1/components/user-sessions";
import { AdminAccountTypes, UserManagementConstants } from "@wso2is/admin.users.v1/constants/user-management-constants";
import { UserManagementUtils } from "@wso2is/admin.users.v1/utils/user-management-utils";
import { UserstoreConstants } from "@wso2is/core/constants";
import { hasRequiredScopes, isFeatureEnabled } from "@wso2is/core/helpers";
import { isFeatureEnabled } from "@wso2is/core/helpers";
import { AlertInterface, ProfileInfoInterface, SBACInterface } from "@wso2is/core/models";
import { addAlert } from "@wso2is/core/store";
import { ContentLoader, Message, ResourceTab } from "@wso2is/react-components";
Expand Down Expand Up @@ -86,15 +86,18 @@ export const EditGuestUser: FunctionComponent<EditGuestUserPropsInterface> = (
const { t } = useTranslation();
const dispatch: Dispatch = useDispatch();

const allowedScopes: string = useSelector((state: AppState) => state?.auth?.allowedScopes);

const [ isReadOnly, setReadOnly ] = useState<boolean>(false);
const [ allowDeleteOnly, setAllowDeleteOnly ] = useState<boolean>(false);
const [ isProfileTabsLoading, setIsProfileTabsLoading ] = useState<boolean>(true);
const [ isReadOnlyUserStore, setReadOnlyUserStore ] = useState<boolean>(false);
const [ adminUserType, setAdminUserType ] = useState<string>(AdminAccountTypes.EXTERNAL);

const authenticatedUserTenanted: string = useSelector((state: AppState) => state?.auth?.username);
const primaryUserStoreDomainName: string = useSelector((state: AppState) =>
state?.config?.ui?.primaryUserStoreDomainName);

const hasUserUpdatePermission: boolean = useRequiredScopes(featureConfig?.users?.scopes?.update);
const hasUserDeletePermission: boolean = useRequiredScopes(featureConfig?.users?.scopes?.delete);

const authenticatedUser: string = useMemo(() => {
const authenticatedUserComponents: string[] = authenticatedUserTenanted.split("@");
Expand All @@ -120,21 +123,21 @@ export const EditGuestUser: FunctionComponent<EditGuestUserPropsInterface> = (

const userStore: string = user?.userName?.split("/").length > 1
? user?.userName?.split("/")[ 0 ]
: UserstoreConstants.PRIMARY_USER_STORE;
: primaryUserStoreDomainName;

setReadOnlyUserStore(readOnlyUserStores?.includes(userStore?.toString()));

if (!isFeatureEnabled(featureConfig?.users, UserManagementConstants.FEATURE_DICTIONARY.get("USER_UPDATE"))
|| readOnlyUserStores?.includes(userStore?.toString())
|| !hasRequiredScopes(featureConfig?.users, featureConfig?.users?.scopes?.update, allowedScopes)
|| !hasUserUpdatePermission
|| user[ SCIMConfigs.scim.enterpriseSchema ]?.userSourceId
) {
setReadOnly(true);
}

if (isFeatureEnabled(featureConfig?.users, UserManagementConstants.FEATURE_DICTIONARY.get("USER_DELETE")) &&
!(user.userName == realmConfigs?.adminUser) &&
hasRequiredScopes(featureConfig?.users, featureConfig?.users?.scopes?.delete, allowedScopes)) {
hasUserDeletePermission) {
setAllowDeleteOnly(true);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2024-2025, 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
Expand All @@ -24,7 +24,6 @@ import {
UserListInterface
} from "@wso2is/admin.core.v1";
import { InvitationStatus, UserInviteInterface } from "@wso2is/admin.users.v1/models";
import { PRIMARY_USERSTORE } from "@wso2is/admin.userstores.v1/constants/user-store-constants";
import { TestableComponentInterface } from "@wso2is/core/models";
import { DocumentationLink, ListLayout, Message, Text, useDocumentation } from "@wso2is/react-components";
import React, { FunctionComponent, ReactElement, useEffect, useState } from "react";
Expand Down Expand Up @@ -97,6 +96,8 @@ const GuestUsersPage: FunctionComponent<GuestUsersPageInterface> = (
const [ isInvitationStatusOptionChanged, setIsInvitationStatusOptionChanged ] = useState<boolean>(false);

const featureConfig: FeatureConfigInterface = useSelector((state: AppState) => state.config.ui.features);
const primaryUserStoreDomainName: string = useSelector((state: AppState) =>
state?.config?.ui?.primaryUserStoreDomainName);

/**
* Show the description message for the first time.
Expand Down Expand Up @@ -191,9 +192,9 @@ const GuestUsersPage: FunctionComponent<GuestUsersPageInterface> = (
useEffect(() => {
if (invitationStatusOption === InvitationStatus.ACCEPTED) {
if (searchQuery == undefined || searchQuery == "") {
getUsersList(listItemLimit, listOffset + 1, null, null, PRIMARY_USERSTORE);
getUsersList(listItemLimit, listOffset + 1, null, null, primaryUserStoreDomainName);
} else {
getUsersList(listItemLimit, listOffset + 1, searchQuery, null, PRIMARY_USERSTORE);
getUsersList(listItemLimit, listOffset + 1, searchQuery, null, primaryUserStoreDomainName);
}
}
}, [ listOffset, listItemLimit ]);
Expand Down Expand Up @@ -271,11 +272,11 @@ const GuestUsersPage: FunctionComponent<GuestUsersPageInterface> = (
setSearchQuery(query);
if (invitationStatusOption === InvitationStatus.ACCEPTED) {
if (query === "userName sw ") {
getUsersList(listItemLimit, listOffset, null, null, PRIMARY_USERSTORE);
getUsersList(listItemLimit, listOffset, null, null, primaryUserStoreDomainName);

return;
}
getUsersList(listItemLimit, listOffset, query, null, PRIMARY_USERSTORE);
getUsersList(listItemLimit, listOffset, query, null, primaryUserStoreDomainName);
}
};

Expand Down Expand Up @@ -457,7 +458,7 @@ const GuestUsersPage: FunctionComponent<GuestUsersPageInterface> = (
readOnlyUserStores={ null }
featureConfig={ featureConfig }
onUserDelete={ () =>
getUsersList(listItemLimit, listOffset, null, null, PRIMARY_USERSTORE)
getUsersList(listItemLimit, listOffset, null, null, primaryUserStoreDomainName)
}
/>)
}
Expand Down
Loading
Loading