17
17
*/
18
18
19
19
// Keep statement as this to avoid cyclic dependency. Do not import from config index.
20
+ import { AppState } from "@wso2is/admin.core.v1/store" ;
20
21
import { userConfig } from "@wso2is/admin.extensions.v1" ;
21
22
import { administratorConfig } from "@wso2is/admin.extensions.v1/configs/administrator" ;
23
+ import { SCIMConfigInterface } from "@wso2is/admin.extensions.v1/configs/models/scim" ;
22
24
import { SCIMConfigs } from "@wso2is/admin.extensions.v1/configs/scim" ;
23
25
import { userstoresConfig } from "@wso2is/admin.extensions.v1/configs/userstores" ;
24
26
import { updateGroupDetails , useGroupList } from "@wso2is/admin.groups.v1/api/groups" ;
@@ -28,9 +30,12 @@ import useUserStores from "@wso2is/admin.userstores.v1/hooks/use-user-stores";
28
30
import { UserStoreListItem } from "@wso2is/admin.userstores.v1/models" ;
29
31
import { useValidationConfigData } from "@wso2is/admin.validation.v1/api" ;
30
32
import { ValidationFormInterface } from "@wso2is/admin.validation.v1/models" ;
33
+ import { isFeatureEnabled } from "@wso2is/core/helpers" ;
31
34
import {
32
35
AlertLevels ,
36
+ FeatureAccessConfigInterface ,
33
37
IdentifiableComponentInterface ,
38
+ MultiValueAttributeInterface ,
34
39
RolesInterface ,
35
40
TestableComponentInterface
36
41
} from "@wso2is/core/models" ;
@@ -40,10 +45,12 @@ import { Heading, LinkButton, PrimaryButton, Steps, useWizardAlert } from "@wso2
40
45
import { AxiosError , AxiosResponse } from "axios" ;
41
46
import cloneDeep from "lodash-es/cloneDeep" ;
42
47
import intersection from "lodash-es/intersection" ;
48
+ import isEmpty from "lodash-es/isEmpty" ;
43
49
import merge from "lodash-es/merge" ;
50
+ import omit from "lodash-es/omit" ;
44
51
import React , { FunctionComponent , ReactElement , useEffect , useMemo , useState } from "react" ;
45
52
import { useTranslation } from "react-i18next" ;
46
- import { useDispatch } from "react-redux" ;
53
+ import { useDispatch , useSelector } from "react-redux" ;
47
54
import { Dispatch } from "redux" ;
48
55
import { Grid , Icon , Modal } from "semantic-ui-react" ;
49
56
import { AddUserUpdated } from "./steps/add-user-basic" ;
@@ -58,10 +65,11 @@ import {
58
65
} from "../../constants" ;
59
66
import {
60
67
AddUserWizardStateInterface ,
68
+ EmailsInterface ,
61
69
PayloadInterface ,
62
70
UserDetailsInterface ,
63
- WizardStepInterface ,
64
- createEmptyUserDetails } from "../../models/user" ;
71
+ WizardStepInterface
72
+ } from "../../models/user" ;
65
73
import { generatePassword , getConfiguration , getUsernameConfiguration } from "../../utils" ;
66
74
67
75
interface AddUserWizardPropsInterface extends IdentifiableComponentInterface , TestableComponentInterface {
@@ -124,6 +132,9 @@ export const AddUserWizard: FunctionComponent<AddUserWizardPropsInterface> = (
124
132
const [ submitGroupList , setSubmitGroupList ] = useTrigger ( ) ;
125
133
const [ finishSubmit , setFinishSubmit ] = useTrigger ( ) ;
126
134
135
+ const userFeatureConfig : FeatureAccessConfigInterface = useSelector (
136
+ ( state : AppState ) => state . config . ui . features . users ) ;
137
+
127
138
const [ partiallyCompletedStep , setPartiallyCompletedStep ] = useState < number > ( undefined ) ;
128
139
const [ currentWizardStep , setCurrentWizardStep ] = useState < number > ( currentStep ) ;
129
140
const [ wizardState , setWizardState ] = useState < WizardStateInterface > ( undefined ) ;
@@ -141,6 +152,11 @@ export const AddUserWizard: FunctionComponent<AddUserWizardPropsInterface> = (
141
152
const [ submitStep , setSubmitStep ] = useState < WizardStepsFormTypes > ( undefined ) ;
142
153
const [ selectedGroupsList , setSelectedGroupList ] = useState < GroupsInterface [ ] > ( [ ] ) ;
143
154
155
+ const isAttributeProfileForUserCreationEnabled : boolean = isFeatureEnabled (
156
+ userFeatureConfig ,
157
+ UserManagementConstants . ATTRIBUTE_PROFILES_FOR_USER_CREATION_FEATURE_FLAG
158
+ ) ;
159
+
144
160
const excludedAttributes : string = "members" ;
145
161
146
162
const {
@@ -444,62 +460,84 @@ export const AddUserWizard: FunctionComponent<AddUserWizardPropsInterface> = (
444
460
username = userInfo . domain + "/" + userInfo . userName ;
445
461
}
446
462
447
- let userDetails : UserDetailsInterface = createEmptyUserDetails ( ) ;
463
+ let userDetails : UserDetailsInterface = {
464
+ emails : [
465
+ {
466
+ primary : true ,
467
+ value : userInfo . email
468
+ }
469
+ ] ,
470
+ name : {
471
+ familyName : userInfo . lastName ,
472
+ givenName : userInfo . firstName
473
+ } ,
474
+ profileUrl : userInfo . profileUrl ,
475
+ userName : username
476
+ } ;
477
+
448
478
const password : string = userInfo . newPassword ;
449
479
450
480
// Users who get invited offline are also considered as password-based users.
451
481
// They will be assigned a randomly generated temporary password.
452
482
// Temporary password can be changed via the offline invite link.
453
483
if ( askPasswordFromUser ) {
454
- userDetails = {
455
- emails : [
456
- {
457
- primary : true ,
458
- value : userInfo . email
459
- }
460
- ] ,
461
- name : {
462
- familyName : userInfo . lastName ,
463
- givenName : userInfo . firstName
464
- } ,
465
- password : password ,
466
- profileUrl : userInfo . profileUrl ,
467
- userName : username
468
- } ;
484
+ userDetails . password = password ;
469
485
} else if ( isOfflineUser ) {
470
- userDetails = {
471
- emails : [
472
- {
473
- primary : true ,
474
- value : userInfo . email
475
- }
476
- ] ,
477
- name : {
478
- familyName : userInfo . lastName ,
479
- givenName : userInfo . firstName
480
- } ,
481
- password : generateRandomPassword ( ) ,
482
- profileUrl : userInfo . profileUrl ,
483
- userName : username
484
- } ;
486
+ userDetails . password = generateRandomPassword ( ) ;
485
487
} else {
486
- userDetails = {
487
- emails : [
488
- {
489
- primary : true ,
490
- value : userInfo . email
491
- }
492
- ] ,
493
- name : {
494
- familyName : userInfo . lastName ,
495
- givenName : userInfo . firstName
496
- } ,
497
- profileUrl : userInfo . profileUrl ,
498
- [ SCIMConfigs . scim . systemSchema ] : {
499
- askPassword : "true"
500
- } ,
501
- userName : username
488
+ userDetails [ SCIMConfigs . scim . systemSchema ] = {
489
+ askPassword : "true"
490
+ } ;
491
+ }
492
+
493
+ if ( isAttributeProfileForUserCreationEnabled ) {
494
+ const mergedSCIMSchema : SCIMConfigInterface = {
495
+ ...userDetails [ SCIMConfigs . scim . systemSchema ] ,
496
+ ...userInfo [ SCIMConfigs . scim . systemSchema ]
502
497
} ;
498
+
499
+ const combinedUserDetails : UserDetailsInterface = omit ( {
500
+ ...userInfo ,
501
+ ...userDetails ,
502
+ [ SCIMConfigs . scim . systemSchema ] : mergedSCIMSchema
503
+ } , "passwordOption" , "newPassword" , "userType" , "domain" , "firstName" , "lastName" ) ;
504
+
505
+ // If email value is not present, use the emails value.
506
+ if ( isEmpty ( userInfo ?. email ) && userInfo ?. emails . length > 0 ) {
507
+ delete combinedUserDetails . email ;
508
+
509
+ // Primary will be the string value in the emails array.
510
+ const primaryEmail : string = userInfo . emails . find (
511
+ ( subAttribute : ( string | MultiValueAttributeInterface | EmailsInterface ) ) =>
512
+ typeof subAttribute === "string" ) as string ;
513
+
514
+ if ( primaryEmail ) {
515
+ combinedUserDetails . emails = [
516
+ {
517
+ primary : true ,
518
+ value : primaryEmail
519
+ } ,
520
+ ...userInfo ?. emails
521
+ ] ;
522
+ }
523
+ else {
524
+ // This means that the user has not provided any email value.
525
+ // This is an invalid case. Therefore, we need to throw an error.
526
+ dispatch ( addAlert ( {
527
+ description : t (
528
+ "users:notifications.addUser.genericError.description"
529
+ ) ,
530
+ level : AlertLevels . ERROR ,
531
+ message : t (
532
+ "users:notifications.addUser.genericError.message"
533
+ )
534
+ } ) ) ;
535
+
536
+ return ;
537
+ }
538
+ }
539
+
540
+ userDetails = { ...combinedUserDetails } ;
503
541
}
504
542
505
543
setIsSubmitting ( true ) ;
0 commit comments