@cocreate/authorize 1.0.0 → 1.1.0

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 CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.1.0](https://github.com/CoCreate-app/CoCreate-authorize/compare/v1.0.0...v1.1.0) (2023-06-01)
2
+
3
+
4
+ ### Features
5
+
6
+ * **authorize:** Add permission-based authorization function ([dce152f](https://github.com/CoCreate-app/CoCreate-authorize/commit/dce152f6f504a5ac790e98753ad31c5e83cee295))
7
+
1
8
  # 1.0.0 (2023-05-31)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/authorize",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A simple authorize component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
5
5
  "keywords": [
6
6
  "authorize",
package/src/index.js ADDED
@@ -0,0 +1,372 @@
1
+ (function (root, factory) {
2
+ if (typeof define === 'function' && define.amd) {
3
+ define(["@cocreate/crud-client"], function (crud) {
4
+ return factory(true, crud)
5
+ });
6
+ } else if (typeof module === 'object' && module.exports) {
7
+ module.exports = class CoCreateAuthorize {
8
+ constructor(crud) {
9
+ return factory(false, crud);
10
+ }
11
+ }
12
+ } else {
13
+ root.returnExports = factory(true, root["@cocreate/crud-client"]);
14
+ }
15
+ }(typeof self !== 'undefined' ? self : this, function (isBrowser, crud) {
16
+
17
+ const permissions = new Map()
18
+
19
+ if (isBrowser) {
20
+ crud.listen('updateDocument', function (data) {
21
+ updatePermission(data)
22
+ });
23
+
24
+ crud.listen('deleteDocument', function (data) {
25
+ deletePermission(data)
26
+ });
27
+ } else {
28
+ process.on('changed-document', async (data) => {
29
+ updatePermission(data)
30
+ })
31
+ }
32
+
33
+ async function updatePermission(data) {
34
+ const { collection, document, organization_id } = data
35
+
36
+ if (collection === 'keys' && document) {
37
+ let permission = document[0]
38
+ if (permission && permission.key && hasPermission(permission.key)) {
39
+ let newPermission = await readPermisson(permission.key, organization_id)
40
+ setPermission(permission.key, newPermission)
41
+ }
42
+ }
43
+ }
44
+
45
+ function setPermission(key, permission) {
46
+ permissions.set(key, permission)
47
+ }
48
+
49
+ function hasPermission(key) {
50
+ return permissions.has(key)
51
+ }
52
+
53
+ async function getPermission(key, organization_id) {
54
+ if (permissions.get(key)) {
55
+ return permissions.get(key)
56
+ } else {
57
+ let permission = await readPermisson(key, organization_id);
58
+ permissions.set(key, permission)
59
+ return permission
60
+ }
61
+ }
62
+
63
+ async function readPermisson(key, organization_id) {
64
+ try {
65
+ if (!organization_id)
66
+ return null;
67
+
68
+ let request = {
69
+ collection: 'keys',
70
+ organization_id,
71
+ filter: {
72
+ query: []
73
+ }
74
+ }
75
+
76
+ if (key)
77
+ request.filter.query.push({ name: 'key', value: key, operator: '$eq' })
78
+ else
79
+ request.filter.query.push({ name: 'default', value: true, operator: '$eq' })
80
+
81
+
82
+ let permission = await crud.readDocument(request)
83
+ if (permission && permission.document && permission.document[0]) {
84
+ permission = permission.document[0]
85
+
86
+ if (!permission.collections) {
87
+ permission.collections = {};
88
+ }
89
+
90
+ if (permission && permission.roles) {
91
+ const role_ids = []
92
+ permission.roles.forEach((_id) => {
93
+ if (_id)
94
+ role_ids.push({ _id })
95
+ })
96
+
97
+ delete request.filter
98
+ delete request.request
99
+ request.document = role_ids
100
+
101
+ let roles = await crud.readDocument(request)
102
+ roles = roles.document
103
+
104
+ permission = createPermissionObject(permission, roles)
105
+ }
106
+
107
+ }
108
+
109
+ return permission;
110
+
111
+ } catch (error) {
112
+ console.log("Permission Error", error)
113
+ return null;
114
+ }
115
+
116
+ }
117
+
118
+ async function createPermissionObject(permission, roles) {
119
+ roles.map(role => {
120
+ for (const roleKey in role) {
121
+ if (!["_id", "type", "name", "organization_id"].includes(roleKey)) {
122
+ if (!permission[roleKey]) {
123
+ permission[roleKey] = role[roleKey]
124
+ } else {
125
+ if (Array.isArray(role[roleKey])) {
126
+ for (let item of role[roleKey]) {
127
+ if (!permission[roleKey].includes(item))
128
+ permission[roleKey].push(item)
129
+ }
130
+ }
131
+ else if (typeof role[roleKey] == 'object') {
132
+ for (const c of Object.keys(role[roleKey])) {
133
+ if (!permission[roleKey][c]) {
134
+ permission[roleKey][c] = role[roleKey][c]
135
+ } else {
136
+ if (typeof role[roleKey][c] == 'object') {
137
+ permission[roleKey][c] = { ...permission[roleKey][c], ...role[roleKey][c] }
138
+ } else {
139
+ permission[roleKey][c] = role[roleKey][c]
140
+ }
141
+ }
142
+ }
143
+ } else {
144
+ permission[roleKey] = role[roleKey]
145
+ }
146
+ }
147
+ }
148
+ }
149
+ })
150
+ return permission;
151
+ }
152
+
153
+ async function check(action, data, user_id) {
154
+ let permission = false
155
+ if (user_id) {
156
+ permission = await checkPermissionObject({
157
+ key: user_id,
158
+ action,
159
+ data
160
+ })
161
+ }
162
+ if (!permission || permission.error) {
163
+ permission = await checkPermissionObject({
164
+ key: data.key,
165
+ action,
166
+ data
167
+ })
168
+ }
169
+ return permission;
170
+ }
171
+
172
+ async function checkPermissionObject({ key, action, data }) {
173
+ let { organization_id, filter, endPoint } = data
174
+ if (!key || !organization_id) return false;
175
+
176
+ let permission = await getPermission(key, organization_id)
177
+ if (!permission || permission.error)
178
+ return permission
179
+ if (permission.organization_id !== organization_id)
180
+ return false;
181
+ if (permission.hosts && permission.hosts.length) {
182
+ if (!permission.hosts || (!permission.hosts.includes(data.host) && !permission.hosts.includes("*")))
183
+ return false;
184
+
185
+ }
186
+ if (permission.admin == 'true' || permission.admin === true)
187
+ return true;
188
+
189
+ // if (data.request) {
190
+ // let type = action.match(/[A-Z][a-z]+/g);
191
+ // type = type[0].toLowerCase()
192
+ // data[type] = data.request
193
+ // }
194
+
195
+ let status = await checkAction(permission.actions, action, endPoint, data, filter)
196
+
197
+ if (!status)
198
+ return false
199
+
200
+ return { authorized: data };
201
+ }
202
+
203
+ async function checkAction(permissions, action, endPoint, data) {
204
+ if (!permissions || !action || !permissions[action] || permissions[action] == 'false') return false;
205
+ if (permissions[action] === true || permissions[action] == 'true' || permissions[action] == '*') return true;
206
+
207
+ let authorized = permissions[action].authorize
208
+ if (authorized) {
209
+ let status = await checkAthorized(authorized, action, endPoint, data)
210
+ if (!status)
211
+ return false
212
+ else {
213
+ let unauthorized = permissions[action].unauthorize
214
+ if (unauthorized) {
215
+ let status = await checkAthorized(unauthorized, action, endPoint, data, true)
216
+ if (status)
217
+ return false
218
+ }
219
+ return true
220
+ }
221
+ } else
222
+ return false
223
+ }
224
+
225
+ async function checkAthorized(authorized, action, endPoint, data, unauthorize) {
226
+ if (!Array.isArray(authorized))
227
+ authorized = [authorized]
228
+
229
+ let status = false
230
+ for (let i = 0; i < authorized.length; i++) {
231
+ // if authorized[i] is a booleaan
232
+ if (authorized[i] === true)
233
+ return true
234
+
235
+ // if authorized[i] is a string or an array
236
+ if (typeof authorized[i] === "string" || Array.isArray(authorized[i])) {
237
+ if (authorized[i].includes(true) || authorized[i].includes('true') || authorized[i].includes('*'))
238
+ return true
239
+ else if (endPoint)
240
+ return authorized.includes(endPoint)
241
+ else
242
+ return false
243
+ }
244
+
245
+ // if authorized[i] is an object
246
+ for (const key of Object.keys(authorized[i])) {
247
+ status = await checkAthorizedKey(authorized[i], action, endPoint, data, key, unauthorize)
248
+ }
249
+
250
+ }
251
+
252
+ return status
253
+
254
+ }
255
+
256
+ async function checkAthorizedKey(authorized, action, endPoint, data, key, unauthorize) {
257
+ let status = false;
258
+ let keyStatus = false;
259
+
260
+ // if authorized[key] is a booleaan
261
+ if (authorized[key] === true)
262
+ keyStatus = true
263
+
264
+ // if authorized[key] is a string or number
265
+ else if (typeof authorized[key] === "string" || typeof authorized[key] === "number") {
266
+ if (authorized[key] === true || authorized[key] === 'true' || authorized[key] === '*')
267
+ keyStatus = true
268
+ else if (data[key]) {
269
+ keyStatus = await checkArray(authorized, data, key, unauthorize)
270
+ if (await checkFilter(authorized, data, key, unauthorize))
271
+ status = true
272
+ }
273
+ }
274
+
275
+ // if authorized[key] is an array
276
+ else if (Array.isArray(authorized[key])) {
277
+ if (authorized[key].includes(true) || authorized[key].includes('true') || authorized[key].includes('*'))
278
+ keyStatus = true
279
+ else if (data[key]) {
280
+ keyStatus = await checkArray(authorized, data, key, unauthorize)
281
+ if (await checkFilter(authorized, data, key, unauthorize))
282
+ status = true
283
+ }
284
+ }
285
+
286
+ // if authorized[key] is an object
287
+ else if (typeof authorized[key] === "object") {
288
+ console.log('authorized[key] is an object', authorized[key])
289
+ } else
290
+ delete data[key]
291
+
292
+ // if key status is true for unauthorized case
293
+ if (!keyStatus || keyStatus && unauthorize) {
294
+ if (!data.unauthorized || !data.unauthorized[action])
295
+ data.unauthorized = { [action]: { [key]: [data[key]] } }
296
+ else if (!data.unauthorized[action][key])
297
+ data.unauthorized[action][key] = [data[key]]
298
+ else
299
+ data.unauthorized[action][key].push(data[key])
300
+ } else
301
+ status = true
302
+ return status
303
+ }
304
+
305
+ async function checkArray(authorized, data, key, unauthorize) {
306
+ let keyStatus = false
307
+ let authorizedValue = eval('authorized.' + key)
308
+ let dataValue = eval('data.' + key)
309
+
310
+ if (!authorizedValue && !unauthorize) {
311
+ if (key.endsWith(']')) {
312
+ let lastIndex = key.lastIndexOf('[')
313
+ let index = key.slice(lastIndex + 1).slice(0, -1)
314
+ let nkey = key.slice(0, key.length - (index.length + 2))
315
+ console.log(index, nkey);
316
+ eval(`data.${nkey}.splice(${index}, 1)`)
317
+ } else
318
+ eval(`delete data.${key}`)
319
+ } else if (typeof dataValue == "string") {
320
+ if (unauthorize && authorizedValue.includes(dataValue))
321
+ eval(`delete data.${key}`)
322
+ else {
323
+ if (!authorizedValue.includes(dataValue))
324
+ eval(`delete data.${key}`)
325
+ else
326
+ keyStatus = true
327
+ }
328
+
329
+ } else if (Array.isArray(dataValue)) {
330
+ for (let i = 0; i < dataValue.length; i++) {
331
+ let nkey = key + `[${i}]`
332
+ keyStatus = await checkArray(authorized, data, nkey, unauthorize)
333
+ }
334
+ } else if (typeof dataValue === "object") {
335
+ if (authorizedValue['*'] || authorizedValue['*'] == '') {
336
+ keyStatus = true
337
+ } else
338
+ for (const k of Object.keys(dataValue)) {
339
+ let nkey = `${key}.${k}`
340
+ keyStatus = await checkArray(authorized, data, nkey, unauthorize)
341
+ }
342
+ }
343
+ return keyStatus
344
+ }
345
+
346
+ async function checkFilter(authorized, data, key, unauthorize) {
347
+ if (data.filter && data.filter.query) {
348
+ let name
349
+ if (data.filter.type == 'document')
350
+ name = '_id'
351
+ else if (data.filter.type == 'collection')
352
+ name = 'name'
353
+ if (name) {
354
+ for (let value of authorized[key]) {
355
+ if (value[name])
356
+ value = value[name]
357
+ if (unauthorize)
358
+ data.filter.query.push({ name, value, operator: '$ne', logicalOperator: 'or' })
359
+ else
360
+ data.filter.query.push({ name, value, operator: '$eq', logicalOperator: 'or' })
361
+ }
362
+ if (!unauthorize)
363
+ return true
364
+ }
365
+ }
366
+ }
367
+
368
+ return {
369
+ check
370
+ }
371
+
372
+ }));