@pax2pay/model-banking 0.1.508 → 0.1.510
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/Card/Creatable.ts +10 -8
- package/Card/Restriction/Merchant.ts +108 -0
- package/Card/Restriction/index.ts +28 -0
- package/Card/index.ts +17 -8
- package/User/Access/Permission.ts +47 -0
- package/User/Access/index.ts +19 -0
- package/User/Identity.ts +32 -0
- package/User/JWT/Payload.ts +56 -0
- package/User/JWT/Signer.ts +39 -0
- package/User/JWT/index.ts +57 -0
- package/User/Me.ts +15 -0
- package/User/Password.ts +43 -0
- package/User/index.ts +68 -0
- package/dist/cjs/Card/Creatable.d.ts +4 -0
- package/dist/cjs/Card/Creatable.js +9 -8
- package/dist/cjs/Card/Creatable.js.map +1 -1
- package/dist/cjs/Card/Restriction/Merchant.d.ts +16 -0
- package/dist/cjs/Card/Restriction/Merchant.js +101 -0
- package/dist/cjs/Card/Restriction/Merchant.js.map +1 -0
- package/dist/cjs/Card/Restriction/index.d.ts +12 -0
- package/dist/cjs/Card/Restriction/index.js +26 -0
- package/dist/cjs/Card/Restriction/index.js.map +1 -0
- package/dist/cjs/Card/index.d.ts +7 -2
- package/dist/cjs/Card/index.js +16 -8
- package/dist/cjs/Card/index.js.map +1 -1
- package/dist/cjs/User/Access/Permission.d.ts +20 -0
- package/dist/cjs/User/Access/Permission.js +47 -0
- package/dist/cjs/User/Access/Permission.js.map +1 -0
- package/dist/cjs/User/Access/index.d.ts +11 -0
- package/dist/cjs/User/Access/index.js +16 -0
- package/dist/cjs/User/Access/index.js.map +1 -0
- package/dist/cjs/User/Identity.d.ts +16 -0
- package/dist/cjs/User/Identity.js +32 -0
- package/dist/cjs/User/Identity.js.map +1 -0
- package/dist/cjs/User/JWT/Payload.d.ts +43 -0
- package/dist/cjs/User/JWT/Payload.js +42 -0
- package/dist/cjs/User/JWT/Payload.js.map +1 -0
- package/dist/cjs/User/JWT/Signer.d.ts +23 -0
- package/dist/cjs/User/JWT/Signer.js +39 -0
- package/dist/cjs/User/JWT/Signer.js.map +1 -0
- package/dist/cjs/User/JWT/index.d.ts +25 -0
- package/dist/cjs/User/JWT/index.js +59 -0
- package/dist/cjs/User/JWT/index.js.map +1 -0
- package/dist/cjs/User/Me.d.ts +9 -0
- package/dist/cjs/User/Me.js +14 -0
- package/dist/cjs/User/Me.js.map +1 -0
- package/dist/cjs/User/Password.d.ts +20 -0
- package/dist/cjs/User/Password.js +44 -0
- package/dist/cjs/User/Password.js.map +1 -0
- package/dist/cjs/User/index.d.ts +46 -0
- package/dist/cjs/User/index.js +54 -0
- package/dist/cjs/User/index.js.map +1 -0
- package/dist/cjs/pax2pay.d.ts +2 -1
- package/dist/cjs/pax2pay.js +5 -3
- package/dist/cjs/pax2pay.js.map +1 -1
- package/dist/mjs/Card/Creatable.d.ts +4 -0
- package/dist/mjs/Card/Creatable.js +9 -8
- package/dist/mjs/Card/Creatable.js.map +1 -1
- package/dist/mjs/Card/Restriction/Merchant.d.ts +16 -0
- package/dist/mjs/Card/Restriction/Merchant.js +98 -0
- package/dist/mjs/Card/Restriction/Merchant.js.map +1 -0
- package/dist/mjs/Card/Restriction/index.d.ts +12 -0
- package/dist/mjs/Card/Restriction/index.js +23 -0
- package/dist/mjs/Card/Restriction/index.js.map +1 -0
- package/dist/mjs/Card/index.d.ts +7 -2
- package/dist/mjs/Card/index.js +16 -8
- package/dist/mjs/Card/index.js.map +1 -1
- package/dist/mjs/User/Access/Permission.d.ts +20 -0
- package/dist/mjs/User/Access/Permission.js +44 -0
- package/dist/mjs/User/Access/Permission.js.map +1 -0
- package/dist/mjs/User/Access/index.d.ts +11 -0
- package/dist/mjs/User/Access/index.js +13 -0
- package/dist/mjs/User/Access/index.js.map +1 -0
- package/dist/mjs/User/Identity.d.ts +16 -0
- package/dist/mjs/User/Identity.js +28 -0
- package/dist/mjs/User/Identity.js.map +1 -0
- package/dist/mjs/User/JWT/Payload.d.ts +43 -0
- package/dist/mjs/User/JWT/Payload.js +39 -0
- package/dist/mjs/User/JWT/Payload.js.map +1 -0
- package/dist/mjs/User/JWT/Signer.d.ts +23 -0
- package/dist/mjs/User/JWT/Signer.js +35 -0
- package/dist/mjs/User/JWT/Signer.js.map +1 -0
- package/dist/mjs/User/JWT/index.d.ts +25 -0
- package/dist/mjs/User/JWT/index.js +55 -0
- package/dist/mjs/User/JWT/index.js.map +1 -0
- package/dist/mjs/User/Me.d.ts +9 -0
- package/dist/mjs/User/Me.js +11 -0
- package/dist/mjs/User/Me.js.map +1 -0
- package/dist/mjs/User/Password.d.ts +20 -0
- package/dist/mjs/User/Password.js +41 -0
- package/dist/mjs/User/Password.js.map +1 -0
- package/dist/mjs/User/index.d.ts +46 -0
- package/dist/mjs/User/index.js +51 -0
- package/dist/mjs/User/index.js.map +1 -0
- package/dist/mjs/pax2pay.d.ts +2 -1
- package/dist/mjs/pax2pay.js +2 -1
- package/dist/mjs/pax2pay.js.map +1 -1
- package/package.json +1 -1
- package/pax2pay.ts +2 -1
package/Card/Creatable.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { type as ruleType } from "../Rule/type"
|
|
|
8
8
|
import { Expiry } from "./Expiry"
|
|
9
9
|
import { Meta } from "./Meta"
|
|
10
10
|
import { Preset } from "./Preset"
|
|
11
|
+
import { Restriction } from "./Restriction"
|
|
11
12
|
|
|
12
13
|
export interface Creatable {
|
|
13
14
|
account: string
|
|
@@ -21,6 +22,7 @@ export interface Creatable {
|
|
|
21
22
|
rules?: Rule[]
|
|
22
23
|
meta?: Meta
|
|
23
24
|
key?: isoly.Date | string
|
|
25
|
+
restricted?: { to?: Restriction }
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export namespace Creatable {
|
|
@@ -28,24 +30,19 @@ export namespace Creatable {
|
|
|
28
30
|
account: isly.string(),
|
|
29
31
|
number: isly.string().optional(),
|
|
30
32
|
preset: Preset.type,
|
|
31
|
-
details: isly.object({
|
|
32
|
-
expiry: Expiry.type,
|
|
33
|
-
holder: isly.string(),
|
|
34
|
-
}),
|
|
33
|
+
details: isly.object({ expiry: Expiry.type, holder: isly.string() }),
|
|
35
34
|
limit: isly.tuple(isly.fromIs("isoly.Currency", isoly.Currency.is), isly.number()),
|
|
36
35
|
rules: ruleType.array().optional(),
|
|
37
36
|
meta: isly.fromIs("Card.Meta", Meta.is).optional(),
|
|
38
37
|
key: isly.string().optional(),
|
|
38
|
+
restricted: isly.object<Required<Creatable>["restricted"]>({ to: Restriction.type.optional() }).optional(),
|
|
39
39
|
})
|
|
40
40
|
export const type2 = isly2.object<Creatable>({
|
|
41
41
|
account: isly2.string().rename("Account").describe("The account id the card was created on."),
|
|
42
42
|
number: isly2.string().optional().rename("Number").describe("The card identifier of the user of the api."),
|
|
43
43
|
preset: Preset.type2,
|
|
44
44
|
details: isly2
|
|
45
|
-
.object({
|
|
46
|
-
expiry: Expiry.type2,
|
|
47
|
-
holder: isly2.string(),
|
|
48
|
-
})
|
|
45
|
+
.object({ expiry: Expiry.type2, holder: isly2.string() })
|
|
49
46
|
.rename("Details")
|
|
50
47
|
.describe("The card details, the information that will be displayed on the card."),
|
|
51
48
|
limit: isly2
|
|
@@ -60,5 +57,10 @@ export namespace Creatable {
|
|
|
60
57
|
.describe("Card rules that applies to authorizations made with the card."),
|
|
61
58
|
meta: isly2.from("Meta", Meta.is).optional(),
|
|
62
59
|
key: isly2.string().optional(),
|
|
60
|
+
restricted: isly2
|
|
61
|
+
.object<Required<Creatable>["restricted"]>({ to: Restriction.type2.optional() })
|
|
62
|
+
.optional()
|
|
63
|
+
.rename("Restrictions")
|
|
64
|
+
.describe("Set of restrictions that apply to the card."),
|
|
63
65
|
})
|
|
64
66
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { isly } from "isly"
|
|
2
|
+
import { isly as isly2 } from "isly2"
|
|
3
|
+
import type { Transaction } from "../../Transaction"
|
|
4
|
+
|
|
5
|
+
export type Merchant = typeof Merchant.values[number]
|
|
6
|
+
export namespace Merchant {
|
|
7
|
+
export const values = [
|
|
8
|
+
"united airlines",
|
|
9
|
+
"american air",
|
|
10
|
+
"british airways",
|
|
11
|
+
"air france",
|
|
12
|
+
"lufthansa",
|
|
13
|
+
"qantas",
|
|
14
|
+
"alitalia",
|
|
15
|
+
"saudi air",
|
|
16
|
+
"sas",
|
|
17
|
+
"air india",
|
|
18
|
+
"air algerie",
|
|
19
|
+
"emirates",
|
|
20
|
+
"air malta",
|
|
21
|
+
"etihad air",
|
|
22
|
+
"tap",
|
|
23
|
+
"avianca",
|
|
24
|
+
"gulf air",
|
|
25
|
+
"aer lingus",
|
|
26
|
+
"turkish air",
|
|
27
|
+
"royal air maroc",
|
|
28
|
+
"tunis air",
|
|
29
|
+
"austrian air",
|
|
30
|
+
"flydubai",
|
|
31
|
+
"cebu air",
|
|
32
|
+
"thai airways",
|
|
33
|
+
"china airlines",
|
|
34
|
+
"malaysia airlines",
|
|
35
|
+
"iberia",
|
|
36
|
+
"qatar air",
|
|
37
|
+
"ana air",
|
|
38
|
+
"jet blue",
|
|
39
|
+
"middle east airlines",
|
|
40
|
+
"lot polish airlines",
|
|
41
|
+
"norwegian air",
|
|
42
|
+
"air arabia",
|
|
43
|
+
"easyjet",
|
|
44
|
+
"ryanair",
|
|
45
|
+
"air china",
|
|
46
|
+
"ethiopian airlines",
|
|
47
|
+
"wizz air",
|
|
48
|
+
] as const
|
|
49
|
+
export const type = isly.string<Merchant>(values)
|
|
50
|
+
export const type2 = isly2.string<Merchant>("value", ...values)
|
|
51
|
+
export type Checker = (
|
|
52
|
+
merchant: Merchant,
|
|
53
|
+
transaction: Transaction.Creatable.CardTransaction | Transaction.PreTransaction.Authorization
|
|
54
|
+
) => boolean
|
|
55
|
+
export type Attribute = { mccs: string[]; checkers?: Checker[] }
|
|
56
|
+
export const attributes: Record<Merchant, Attribute> = {
|
|
57
|
+
"united airlines": { mccs: ["3000"] },
|
|
58
|
+
"american air": { mccs: ["3001"] },
|
|
59
|
+
"british airways": { mccs: ["3005"] },
|
|
60
|
+
"air france": { mccs: ["3007"] },
|
|
61
|
+
lufthansa: { mccs: ["3008"] },
|
|
62
|
+
qantas: { mccs: ["3012"] },
|
|
63
|
+
alitalia: { mccs: ["3013"] },
|
|
64
|
+
"saudi air": { mccs: ["3014"] },
|
|
65
|
+
sas: { mccs: ["3016"] },
|
|
66
|
+
"air india": { mccs: ["3020"] },
|
|
67
|
+
"air algerie": { mccs: ["3021"] },
|
|
68
|
+
emirates: { mccs: ["3026"] },
|
|
69
|
+
"air malta": { mccs: ["3028"] },
|
|
70
|
+
"etihad air": { mccs: ["3034"] },
|
|
71
|
+
tap: { mccs: ["3035"] },
|
|
72
|
+
avianca: { mccs: ["3039"] },
|
|
73
|
+
"gulf air": { mccs: ["3040"] },
|
|
74
|
+
"aer lingus": { mccs: ["3043"] },
|
|
75
|
+
"turkish air": { mccs: ["3047"] },
|
|
76
|
+
"royal air maroc": { mccs: ["3048"] },
|
|
77
|
+
"tunis air": { mccs: ["3049"] },
|
|
78
|
+
"austrian air": { mccs: ["3051"] },
|
|
79
|
+
flydubai: { mccs: ["3070"] },
|
|
80
|
+
"cebu air": { mccs: ["3072"] },
|
|
81
|
+
"thai airways": { mccs: ["3077"] },
|
|
82
|
+
"china airlines": { mccs: ["3078"] },
|
|
83
|
+
"malaysia airlines": { mccs: ["3100"] },
|
|
84
|
+
iberia: { mccs: ["3102"] },
|
|
85
|
+
"qatar air": { mccs: ["3136"] },
|
|
86
|
+
"ana air": { mccs: ["3161"] },
|
|
87
|
+
"jet blue": { mccs: ["3174"] },
|
|
88
|
+
"middle east airlines": { mccs: ["3175"] },
|
|
89
|
+
"lot polish airlines": { mccs: ["3182"] },
|
|
90
|
+
"norwegian air": { mccs: ["3211"] },
|
|
91
|
+
"air arabia": { mccs: ["3236"] },
|
|
92
|
+
easyjet: { mccs: ["3245"] },
|
|
93
|
+
ryanair: { mccs: ["3246"] },
|
|
94
|
+
"air china": { mccs: ["3261"] },
|
|
95
|
+
"ethiopian airlines": { mccs: ["3294"] },
|
|
96
|
+
"wizz air": { mccs: ["3301"] },
|
|
97
|
+
}
|
|
98
|
+
export function check(
|
|
99
|
+
merchant: Merchant,
|
|
100
|
+
transaction: Transaction.Creatable.CardTransaction | Transaction.PreTransaction.Authorization
|
|
101
|
+
): boolean {
|
|
102
|
+
const attribute = Merchant.attributes[merchant]
|
|
103
|
+
return (
|
|
104
|
+
attribute.mccs.some(mcc => transaction.counterpart.merchant.category == mcc) ||
|
|
105
|
+
(attribute.checkers ?? []).some(checker => checker(merchant, transaction))
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { isly } from "isly"
|
|
2
|
+
import { isly as isly2 } from "isly2"
|
|
3
|
+
import type { Transaction } from "../../Transaction"
|
|
4
|
+
import { Merchant as RestrictionsMerchant } from "./Merchant"
|
|
5
|
+
|
|
6
|
+
export interface Restriction {
|
|
7
|
+
merchants?: Restriction.Merchant[]
|
|
8
|
+
}
|
|
9
|
+
export namespace Restriction {
|
|
10
|
+
export import Merchant = RestrictionsMerchant
|
|
11
|
+
export const type = isly.object<Restriction>({ merchants: Merchant.type.array().optional() })
|
|
12
|
+
export const type2 = isly2.object<Restriction>({
|
|
13
|
+
merchants: Merchant.type2
|
|
14
|
+
.array()
|
|
15
|
+
.optional()
|
|
16
|
+
.rename("Merchants")
|
|
17
|
+
.describe("List of merchants that the card can be used with."),
|
|
18
|
+
})
|
|
19
|
+
export function check(
|
|
20
|
+
restrictions: Restriction,
|
|
21
|
+
transaction: Transaction.Creatable.CardTransaction | Transaction.PreTransaction.Authorization
|
|
22
|
+
): boolean {
|
|
23
|
+
let result: boolean = true
|
|
24
|
+
if (restrictions.merchants?.length)
|
|
25
|
+
result = restrictions.merchants.some(merchant => Merchant.check(merchant, transaction))
|
|
26
|
+
return result
|
|
27
|
+
}
|
|
28
|
+
}
|
package/Card/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { Expiry as CardExpiry } from "./Expiry"
|
|
|
12
12
|
import { Meta as CardMeta } from "./Meta"
|
|
13
13
|
import { Operation as CardOperation } from "./Operation"
|
|
14
14
|
import { Preset as CardPreset } from "./Preset"
|
|
15
|
+
import { Restriction as CardRestriction } from "./Restriction"
|
|
15
16
|
import { Scheme as CardScheme } from "./Scheme"
|
|
16
17
|
import { Stack as CardStack } from "./Stack"
|
|
17
18
|
|
|
@@ -38,8 +39,18 @@ export interface Card {
|
|
|
38
39
|
history: CardOperation[]
|
|
39
40
|
rules: Rule[]
|
|
40
41
|
meta?: CardMeta
|
|
42
|
+
restricted?: { to?: CardRestriction }
|
|
41
43
|
}
|
|
42
44
|
export namespace Card {
|
|
45
|
+
export import Creatable = CardCreatable
|
|
46
|
+
export import Preset = CardPreset
|
|
47
|
+
export import Meta = CardMeta
|
|
48
|
+
export import Expiry = CardExpiry
|
|
49
|
+
export import Changeable = CardChangeable
|
|
50
|
+
export import Operation = CardOperation
|
|
51
|
+
export import Scheme = CardScheme
|
|
52
|
+
export import Stack = CardStack
|
|
53
|
+
export import Restriction = CardRestriction
|
|
43
54
|
export const type = isly.object<Card>({
|
|
44
55
|
id: isly.string(),
|
|
45
56
|
number: isly.string().optional(),
|
|
@@ -63,6 +74,7 @@ export namespace Card {
|
|
|
63
74
|
history: isly.array(CardOperation.type),
|
|
64
75
|
rules: ruleType.array(),
|
|
65
76
|
meta: isly.fromIs("Card.Meta", CardMeta.is).optional(),
|
|
77
|
+
restricted: isly.object<Required<Card>["restricted"]>({ to: CardRestriction.type.optional() }).optional(),
|
|
66
78
|
})
|
|
67
79
|
export const type2: isly2.Object<Card> = isly2
|
|
68
80
|
.object<Card>({
|
|
@@ -92,17 +104,14 @@ export namespace Card {
|
|
|
92
104
|
.rename("Rules")
|
|
93
105
|
.describe("Card rules that applies to authorizations made with the card."),
|
|
94
106
|
meta: isly2.from("Card.Meta", CardMeta.is).optional().rename("Meta").describe("Additional card information."),
|
|
107
|
+
restricted: isly2
|
|
108
|
+
.object<Required<Card>["restricted"]>({ to: CardRestriction.type2.optional() })
|
|
109
|
+
.optional()
|
|
110
|
+
.rename("Restrictions")
|
|
111
|
+
.describe("Set of restrictions that apply to the card."),
|
|
95
112
|
})
|
|
96
113
|
.rename("Card")
|
|
97
114
|
.describe("Card information.")
|
|
98
|
-
export import Creatable = CardCreatable
|
|
99
|
-
export import Preset = CardPreset
|
|
100
|
-
export import Meta = CardMeta
|
|
101
|
-
export import Expiry = CardExpiry
|
|
102
|
-
export import Changeable = CardChangeable
|
|
103
|
-
export import Operation = CardOperation
|
|
104
|
-
export import Scheme = CardScheme
|
|
105
|
-
export import Stack = CardStack
|
|
106
115
|
const csvMap: Record<string, (card: Card) => string | number | undefined> = {
|
|
107
116
|
id: card => card.id,
|
|
108
117
|
created: card => readableDate(card.created),
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { isly } from "isly"
|
|
2
|
+
import { typedly } from "typedly"
|
|
3
|
+
|
|
4
|
+
export type Permission = Partial<Record<Permission.Collection, Permission.Level>>
|
|
5
|
+
export namespace Permission {
|
|
6
|
+
export type Realm = Omit<Permission, "user">
|
|
7
|
+
export type Global = Pick<Permission, "user">
|
|
8
|
+
export function check(constraint: Permission, privilege: Permission): boolean {
|
|
9
|
+
return typedly.Object.entries(constraint).every(
|
|
10
|
+
([collection, level]) =>
|
|
11
|
+
Permission.Level.get(privilege[collection]) >= Permission.Level.get(level) ||
|
|
12
|
+
Permission.Level.get(privilege["*"]) >= Permission.Level.get(level)
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
export type Level = typeof Level.values[number]
|
|
16
|
+
export namespace Level {
|
|
17
|
+
export const values = ["read", "write", "developer", "admin"] as const
|
|
18
|
+
export const type = isly.string(values)
|
|
19
|
+
export function get(level: Level | undefined): number {
|
|
20
|
+
return level ? value[level] : 0
|
|
21
|
+
}
|
|
22
|
+
export const value: Record<Level, number> = {
|
|
23
|
+
read: 1,
|
|
24
|
+
write: 2,
|
|
25
|
+
developer: 3,
|
|
26
|
+
admin: 4,
|
|
27
|
+
} as const
|
|
28
|
+
}
|
|
29
|
+
export type Collection = typeof Collection.values[number]
|
|
30
|
+
export namespace Collection {
|
|
31
|
+
export const values = [
|
|
32
|
+
"account",
|
|
33
|
+
"card",
|
|
34
|
+
"log",
|
|
35
|
+
"operation",
|
|
36
|
+
"organization",
|
|
37
|
+
"rule", // realm rules
|
|
38
|
+
"settlement",
|
|
39
|
+
"transaction",
|
|
40
|
+
"treasury",
|
|
41
|
+
"user",
|
|
42
|
+
"*",
|
|
43
|
+
] as const
|
|
44
|
+
export const type = isly.string(values)
|
|
45
|
+
}
|
|
46
|
+
export const type = isly.record<Permission>(Collection.type, Level.type)
|
|
47
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { isly } from "isly"
|
|
2
|
+
import { Permission as AccessPermission } from "./Permission"
|
|
3
|
+
|
|
4
|
+
/** read < write < developer < admin */
|
|
5
|
+
export interface Access {
|
|
6
|
+
uk?: Access.Permission.Realm
|
|
7
|
+
eea?: Access.Permission.Realm
|
|
8
|
+
test?: Access.Permission.Realm
|
|
9
|
+
"*"?: Access.Permission.Global
|
|
10
|
+
}
|
|
11
|
+
export namespace Access {
|
|
12
|
+
export import Permission = AccessPermission
|
|
13
|
+
export const type = isly.object({
|
|
14
|
+
uk: Access.Permission.type.optional(),
|
|
15
|
+
eea: Access.Permission.type.optional(),
|
|
16
|
+
test: Access.Permission.type.optional(),
|
|
17
|
+
"*": isly.object({ user: Access.Permission.Level.type.optional() }).optional(),
|
|
18
|
+
})
|
|
19
|
+
}
|
package/User/Identity.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { gracely } from "gracely"
|
|
2
|
+
import { http } from "cloudly-http"
|
|
3
|
+
import { Realm } from "../Realm"
|
|
4
|
+
import { Access } from "./Access"
|
|
5
|
+
import { JWT } from "./JWT"
|
|
6
|
+
|
|
7
|
+
export class Identity {
|
|
8
|
+
get realm(): Realm {
|
|
9
|
+
return this.payload.realm
|
|
10
|
+
}
|
|
11
|
+
constructor(public readonly payload: JWT.Payload, private readonly jwt: string) {}
|
|
12
|
+
|
|
13
|
+
authenticate(constraint: Access.Permission | Access.Permission[]): Identity | gracely.Error {
|
|
14
|
+
let allowed: boolean
|
|
15
|
+
if (Array.isArray(constraint))
|
|
16
|
+
allowed = constraint.some(c => this.authenticate(c))
|
|
17
|
+
else
|
|
18
|
+
allowed = Access.Permission.check(constraint, this.payload.permission)
|
|
19
|
+
return allowed ? this : gracely.client.forbidden()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Key will default to production jwt verification key */
|
|
23
|
+
static async open(
|
|
24
|
+
authorization: string | undefined,
|
|
25
|
+
options: { whitelist?: JWT.Whitelist; key?: string }
|
|
26
|
+
): Promise<Identity | gracely.Error> {
|
|
27
|
+
const jwt = authorization?.startsWith("Bearer ") ? authorization.replace("Bearer ", "") : undefined
|
|
28
|
+
const payload = jwt ? await JWT.open({ public: options.key }, options.whitelist).verify(jwt) : undefined
|
|
29
|
+
return jwt && payload ? new Identity(payload, jwt) : gracely.client.unauthorized()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export namespace Identity {}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { authly } from "authly"
|
|
2
|
+
import { isly } from "isly"
|
|
3
|
+
import { Realm } from "../../Realm"
|
|
4
|
+
import { Access } from "../Access"
|
|
5
|
+
|
|
6
|
+
export type Payload = Payload.LongTerm | Payload.ShortTerm
|
|
7
|
+
export namespace Payload {
|
|
8
|
+
export interface Creatable {
|
|
9
|
+
sub: string
|
|
10
|
+
permission: Access.Permission
|
|
11
|
+
realm: Realm
|
|
12
|
+
}
|
|
13
|
+
export namespace Creatable {
|
|
14
|
+
export const type = isly.object<Creatable>({
|
|
15
|
+
sub: isly.string(),
|
|
16
|
+
permission: Access.Permission.type,
|
|
17
|
+
realm: Realm.type,
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
export interface Base extends authly.Payload {
|
|
21
|
+
aud: string
|
|
22
|
+
iat: number
|
|
23
|
+
iss: string
|
|
24
|
+
sub: string
|
|
25
|
+
permission: Access.Permission
|
|
26
|
+
realm: Realm
|
|
27
|
+
}
|
|
28
|
+
export namespace Base {
|
|
29
|
+
export const type = isly.object<Payload>({
|
|
30
|
+
aud: isly.string(),
|
|
31
|
+
iat: isly.number(),
|
|
32
|
+
iss: isly.string(),
|
|
33
|
+
sub: isly.string(),
|
|
34
|
+
permission: Access.Permission.type,
|
|
35
|
+
realm: Realm.type,
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
export interface LongTerm extends Base {
|
|
39
|
+
id: string
|
|
40
|
+
}
|
|
41
|
+
export namespace LongTerm {
|
|
42
|
+
export const type = Base.type.extend<LongTerm>({ id: isly.string() })
|
|
43
|
+
}
|
|
44
|
+
export interface ShortTerm extends Base {
|
|
45
|
+
exp: number
|
|
46
|
+
}
|
|
47
|
+
export namespace ShortTerm {
|
|
48
|
+
export const type = Base.type.extend<ShortTerm>({ exp: isly.number() })
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const configuration = {
|
|
52
|
+
aud: "https://banking.pax2pay.app",
|
|
53
|
+
iss: "pax2pay",
|
|
54
|
+
}
|
|
55
|
+
export const type = isly.union<Payload>(LongTerm.type, ShortTerm.type)
|
|
56
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { authly } from "authly"
|
|
2
|
+
import { Payload } from "./Payload"
|
|
3
|
+
|
|
4
|
+
export class Signer {
|
|
5
|
+
constructor(private readonly key?: { public?: string; private?: string }) {}
|
|
6
|
+
|
|
7
|
+
/** Duration in seconds */
|
|
8
|
+
async sign(data: Payload.Creatable, duration: number | "infinite" = 60 * 60 * 12): Promise<string | undefined> {
|
|
9
|
+
let result: string | undefined
|
|
10
|
+
if (duration === "infinite") {
|
|
11
|
+
const signer = Signer.create({ key: this.key })
|
|
12
|
+
result = await signer?.sign({ id: authly.Identifier.generate(16), ...data })
|
|
13
|
+
} else {
|
|
14
|
+
const signer = Signer.create({ key: this.key, duration })
|
|
15
|
+
result = await signer?.sign({ ...data })
|
|
16
|
+
}
|
|
17
|
+
return result
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static open(key?: { public?: string; private?: string }): Signer {
|
|
21
|
+
return new this(key)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export namespace Signer {
|
|
25
|
+
export function create<T extends authly.Payload>(options: {
|
|
26
|
+
key?: { public?: string; private?: string }
|
|
27
|
+
duration?: number
|
|
28
|
+
}): authly.Issuer<T> | undefined {
|
|
29
|
+
const signer = authly.Issuer.create(
|
|
30
|
+
Payload.configuration.iss,
|
|
31
|
+
authly.Algorithm.RS256(options.key?.public, options.key?.private)
|
|
32
|
+
)
|
|
33
|
+
if (signer) {
|
|
34
|
+
signer.duration = options.duration
|
|
35
|
+
signer.audience = Payload.configuration.aud
|
|
36
|
+
}
|
|
37
|
+
return signer
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { authly } from "authly"
|
|
2
|
+
import { Realm } from "../../Realm"
|
|
3
|
+
import { Payload as JWTPayload } from "./Payload"
|
|
4
|
+
import { Signer as JWTSigner } from "./Signer"
|
|
5
|
+
|
|
6
|
+
export class JWT {
|
|
7
|
+
#verifier?: authly.Verifier<JWT.Payload>
|
|
8
|
+
private get verifier(): authly.Verifier<JWT.Payload> | undefined {
|
|
9
|
+
if (!this.#verifier && this.key?.public) {
|
|
10
|
+
const algorithm = authly.Algorithm.RS256(this.key.public)
|
|
11
|
+
this.#verifier = algorithm ? authly.Verifier.create(algorithm) : undefined
|
|
12
|
+
}
|
|
13
|
+
return this.#verifier
|
|
14
|
+
}
|
|
15
|
+
#signer?: JWTSigner
|
|
16
|
+
private get signer(): JWTSigner | undefined {
|
|
17
|
+
return this.key?.private ? (this.#signer ??= JWTSigner.open(this.key)) : undefined
|
|
18
|
+
}
|
|
19
|
+
get sign() {
|
|
20
|
+
return this.signer?.sign
|
|
21
|
+
}
|
|
22
|
+
private constructor(
|
|
23
|
+
private readonly key?: { public?: string; private?: string },
|
|
24
|
+
readonly whitelist?: JWT.Whitelist
|
|
25
|
+
) {}
|
|
26
|
+
|
|
27
|
+
async verify(token: string): Promise<JWT.Payload | undefined> {
|
|
28
|
+
const verified = await this.verifier?.verify(token, JWT.Payload.configuration.aud)
|
|
29
|
+
delete verified?.token
|
|
30
|
+
return JWT.Payload.type.is(verified) &&
|
|
31
|
+
verified?.iss == JWT.Payload.configuration.iss &&
|
|
32
|
+
(verified.exp || (verified.id && this.whitelist?.[verified.realm]?.some(e => e.id === verified.id)))
|
|
33
|
+
? verified
|
|
34
|
+
: undefined
|
|
35
|
+
}
|
|
36
|
+
async unpack(token: string): Promise<JWT.Payload | undefined> {
|
|
37
|
+
const unpacked = await JWT.unpack(token)
|
|
38
|
+
delete unpacked?.token
|
|
39
|
+
return unpacked
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static open(key?: { private?: string; public?: string }, whitelist?: JWT.Whitelist): JWT {
|
|
43
|
+
return new this({ private: key?.private, public: key?.public ?? JWT.key }, whitelist)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export namespace JWT {
|
|
47
|
+
export import Signer = JWTSigner
|
|
48
|
+
export type Whitelist = Partial<Record<Realm, Payload.LongTerm[]>>
|
|
49
|
+
export async function unpack(token: string): Promise<JWT.Payload | undefined> {
|
|
50
|
+
const algorithm = authly.Algorithm.RS256(undefined)
|
|
51
|
+
const verifier = algorithm ? authly.Verifier.create<JWT.Payload>(algorithm) : undefined
|
|
52
|
+
return verifier?.unpack(token)
|
|
53
|
+
}
|
|
54
|
+
export import Payload = JWTPayload
|
|
55
|
+
export const key =
|
|
56
|
+
"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2W8CD2kpfS4QIRV2/rgm4NVvsvJsYNMHtnIl9ADvO3A81hAmRKvOAPVoXICe6+EuZ47jGjGL7f48GEoQuITfBPv/MosCDj1YhJ56ILDynCSd8FlxDrhv8pl5IquST7tcL6Hc6m+vuvoTLrFQ5QqNxv0a5eDd/YTrWv7SUuRfBEhYd/wMysGynN3QauHqy5ceBCt1nv1MJLGlSzczMRK7wjy1zi2g9NCHZBOoo1HXOpi727Xh+YXHc9EP2TN0oOXyxykv45nkGIDI0Qek3/pfkavClBffc1sEqA+rUx7YqRN9KGYxwLMLug+NOOh3ptqjfobXbR5fx/sUWhvcjUMTE1JreTrWYbGmVnjd/SeYSClfmGhdTBUfqnZbaABv0ruTXva18qRhP4y143vHMk/k8HzbuROTKAzrtEeLIjgwUgDcnE+JwDqcb8tKSGV6i++TiTldlSBCRTT4dK2hpHJje80b2abqtrbCkxbJlT98UsAAoiq2eW1X6lYmCfiGCJPkfswibQ2tPAKKNe/2xuHPsjx4FuXGmV0dbzmCwSIQoApXqOvKzoNFi6AaKIjxfNmiEigLwKpNrw08H0lVZbq/9MMxI3TzMTZjY9QmBKVLSGy3Z6IJqZpyK22lv7whJcllG0Qw8tv8+7wmC8SR3+4jpuxuFGZ+69CW+otx+CPMJjcCAwEAAQ=="
|
|
57
|
+
}
|
package/User/Me.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isly } from "isly"
|
|
2
|
+
import { Realm } from "../Realm"
|
|
3
|
+
|
|
4
|
+
export interface Me {
|
|
5
|
+
email: string
|
|
6
|
+
password: string
|
|
7
|
+
realm: Realm
|
|
8
|
+
}
|
|
9
|
+
export namespace Me {
|
|
10
|
+
export const type = isly.object<Me>({
|
|
11
|
+
email: isly.string(),
|
|
12
|
+
password: isly.string(),
|
|
13
|
+
realm: Realm.type,
|
|
14
|
+
})
|
|
15
|
+
}
|
package/User/Password.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { cryptly } from "cryptly"
|
|
2
|
+
import { gracely } from "gracely"
|
|
3
|
+
import { isoly } from "isoly"
|
|
4
|
+
import { isly } from "isly"
|
|
5
|
+
|
|
6
|
+
export interface Password {
|
|
7
|
+
hash: cryptly.Password.Hash
|
|
8
|
+
changed: isoly.DateTime
|
|
9
|
+
}
|
|
10
|
+
export namespace Password {
|
|
11
|
+
export interface Creatable {
|
|
12
|
+
new: string
|
|
13
|
+
repeat: string
|
|
14
|
+
}
|
|
15
|
+
export namespace Creatable {
|
|
16
|
+
export const type = isly.object<Creatable>({
|
|
17
|
+
new: isly.string(),
|
|
18
|
+
repeat: isly.string(),
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
export async function create(creatable: Creatable, pepper: string | undefined): Promise<Password | gracely.Error> {
|
|
22
|
+
let result: Awaited<ReturnType<typeof create>>
|
|
23
|
+
if (creatable.new !== creatable.repeat)
|
|
24
|
+
result = gracely.client.forbidden("The new password and the repeated password do not match.")
|
|
25
|
+
else if (!pepper)
|
|
26
|
+
result = gracely.server.backendFailure("The password cannot be created without a pepper.")
|
|
27
|
+
else
|
|
28
|
+
result = { hash: await hash(creatable.new, pepper), changed: isoly.DateTime.now() }
|
|
29
|
+
return result
|
|
30
|
+
}
|
|
31
|
+
export function salt(): string {
|
|
32
|
+
return cryptly.Base64.encode(cryptly.RandomValue.generate(new Uint8Array(64)).toString())
|
|
33
|
+
}
|
|
34
|
+
export async function hash(password: string, pepper: string): Promise<cryptly.Password.Hash> {
|
|
35
|
+
return cryptly.Password.hash(signer(pepper), password, salt())
|
|
36
|
+
}
|
|
37
|
+
export async function verify(password: string, hash: cryptly.Password.Hash, pepper: string): Promise<boolean> {
|
|
38
|
+
return cryptly.Password.verify(signer(pepper), hash, password)
|
|
39
|
+
}
|
|
40
|
+
function signer(pepper: string): { sign: (data: string) => Promise<string> } {
|
|
41
|
+
return cryptly.Signer.create("HMAC", "SHA-512", pepper)
|
|
42
|
+
}
|
|
43
|
+
}
|
package/User/index.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { isoly } from "isoly"
|
|
2
|
+
import { isly } from "isly"
|
|
3
|
+
import { Realm } from "../Realm"
|
|
4
|
+
import { Access as UserAccess } from "./Access"
|
|
5
|
+
import { Identity as UserIdentity } from "./Identity"
|
|
6
|
+
import { JWT as UserJWT } from "./JWT"
|
|
7
|
+
import { Me as UserMe } from "./Me"
|
|
8
|
+
import { Password as UserPassword } from "./Password"
|
|
9
|
+
|
|
10
|
+
export interface User {
|
|
11
|
+
email: string
|
|
12
|
+
access: User.Access
|
|
13
|
+
changed: isoly.DateTime
|
|
14
|
+
created: isoly.DateTime
|
|
15
|
+
password: {
|
|
16
|
+
changed: isoly.DateTime
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export namespace User {
|
|
20
|
+
export import Access = UserAccess
|
|
21
|
+
export import Identity = UserIdentity
|
|
22
|
+
export import JWT = UserJWT
|
|
23
|
+
export import Me = UserMe
|
|
24
|
+
export import Password = UserPassword
|
|
25
|
+
export function fromInvite(invite: Invite): User {
|
|
26
|
+
const now = isoly.DateTime.now()
|
|
27
|
+
return {
|
|
28
|
+
email: invite.email,
|
|
29
|
+
access: invite.access,
|
|
30
|
+
changed: now,
|
|
31
|
+
created: now,
|
|
32
|
+
password: {
|
|
33
|
+
changed: now,
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function toJWTPayloadCreatable(user: User, realm: Realm): User.JWT.Payload.Creatable {
|
|
38
|
+
return {
|
|
39
|
+
sub: user.email,
|
|
40
|
+
permission: { ...(user.access[realm] ?? {}), ...(user.access["*"] ?? {}) },
|
|
41
|
+
realm,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export interface Creatable {
|
|
45
|
+
invite: string
|
|
46
|
+
password: Password.Creatable
|
|
47
|
+
}
|
|
48
|
+
export namespace Creatable {
|
|
49
|
+
export const type = isly.object<Creatable>({
|
|
50
|
+
invite: isly.string(),
|
|
51
|
+
password: isly.object({ new: isly.string(), repeat: isly.string() }),
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
export interface Invite {
|
|
55
|
+
id: string
|
|
56
|
+
email: string
|
|
57
|
+
access: Access
|
|
58
|
+
}
|
|
59
|
+
export namespace Invite {
|
|
60
|
+
export interface Creatable {
|
|
61
|
+
email: string
|
|
62
|
+
access: Access
|
|
63
|
+
}
|
|
64
|
+
export namespace Creatable {
|
|
65
|
+
export const type = isly.object<Creatable>({ email: isly.string(), access: Access.type })
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -5,6 +5,7 @@ import type { Rule } from "../Rule";
|
|
|
5
5
|
import { Expiry } from "./Expiry";
|
|
6
6
|
import { Meta } from "./Meta";
|
|
7
7
|
import { Preset } from "./Preset";
|
|
8
|
+
import { Restriction } from "./Restriction";
|
|
8
9
|
export interface Creatable {
|
|
9
10
|
account: string;
|
|
10
11
|
number?: string;
|
|
@@ -17,6 +18,9 @@ export interface Creatable {
|
|
|
17
18
|
rules?: Rule[];
|
|
18
19
|
meta?: Meta;
|
|
19
20
|
key?: isoly.Date | string;
|
|
21
|
+
restricted?: {
|
|
22
|
+
to?: Restriction;
|
|
23
|
+
};
|
|
20
24
|
}
|
|
21
25
|
export declare namespace Creatable {
|
|
22
26
|
const type: import("isly/dist/cjs/object").IslyObject<Creatable, object>;
|