@webitel/ui-sdk 24.12.95 → 24.12.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/ui-sdk.css +1 -1
- package/dist/ui-sdk.js +3179 -3219
- package/dist/ui-sdk.umd.cjs +16 -16
- package/package.json +14 -8
- package/src/api/defaults/getDefaultInstance/getDefaultInstance.js +5 -2
- package/src/api/defaults/index.js +1 -1
- package/src/components/wt-dual-panel/wt-dual-panel.vue +1 -2
- package/src/components/wt-icon-action/iconMappings.js +1 -1
- package/src/composables/useAccessControl/v2/createUserAccessControl.ts +66 -0
- package/src/composables/useAccessControl/v2/types/CreateUserAccessControl.d.ts +21 -0
- package/src/enums/CrudAction/CrudAction.js +6 -0
- package/src/enums/CrudAction/CrudAction.ts +8 -0
- package/src/enums/WtObject/WtObject.js +51 -1
- package/src/enums/WtObject/WtObject.ts +51 -1
- package/src/enums/index.js +6 -19
- package/src/enums/index.ts +33 -0
- package/src/locale/en/en.js +1 -1
- package/src/locale/ru/ru.js +1 -1
- package/src/locale/ua/ua.js +1 -1
- package/src/modules/Filters/v2/headers/createTableHeadersStore.ts +127 -78
- package/src/modules/Filters/v2/persist/usePersistedStorage.ts +0 -5
- package/src/modules/Filters/v2/table/createTableStore.store.ts +3 -2
- package/src/modules/Userinfo/api/userinfo.js +21 -34
- package/src/modules/Userinfo/store/UserinfoStoreModule.js +1 -1
- package/src/modules/Userinfo/v2/api/UserinfoAPI.ts +62 -0
- package/src/modules/Userinfo/v2/enums/GlobalActions/GlobalActions.ts +36 -0
- package/src/modules/Userinfo/v2/enums/ScopeClass/ScopeClass.ts +47 -0
- package/src/modules/Userinfo/v2/enums/index.ts +7 -0
- package/src/modules/Userinfo/v2/index.ts +0 -0
- package/src/modules/Userinfo/v2/mappings/mappings.ts +161 -0
- package/src/modules/Userinfo/v2/scripts/utils.ts +123 -0
- package/src/modules/Userinfo/v2/stores/__tests__/accessStore.spec.ts +136 -0
- package/src/modules/Userinfo/v2/stores/accessStore.ts +131 -0
- package/src/modules/Userinfo/v2/stores/userinfoStore.ts +56 -0
- package/src/modules/Userinfo/v2/types/UserAccess.d.ts +118 -0
|
@@ -32,7 +32,7 @@ export const createTableStore = <Entity extends { id: string; etag?: string }>(
|
|
|
32
32
|
|
|
33
33
|
const headersStore = useHeadersStore();
|
|
34
34
|
const { headers, shownHeaders, fields, sort } = storeToRefs(headersStore);
|
|
35
|
-
const { updateSort, updateShownHeaders } = headersStore;
|
|
35
|
+
const { updateSort, updateShownHeaders, setupPersistence: setupHeadersPersistence } = headersStore;
|
|
36
36
|
|
|
37
37
|
const filtersStore = useFiltersStore();
|
|
38
38
|
const { filtersManager, isRestoring: isFiltersRestoring } =
|
|
@@ -116,12 +116,13 @@ export const createTableStore = <Entity extends { id: string; etag?: string }>(
|
|
|
116
116
|
await Promise.allSettled([
|
|
117
117
|
setupPaginationPersistence(),
|
|
118
118
|
setupFiltersPersistence(),
|
|
119
|
+
setupHeadersPersistence(),
|
|
119
120
|
]);
|
|
120
121
|
|
|
121
122
|
let loadingAfterFiltersChange = false;
|
|
122
123
|
|
|
123
124
|
watch(
|
|
124
|
-
[() => filtersManager.value.getAllValues(), sort, size],
|
|
125
|
+
[() => filtersManager.value.getAllValues(), sort, fields, size],
|
|
125
126
|
async () => {
|
|
126
127
|
loadingAfterFiltersChange = true;
|
|
127
128
|
updatePage(1);
|
|
@@ -1,38 +1,25 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
getSession,
|
|
3
|
+
getUiVisibilityAccess,
|
|
4
|
+
logout,
|
|
5
|
+
setInstance,
|
|
6
|
+
} from '../v2/api/UserinfoAPI';
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated remove after v25.06 release
|
|
10
|
+
* @description
|
|
11
|
+
* backward compat
|
|
12
|
+
* */
|
|
13
|
+
const userinfo = (instance) => {
|
|
14
|
+
if (instance) {
|
|
15
|
+
setInstance(instance);
|
|
16
|
+
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
throw applyTransform(err, [notify]);
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
async logout() {
|
|
28
|
-
const url = '/logout';
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
return await instance.post(url, {});
|
|
32
|
-
} catch (err) {
|
|
33
|
-
throw applyTransform(err, [notify]);
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
});
|
|
18
|
+
return {
|
|
19
|
+
getSession,
|
|
20
|
+
logout,
|
|
21
|
+
getUiVisibilityAccess,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
37
24
|
|
|
38
25
|
export default userinfo;
|
|
@@ -117,7 +117,7 @@ export default class UserinfoStoreModule extends BaseStoreModule {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
await context.dispatch('SET_SESSION', session);
|
|
120
|
-
const access = await userinfo.
|
|
120
|
+
const access = await userinfo.getUiVisibilityAccess();
|
|
121
121
|
await context.dispatch(
|
|
122
122
|
'SET_APPLICATIONS_ACCESS',
|
|
123
123
|
new ApplicationsAccess({ access }).getAccess(),
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
import getDefaultInstance from '../../../../api/defaults/getDefaultInstance/getDefaultInstance';
|
|
4
|
+
import applyTransform, {
|
|
5
|
+
merge,
|
|
6
|
+
notify,
|
|
7
|
+
snakeToCamel,
|
|
8
|
+
} from '../../../../api/transformers/index.js';
|
|
9
|
+
import type {
|
|
10
|
+
GlobalAccessApiResponseItem,
|
|
11
|
+
ScopeAccessApiResponseItem,
|
|
12
|
+
VisibilityAccess,
|
|
13
|
+
} from '../types/UserAccess';
|
|
14
|
+
|
|
15
|
+
let instance = getDefaultInstance();
|
|
16
|
+
|
|
17
|
+
const setInstance = (newInstance: AxiosInstance) => {
|
|
18
|
+
instance = newInstance;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const getSession = async (): Promise<{
|
|
22
|
+
scope: ScopeAccessApiResponseItem[];
|
|
23
|
+
permissions: GlobalAccessApiResponseItem[];
|
|
24
|
+
userId: string;
|
|
25
|
+
}> => {
|
|
26
|
+
const url = '/userinfo';
|
|
27
|
+
try {
|
|
28
|
+
const defaultObject = () => ({
|
|
29
|
+
scope: [],
|
|
30
|
+
permissions: [],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const response = await instance.get(url);
|
|
34
|
+
return applyTransform(response.data, [
|
|
35
|
+
merge(defaultObject()),
|
|
36
|
+
snakeToCamel(),
|
|
37
|
+
]);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
throw applyTransform(err, [notify]);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const getUiVisibilityAccess = async (): Promise<VisibilityAccess> => {
|
|
44
|
+
const url = 'role/metadata/access';
|
|
45
|
+
try {
|
|
46
|
+
const response = await instance.get(url);
|
|
47
|
+
return applyTransform(response.data, [snakeToCamel()]);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
throw applyTransform(err, [notify]);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const logout = async () => {
|
|
54
|
+
const url = '/logout';
|
|
55
|
+
try {
|
|
56
|
+
return await instance.post(url, {});
|
|
57
|
+
} catch (err) {
|
|
58
|
+
throw applyTransform(err, [notify]);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export { getSession, getUiVisibilityAccess, logout, setInstance };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
* @description
|
|
4
|
+
* Represents backend response
|
|
5
|
+
* format for Global Access to Crud Actions.
|
|
6
|
+
* Should be converted to {@link CrudAction}
|
|
7
|
+
* */
|
|
8
|
+
export const CrudGlobalAction = {
|
|
9
|
+
Read: 'read',
|
|
10
|
+
Write: 'write',
|
|
11
|
+
Delete: 'delete',
|
|
12
|
+
Add: 'add',
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
export type CrudGlobalAction =
|
|
16
|
+
(typeof CrudGlobalAction)[keyof typeof CrudGlobalAction];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @description
|
|
20
|
+
* Represents access to global specific actions
|
|
21
|
+
* like downloading files or exporting data
|
|
22
|
+
* */
|
|
23
|
+
export const SpecialGlobalAction = {
|
|
24
|
+
PlaybackRecordFile: 'playbackRecordFile',
|
|
25
|
+
ManageUserLicense: 'manageUserLicense',
|
|
26
|
+
SchemeVariables: 'schemeVariables',
|
|
27
|
+
SystemSetting: 'systemSetting',
|
|
28
|
+
ManageUserRoles: 'manageUserRoles',
|
|
29
|
+
ExportDataGrid: 'exportDataGrid',
|
|
30
|
+
ViewCdrPhoneNumbers: 'viewCdrPhoneNumbers',
|
|
31
|
+
ChangeUserPassword: 'changeUserPassword',
|
|
32
|
+
EavesdropCall: 'eavesdropCall',
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
export type SpecialGlobalAction =
|
|
36
|
+
(typeof SpecialGlobalAction)[keyof typeof SpecialGlobalAction];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description
|
|
3
|
+
* backend `scope` field userinfo response classes
|
|
4
|
+
* `class` represents a group of entities, managed by this one class
|
|
5
|
+
*/
|
|
6
|
+
export const ScopeClass = {
|
|
7
|
+
/**
|
|
8
|
+
* Includes:
|
|
9
|
+
* - {@link WtObject.Agent}
|
|
10
|
+
* - {@link }
|
|
11
|
+
*/
|
|
12
|
+
Agent: 'cc_agent',
|
|
13
|
+
Queue: 'cc_queue',
|
|
14
|
+
Dictionaries: 'dictionaries',
|
|
15
|
+
EmailProfile: 'email_profile',
|
|
16
|
+
|
|
17
|
+
Skills: 'cc_skill',
|
|
18
|
+
Users: 'users',
|
|
19
|
+
Devices: 'devices',
|
|
20
|
+
AcrRouting: 'acr_routing',
|
|
21
|
+
Gateways: 'gateways',
|
|
22
|
+
AcrChatPlan: 'acr_chat_plan',
|
|
23
|
+
Chats: 'chats',
|
|
24
|
+
List: 'cc_list',
|
|
25
|
+
Calendars: 'calendars',
|
|
26
|
+
Team: 'cc_team',
|
|
27
|
+
ResourceGroup: 'cc_resource_group',
|
|
28
|
+
Resource: 'cc_resource',
|
|
29
|
+
StorageProfile: 'storage_profile',
|
|
30
|
+
CognitiveProfile: 'cognitive_profile',
|
|
31
|
+
SingleSignOn: 'single_sign_on',
|
|
32
|
+
ImportTemplate: 'import_template',
|
|
33
|
+
Trigger: 'trigger',
|
|
34
|
+
Schema: 'schema',
|
|
35
|
+
Role: 'roles',
|
|
36
|
+
Contacts: 'contacts',
|
|
37
|
+
MediaFile: 'media_file',
|
|
38
|
+
Logger: 'logger',
|
|
39
|
+
Calls: 'calls',
|
|
40
|
+
RecordFile: 'record_file',
|
|
41
|
+
ContactGroups: 'contact_groups',
|
|
42
|
+
ChatBots: 'chat_bots',
|
|
43
|
+
Cases: 'cases',
|
|
44
|
+
CaseComments: 'case_comments',
|
|
45
|
+
} as const;
|
|
46
|
+
|
|
47
|
+
export type ScopeClass = (typeof ScopeClass)[keyof typeof ScopeClass];
|
|
File without changes
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import invert from 'lodash/fp/invert';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AdminSections,
|
|
5
|
+
AuditorSections,
|
|
6
|
+
CrmSections,
|
|
7
|
+
CrudAction,
|
|
8
|
+
SupervisorSections,
|
|
9
|
+
WtApplication,
|
|
10
|
+
WtObject,
|
|
11
|
+
} from '../../../../enums';
|
|
12
|
+
import { CrudGlobalAction, ScopeClass } from '../enums';
|
|
13
|
+
|
|
14
|
+
export const mapGlobalActionToCrudAction = {
|
|
15
|
+
[CrudGlobalAction.Add]: CrudAction.Create,
|
|
16
|
+
[CrudGlobalAction.Read]: CrudAction.Read,
|
|
17
|
+
[CrudGlobalAction.Write]: CrudAction.Update,
|
|
18
|
+
[CrudGlobalAction.Delete]: CrudAction.Delete,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const mapCrudActionToGlobalAction = invert(mapGlobalActionToCrudAction);
|
|
22
|
+
|
|
23
|
+
/* one-to-many */
|
|
24
|
+
export const mapScopeClassToWtObjects: Record<ScopeClass, WtObject[]> = {
|
|
25
|
+
[ScopeClass.Users]: [WtObject.User],
|
|
26
|
+
[ScopeClass.Devices]: [WtObject.Device],
|
|
27
|
+
[ScopeClass.Schema]: [WtObject.Flow],
|
|
28
|
+
[ScopeClass.AcrRouting]: [WtObject.Dialplan],
|
|
29
|
+
[ScopeClass.Gateways]: [WtObject.Gateway],
|
|
30
|
+
[ScopeClass.AcrChatPlan]: [WtObject.Chatplan],
|
|
31
|
+
[ScopeClass.Chats]: [WtObject.ChatGateway],
|
|
32
|
+
[ScopeClass.Dictionaries]: [
|
|
33
|
+
WtObject.Region,
|
|
34
|
+
WtObject.Bucket,
|
|
35
|
+
WtObject.Communication,
|
|
36
|
+
WtObject.PauseCause,
|
|
37
|
+
],
|
|
38
|
+
[ScopeClass.List]: [WtObject.Blacklist],
|
|
39
|
+
[ScopeClass.Skills]: [WtObject.Skill],
|
|
40
|
+
[ScopeClass.Calendars]: [WtObject.Calendar],
|
|
41
|
+
[ScopeClass.MediaFile]: [WtObject.Media],
|
|
42
|
+
[ScopeClass.Agent]: [WtObject.Agent],
|
|
43
|
+
[ScopeClass.Queue]: [WtObject.Queue, WtObject.Member],
|
|
44
|
+
[ScopeClass.ResourceGroup]: [WtObject.ResourceGroup],
|
|
45
|
+
[ScopeClass.Resource]: [WtObject.Resource],
|
|
46
|
+
[ScopeClass.Team]: [WtObject.Team],
|
|
47
|
+
[ScopeClass.StorageProfile]: [WtObject.Storage],
|
|
48
|
+
[ScopeClass.CognitiveProfile]: [WtObject.CognitiveProfile],
|
|
49
|
+
[ScopeClass.EmailProfile]: [WtObject.EmailProfile],
|
|
50
|
+
[ScopeClass.SingleSignOn]: [WtObject.SingleSignOn],
|
|
51
|
+
[ScopeClass.ImportTemplate]: [WtObject.ImportCsv],
|
|
52
|
+
[ScopeClass.Trigger]: [WtObject.Trigger],
|
|
53
|
+
[ScopeClass.Role]: [WtObject.Role],
|
|
54
|
+
[ScopeClass.Contacts]: [WtObject.Contact],
|
|
55
|
+
[ScopeClass.Logger]: [WtObject.Logger], // Change log in Admin
|
|
56
|
+
[ScopeClass.Calls]: [WtObject.Call], // Call history
|
|
57
|
+
[ScopeClass.RecordFile]: [WtObject.RecordFile], // Call history
|
|
58
|
+
[ScopeClass.ContactGroups]: [WtObject.ContactGroup], //CRM
|
|
59
|
+
[ScopeClass.ChatBots]: [WtObject.ChatBot], // routing cht_gateway
|
|
60
|
+
[ScopeClass.Cases]: [WtObject.Cases], // CRM
|
|
61
|
+
[ScopeClass.CaseComments]: [WtObject.CaseComment],
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const mapScopeClassAccessTokenToCrudAction = {
|
|
65
|
+
r: CrudAction.Read,
|
|
66
|
+
w: CrudAction.Update,
|
|
67
|
+
d: CrudAction.Delete,
|
|
68
|
+
x: CrudAction.Create,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const mapCrudActionToScopeClassAccessToken = invert(
|
|
72
|
+
mapScopeClassAccessTokenToCrudAction,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
type UiSection =
|
|
76
|
+
| AdminSections
|
|
77
|
+
| AuditorSections
|
|
78
|
+
| CrmSections
|
|
79
|
+
| SupervisorSections;
|
|
80
|
+
export const mapWtObjectToUiSection: Record<
|
|
81
|
+
WtApplication,
|
|
82
|
+
Partial<Record<WtObject, UiSection>>
|
|
83
|
+
> = {
|
|
84
|
+
// if we don`t need empty objects we can use Partial<Record<WtApplication, Partial<Record<WtObject, UiSection>>>>
|
|
85
|
+
[WtApplication.Agent]: {},
|
|
86
|
+
[WtApplication.History]: {},
|
|
87
|
+
[WtApplication.Analytics]: {},
|
|
88
|
+
|
|
89
|
+
// Admin sections
|
|
90
|
+
[WtApplication.Admin]: {
|
|
91
|
+
[WtObject.User]: AdminSections.Users,
|
|
92
|
+
[WtObject.Agent]: AdminSections.Agents,
|
|
93
|
+
[WtObject.License]: AdminSections.License,
|
|
94
|
+
[WtObject.Device]: AdminSections.Devices,
|
|
95
|
+
[WtObject.Flow]: AdminSections.Flow,
|
|
96
|
+
[WtObject.Dialplan]: AdminSections.Dialplan,
|
|
97
|
+
[WtObject.Gateway]: AdminSections.Gateways,
|
|
98
|
+
[WtObject.Chatplan]: AdminSections.Chatplan,
|
|
99
|
+
[WtObject.ChatGateway]: AdminSections.ChatGateways,
|
|
100
|
+
[WtObject.Skill]: AdminSections.Skills,
|
|
101
|
+
[WtObject.Bucket]: AdminSections.Buckets,
|
|
102
|
+
[WtObject.Blacklist]: AdminSections.Blacklist,
|
|
103
|
+
[WtObject.Region]: AdminSections.Regions,
|
|
104
|
+
[WtObject.Calendar]: AdminSections.Calendars,
|
|
105
|
+
[WtObject.Communication]: AdminSections.Communications,
|
|
106
|
+
[WtObject.PauseCause]: AdminSections.PauseCause,
|
|
107
|
+
[WtObject.Media]: AdminSections.Media,
|
|
108
|
+
[WtObject.Team]: AdminSections.Teams,
|
|
109
|
+
[WtObject.Resource]: AdminSections.Resources,
|
|
110
|
+
[WtObject.ResourceGroup]: AdminSections.ResourceGroups,
|
|
111
|
+
[WtObject.Queue]: AdminSections.Queues,
|
|
112
|
+
[WtObject.Storage]: AdminSections.Storage,
|
|
113
|
+
[WtObject.CognitiveProfile]: AdminSections.CognitiveProfiles,
|
|
114
|
+
[WtObject.EmailProfile]: AdminSections.EmailProfiles,
|
|
115
|
+
[WtObject.ImportCsv]: AdminSections.ImportCsv,
|
|
116
|
+
[WtObject.Trigger]: AdminSections.Triggers,
|
|
117
|
+
[WtObject.Role]: AdminSections.Roles,
|
|
118
|
+
[WtObject.Object]: AdminSections.Objects,
|
|
119
|
+
[WtObject.ChangeLog]: AdminSections.Changelogs,
|
|
120
|
+
[WtObject.Configuration]: AdminSections.Configuration,
|
|
121
|
+
[WtObject.GlobalVariable]: AdminSections.GlobalVariables,
|
|
122
|
+
[WtObject.ShiftTemplate]: AdminSections.ShiftTemplates,
|
|
123
|
+
[WtObject.PauseTemplate]: AdminSections.PauseTemplates,
|
|
124
|
+
[WtObject.WorkingCondition]: AdminSections.WorkingConditions,
|
|
125
|
+
[WtObject.Member]: AdminSections.Members,
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
// Auditor sections
|
|
129
|
+
[WtApplication.Audit]: {
|
|
130
|
+
[WtObject.Scorecard]: AuditorSections.Scorecards,
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
// Crm sections
|
|
134
|
+
[WtApplication.Crm]: {
|
|
135
|
+
[WtObject.Contact]: CrmSections.Contacts,
|
|
136
|
+
[WtObject.Cases]: CrmSections.Cases,
|
|
137
|
+
[WtObject.Slas]: CrmSections.Slas,
|
|
138
|
+
[WtObject.ServiceCatalog]: CrmSections.ServiceCatalogs,
|
|
139
|
+
[WtObject.CaseSource]: CrmSections.CaseSources,
|
|
140
|
+
[WtObject.CloseReasonGroup]: CrmSections.CloseReasonGroups,
|
|
141
|
+
[WtObject.Priorities]: CrmSections.Priorities,
|
|
142
|
+
[WtObject.Status]: CrmSections.Statuses,
|
|
143
|
+
[WtObject.Source]: CrmSections.Sources,
|
|
144
|
+
[WtObject.ContactGroup]: CrmSections.ContactGroups,
|
|
145
|
+
[WtObject.CustomLookup]: CrmSections.CustomLookups,
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
// Supervisor sections
|
|
149
|
+
[WtApplication.Supervisor]: {
|
|
150
|
+
[WtObject.Queue]: SupervisorSections.Queues,
|
|
151
|
+
[WtObject.Agent]: SupervisorSections.Agents,
|
|
152
|
+
[WtObject.Communication]: SupervisorSections.ActiveCalls,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const mapUiSectionToWtObject = invert(mapWtObjectToUiSection);
|
|
157
|
+
|
|
158
|
+
export const AdminSectionsValues = invert(AdminSections);
|
|
159
|
+
export const AuditorSectionsValues = invert(AuditorSections);
|
|
160
|
+
export const CrmSectionsValues = invert(CrmSections);
|
|
161
|
+
export const SupervisorSectionsValues = invert(SupervisorSections);
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { CrudAction, WtApplication, WtObject } from '../../../../enums';
|
|
2
|
+
import { _wtUiLog as wtlog } from '../../../../scripts/logger';
|
|
3
|
+
import {
|
|
4
|
+
AdminSectionsValues,
|
|
5
|
+
AuditorSectionsValues,
|
|
6
|
+
CrmSectionsValues,
|
|
7
|
+
mapGlobalActionToCrudAction,
|
|
8
|
+
mapScopeClassAccessTokenToCrudAction,
|
|
9
|
+
mapScopeClassToWtObjects,
|
|
10
|
+
mapUiSectionToWtObject,
|
|
11
|
+
mapWtObjectToUiSection,
|
|
12
|
+
SupervisorSectionsValues,
|
|
13
|
+
} from '../mappings/mappings';
|
|
14
|
+
import type {
|
|
15
|
+
AppVisibilityMap,
|
|
16
|
+
GlobalAccessApiResponseItem,
|
|
17
|
+
GlobalAction,
|
|
18
|
+
GlobalActionAccessMap,
|
|
19
|
+
ScopeAccessApiResponseItem,
|
|
20
|
+
ScopeAccessMap,
|
|
21
|
+
SectionVisibilityMap,
|
|
22
|
+
UiSection,
|
|
23
|
+
VisibilityAccess,
|
|
24
|
+
} from '../types/UserAccess.d.ts';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @internal
|
|
28
|
+
* @description
|
|
29
|
+
* backend -> frontend
|
|
30
|
+
* */
|
|
31
|
+
const castGlobalActionToCrudAction = (
|
|
32
|
+
globalAction: GlobalAction,
|
|
33
|
+
): CrudAction | null => {
|
|
34
|
+
return mapGlobalActionToCrudAction[globalAction] || null;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const makeGlobalAccessMap = (
|
|
38
|
+
rawGlobalAccess: GlobalAccessApiResponseItem[],
|
|
39
|
+
): GlobalActionAccessMap => {
|
|
40
|
+
return rawGlobalAccess.reduce((map, { id }) => {
|
|
41
|
+
const key = castGlobalActionToCrudAction(id) || id;
|
|
42
|
+
return map.set(key, true);
|
|
43
|
+
}, new Map());
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const makeScopeAccessMap = (
|
|
47
|
+
rawScope: ScopeAccessApiResponseItem[],
|
|
48
|
+
): ScopeAccessMap => {
|
|
49
|
+
const map = new Map();
|
|
50
|
+
|
|
51
|
+
rawScope.forEach(({ class: scopeClass, access: scopeAccess }) => {
|
|
52
|
+
const access = scopeAccess.split('').reduce((accessMap, token) => {
|
|
53
|
+
accessMap.set(mapScopeClassAccessTokenToCrudAction[token], true);
|
|
54
|
+
return accessMap;
|
|
55
|
+
}, new Map());
|
|
56
|
+
|
|
57
|
+
const scopeClassObjects = mapScopeClassToWtObjects[scopeClass];
|
|
58
|
+
|
|
59
|
+
if (!scopeClassObjects) {
|
|
60
|
+
wtlog.error({ module: 'modules/userinfo' })(
|
|
61
|
+
'Unknown scope class to convert to WtObject:',
|
|
62
|
+
scopeClass,
|
|
63
|
+
);
|
|
64
|
+
map.set(scopeClass, access);
|
|
65
|
+
} else {
|
|
66
|
+
scopeClassObjects.forEach((object) => {
|
|
67
|
+
map.set(object, access);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return map;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const makeAppVisibilityMap = (
|
|
76
|
+
rawVisibility: VisibilityAccess,
|
|
77
|
+
): AppVisibilityMap => {
|
|
78
|
+
const map = new Map();
|
|
79
|
+
Object.entries(rawVisibility).forEach(([app, visibility]) => {
|
|
80
|
+
map.set(app, visibility._enabled);
|
|
81
|
+
});
|
|
82
|
+
return map;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const makeSectionVisibilityMap = (
|
|
86
|
+
rawVisibility: VisibilityAccess,
|
|
87
|
+
): SectionVisibilityMap => {
|
|
88
|
+
const map = new Map();
|
|
89
|
+
|
|
90
|
+
Object.values(rawVisibility).forEach((appSectionsVisibility) => {
|
|
91
|
+
Object.entries(appSectionsVisibility).forEach(([section, visibility]) => {
|
|
92
|
+
if (section.startsWith('_')) return map; // skip private fields
|
|
93
|
+
map.set(section, visibility._enabled);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return map;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const castUiSectionToWtObject = (section: UiSection): WtObject => {
|
|
101
|
+
return mapUiSectionToWtObject[section];
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const castWtObjectToUiSection = (object: WtObject): UiSection => {
|
|
105
|
+
return mapWtObjectToUiSection[object];
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const getWtAppByUiSection = (section: UiSection): WtApplication => {
|
|
109
|
+
/* use inverted maps because UiSection is the enum value, not key */
|
|
110
|
+
if (AdminSectionsValues[section]) {
|
|
111
|
+
return WtApplication.Admin;
|
|
112
|
+
}
|
|
113
|
+
if (AuditorSectionsValues[section]) {
|
|
114
|
+
return WtApplication.Audit;
|
|
115
|
+
}
|
|
116
|
+
if (CrmSectionsValues[section]) {
|
|
117
|
+
return WtApplication.Crm;
|
|
118
|
+
}
|
|
119
|
+
if (SupervisorSectionsValues[section]) {
|
|
120
|
+
return WtApplication.Supervisor;
|
|
121
|
+
}
|
|
122
|
+
wtlog.error({ module: 'modules/userinfo' })('Unknown section:', section);
|
|
123
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {createPinia, setActivePinia, type StoreDefinition} from 'pinia';
|
|
2
|
+
import { beforeEach, describe, expect,it } from 'vitest';
|
|
3
|
+
import {createApp, h} from "vue";
|
|
4
|
+
import {createRouter, createWebHistory, type Router} from "vue-router";
|
|
5
|
+
|
|
6
|
+
import {AdminSections, WtApplication, WtObject} from "../../../../../enums";
|
|
7
|
+
import {CrudGlobalAction} from "../../enums";
|
|
8
|
+
import { createUserAccessStore } from '../accessStore';
|
|
9
|
+
|
|
10
|
+
describe('AccessStore', () => {
|
|
11
|
+
let router: Router;
|
|
12
|
+
let useAccessStore: StoreDefinition;
|
|
13
|
+
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
router = createRouter({
|
|
16
|
+
history: createWebHistory(),
|
|
17
|
+
routes: [
|
|
18
|
+
{
|
|
19
|
+
path: '/',
|
|
20
|
+
name: 'home',
|
|
21
|
+
component: () => h('div', 'home'),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
path: '/users',
|
|
25
|
+
name: 'users',
|
|
26
|
+
component: () => h('div', 'users'),
|
|
27
|
+
meta: {
|
|
28
|
+
WtObject: WtObject.User,
|
|
29
|
+
UiSection: AdminSections.Users,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const pinia = createPinia();
|
|
36
|
+
const app = createApp({});
|
|
37
|
+
app.use(router);
|
|
38
|
+
app.use(pinia)
|
|
39
|
+
setActivePinia(pinia);
|
|
40
|
+
useAccessStore = createUserAccessStore();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('restricts route if no access', async () => {
|
|
44
|
+
const { initialize, routeAccessGuard } = useAccessStore();
|
|
45
|
+
router.beforeEach(routeAccessGuard);
|
|
46
|
+
|
|
47
|
+
initialize({
|
|
48
|
+
permissions: [],
|
|
49
|
+
scope: [],
|
|
50
|
+
access: {},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await router.push({ name: 'users' });
|
|
54
|
+
|
|
55
|
+
/* because guard should not allow to navigate
|
|
56
|
+
there since we pass empty permissions */
|
|
57
|
+
expect(router.currentRoute.value.name).not.toBe('users');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('allows route access if has global permission', async () => {
|
|
61
|
+
const { initialize, routeAccessGuard } = useAccessStore();
|
|
62
|
+
router.beforeEach(routeAccessGuard);
|
|
63
|
+
|
|
64
|
+
initialize({
|
|
65
|
+
permissions: [{ id: CrudGlobalAction.Read }],
|
|
66
|
+
scope: [],
|
|
67
|
+
access: {},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await router.push({ name: 'users' });
|
|
71
|
+
expect(router.currentRoute.value.name).toBe('users');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('allows route access if has scope permission, app visibility and section visibility', async () => {
|
|
75
|
+
const { initialize, routeAccessGuard } = useAccessStore();
|
|
76
|
+
router.beforeEach(routeAccessGuard);
|
|
77
|
+
|
|
78
|
+
initialize({
|
|
79
|
+
permissions: [],
|
|
80
|
+
scope: [{ class: 'users', access: 'r' }],
|
|
81
|
+
access: {
|
|
82
|
+
[WtApplication.Admin]: {
|
|
83
|
+
_enabled: true,
|
|
84
|
+
[AdminSections.Users]: {
|
|
85
|
+
_enabled: true,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
await router.push({ name: 'users' });
|
|
92
|
+
expect(router.currentRoute.value.name).toBe('users');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('restricts route access if has scope permission, app visibility, but no section visibility', async () => {
|
|
96
|
+
const { initialize, routeAccessGuard } = useAccessStore();
|
|
97
|
+
router.beforeEach(routeAccessGuard);
|
|
98
|
+
|
|
99
|
+
initialize({
|
|
100
|
+
permissions: [],
|
|
101
|
+
scope: [{ class: 'users', access: 'r' }],
|
|
102
|
+
access: {
|
|
103
|
+
[WtApplication.Admin]: {
|
|
104
|
+
_enabled: true,
|
|
105
|
+
[AdminSections.Users]: {
|
|
106
|
+
_enabled: false,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await router.push({ name: 'users' });
|
|
113
|
+
expect(router.currentRoute.value.name).not.toBe('users');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('restricts route access if has scope permission, section visibility but no app visibility', async () => {
|
|
117
|
+
const { initialize, routeAccessGuard } = useAccessStore();
|
|
118
|
+
router.beforeEach(routeAccessGuard);
|
|
119
|
+
|
|
120
|
+
initialize({
|
|
121
|
+
permissions: [],
|
|
122
|
+
scope: [{ class: 'users', access: 'r' }],
|
|
123
|
+
access: {
|
|
124
|
+
[WtApplication.Admin]: {
|
|
125
|
+
_enabled: false,
|
|
126
|
+
[AdminSections.Users]: {
|
|
127
|
+
_enabled: true,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
await router.push({ name: 'users' });
|
|
134
|
+
expect(router.currentRoute.value.name).not.toBe('users');
|
|
135
|
+
});
|
|
136
|
+
});
|