zavadil-ts-common 2.2.6 → 2.2.8
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/dist/index.d.ts +5 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/util/ArrayUtil.d.ts +1 -0
- package/dist/util/JsonUtil.d.ts +1 -0
- package/dist/util/NumberUtil.d.ts +1 -0
- package/dist/util/ObjectUtil.d.ts +2 -1
- package/package.json +1 -1
- package/src/oauth/OAuthUtil.ts +89 -92
- package/src/util/ArrayUtil.ts +4 -0
- package/src/util/JsonUtil.ts +5 -0
- package/src/util/NumberUtil.ts +5 -0
- package/src/util/ObjectUtil.ts +7 -2
- package/src/util/StringUtil.ts +12 -2
- package/tests/index.test.ts +57 -2
package/src/oauth/OAuthUtil.ts
CHANGED
|
@@ -1,107 +1,104 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {StringUtil, ObjectUtil} from "../util";
|
|
2
2
|
import {PERMISSION_LEVELS, PermissionLevel, TokenResponsePayloadBase} from "./Types";
|
|
3
3
|
|
|
4
4
|
export class OAuthUtil {
|
|
5
|
-
static isValidToken(token?: TokenResponsePayloadBase | null): boolean {
|
|
6
|
-
return (
|
|
7
|
-
ObjectUtil.notEmpty(token) &&
|
|
8
|
-
StringUtil.notBlank(token.token) &&
|
|
9
|
-
!OAuthUtil.isTokenExpired(token)
|
|
10
|
-
);
|
|
11
|
-
}
|
|
12
5
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return token.expires < new Date();
|
|
17
|
-
}
|
|
6
|
+
static isValidToken(token?: TokenResponsePayloadBase | null): boolean {
|
|
7
|
+
return ObjectUtil.isObject(token) && StringUtil.notBlank(token.token) && !OAuthUtil.isTokenExpired(token);
|
|
8
|
+
}
|
|
18
9
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (token.expires === undefined || token.expires === null) return false;
|
|
25
|
-
const middle = new Date(
|
|
26
|
-
(token.expires.getTime() + token.issuedAt.getTime()) / 2,
|
|
27
|
-
);
|
|
28
|
-
const now = new Date();
|
|
29
|
-
return middle < now;
|
|
30
|
-
}
|
|
10
|
+
static isTokenExpired(token?: TokenResponsePayloadBase | null): boolean {
|
|
11
|
+
if (token === undefined || token === null) return false;
|
|
12
|
+
if (token.expires === undefined || token.expires === null) return false;
|
|
13
|
+
return token.expires < new Date();
|
|
14
|
+
}
|
|
31
15
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
16
|
+
static isTokenReadyForRefresh(
|
|
17
|
+
token?: TokenResponsePayloadBase | null,
|
|
18
|
+
): boolean {
|
|
19
|
+
if (token === undefined || token === null) return false;
|
|
20
|
+
if (OAuthUtil.isTokenExpired(token)) return false;
|
|
21
|
+
if (token.expires === undefined || token.expires === null) return false;
|
|
22
|
+
const middle = new Date(
|
|
23
|
+
(token.expires.getTime() + token.issuedAt.getTime()) / 2,
|
|
24
|
+
);
|
|
25
|
+
const now = new Date();
|
|
26
|
+
return middle < now;
|
|
27
|
+
}
|
|
35
28
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return arr[1];
|
|
40
|
-
}
|
|
29
|
+
public static getScopeString(privilege: string, level: PermissionLevel) {
|
|
30
|
+
return `${level}:${privilege}`;
|
|
31
|
+
}
|
|
41
32
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
public static extractPrivilege(scope: string): string {
|
|
34
|
+
const arr = scope.split(":");
|
|
35
|
+
if (arr.length < 2) return "";
|
|
36
|
+
return arr[1];
|
|
37
|
+
}
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const str = arr[0];
|
|
50
|
-
if (!OAuthUtil.isPermissionLevel(str))
|
|
51
|
-
throw new Error(`"${str} is not valid permission level!`);
|
|
52
|
-
return str;
|
|
53
|
-
}
|
|
39
|
+
public static isPermissionLevel(value: string): value is PermissionLevel {
|
|
40
|
+
return (PERMISSION_LEVELS as readonly string[]).includes(value);
|
|
41
|
+
}
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
default:
|
|
64
|
-
return 0;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
43
|
+
public static extractPermissionLevel(scope: string): PermissionLevel {
|
|
44
|
+
const arr = scope.split(":");
|
|
45
|
+
if (arr.length === 0) throw new Error(`Scope value ${scope} is invalid!`);
|
|
46
|
+
const str = arr[0];
|
|
47
|
+
if (!OAuthUtil.isPermissionLevel(str))
|
|
48
|
+
throw new Error(`"${str} is not valid permission level!`);
|
|
49
|
+
return str;
|
|
50
|
+
}
|
|
67
51
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (minLevel === undefined) {
|
|
81
|
-
try {
|
|
82
|
-
minLevel = OAuthUtil.extractPermissionLevel(requiredScopeOrPrivilege);
|
|
83
|
-
requiredScopeOrPrivilege = OAuthUtil.extractPrivilege(
|
|
84
|
-
requiredScopeOrPrivilege,
|
|
85
|
-
);
|
|
86
|
-
} catch (e) {
|
|
87
|
-
minLevel = "admin";
|
|
88
|
-
}
|
|
89
|
-
}
|
|
52
|
+
public static getLevel(level: PermissionLevel): number {
|
|
53
|
+
switch (level) {
|
|
54
|
+
case "admin":
|
|
55
|
+
return 3;
|
|
56
|
+
case "write":
|
|
57
|
+
return 2;
|
|
58
|
+
case "read":
|
|
59
|
+
return 1;
|
|
60
|
+
default:
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
90
64
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Returns true if given scope grants permission for specific privilege on at least required level.
|
|
67
|
+
*
|
|
68
|
+
* @param ownedScope Existing scope, usually owned by user in form of access token
|
|
69
|
+
* @param requiredScopeOrPrivilege Required privilege name or whole scope (omit minLevel)
|
|
70
|
+
* @param minLevel Required minimal permission level
|
|
71
|
+
*/
|
|
72
|
+
public static hasPermission(
|
|
73
|
+
ownedScope: string,
|
|
74
|
+
requiredScopeOrPrivilege: string,
|
|
75
|
+
minLevel?: PermissionLevel,
|
|
76
|
+
): boolean {
|
|
77
|
+
if (minLevel === undefined) {
|
|
78
|
+
try {
|
|
79
|
+
minLevel = OAuthUtil.extractPermissionLevel(requiredScopeOrPrivilege);
|
|
80
|
+
requiredScopeOrPrivilege = OAuthUtil.extractPrivilege(
|
|
81
|
+
requiredScopeOrPrivilege,
|
|
82
|
+
);
|
|
83
|
+
} catch (e) {
|
|
84
|
+
minLevel = "admin";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
95
87
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (ownedPrivilege === requiredScopeOrPrivilege) return true;
|
|
101
|
-
// now the only possibility is that user has privilege ending with * covering the required privilege
|
|
102
|
-
if (!ownedPrivilege?.endsWith("/*")) return false;
|
|
88
|
+
const ownedLevel = OAuthUtil.extractPermissionLevel(ownedScope);
|
|
89
|
+
// owned level must be at least equal to the required
|
|
90
|
+
if (OAuthUtil.getLevel(ownedLevel) < OAuthUtil.getLevel(minLevel))
|
|
91
|
+
return false;
|
|
103
92
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
93
|
+
const ownedPrivilege = OAuthUtil.extractPrivilege(ownedScope);
|
|
94
|
+
// super admin, has privilege for all resources
|
|
95
|
+
if (ownedPrivilege === "*") return true;
|
|
96
|
+
// user has exactly the required privilege
|
|
97
|
+
if (ownedPrivilege === requiredScopeOrPrivilege) return true;
|
|
98
|
+
// now the only possibility is that user has privilege ending with * covering the required privilege
|
|
99
|
+
if (!ownedPrivilege?.endsWith("/*")) return false;
|
|
100
|
+
|
|
101
|
+
const ownedParent = ownedPrivilege.replace("/*", "");
|
|
102
|
+
return requiredScopeOrPrivilege?.startsWith(ownedParent) ?? false;
|
|
103
|
+
}
|
|
107
104
|
}
|
package/src/util/ArrayUtil.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { ObjectUtil } from "./ObjectUtil";
|
|
|
2
2
|
|
|
3
3
|
export class ArrayUtil {
|
|
4
4
|
|
|
5
|
+
static isArray(arr: any): arr is Array<any> {
|
|
6
|
+
return Array.isArray(arr);
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
static isEmpty(arr?: Array<any> | null): arr is null | undefined {
|
|
6
10
|
return ObjectUtil.isEmpty(arr) || arr.length === 0;
|
|
7
11
|
}
|
package/src/util/JsonUtil.ts
CHANGED
package/src/util/NumberUtil.ts
CHANGED
|
@@ -4,6 +4,11 @@ export class NumberUtil {
|
|
|
4
4
|
return n === undefined || n === null || Number.isNaN(n);
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
static isNumber(n: any): n is number {
|
|
8
|
+
if (NumberUtil.isEmpty(n)) return false;
|
|
9
|
+
return typeof n === "number";
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
static notEmpty(n?: number | null): n is number {
|
|
8
13
|
return !NumberUtil.isEmpty(n);
|
|
9
14
|
}
|
package/src/util/ObjectUtil.ts
CHANGED
|
@@ -4,8 +4,13 @@ export class ObjectUtil {
|
|
|
4
4
|
return obj === undefined || obj === null;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
static
|
|
8
|
-
|
|
7
|
+
static isObject(o: any): o is Record<string, unknown> {
|
|
8
|
+
if (ObjectUtil.isEmpty(o)) return false;
|
|
9
|
+
return typeof o === "object";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static notEmpty(obj: any): obj is Record<string, unknown> {
|
|
13
|
+
return !ObjectUtil.isObject(obj);
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
static clone<T>(obj: T): T {
|
package/src/util/StringUtil.ts
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import {HashUtil} from "./HashUtil";
|
|
2
2
|
import {ObjectUtil} from "./ObjectUtil";
|
|
3
|
+
import {NumberUtil} from "./NumberUtil";
|
|
4
|
+
import {ArrayUtil} from "./ArrayUtil";
|
|
5
|
+
import {JsonUtil} from "./JsonUtil";
|
|
3
6
|
|
|
4
7
|
export class StringUtil {
|
|
8
|
+
|
|
5
9
|
static isString(str: any): str is string {
|
|
10
|
+
if (ObjectUtil.isEmpty(str)) return false;
|
|
6
11
|
return typeof str === "string";
|
|
7
12
|
}
|
|
8
13
|
|
|
9
14
|
static toString(s: any): string {
|
|
10
15
|
if (StringUtil.isString(s)) return s;
|
|
11
|
-
if (
|
|
12
|
-
return s
|
|
16
|
+
if (NumberUtil.isNumber(s)) return s.toString();
|
|
17
|
+
if (ArrayUtil.isArray(s)) return `[${s.map(i => StringUtil.toString(i)).join(',')}]`;
|
|
18
|
+
if (ObjectUtil.isObject(s)) {
|
|
19
|
+
if (StringUtil.isString(s.message)) return s.message; // Errors
|
|
20
|
+
return JsonUtil.serialize(s);
|
|
21
|
+
}
|
|
22
|
+
return StringUtil.getNonEmpty(s);
|
|
13
23
|
}
|
|
14
24
|
|
|
15
25
|
static isEmpty(str: string | null | undefined): str is null | undefined {
|
package/tests/index.test.ts
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
import {StringUtil, JsonUtil, RestClient, UrlUtil, EmailUtil} from '../src';
|
|
1
|
+
import {StringUtil, JsonUtil, RestClient, UrlUtil, EmailUtil, ObjectUtil, ArrayUtil} from '../src';
|
|
2
|
+
|
|
3
|
+
describe('testing ObjectUtil', () => {
|
|
4
|
+
test('isEmpty', () => {
|
|
5
|
+
expect(ObjectUtil.isEmpty(undefined)).toBe(true);
|
|
6
|
+
expect(ObjectUtil.isEmpty(null)).toBe(true);
|
|
7
|
+
expect(ObjectUtil.isEmpty([])).toBe(false);
|
|
8
|
+
expect(ObjectUtil.isEmpty({})).toBe(false);
|
|
9
|
+
expect(ObjectUtil.isEmpty('')).toBe(false);
|
|
10
|
+
expect(ObjectUtil.isEmpty('test')).toBe(false);
|
|
11
|
+
});
|
|
12
|
+
test('isObject', () => {
|
|
13
|
+
expect(ObjectUtil.isObject(undefined)).toBe(false);
|
|
14
|
+
expect(ObjectUtil.isObject(null)).toBe(false);
|
|
15
|
+
expect(ObjectUtil.isObject([])).toBe(true);
|
|
16
|
+
expect(ObjectUtil.isObject({})).toBe(true);
|
|
17
|
+
expect(ObjectUtil.isObject('')).toBe(false);
|
|
18
|
+
expect(ObjectUtil.isObject('test')).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
2
21
|
|
|
3
22
|
describe('testing StringUtil', () => {
|
|
4
23
|
test('isEmpty', () => {
|
|
@@ -15,6 +34,24 @@ describe('testing StringUtil', () => {
|
|
|
15
34
|
expect(StringUtil.isBlank(null)).toBe(true);
|
|
16
35
|
expect(StringUtil.isBlank(undefined)).toBe(true);
|
|
17
36
|
});
|
|
37
|
+
test('isString', () => {
|
|
38
|
+
expect(StringUtil.isString('test')).toBe(true);
|
|
39
|
+
expect(StringUtil.isString(' ')).toBe(true);
|
|
40
|
+
expect(StringUtil.isString('')).toBe(true);
|
|
41
|
+
expect(StringUtil.isString(null)).toBe(false);
|
|
42
|
+
expect(StringUtil.isString(undefined)).toBe(false);
|
|
43
|
+
expect(StringUtil.isString({})).toBe(false);
|
|
44
|
+
expect(StringUtil.isString([])).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
test('toString', () => {
|
|
47
|
+
expect(StringUtil.toString('test')).toBe('test');
|
|
48
|
+
expect(StringUtil.toString(' ')).toBe(' ');
|
|
49
|
+
expect(StringUtil.toString('')).toBe('');
|
|
50
|
+
expect(StringUtil.toString(null)).toBe('');
|
|
51
|
+
expect(StringUtil.toString(undefined)).toBe('');
|
|
52
|
+
expect(StringUtil.toString({})).toBe('{}');
|
|
53
|
+
expect(StringUtil.toString([1, 2, 3])).toBe('[1,2,3]');
|
|
54
|
+
});
|
|
18
55
|
test('getNonEmpty', () => {
|
|
19
56
|
expect(StringUtil.getNonEmpty('test')).toBe('test');
|
|
20
57
|
expect(StringUtil.getNonEmpty('', 'test')).toBe('test');
|
|
@@ -30,6 +67,24 @@ describe('testing StringUtil', () => {
|
|
|
30
67
|
});
|
|
31
68
|
});
|
|
32
69
|
|
|
70
|
+
describe('testing ArrayUtil', () => {
|
|
71
|
+
test('isEmpty', () => {
|
|
72
|
+
expect(ArrayUtil.isEmpty(undefined)).toBe(true);
|
|
73
|
+
expect(ArrayUtil.isEmpty(null)).toBe(true);
|
|
74
|
+
expect(ArrayUtil.isEmpty([])).toBe(true);
|
|
75
|
+
expect(ArrayUtil.isEmpty([1,2,3])).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
test('isArray', () => {
|
|
78
|
+
expect(ArrayUtil.isArray(undefined)).toBe(false);
|
|
79
|
+
expect(ArrayUtil.isArray(null)).toBe(false);
|
|
80
|
+
expect(ArrayUtil.isArray([])).toBe(true);
|
|
81
|
+
expect(ArrayUtil.isArray([1,2,3])).toBe(true);
|
|
82
|
+
expect(ArrayUtil.isArray({})).toBe(false);
|
|
83
|
+
expect(ArrayUtil.isArray('')).toBe(false);
|
|
84
|
+
expect(ArrayUtil.isArray('test')).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
33
88
|
describe('testing JsonUtil', () => {
|
|
34
89
|
test('parseWithDates', () => {
|
|
35
90
|
const myObj = {
|
|
@@ -37,7 +92,7 @@ describe('testing JsonUtil', () => {
|
|
|
37
92
|
sval: "str",
|
|
38
93
|
nval: 13
|
|
39
94
|
}
|
|
40
|
-
const myStr =
|
|
95
|
+
const myStr = JsonUtil.serialize(myObj);
|
|
41
96
|
const parsed = JsonUtil.parseWithDates(myStr);
|
|
42
97
|
|
|
43
98
|
expect(typeof parsed.dval).toBe('object');
|