@things-factory/auth-base 10.1.31 → 10.1.33
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-server/controllers/invitation.d.ts +47 -10
- package/dist-server/controllers/invitation.js +159 -113
- package/dist-server/controllers/invitation.js.map +1 -1
- package/dist-server/controllers/profile.d.ts +1 -0
- package/dist-server/router/auth-public-process-router.js +36 -13
- package/dist-server/router/auth-public-process-router.js.map +1 -1
- package/dist-server/router/oauth2/oauth2-router.js +7 -2
- package/dist-server/router/oauth2/oauth2-router.js.map +1 -1
- package/dist-server/router/oauth2/oauth2-server.js +4 -4
- package/dist-server/router/oauth2/oauth2-server.js.map +1 -1
- package/dist-server/service/app-binding/app-binding-mutation.d.ts +27 -0
- package/dist-server/service/app-binding/app-binding-mutation.js +39 -6
- package/dist-server/service/app-binding/app-binding-mutation.js.map +1 -1
- package/dist-server/service/app-binding/app-binding-query.js +14 -7
- package/dist-server/service/app-binding/app-binding-query.js.map +1 -1
- package/dist-server/service/app-binding/app-binding.d.ts +9 -0
- package/dist-server/service/app-binding/app-binding.js +10 -1
- package/dist-server/service/app-binding/app-binding.js.map +1 -1
- package/dist-server/service/appliance/appliance-mutation.js +34 -1
- package/dist-server/service/appliance/appliance-mutation.js.map +1 -1
- package/dist-server/service/appliance/appliance-query.d.ts +2 -0
- package/dist-server/service/appliance/appliance-query.js +48 -0
- package/dist-server/service/appliance/appliance-query.js.map +1 -1
- package/dist-server/service/appliance/appliance.d.ts +1 -0
- package/dist-server/service/appliance/appliance.js +33 -3
- package/dist-server/service/appliance/appliance.js.map +1 -1
- package/dist-server/service/application/application-mutation.js +51 -6
- package/dist-server/service/application/application-mutation.js.map +1 -1
- package/dist-server/service/application/application.d.ts +9 -3
- package/dist-server/service/application/application.js +24 -8
- package/dist-server/service/application/application.js.map +1 -1
- package/dist-server/service/auth-provider/auth-provider-mutation.js +5 -0
- package/dist-server/service/auth-provider/auth-provider-mutation.js.map +1 -1
- package/dist-server/service/domain-generator/domain-generator-mutation.js +3 -0
- package/dist-server/service/domain-generator/domain-generator-mutation.js.map +1 -1
- package/dist-server/service/invitation/invitation-mutation.d.ts +17 -15
- package/dist-server/service/invitation/invitation-mutation.js +83 -56
- package/dist-server/service/invitation/invitation-mutation.js.map +1 -1
- package/dist-server/service/invitation/invitation-query.d.ts +20 -5
- package/dist-server/service/invitation/invitation-query.js +60 -19
- package/dist-server/service/invitation/invitation-query.js.map +1 -1
- package/dist-server/service/invitation/invitation.d.ts +14 -2
- package/dist-server/service/invitation/invitation.js +77 -8
- package/dist-server/service/invitation/invitation.js.map +1 -1
- package/dist-server/service/login-history/login-history-query.js +3 -0
- package/dist-server/service/login-history/login-history-query.js.map +1 -1
- package/dist-server/service/role/role-mutation.js +14 -15
- package/dist-server/service/role/role-mutation.js.map +1 -1
- package/dist-server/service/role/role-query.js +26 -4
- package/dist-server/service/role/role-query.js.map +1 -1
- package/dist-server/service/role/role-types.d.ts +2 -0
- package/dist-server/service/role/role-types.js +8 -0
- package/dist-server/service/role/role-types.js.map +1 -1
- package/dist-server/service/role-template/role-template-mutation.d.ts +17 -1
- package/dist-server/service/role-template/role-template-mutation.js +14 -3
- package/dist-server/service/role-template/role-template-mutation.js.map +1 -1
- package/dist-server/service/user/user-mutation.js +1 -0
- package/dist-server/service/user/user-mutation.js.map +1 -1
- package/dist-server/service/user/user.d.ts +1 -0
- package/dist-server/service/user/user.js +19 -4
- package/dist-server/service/user/user.js.map +1 -1
- package/dist-server/templates/invitation-email.d.ts +2 -1
- package/dist-server/templates/invitation-email.js +16 -4
- package/dist-server/templates/invitation-email.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/utils/credential-at-rest.d.ts +28 -0
- package/dist-server/utils/credential-at-rest.js +36 -0
- package/dist-server/utils/credential-at-rest.js.map +1 -0
- package/dist-server/utils/credential-features.d.ts +35 -0
- package/dist-server/utils/credential-features.js +45 -0
- package/dist-server/utils/credential-features.js.map +1 -0
- package/dist-server/utils/credential-serial-rule.d.ts +41 -0
- package/dist-server/utils/credential-serial-rule.js +43 -0
- package/dist-server/utils/credential-serial-rule.js.map +1 -0
- package/dist-server/utils/invitation-state.d.ts +52 -0
- package/dist-server/utils/invitation-state.js +76 -0
- package/dist-server/utils/invitation-state.js.map +1 -0
- package/dist-server/utils/refuse-role-name.d.ts +42 -0
- package/dist-server/utils/refuse-role-name.js +76 -0
- package/dist-server/utils/refuse-role-name.js.map +1 -0
- package/dist-server/utils/role-name-standing.d.ts +58 -0
- package/dist-server/utils/role-name-standing.js +72 -0
- package/dist-server/utils/role-name-standing.js.map +1 -0
- package/package.json +4 -4
- package/tests/app-binding-delete-db.test.ts +188 -0
- package/tests/appliance-credential-state-db.test.ts +207 -0
- package/tests/appliance-delete-db.test.ts +119 -0
- package/tests/appliance-revoke-db.test.ts +73 -28
- package/tests/appliance-schema.test.ts +129 -0
- package/tests/application-delete-db.test.ts +190 -0
- package/tests/application-token-payload-db.test.ts +185 -0
- package/tests/checkin-privilege-seam-db.test.ts +158 -0
- package/tests/credential-at-rest-db.test.ts +246 -0
- package/tests/credential-features-off-db.test.ts +189 -0
- package/tests/credential-serial-rule.test.ts +84 -0
- package/tests/invitation-db.test.ts +416 -0
- package/tests/invitation-state.test.ts +92 -0
- package/tests/open-doors.test.ts +132 -0
- package/tests/role-inheritance-db.test.ts +286 -0
- package/tests/role-mutation-db.test.ts +21 -7
- package/tests/role-name-lookup-sentinel.test.ts +79 -0
- package/tests/role-name-standing.test.ts +83 -0
- package/tests/token-issuance.test.ts +7 -3
- package/translations/en.json +2 -0
- package/translations/ja.json +2 -0
- package/translations/ko.json +2 -0
- package/translations/ms.json +2 -0
- package/translations/zh.json +2 -0
|
@@ -4,6 +4,7 @@ exports.ApplianceQuery = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const type_graphql_1 = require("type-graphql");
|
|
6
6
|
const shell_1 = require("@things-factory/shell");
|
|
7
|
+
const credential_features_js_1 = require("../../utils/credential-features.js");
|
|
7
8
|
const appliance_js_1 = require("../appliance/appliance.js");
|
|
8
9
|
const user_js_1 = require("../user/user.js");
|
|
9
10
|
const appliance_types_js_1 = require("./appliance-types.js");
|
|
@@ -36,9 +37,42 @@ let ApplianceQuery = class ApplianceQuery {
|
|
|
36
37
|
const [items, total] = await queryBuilder.getManyAndCount();
|
|
37
38
|
return { items, total };
|
|
38
39
|
}
|
|
40
|
+
/*
|
|
41
|
+
* Does revoking an appliance credential work here? (ADR-0085 decision 1.)
|
|
42
|
+
*
|
|
43
|
+
* ── Why the screen has to ask ───────────────────────────────────────────
|
|
44
|
+
* Revocation is behind `credential/revocable`, and an installation that never configured it
|
|
45
|
+
* has no such door — `revokeApplianceCredential` answers 404. The screen cannot read the
|
|
46
|
+
* config, so without this it would either hide a feature the installation paid for or show a
|
|
47
|
+
* button that always refuses. The second is the worse one: it reads as a broken product
|
|
48
|
+
* rather than as something nobody turned on.
|
|
49
|
+
*
|
|
50
|
+
* ── Why it carries the mutation's own gate ──────────────────────────────
|
|
51
|
+
* The same `category` / `privilege` pair as `revokeApplianceCredential`, so what comes back is
|
|
52
|
+
* not "the installation has this" but **"pressing it would work for you"**. Those differ for a
|
|
53
|
+
* reader who may list appliances and may not act on their credentials, and a button should
|
|
54
|
+
* follow the second. A refusal here reaches the client as an error, which it reads the same
|
|
55
|
+
* way as false.
|
|
56
|
+
*/
|
|
57
|
+
async applianceCredentialRevocable() {
|
|
58
|
+
return (0, credential_features_js_1.offersCredentialRevocation)();
|
|
59
|
+
}
|
|
39
60
|
async accessToken(appliance, context) {
|
|
40
61
|
return appliance.accessToken;
|
|
41
62
|
}
|
|
63
|
+
/*
|
|
64
|
+
* Revocation is recorded on the shadow user, so the answer is not on the row that was asked
|
|
65
|
+
* for. INACTIVE means revoked and nothing else does: `ensureApplianceUser` creates the user
|
|
66
|
+
* ACTIVATED, `generateApplianceSecret` puts it back to ACTIVATED, and `revokeApplianceCredential`
|
|
67
|
+
* is the one place that sets INACTIVE.
|
|
68
|
+
*
|
|
69
|
+
* An appliance with no shadow user at all is not revoked — it is one nothing was ever issued
|
|
70
|
+
* for, which is the state a freshly registered appliance is in.
|
|
71
|
+
*/
|
|
72
|
+
async credentialRevoked(appliance) {
|
|
73
|
+
const appuser = await (0, shell_1.getRepository)(user_js_1.User).findOneBy({ reference: appliance.id, userType: 'appliance' });
|
|
74
|
+
return appuser?.status === user_js_1.UserStatus.INACTIVE;
|
|
75
|
+
}
|
|
42
76
|
async domain(context) {
|
|
43
77
|
return context.state.domain;
|
|
44
78
|
}
|
|
@@ -77,6 +111,13 @@ tslib_1.__decorate([
|
|
|
77
111
|
tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
|
|
78
112
|
tslib_1.__metadata("design:returntype", Promise)
|
|
79
113
|
], ApplianceQuery.prototype, "edges", null);
|
|
114
|
+
tslib_1.__decorate([
|
|
115
|
+
(0, type_graphql_1.Directive)('@privilege(category: "security", privilege: "mutation", domainOwnerGranted: true)'),
|
|
116
|
+
(0, type_graphql_1.Query)(returns => Boolean, { description: 'Whether this installation offers appliance credential revocation' }),
|
|
117
|
+
tslib_1.__metadata("design:type", Function),
|
|
118
|
+
tslib_1.__metadata("design:paramtypes", []),
|
|
119
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
120
|
+
], ApplianceQuery.prototype, "applianceCredentialRevocable", null);
|
|
80
121
|
tslib_1.__decorate([
|
|
81
122
|
(0, type_graphql_1.FieldResolver)(type => String),
|
|
82
123
|
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
@@ -85,6 +126,13 @@ tslib_1.__decorate([
|
|
|
85
126
|
tslib_1.__metadata("design:paramtypes", [appliance_js_1.Appliance, Object]),
|
|
86
127
|
tslib_1.__metadata("design:returntype", Promise)
|
|
87
128
|
], ApplianceQuery.prototype, "accessToken", null);
|
|
129
|
+
tslib_1.__decorate([
|
|
130
|
+
(0, type_graphql_1.FieldResolver)(type => Boolean),
|
|
131
|
+
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
132
|
+
tslib_1.__metadata("design:type", Function),
|
|
133
|
+
tslib_1.__metadata("design:paramtypes", [appliance_js_1.Appliance]),
|
|
134
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
135
|
+
], ApplianceQuery.prototype, "credentialRevoked", null);
|
|
88
136
|
tslib_1.__decorate([
|
|
89
137
|
(0, type_graphql_1.FieldResolver)(type => shell_1.Domain),
|
|
90
138
|
tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appliance-query.js","sourceRoot":"","sources":["../../../server/service/appliance/appliance-query.ts"],"names":[],"mappings":";;;;AAAA,+CAA8F;AAE9F,iDAAuG;AAEvG,4DAAqD;AACrD,
|
|
1
|
+
{"version":3,"file":"appliance-query.js","sourceRoot":"","sources":["../../../server/service/appliance/appliance-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'\n\nimport { getQueryBuilderFromListParams, Domain, getRepository, ListParam } from '@things-factory/shell'\n\nimport { offersCredentialRevocation } from '../../utils/credential-features.js'\nimport { Appliance } from '../appliance/appliance.js'\nimport { User, UserStatus } from '../user/user.js'\nimport { ApplianceList } from './appliance-types.js'\n\n@Resolver(Appliance)\nexport class ApplianceQuery {\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => Appliance, { description: ' To fetch appliance' })\n async appliance(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Appliance> {\n const { domain } = context.state\n return await getRepository(Appliance).findOneBy({ domain: { id: domain.id }, id })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => ApplianceList, { description: 'To fetch multiple appliance' })\n async appliances(\n @Args(type => ListParam) params: ListParam,\n @Ctx() context: ResolverContext\n ): Promise<ApplianceList> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n domain,\n params,\n repository: getRepository(Appliance),\n alias: 'appliance',\n searchables: ['name', 'description']\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"query\", domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => ApplianceList, { description: 'To fetch multiple appliance' })\n async edges(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<ApplianceList> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n domain,\n params,\n repository: getRepository(Appliance),\n alias: 'appliance',\n searchables: ['name', 'description']\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n /*\n * Does revoking an appliance credential work here? (ADR-0085 decision 1.)\n *\n * ── Why the screen has to ask ───────────────────────────────────────────\n * Revocation is behind `credential/revocable`, and an installation that never configured it\n * has no such door — `revokeApplianceCredential` answers 404. The screen cannot read the\n * config, so without this it would either hide a feature the installation paid for or show a\n * button that always refuses. The second is the worse one: it reads as a broken product\n * rather than as something nobody turned on.\n *\n * ── Why it carries the mutation's own gate ──────────────────────────────\n * The same `category` / `privilege` pair as `revokeApplianceCredential`, so what comes back is\n * not \"the installation has this\" but **\"pressing it would work for you\"**. Those differ for a\n * reader who may list appliances and may not act on their credentials, and a button should\n * follow the second. A refusal here reaches the client as an error, which it reads the same\n * way as false.\n */\n @Directive('@privilege(category: \"security\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Query(returns => Boolean, { description: 'Whether this installation offers appliance credential revocation' })\n async applianceCredentialRevocable(): Promise<boolean> {\n return offersCredentialRevocation()\n }\n\n @FieldResolver(type => String)\n async accessToken(@Root() appliance: Appliance, @Ctx() context: ResolverContext) {\n return appliance.accessToken\n }\n\n /*\n * Revocation is recorded on the shadow user, so the answer is not on the row that was asked\n * for. INACTIVE means revoked and nothing else does: `ensureApplianceUser` creates the user\n * ACTIVATED, `generateApplianceSecret` puts it back to ACTIVATED, and `revokeApplianceCredential`\n * is the one place that sets INACTIVE.\n *\n * An appliance with no shadow user at all is not revoked — it is one nothing was ever issued\n * for, which is the state a freshly registered appliance is in.\n */\n @FieldResolver(type => Boolean)\n async credentialRevoked(@Root() appliance: Appliance): Promise<boolean> {\n const appuser = await getRepository(User).findOneBy({ reference: appliance.id, userType: 'appliance' })\n\n return appuser?.status === UserStatus.INACTIVE\n }\n\n @FieldResolver(type => Domain)\n async domain(@Ctx() context: ResolverContext) {\n return context.state.domain\n }\n\n @FieldResolver(type => User)\n async updater(@Root() appliance: Appliance): Promise<User> {\n return await getRepository(User).findOneBy({ id: appliance.updaterId })\n }\n\n @FieldResolver(type => User)\n async creator(@Root() appliance: Appliance): Promise<User> {\n return await getRepository(User).findOneBy({ id: appliance.creatorId })\n }\n}\n"]}
|
|
@@ -6,13 +6,23 @@ const typeorm_1 = require("typeorm");
|
|
|
6
6
|
const type_graphql_1 = require("type-graphql");
|
|
7
7
|
const shell_1 = require("@things-factory/shell");
|
|
8
8
|
const env_1 = require("@things-factory/env");
|
|
9
|
+
const credential_at_rest_js_1 = require("../../utils/credential-at-rest.js");
|
|
9
10
|
const user_js_1 = require("../user/user.js");
|
|
10
11
|
const get_secret_js_1 = require("../../utils/get-secret.js");
|
|
11
12
|
const jsonwebtoken_1 = tslib_1.__importDefault(require("jsonwebtoken"));
|
|
12
13
|
const ORMCONFIG = env_1.config.get('ormconfig', {});
|
|
13
14
|
const DATABASE_TYPE = ORMCONFIG.type;
|
|
14
15
|
let Appliance = class Appliance {
|
|
15
|
-
/*
|
|
16
|
+
/*
|
|
17
|
+
* `cs` is the credential serial (ADR-0085 decision 3) — which generation of this appliance's
|
|
18
|
+
* credential the token belongs to. It is absent for a subject that has never been through the
|
|
19
|
+
* new path, and `checkAuth` lets an absent one by, which is what keeps the tokens already in
|
|
20
|
+
* the field working.
|
|
21
|
+
*
|
|
22
|
+
* It also makes each issue distinct. The rest of this payload is the same every time and
|
|
23
|
+
* `iat` counts whole seconds, so two signings inside one second used to produce the identical
|
|
24
|
+
* string — revoking and immediately re-issuing handed back the credential just revoked.
|
|
25
|
+
*/
|
|
16
26
|
static sign(subject, expiresIn, domain, user, appliance) {
|
|
17
27
|
var credential = {
|
|
18
28
|
id: user.id,
|
|
@@ -25,6 +35,9 @@ let Appliance = class Appliance {
|
|
|
25
35
|
subdomain: domain.subdomain
|
|
26
36
|
}
|
|
27
37
|
};
|
|
38
|
+
if (user.credentialSerial !== null && user.credentialSerial !== undefined) {
|
|
39
|
+
credential.cs = user.credentialSerial;
|
|
40
|
+
}
|
|
28
41
|
return jsonwebtoken_1.default.sign(credential, get_secret_js_1.SECRET, {
|
|
29
42
|
expiresIn,
|
|
30
43
|
issuer: 'hatiolab.com',
|
|
@@ -145,12 +158,20 @@ tslib_1.__decorate([
|
|
|
145
158
|
: DATABASE_TYPE == 'mssql'
|
|
146
159
|
? 'nvarchar'
|
|
147
160
|
: 'varchar',
|
|
148
|
-
length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined
|
|
161
|
+
length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined,
|
|
162
|
+
transformer: credential_at_rest_js_1.credentialAtRestTransformer
|
|
149
163
|
}),
|
|
150
164
|
(0, type_graphql_1.Field)({ nullable: true, description: 'The access token for the appliance.' }),
|
|
151
165
|
(0, type_graphql_1.Directive)('@privilege(category: "security", privilege: "query", domainOwnerGranted: true)'),
|
|
152
166
|
tslib_1.__metadata("design:type", String)
|
|
153
167
|
], Appliance.prototype, "accessToken", void 0);
|
|
168
|
+
tslib_1.__decorate([
|
|
169
|
+
(0, type_graphql_1.Field)(type => Boolean, {
|
|
170
|
+
nullable: true,
|
|
171
|
+
description: "Whether this appliance's credential has been revoked."
|
|
172
|
+
}),
|
|
173
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
174
|
+
], Appliance.prototype, "credentialRevoked", void 0);
|
|
154
175
|
tslib_1.__decorate([
|
|
155
176
|
(0, typeorm_1.ManyToOne)(type => user_js_1.User, { nullable: true }),
|
|
156
177
|
(0, type_graphql_1.Field)({ nullable: true, description: 'The user who created the appliance.' }),
|
|
@@ -178,7 +199,16 @@ tslib_1.__decorate([
|
|
|
178
199
|
(0, typeorm_1.UpdateDateColumn)(),
|
|
179
200
|
(0, type_graphql_1.Field)({ nullable: true, description: 'The timestamp when the appliance was last updated.' }),
|
|
180
201
|
tslib_1.__metadata("design:type", Date
|
|
181
|
-
/*
|
|
202
|
+
/*
|
|
203
|
+
* `cs` is the credential serial (ADR-0085 decision 3) — which generation of this appliance's
|
|
204
|
+
* credential the token belongs to. It is absent for a subject that has never been through the
|
|
205
|
+
* new path, and `checkAuth` lets an absent one by, which is what keeps the tokens already in
|
|
206
|
+
* the field working.
|
|
207
|
+
*
|
|
208
|
+
* It also makes each issue distinct. The rest of this payload is the same every time and
|
|
209
|
+
* `iat` counts whole seconds, so two signings inside one second used to produce the identical
|
|
210
|
+
* string — revoking and immediately re-issuing handed back the credential just revoked.
|
|
211
|
+
*/
|
|
182
212
|
)
|
|
183
213
|
], Appliance.prototype, "updatedAt", void 0);
|
|
184
214
|
exports.Appliance = Appliance = tslib_1.__decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appliance.js","sourceRoot":"","sources":["../../../server/service/appliance/appliance.ts"],"names":[],"mappings":";;;;AAAA,qCAUgB;AAChB,+CAA+D;AAC/D,iDAA6D;AAC7D,6CAA4C;AAE5C,6CAAkD;AAClD,6DAAkD;AAClD,wEAA8B;AAE9B,MAAM,SAAS,GAAG,YAAM,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,IAAI,CAAA;AAO7B,IAAM,SAAS,GAAf,MAAM,SAAS;IAgFpB,8BAA8B;IAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS;QACrD,IAAI,UAAU,GAAG;YACf,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,QAAQ,EAAE,WAAW;YACrB,SAAS,EAAE;gBACT,EAAE,EAAE,SAAS,CAAC,EAAE;aACjB;YACD,MAAM,EAAE,oBAAU,CAAC,SAAS;YAC5B,MAAM,EAAE;gBACN,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B;SACF,CAAA;QAED,OAAO,sBAAG,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAM,EAAE;YAClC,SAAS;YACT,MAAM,EAAE,cAAc;YACtB,OAAO;SACR,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS;QAChD,uDAAuD;QACvD,IAAI,SAAS,GAAG,YAAM,CAAC,GAAG,CAAC,uBAAuB,EAAE,IAAI,CAAC,CAAA;QACzD,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAA;IACtE,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,SAAoB,EAAE,IAAU,EAAE,EAAiB;QAClF,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;QAChC,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAA;QAE9C,IAAI,OAAO,GAAS,MAAM,cAAc,CAAC,OAAO,CAAC;YAC/C,KAAK,EAAE;gBACL,SAAS,EAAE,EAAE;gBACb,QAAQ,EAAE,WAAW;aACtB;YACD,SAAS,EAAE,CAAC,SAAS,CAAC;SACvB,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,GAAG,SAAS,CAAC,EAAE,IAAI,MAAM,EAAE,SAAS,IAAI,CAAA;YAE9E,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC;gBAClC,QAAQ;gBACR,KAAK,EAAE,QAAQ;gBACf,IAAI,EAAE,SAAS,CAAC,IAAI;gBACpB,QAAQ,EAAE,WAAW;gBACrB,SAAS,EAAE,EAAE;gBACb,MAAM,EAAE,oBAAU,CAAC,SAAS;gBAC5B,OAAO,EAAE,CAAC,MAAM,CAAC;gBACjB,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI;aAClC,CAAC,CAAA;YAEF,OAAO,MAAM,cAAc,CAAC,OAAO,CAAC;gBAClC,KAAK,EAAE;oBACL,EAAE,EAAE,OAAO,CAAC,EAAE;iBACf;gBACD,SAAS,EAAE,CAAC,SAAS,CAAC;aACvB,CAAC,CAAA;QACJ,CAAC;aAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YAC7B,0DAA0D;YAC1D,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,GAAG,SAAS,CAAC,EAAE,IAAI,MAAM,EAAE,SAAS,IAAI,CAAA;YAE1E,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC;gBAClC,GAAG,OAAO;gBACV,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,QAAQ;gBACtC,OAAO,EAAE,IAAI;aACd,CAAC,CAAA;YAEF,OAAO,MAAM,cAAc,CAAC,OAAO,CAAC;gBAClC,KAAK,EAAE;oBACL,EAAE,EAAE,OAAO,CAAC,EAAE;iBACf;gBACD,SAAS,EAAE,CAAC,SAAS,CAAC;aACvB,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC;CACF,CAAA;AAhKY,8BAAS;AAGX;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;qCACxD;AAInB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;sCAC5E,cAAM;yCAAA;AAGf;IADC,IAAA,oBAAU,EAAC,CAAC,SAAoB,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC;;2CACvC;AAMhB;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;;2CAC9D;AAIhB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;uCACzC;AAIZ;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6CAA6C,EAAE,CAAC;;wCACzE;AAIb;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;;wCACzD;AAMb;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;;0CAC9D;AAMf;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC;;8CACvD;AAgBnB;IAdC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;QACd,IAAI,EACF,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS;YACpD,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,aAAa,IAAI,QAAQ;gBACzB,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,aAAa,IAAI,OAAO;oBACxB,CAAC,CAAC,UAAU;oBACZ,CAAC,CAAC,SAAS;QACnB,MAAM,EAAE,aAAa,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;KACrD,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;IAC7E,IAAA,wBAAS,EAAC,gFAAgF,CAAC;;8CACzE;AAInB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;sCACrE,cAAI;0CAAA;AAGb;IADC,IAAA,oBAAU,EAAC,CAAC,SAAoB,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC;;4CACvC;AAIjB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;sCAC1E,cAAI;0CAAA;AAGb;IADC,IAAA,oBAAU,EAAC,CAAC,SAAoB,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC;;4CACvC;AAIjB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;sCAC7E,IAAI;4CAAA;AAIf;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;sCAClF,IAAI;IAEf,8BAA8B;;4CAFf;oBA9EJ,SAAS;IALrB,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,gBAAgB,EAAE,CAAC,SAAoB,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE;QACrF,MAAM,EAAE,IAAI;KACb,CAAC;IACD,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,+EAA+E,EAAE,CAAC;GAChG,SAAS,CAgKrB","sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n EntityManager,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Directive, Field, ID, ObjectType } from 'type-graphql'\nimport { Domain, getRepository } from '@things-factory/shell'\nimport { config } from '@things-factory/env'\n\nimport { User, UserStatus } from '../user/user.js'\nimport { SECRET } from '../../utils/get-secret.js'\nimport jwt from 'jsonwebtoken'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\n@Entity()\n@Index('ix_appliance_0', (appliance: Appliance) => [appliance.domain, appliance.name], {\n unique: true\n})\n@ObjectType({ description: 'Represents a physical or virtual appliance that can be managed by the system.' })\nexport class Appliance {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the appliance.' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { description: 'The domain to which the appliance belongs.' })\n domain?: Domain\n\n @RelationId((appliance: Appliance) => appliance.domain)\n domainId: string\n\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'The serial number of the appliance.' })\n serialNo: string\n\n @Column()\n @Field({ description: 'The name of the appliance.' })\n name: string\n\n @Column()\n @Field({ nullable: true, description: 'The brand or manufacturer of the appliance.' })\n brand: string\n\n @Column()\n @Field({ nullable: true, description: 'The model of the appliance.' })\n model: string\n\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'The network mask of the appliance.' })\n netmask: string\n\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'A description of the appliance.' })\n description: string\n\n @Column({\n nullable: true,\n type:\n DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'longtext'\n : DATABASE_TYPE == 'oracle'\n ? 'clob'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined\n })\n @Field({ nullable: true, description: 'The access token for the appliance.' })\n @Directive('@privilege(category: \"security\", privilege: \"query\", domainOwnerGranted: true)')\n accessToken: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'The user who created the appliance.' })\n creator: User\n\n @RelationId((appliance: Appliance) => appliance.creator)\n creatorId: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'The user who last updated the appliance.' })\n updater: User\n\n @RelationId((appliance: Appliance) => appliance.updater)\n updaterId: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the appliance was created.' })\n createdAt: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the appliance was last updated.' })\n updatedAt: Date\n\n /* signing for jsonwebtoken */\n static sign(subject, expiresIn, domain, user, appliance) {\n var credential = {\n id: user.id,\n userType: 'appliance',\n appliance: {\n id: appliance.id\n },\n status: UserStatus.ACTIVATED,\n domain: {\n subdomain: domain.subdomain\n }\n }\n\n return jwt.sign(credential, SECRET, {\n expiresIn,\n issuer: 'hatiolab.com',\n subject\n })\n }\n\n static generateAccessToken(domain, user, appliance) {\n /* how to set expiresIn https://github.com/vercel/ms */\n let expiresIn = config.get('applianceJwtExpiresIn', '1y')\n return this.sign('access-token', expiresIn, domain, user, appliance)\n }\n\n static async ensureApplianceUser(appliance: Appliance, user: User, tx: EntityManager) {\n const { id, domain } = appliance\n const userRepository = getRepository(User, tx)\n\n let appuser: User = await userRepository.findOne({\n where: {\n reference: id,\n userType: 'appliance'\n },\n relations: ['domains']\n })\n\n if (!appuser) {\n const username = appuser?.username || `${appliance.id}@${domain?.subdomain}.z`\n\n appuser = await userRepository.save({\n username,\n email: username,\n name: appliance.name,\n userType: 'appliance',\n reference: id,\n status: UserStatus.ACTIVATED,\n domains: [domain],\n updater: user,\n creator: appuser?.creator || user\n })\n\n return await userRepository.findOne({\n where: {\n id: appuser.id\n },\n relations: ['domains']\n })\n } else if (!appuser.username) {\n /* username을 사용하지 않았던 하위버전에서 생성된 데이타를 자동으로 수정하기 위한 로직임. */\n const username = appuser.email || `${appliance.id}@${domain?.subdomain}.z`\n\n appuser = await userRepository.save({\n ...appuser,\n username: appuser.username || username,\n updater: user\n })\n\n return await userRepository.findOne({\n where: {\n id: appuser.id\n },\n relations: ['domains']\n })\n }\n\n return appuser\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"appliance.js","sourceRoot":"","sources":["../../../server/service/appliance/appliance.ts"],"names":[],"mappings":";;;;AAAA,qCAUgB;AAChB,+CAA+D;AAC/D,iDAA6D;AAC7D,6CAA4C;AAE5C,6EAA+E;AAE/E,6CAAkD;AAClD,6DAAkD;AAClD,wEAA8B;AAE9B,MAAM,SAAS,GAAG,YAAM,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,IAAI,CAAA;AAO7B,IAAM,SAAS,GAAf,MAAM,SAAS;IAkGpB;;;;;;;;;OASG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS;QACrD,IAAI,UAAU,GAAQ;YACpB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,QAAQ,EAAE,WAAW;YACrB,SAAS,EAAE;gBACT,EAAE,EAAE,SAAS,CAAC,EAAE;aACjB;YACD,MAAM,EAAE,oBAAU,CAAC,SAAS;YAC5B,MAAM,EAAE;gBACN,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B;SACF,CAAA;QAED,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI,IAAI,IAAI,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YAC1E,UAAU,CAAC,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAA;QACvC,CAAC;QAED,OAAO,sBAAG,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAM,EAAE;YAClC,SAAS;YACT,MAAM,EAAE,cAAc;YACtB,OAAO;SACR,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS;QAChD,uDAAuD;QACvD,IAAI,SAAS,GAAG,YAAM,CAAC,GAAG,CAAC,uBAAuB,EAAE,IAAI,CAAC,CAAA;QACzD,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAA;IACtE,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,SAAoB,EAAE,IAAU,EAAE,EAAiB;QAClF,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;QAChC,MAAM,cAAc,GAAG,IAAA,qBAAa,EAAC,cAAI,EAAE,EAAE,CAAC,CAAA;QAE9C,IAAI,OAAO,GAAS,MAAM,cAAc,CAAC,OAAO,CAAC;YAC/C,KAAK,EAAE;gBACL,SAAS,EAAE,EAAE;gBACb,QAAQ,EAAE,WAAW;aACtB;YACD,SAAS,EAAE,CAAC,SAAS,CAAC;SACvB,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,GAAG,SAAS,CAAC,EAAE,IAAI,MAAM,EAAE,SAAS,IAAI,CAAA;YAE9E,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC;gBAClC,QAAQ;gBACR,KAAK,EAAE,QAAQ;gBACf,IAAI,EAAE,SAAS,CAAC,IAAI;gBACpB,QAAQ,EAAE,WAAW;gBACrB,SAAS,EAAE,EAAE;gBACb,MAAM,EAAE,oBAAU,CAAC,SAAS;gBAC5B,OAAO,EAAE,CAAC,MAAM,CAAC;gBACjB,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI;aAClC,CAAC,CAAA;YAEF,OAAO,MAAM,cAAc,CAAC,OAAO,CAAC;gBAClC,KAAK,EAAE;oBACL,EAAE,EAAE,OAAO,CAAC,EAAE;iBACf;gBACD,SAAS,EAAE,CAAC,SAAS,CAAC;aACvB,CAAC,CAAA;QACJ,CAAC;aAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YAC7B,0DAA0D;YAC1D,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,GAAG,SAAS,CAAC,EAAE,IAAI,MAAM,EAAE,SAAS,IAAI,CAAA;YAE1E,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC;gBAClC,GAAG,OAAO;gBACV,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,QAAQ;gBACtC,OAAO,EAAE,IAAI;aACd,CAAC,CAAA;YAEF,OAAO,MAAM,cAAc,CAAC,OAAO,CAAC;gBAClC,KAAK,EAAE;oBACL,EAAE,EAAE,OAAO,CAAC,EAAE;iBACf;gBACD,SAAS,EAAE,CAAC,SAAS,CAAC;aACvB,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC;CACF,CAAA;AA/LY,8BAAS;AAGX;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;;qCACxD;AAInB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;sCAC5E,cAAM;yCAAA;AAGf;IADC,IAAA,oBAAU,EAAC,CAAC,SAAoB,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC;;2CACvC;AAMhB;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;;2CAC9D;AAIhB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;;uCACzC;AAIZ;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6CAA6C,EAAE,CAAC;;wCACzE;AAIb;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;;wCACzD;AAMb;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;;0CAC9D;AAMf;IAJC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC;;8CACvD;AAiBnB;IAfC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;QACd,IAAI,EACF,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS;YACpD,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,aAAa,IAAI,QAAQ;gBACzB,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,aAAa,IAAI,OAAO;oBACxB,CAAC,CAAC,UAAU;oBACZ,CAAC,CAAC,SAAS;QACnB,MAAM,EAAE,aAAa,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QACpD,WAAW,EAAE,mDAA2B;KACzC,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;IAC7E,IAAA,wBAAS,EAAC,gFAAgF,CAAC;;8CACzE;AAiBnB;IAJC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE;QACtB,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,uDAAuD;KACrE,CAAC;;oDACyB;AAI3B;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;sCACrE,cAAI;0CAAA;AAGb;IADC,IAAA,oBAAU,EAAC,CAAC,SAAoB,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC;;4CACvC;AAIjB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;sCAC1E,cAAI;0CAAA;AAGb;IADC,IAAA,oBAAU,EAAC,CAAC,SAAoB,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC;;4CACvC;AAIjB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;sCAC7E,IAAI;4CAAA;AAIf;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;sCAClF,IAAI;IAEf;;;;;;;;;OASG;;4CAXY;oBAhGJ,SAAS;IALrB,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,gBAAgB,EAAE,CAAC,SAAoB,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE;QACrF,MAAM,EAAE,IAAI;KACb,CAAC;IACD,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,+EAA+E,EAAE,CAAC;GAChG,SAAS,CA+LrB","sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n EntityManager,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Directive, Field, ID, ObjectType } from 'type-graphql'\nimport { Domain, getRepository } from '@things-factory/shell'\nimport { config } from '@things-factory/env'\n\nimport { credentialAtRestTransformer } from '../../utils/credential-at-rest.js'\n\nimport { User, UserStatus } from '../user/user.js'\nimport { SECRET } from '../../utils/get-secret.js'\nimport jwt from 'jsonwebtoken'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\n@Entity()\n@Index('ix_appliance_0', (appliance: Appliance) => [appliance.domain, appliance.name], {\n unique: true\n})\n@ObjectType({ description: 'Represents a physical or virtual appliance that can be managed by the system.' })\nexport class Appliance {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the appliance.' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { description: 'The domain to which the appliance belongs.' })\n domain?: Domain\n\n @RelationId((appliance: Appliance) => appliance.domain)\n domainId: string\n\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'The serial number of the appliance.' })\n serialNo: string\n\n @Column()\n @Field({ description: 'The name of the appliance.' })\n name: string\n\n @Column()\n @Field({ nullable: true, description: 'The brand or manufacturer of the appliance.' })\n brand: string\n\n @Column()\n @Field({ nullable: true, description: 'The model of the appliance.' })\n model: string\n\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'The network mask of the appliance.' })\n netmask: string\n\n @Column({\n nullable: true\n })\n @Field({ nullable: true, description: 'A description of the appliance.' })\n description: string\n\n @Column({\n nullable: true,\n type:\n DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'longtext'\n : DATABASE_TYPE == 'oracle'\n ? 'clob'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined,\n transformer: credentialAtRestTransformer\n })\n @Field({ nullable: true, description: 'The access token for the appliance.' })\n @Directive('@privilege(category: \"security\", privilege: \"query\", domainOwnerGranted: true)')\n accessToken: string\n\n /*\n * Has this appliance's credential been taken away? Not a column.\n *\n * Revocation lives in the shadow user's status, not here — `revokeApplianceCredential` clears\n * `accessToken` as tidiness and sets that user INACTIVE, which is what actually stops the\n * token. So an empty `accessToken` says two different things: never issued, or revoked. A\n * screen that cannot tell them apart shows the same blank box for a new appliance and for a\n * lost one.\n *\n * Resolved in `ApplianceQuery`, so it costs a lookup and only in the queries that ask for it.\n */\n @Field(type => Boolean, {\n nullable: true,\n description: \"Whether this appliance's credential has been revoked.\"\n })\n credentialRevoked?: boolean\n\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'The user who created the appliance.' })\n creator: User\n\n @RelationId((appliance: Appliance) => appliance.creator)\n creatorId: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field({ nullable: true, description: 'The user who last updated the appliance.' })\n updater: User\n\n @RelationId((appliance: Appliance) => appliance.updater)\n updaterId: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the appliance was created.' })\n createdAt: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the appliance was last updated.' })\n updatedAt: Date\n\n /*\n * `cs` is the credential serial (ADR-0085 decision 3) — which generation of this appliance's\n * credential the token belongs to. It is absent for a subject that has never been through the\n * new path, and `checkAuth` lets an absent one by, which is what keeps the tokens already in\n * the field working.\n *\n * It also makes each issue distinct. The rest of this payload is the same every time and\n * `iat` counts whole seconds, so two signings inside one second used to produce the identical\n * string — revoking and immediately re-issuing handed back the credential just revoked.\n */\n static sign(subject, expiresIn, domain, user, appliance) {\n var credential: any = {\n id: user.id,\n userType: 'appliance',\n appliance: {\n id: appliance.id\n },\n status: UserStatus.ACTIVATED,\n domain: {\n subdomain: domain.subdomain\n }\n }\n\n if (user.credentialSerial !== null && user.credentialSerial !== undefined) {\n credential.cs = user.credentialSerial\n }\n\n return jwt.sign(credential, SECRET, {\n expiresIn,\n issuer: 'hatiolab.com',\n subject\n })\n }\n\n static generateAccessToken(domain, user, appliance) {\n /* how to set expiresIn https://github.com/vercel/ms */\n let expiresIn = config.get('applianceJwtExpiresIn', '1y')\n return this.sign('access-token', expiresIn, domain, user, appliance)\n }\n\n static async ensureApplianceUser(appliance: Appliance, user: User, tx: EntityManager) {\n const { id, domain } = appliance\n const userRepository = getRepository(User, tx)\n\n let appuser: User = await userRepository.findOne({\n where: {\n reference: id,\n userType: 'appliance'\n },\n relations: ['domains']\n })\n\n if (!appuser) {\n const username = appuser?.username || `${appliance.id}@${domain?.subdomain}.z`\n\n appuser = await userRepository.save({\n username,\n email: username,\n name: appliance.name,\n userType: 'appliance',\n reference: id,\n status: UserStatus.ACTIVATED,\n domains: [domain],\n updater: user,\n creator: appuser?.creator || user\n })\n\n return await userRepository.findOne({\n where: {\n id: appuser.id\n },\n relations: ['domains']\n })\n } else if (!appuser.username) {\n /* username을 사용하지 않았던 하위버전에서 생성된 데이타를 자동으로 수정하기 위한 로직임. */\n const username = appuser.email || `${appliance.id}@${domain?.subdomain}.z`\n\n appuser = await userRepository.save({\n ...appuser,\n username: appuser.username || username,\n updater: user\n })\n\n return await userRepository.findOne({\n where: {\n id: appuser.id\n },\n relations: ['domains']\n })\n }\n\n return appuser\n }\n}\n"]}
|
|
@@ -19,12 +19,37 @@ let ApplicationMutation = class ApplicationMutation {
|
|
|
19
19
|
updater: context.state.user
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
|
+
/*
|
|
23
|
+
* Deleting an application takes its bindings with it.
|
|
24
|
+
*
|
|
25
|
+
* ── Why the row alone was not enough ────────────────────────────────────
|
|
26
|
+
* A binding is a shadow `User` standing in for the client, and that user is what a token
|
|
27
|
+
* names. `User.checkAuth` looks the subject up by the id baked into the token and never asks
|
|
28
|
+
* whether the application still exists, so a surviving binding keeps authenticating — and
|
|
29
|
+
* `/oauth2/refresh-token` finds it by the refresh token in `password` and mints another year.
|
|
30
|
+
*
|
|
31
|
+
* So the screen said the application was gone while its clients kept working, with nothing
|
|
32
|
+
* left on any screen to reach them: the list they appear in is keyed on an application that
|
|
33
|
+
* no longer exists. `deleteAppliance` has always deleted its shadow user; this is the same
|
|
34
|
+
* shape, and the asymmetry was the defect.
|
|
35
|
+
*
|
|
36
|
+
* ── Why the application is read before anything is deleted ──────────────
|
|
37
|
+
* The bindings are found by `reference`, which carries no tenant of its own. Deleting them
|
|
38
|
+
* first and scoping only the row afterwards would clear another tenant's bindings on the way
|
|
39
|
+
* to discovering the application is not ours.
|
|
40
|
+
*/
|
|
22
41
|
async deleteApplication(id, context) {
|
|
23
|
-
const { domain } = context.state;
|
|
24
|
-
await (0, shell_1.getRepository)(application_js_1.Application).
|
|
25
|
-
|
|
26
|
-
|
|
42
|
+
const { domain, tx } = context.state;
|
|
43
|
+
const application = await (0, shell_1.getRepository)(application_js_1.Application, tx).findOneBy({ domain: { id: domain.id }, id });
|
|
44
|
+
if (!application) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
/* `userType` matters: `reference` is a plain column both kinds of shadow user write to. */
|
|
48
|
+
await (0, shell_1.getRepository)(user_js_1.User, tx).delete({
|
|
49
|
+
reference: id,
|
|
50
|
+
userType: 'application'
|
|
27
51
|
});
|
|
52
|
+
await (0, shell_1.getRepository)(application_js_1.Application, tx).delete({ domain: { id: domain.id }, id });
|
|
28
53
|
return true;
|
|
29
54
|
}
|
|
30
55
|
async generateApplicationSecret(id, context) {
|
|
@@ -51,8 +76,27 @@ let ApplicationMutation = class ApplicationMutation {
|
|
|
51
76
|
if (!application) {
|
|
52
77
|
throw new Error('application not found');
|
|
53
78
|
}
|
|
54
|
-
|
|
55
|
-
|
|
79
|
+
/*
|
|
80
|
+
* `appKey`, not `appSecret`.
|
|
81
|
+
*
|
|
82
|
+
* These two are both 32 hex characters on the same object and they are opposite kinds of
|
|
83
|
+
* thing: the key is the client id and is public, the secret is what `/oauth2/access-token`
|
|
84
|
+
* compares against to decide a caller is that client. This line passed the secret from the
|
|
85
|
+
* day the file was written, and it cost twice over.
|
|
86
|
+
*
|
|
87
|
+
* the secret went out a JWT payload is base64, not encryption. Whoever held one of
|
|
88
|
+
* these tokens could read the client secret out of it and then
|
|
89
|
+
* authenticate as the application.
|
|
90
|
+
* refreshing was broken the OAuth2 refresh exchange reads this field back and does
|
|
91
|
+
* `findOneBy({ appKey })` (`router/oauth2/oauth2-server.ts`). A
|
|
92
|
+
* payload naming the secret matched no row, so the exchange
|
|
93
|
+
* answered "application is not exist".
|
|
94
|
+
*
|
|
95
|
+
* Tokens minted before this fix still carry the live secret and are left alone
|
|
96
|
+
* (2026-09-20 사용자 결정). Nothing new goes out that way.
|
|
97
|
+
*/
|
|
98
|
+
var accessToken = application_js_1.Application.generateAccessToken(domain, appuser, application, scope || '');
|
|
99
|
+
var refreshToken = application_js_1.Application.generateRefreshToken(domain, appuser, application, scope || '');
|
|
56
100
|
await (0, shell_1.getRepository)(user_js_1.User).save({
|
|
57
101
|
...appuser,
|
|
58
102
|
password: refreshToken
|
|
@@ -84,6 +128,7 @@ tslib_1.__decorate([
|
|
|
84
128
|
], ApplicationMutation.prototype, "createApplication", null);
|
|
85
129
|
tslib_1.__decorate([
|
|
86
130
|
(0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
|
|
131
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
87
132
|
(0, type_graphql_1.Mutation)(returns => Boolean, { description: 'To delete application' }),
|
|
88
133
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('id')),
|
|
89
134
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application-mutation.js","sourceRoot":"","sources":["../../../server/service/application/application-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Directive, Arg, Ctx, Mutation, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { User } from '../user/user.js'\nimport { Application } from './application.js'\nimport { AccessToken, ApplicationPatch, NewApplication } from './application-types.js'\n\n@Resolver(Application)\nexport class ApplicationMutation {\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Application, { description: 'To create new application' })\n async createApplication(@Arg('application') application: NewApplication, @Ctx() context: ResolverContext) {\n const { domain } = context.state\n return await getRepository(Application).save({\n ...application,\n domain,\n appKey: Application.generateAppKey(),\n appSecret: Application.generateAppSecret(),\n creator: context.state.user,\n updater: context.state.user\n })\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'To delete application' })\n async deleteApplication(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n const { domain } = context.state\n await getRepository(Application).delete({\n domain: { id: domain.id },\n id\n })\n return true\n }\n\n @Directive('@privilege(category: \"security\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Application)\n async generateApplicationSecret(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n const { domain } = context.state\n const repository = getRepository(Application)\n const application = await repository.findOneBy({ domain: { id: domain.id }, id })\n\n return await repository.save({\n ...application,\n appSecret: Application.generateAppSecret(),\n updater: context.state.user\n })\n }\n\n @Directive('@privilege(category: \"security\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => AccessToken)\n async renewApplicationAccessToken(\n @Arg('id') id: string,\n @Ctx() context: ResolverContext,\n @Arg('scope') scope?: string\n ) {\n const { domain } = context.state\n\n var appuser: User = await getRepository(User).findOneBy({\n id,\n userType: 'application'\n })\n\n if (!appuser) {\n throw new Error('application is not bound')\n }\n\n const repository = getRepository(Application)\n const application = await repository.findOneBy({ id: appuser.reference })\n if (!application) {\n throw new Error('application not found')\n }\n\n var accessToken = Application.generateAccessToken(domain, appuser, application.appSecret, scope || '')\n var refreshToken = Application.generateRefreshToken(domain, appuser, application.appSecret, scope || '')\n\n await getRepository(User).save({\n ...(appuser as any),\n password: refreshToken\n })\n\n return {\n accessToken,\n refreshToken\n }\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Application)\n async updateApplication(\n @Arg('id') id: string,\n @Arg('patch') patch: ApplicationPatch,\n @Ctx() context: ResolverContext\n ) {\n const repository = getRepository(Application)\n const application = await repository.findOneBy({ id })\n\n return await repository.save({\n ...application,\n ...patch,\n updater: context.state.user\n })\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"application-mutation.js","sourceRoot":"","sources":["../../../server/service/application/application-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Directive, Arg, Ctx, Mutation, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { User } from '../user/user.js'\nimport { Application } from './application.js'\nimport { AccessToken, ApplicationPatch, NewApplication } from './application-types.js'\n\n@Resolver(Application)\nexport class ApplicationMutation {\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Application, { description: 'To create new application' })\n async createApplication(@Arg('application') application: NewApplication, @Ctx() context: ResolverContext) {\n const { domain } = context.state\n return await getRepository(Application).save({\n ...application,\n domain,\n appKey: Application.generateAppKey(),\n appSecret: Application.generateAppSecret(),\n creator: context.state.user,\n updater: context.state.user\n })\n }\n\n /*\n * Deleting an application takes its bindings with it.\n *\n * ── Why the row alone was not enough ────────────────────────────────────\n * A binding is a shadow `User` standing in for the client, and that user is what a token\n * names. `User.checkAuth` looks the subject up by the id baked into the token and never asks\n * whether the application still exists, so a surviving binding keeps authenticating — and\n * `/oauth2/refresh-token` finds it by the refresh token in `password` and mints another year.\n *\n * So the screen said the application was gone while its clients kept working, with nothing\n * left on any screen to reach them: the list they appear in is keyed on an application that\n * no longer exists. `deleteAppliance` has always deleted its shadow user; this is the same\n * shape, and the asymmetry was the defect.\n *\n * ── Why the application is read before anything is deleted ──────────────\n * The bindings are found by `reference`, which carries no tenant of its own. Deleting them\n * first and scoping only the row afterwards would clear another tenant's bindings on the way\n * to discovering the application is not ours.\n */\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: 'To delete application' })\n async deleteApplication(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n const { domain, tx } = context.state\n\n const application = await getRepository(Application, tx).findOneBy({ domain: { id: domain.id }, id })\n\n if (!application) {\n return true\n }\n\n /* `userType` matters: `reference` is a plain column both kinds of shadow user write to. */\n await getRepository(User, tx).delete({\n reference: id,\n userType: 'application'\n })\n\n await getRepository(Application, tx).delete({ domain: { id: domain.id }, id })\n\n return true\n }\n\n @Directive('@privilege(category: \"security\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Application)\n async generateApplicationSecret(@Arg('id') id: string, @Ctx() context: ResolverContext) {\n const { domain } = context.state\n const repository = getRepository(Application)\n const application = await repository.findOneBy({ domain: { id: domain.id }, id })\n\n return await repository.save({\n ...application,\n appSecret: Application.generateAppSecret(),\n updater: context.state.user\n })\n }\n\n @Directive('@privilege(category: \"security\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => AccessToken)\n async renewApplicationAccessToken(\n @Arg('id') id: string,\n @Ctx() context: ResolverContext,\n @Arg('scope') scope?: string\n ) {\n const { domain } = context.state\n\n var appuser: User = await getRepository(User).findOneBy({\n id,\n userType: 'application'\n })\n\n if (!appuser) {\n throw new Error('application is not bound')\n }\n\n const repository = getRepository(Application)\n const application = await repository.findOneBy({ id: appuser.reference })\n if (!application) {\n throw new Error('application not found')\n }\n\n /*\n * `appKey`, not `appSecret`.\n *\n * These two are both 32 hex characters on the same object and they are opposite kinds of\n * thing: the key is the client id and is public, the secret is what `/oauth2/access-token`\n * compares against to decide a caller is that client. This line passed the secret from the\n * day the file was written, and it cost twice over.\n *\n * the secret went out a JWT payload is base64, not encryption. Whoever held one of\n * these tokens could read the client secret out of it and then\n * authenticate as the application.\n * refreshing was broken the OAuth2 refresh exchange reads this field back and does\n * `findOneBy({ appKey })` (`router/oauth2/oauth2-server.ts`). A\n * payload naming the secret matched no row, so the exchange\n * answered \"application is not exist\".\n *\n * Tokens minted before this fix still carry the live secret and are left alone\n * (2026-09-20 사용자 결정). Nothing new goes out that way.\n */\n var accessToken = Application.generateAccessToken(domain, appuser, application, scope || '')\n var refreshToken = Application.generateRefreshToken(domain, appuser, application, scope || '')\n\n await getRepository(User).save({\n ...(appuser as any),\n password: refreshToken\n })\n\n return {\n accessToken,\n refreshToken\n }\n }\n\n @Directive('@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Application)\n async updateApplication(\n @Arg('id') id: string,\n @Arg('patch') patch: ApplicationPatch,\n @Ctx() context: ResolverContext\n ) {\n const repository = getRepository(Application)\n const application = await repository.findOneBy({ id })\n\n return await repository.save({\n ...application,\n ...patch,\n updater: context.state.user\n })\n }\n}\n"]}
|
|
@@ -36,9 +36,15 @@ export declare class Application {
|
|
|
36
36
|
updaterId?: string;
|
|
37
37
|
static generateAppSecret(): string;
|
|
38
38
|
static generateAppKey(): string;
|
|
39
|
-
static sign(subject: any, expiresIn: any, domain: any, user: any,
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
static sign(subject: any, expiresIn: any, domain: any, user: any, client: {
|
|
40
|
+
appKey?: string;
|
|
41
|
+
}, scope: any): string;
|
|
42
|
+
static generateAccessToken(domain: any, user: any, client: {
|
|
43
|
+
appKey?: string;
|
|
44
|
+
}, scope: any): string;
|
|
45
|
+
static generateRefreshToken(domain: any, user: any, client: {
|
|
46
|
+
appKey?: string;
|
|
47
|
+
}, scope: any): string;
|
|
42
48
|
static generateAuthCode(email: any, appKey: any, subdomain: any, scopes: any, state: any): string;
|
|
43
49
|
static verifyAuthCode(authcode: any): string | jwt.JwtPayload;
|
|
44
50
|
}
|
|
@@ -11,6 +11,7 @@ const env_1 = require("@things-factory/env");
|
|
|
11
11
|
const shell_1 = require("@things-factory/shell");
|
|
12
12
|
const get_secret_js_1 = require("../../utils/get-secret.js");
|
|
13
13
|
const user_js_1 = require("../user/user.js");
|
|
14
|
+
const credential_at_rest_js_1 = require("../../utils/credential-at-rest.js");
|
|
14
15
|
const ORMCONFIG = env_1.config.get('ormconfig', {});
|
|
15
16
|
const DATABASE_TYPE = ORMCONFIG.type;
|
|
16
17
|
var ApplicationStatus;
|
|
@@ -43,13 +44,27 @@ let Application = class Application {
|
|
|
43
44
|
static generateAppKey() {
|
|
44
45
|
return crypto_1.default.randomBytes(16).toString('hex');
|
|
45
46
|
}
|
|
46
|
-
/*
|
|
47
|
-
|
|
47
|
+
/*
|
|
48
|
+
* Signing a token for an application.
|
|
49
|
+
*
|
|
50
|
+
* ── Why this slot takes an object and not the key ───────────────────────
|
|
51
|
+
* An application has two 32-character hex strings on it — `appKey`, the public client id, and
|
|
52
|
+
* `appSecret`, the client secret. A positional `appKey: string` parameter accepts either, and
|
|
53
|
+
* a call site reading `..., application.appSecret, ...` looks no different from the right one.
|
|
54
|
+
* `renewApplicationAccessToken` passed the secret that way from the day it was written, which
|
|
55
|
+
* put the client secret into a payload anyone holding the token can read.
|
|
56
|
+
*
|
|
57
|
+
* Taking `{ appKey }` does not make the wrong value unreachable, but it moves the mistake to
|
|
58
|
+
* where it is visible: the call site has to write the field name, and a bare string no longer
|
|
59
|
+
* type-checks. The caller that genuinely holds only a key (the refresh route, which reads it
|
|
60
|
+
* back out of a token) writes `{ appKey }` and says so.
|
|
61
|
+
*/
|
|
62
|
+
static sign(subject, expiresIn, domain, user, client, scope) {
|
|
48
63
|
var application = {
|
|
49
64
|
id: user.id,
|
|
50
65
|
userType: 'application',
|
|
51
66
|
application: {
|
|
52
|
-
appKey
|
|
67
|
+
appKey: client?.appKey
|
|
53
68
|
},
|
|
54
69
|
status: user_js_1.UserStatus.ACTIVATED,
|
|
55
70
|
domain: {
|
|
@@ -63,13 +78,13 @@ let Application = class Application {
|
|
|
63
78
|
subject
|
|
64
79
|
});
|
|
65
80
|
}
|
|
66
|
-
static generateAccessToken(domain, user,
|
|
81
|
+
static generateAccessToken(domain, user, client, scope) {
|
|
67
82
|
/* how to set expiresIn https://github.com/vercel/ms */
|
|
68
|
-
return this.sign('access-token', '30d', domain, user,
|
|
83
|
+
return this.sign('access-token', '30d', domain, user, client, scope);
|
|
69
84
|
}
|
|
70
|
-
static generateRefreshToken(domain, user,
|
|
85
|
+
static generateRefreshToken(domain, user, client, scope) {
|
|
71
86
|
/* how to set expiresIn https://github.com/vercel/ms */
|
|
72
|
-
return this.sign('refresh-token', '1y', domain, user,
|
|
87
|
+
return this.sign('refresh-token', '1y', domain, user, client, scope);
|
|
73
88
|
}
|
|
74
89
|
/* auth-code signing for jsonwebtoken */
|
|
75
90
|
static generateAuthCode(email, appKey, subdomain, scopes, state) {
|
|
@@ -154,7 +169,8 @@ tslib_1.__decorate([
|
|
|
154
169
|
: DATABASE_TYPE == 'mssql'
|
|
155
170
|
? 'nvarchar'
|
|
156
171
|
: 'varchar',
|
|
157
|
-
length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined
|
|
172
|
+
length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined,
|
|
173
|
+
transformer: credential_at_rest_js_1.credentialAtRestTransformer
|
|
158
174
|
}),
|
|
159
175
|
(0, type_graphql_1.Field)({ nullable: true, description: 'The secret key for the application (client secret).' }),
|
|
160
176
|
(0, type_graphql_1.Directive)('@privilege(category: "security", privilege: "query", domainOwnerGranted: true)'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application.js","sourceRoot":"","sources":["../../../server/service/application/application.ts"],"names":[],"mappings":";;;;AAAA,4DAA2B;AAC3B,wEAA8B;AAC9B,+CAAiF;AACjF,qDAAqD;AACrD,qCASgB;AAEhB,6CAA4C;AAC5C,iDAA8C;AAE9C,6DAAkD;AAClD,6CAAkD;AAElD,MAAM,SAAS,GAAG,YAAM,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,IAAI,CAAA;AAEpC,IAAY,iBAGX;AAHD,WAAY,iBAAiB;IAC3B,oCAAe,CAAA;IACf,4CAAuB,CAAA,CAAC,6CAA6C;AACvE,CAAC,EAHW,iBAAiB,iCAAjB,iBAAiB,QAG5B;AAED,IAAA,+BAAgB,EAAC,iBAAiB,EAAE;IAClC,IAAI,EAAE,mBAAmB;IACzB,WAAW,EAAE,0CAA0C;CACxD,CAAC,CAAA;AAEF,IAAY,eAOX;AAPD,WAAY,eAAe;IACzB,8CAA2B,CAAA;IAC3B,oCAAiB,CAAA;IACjB,8BAAW,CAAA;IACX,gCAAa,CAAA;IACb,oCAAiB,CAAA;IACjB,gCAAa,CAAA,CAAC,kCAAkC;AAClD,CAAC,EAPW,eAAe,+BAAf,eAAe,QAO1B;AAED,IAAA,+BAAgB,EAAC,eAAe,EAAE;IAChC,IAAI,EAAE,iBAAiB;IACvB,WAAW,EAAE,6BAA6B;CAC3C,CAAC,CAAA;AAIK,IAAM,WAAW,GAAjB,MAAM,WAAW;IAyGtB,uBAAuB;IACvB,MAAM,CAAC,iBAAiB;QACtB,OAAO,gBAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/C,CAAC;IAED,MAAM,CAAC,cAAc;QACnB,OAAO,gBAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/C,CAAC;IAED,8BAA8B;IAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK;QACzD,IAAI,WAAW,GAAG;YAChB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,QAAQ,EAAE,aAAa;YACvB,WAAW,EAAE;gBACX,MAAM;aACP;YACD,MAAM,EAAE,oBAAU,CAAC,SAAS;YAC5B,MAAM,EAAE;gBACN,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B;YACD,KAAK;SACN,CAAA;QAED,OAAO,sBAAG,CAAC,IAAI,CAAC,WAAW,EAAE,sBAAM,EAAE;YACnC,SAAS;YACT,MAAM,EAAE,cAAc;YACtB,OAAO;SACR,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK;QACpD,uDAAuD;QACvD,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;IACtE,CAAC;IAED,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK;QACrD,uDAAuD;QACvD,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;IACtE,CAAC;IAED,wCAAwC;IACxC,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK;QAC7D,IAAI,UAAU,GAAG;YACf,KAAK;YACL,MAAM;YACN,SAAS;YACT,MAAM;YACN,KAAK;SACN,CAAA;QAED,OAAO,sBAAG,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAM,EAAE;YAClC,SAAS,EAAE,IAAI;SAChB,CAAC,CAAA;IACJ,CAAC;IAED,wCAAwC;IACxC,MAAM,CAAC,cAAc,CAAC,QAAQ;QAC5B,OAAO,sBAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,sBAAM,CAAC,CAAA;IACrC,CAAC;CACF,CAAA;AArKY,kCAAW;AAGb;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC;;uCACzD;AAIpB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;sCAC9E,cAAM;2CAAA;AAGf;IADC,IAAA,oBAAU,EAAC,CAAC,WAAwB,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC;;6CAC5C;AAIjB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;;yCAC1C;AAIb;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,mCAAmC,EAAE,CAAC;;gDACxD;AAIpB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,qCAAmB,EAAE,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC;;0CAChF;AAId;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;;wCAC1C;AAIZ;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC;;yCACpE;AAIb;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;;gDACzC;AAIpB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wDAAwD,EAAE,CAAC;;4CACjF;AAIhB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC;;2CAC3E;AAgBf;IAdC,IAAA,gBAAM,EAAC;QACN,QAAQ,EAAE,IAAI;QACd,IAAI,EACF,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS;YACpD,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,aAAa,IAAI,QAAQ;gBACzB,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,aAAa,IAAI,OAAO;oBACxB,CAAC,CAAC,UAAU;oBACZ,CAAC,CAAC,SAAS;QACnB,MAAM,EAAE,aAAa,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;KACrD,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC;IAC7F,IAAA,wBAAS,EAAC,gFAAgF,CAAC;;8CAC1E;AAIlB;IAFC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,iBAAiB,CAAC,KAAK,EAAE,CAAC;IAC5C,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;;2CACxD;AAmB1B;IAjBC,IAAA,gBAAM,EAAC;QACN,IAAI,EACF,aAAa,IAAI,UAAU,IAAI,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS;YACnF,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,aAAa,IAAI,QAAQ;gBACzB,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,aAAa,IAAI,OAAO;oBACxB,CAAC,CAAC,UAAU;oBACZ,CAAC,CAAC,SAAS;QACnB,IAAI,EACF,aAAa,IAAI,UAAU,IAAI,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS;YACnF,CAAC,CAAC,eAAe;YACjB,CAAC,CAAC,SAAS;QACf,MAAM,EAAE,aAAa,IAAI,UAAU,IAAI,aAAa,IAAI,OAAO,IAAI,aAAa,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;QAC9G,OAAO,EAAE,eAAe,CAAC,MAAM;KAChC,CAAC;IACD,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;;yCACjC;AAItB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC;sCAC9E,IAAI;8CAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC;sCACnF,IAAI;8CAAA;AAIhB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uCAAuC,EAAE,CAAC;sCACpF,cAAI;4CAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,WAAwB,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC;;8CAC5C;AAIlB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;sCACzF,cAAI;4CAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,WAAwB,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC;;8CAC5C;sBAvGP,WAAW;IAHvB,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,kBAAkB,EAAE,CAAC,WAAwB,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC/F,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,8EAA8E,EAAE,CAAC;GAC/F,WAAW,CAqKvB","sourcesContent":["import crypto from 'crypto'\nimport jwt from 'jsonwebtoken'\nimport { Directive, Field, ID, ObjectType, registerEnumType } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { Domain } from '@things-factory/shell'\n\nimport { SECRET } from '../../utils/get-secret.js'\nimport { User, UserStatus } from '../user/user.js'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\nexport enum ApplicationStatus {\n DRAFT = 'DRAFT', // The application is in a draft state and not yet active.\n ACTIVATED = 'ACTIVATED' // The application is active and can be used.\n}\n\nregisterEnumType(ApplicationStatus, {\n name: 'ApplicationStatus',\n description: 'The activation status of an application.'\n})\n\nexport enum ApplicationType {\n SELLERCRAFT = 'SELLERCRAFT', // Represents a Sellercraft application.\n XILNEX = 'XILNEX', // Represents a Xilnex application.\n MMS = 'MMS', // Represents a MMS application.\n XERO = 'XERO', // Represents a Xero application.\n OTHERS = 'OTHERS', // Represents other types of applications.\n SFTP = 'SFTP' // Represents an SFTP application.\n}\n\nregisterEnumType(ApplicationType, {\n name: 'ApplicationType',\n description: 'The type of an application.'\n})\n@Entity()\n@Index('ix_application_0', (application: Application) => [application.appKey], { unique: true })\n@ObjectType({ description: 'Represents a third-party application that can be integrated with the system.' })\nexport class Application {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the application.' })\n readonly id?: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { description: 'The domain to which the application belongs.' })\n domain?: Domain\n\n @RelationId((application: Application) => application.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'The name of the application.' })\n name?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'A description of the application.' })\n description?: string\n\n @Column()\n @Field(type => GraphQLEmailAddress, { description: 'The contact email for the application.' })\n email?: string\n\n @Column()\n @Field({ description: 'The URL of the application.' })\n url?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'A URL to the icon for the application.' })\n icon?: string\n\n @Column()\n @Field({ description: 'The redirect URL for OAuth2 flows.' })\n redirectUrl?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The webhook URL for the application to receive events.' })\n webhook?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The unique key for the application (client ID).' })\n appKey?: string\n\n @Column({\n nullable: true,\n type:\n DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'longtext'\n : DATABASE_TYPE == 'oracle'\n ? 'clob'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined\n })\n @Field({ nullable: true, description: 'The secret key for the application (client secret).' })\n @Directive('@privilege(category: \"security\", privilege: \"query\", domainOwnerGranted: true)')\n appSecret?: string\n\n @Column({ default: ApplicationStatus.DRAFT })\n @Field({ description: 'The status of the application (e.g., DRAFT, ACTIVATED).' })\n status?: ApplicationStatus\n\n @Column({\n type:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'enum'\n : DATABASE_TYPE == 'oracle'\n ? 'varchar2'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n enum:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? ApplicationType\n : undefined,\n length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined : 32,\n default: ApplicationType.OTHERS\n })\n @Field({ description: 'The type of the application.' })\n type?: ApplicationType\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the application was created.' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the application was last updated.' })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who created the application.' })\n creator?: User\n\n @RelationId((application: Application) => application.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who last updated the application.' })\n updater?: User\n\n @RelationId((application: Application) => application.updater)\n updaterId?: string\n\n /* generateAppSecret */\n static generateAppSecret() {\n return crypto.randomBytes(16).toString('hex')\n }\n\n static generateAppKey() {\n return crypto.randomBytes(16).toString('hex')\n }\n\n /* signing for jsonwebtoken */\n static sign(subject, expiresIn, domain, user, appKey, scope) {\n var application = {\n id: user.id,\n userType: 'application',\n application: {\n appKey\n },\n status: UserStatus.ACTIVATED,\n domain: {\n subdomain: domain.subdomain\n },\n scope\n }\n\n return jwt.sign(application, SECRET, {\n expiresIn,\n issuer: 'hatiolab.com',\n subject\n })\n }\n\n static generateAccessToken(domain, user, appKey, scope) {\n /* how to set expiresIn https://github.com/vercel/ms */\n return this.sign('access-token', '30d', domain, user, appKey, scope)\n }\n\n static generateRefreshToken(domain, user, appKey, scope) {\n /* how to set expiresIn https://github.com/vercel/ms */\n return this.sign('refresh-token', '1y', domain, user, appKey, scope)\n }\n\n /* auth-code signing for jsonwebtoken */\n static generateAuthCode(email, appKey, subdomain, scopes, state) {\n var credential = {\n email,\n appKey,\n subdomain,\n scopes,\n state\n }\n\n return jwt.sign(credential, SECRET, {\n expiresIn: '1m'\n })\n }\n\n /* auth-code signing for jsonwebtoken */\n static verifyAuthCode(authcode) {\n return jwt.verify(authcode, SECRET)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"application.js","sourceRoot":"","sources":["../../../server/service/application/application.ts"],"names":[],"mappings":"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","sourcesContent":["import crypto from 'crypto'\nimport jwt from 'jsonwebtoken'\nimport { Directive, Field, ID, ObjectType, registerEnumType } from 'type-graphql'\nimport { GraphQLEmailAddress } from 'graphql-scalars'\nimport {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { Domain } from '@things-factory/shell'\n\nimport { SECRET } from '../../utils/get-secret.js'\nimport { User, UserStatus } from '../user/user.js'\nimport { credentialAtRestTransformer } from '../../utils/credential-at-rest.js'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\nexport enum ApplicationStatus {\n DRAFT = 'DRAFT', // The application is in a draft state and not yet active.\n ACTIVATED = 'ACTIVATED' // The application is active and can be used.\n}\n\nregisterEnumType(ApplicationStatus, {\n name: 'ApplicationStatus',\n description: 'The activation status of an application.'\n})\n\nexport enum ApplicationType {\n SELLERCRAFT = 'SELLERCRAFT', // Represents a Sellercraft application.\n XILNEX = 'XILNEX', // Represents a Xilnex application.\n MMS = 'MMS', // Represents a MMS application.\n XERO = 'XERO', // Represents a Xero application.\n OTHERS = 'OTHERS', // Represents other types of applications.\n SFTP = 'SFTP' // Represents an SFTP application.\n}\n\nregisterEnumType(ApplicationType, {\n name: 'ApplicationType',\n description: 'The type of an application.'\n})\n@Entity()\n@Index('ix_application_0', (application: Application) => [application.appKey], { unique: true })\n@ObjectType({ description: 'Represents a third-party application that can be integrated with the system.' })\nexport class Application {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the application.' })\n readonly id?: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { description: 'The domain to which the application belongs.' })\n domain?: Domain\n\n @RelationId((application: Application) => application.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'The name of the application.' })\n name?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'A description of the application.' })\n description?: string\n\n @Column()\n @Field(type => GraphQLEmailAddress, { description: 'The contact email for the application.' })\n email?: string\n\n @Column()\n @Field({ description: 'The URL of the application.' })\n url?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'A URL to the icon for the application.' })\n icon?: string\n\n @Column()\n @Field({ description: 'The redirect URL for OAuth2 flows.' })\n redirectUrl?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The webhook URL for the application to receive events.' })\n webhook?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The unique key for the application (client ID).' })\n appKey?: string\n\n @Column({\n nullable: true,\n type:\n DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'longtext'\n : DATABASE_TYPE == 'oracle'\n ? 'clob'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined,\n transformer: credentialAtRestTransformer\n })\n @Field({ nullable: true, description: 'The secret key for the application (client secret).' })\n @Directive('@privilege(category: \"security\", privilege: \"query\", domainOwnerGranted: true)')\n appSecret?: string\n\n @Column({ default: ApplicationStatus.DRAFT })\n @Field({ description: 'The status of the application (e.g., DRAFT, ACTIVATED).' })\n status?: ApplicationStatus\n\n @Column({\n type:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'enum'\n : DATABASE_TYPE == 'oracle'\n ? 'varchar2'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n enum:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? ApplicationType\n : undefined,\n length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined : 32,\n default: ApplicationType.OTHERS\n })\n @Field({ description: 'The type of the application.' })\n type?: ApplicationType\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the application was created.' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'The timestamp when the application was last updated.' })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who created the application.' })\n creator?: User\n\n @RelationId((application: Application) => application.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who last updated the application.' })\n updater?: User\n\n @RelationId((application: Application) => application.updater)\n updaterId?: string\n\n /* generateAppSecret */\n static generateAppSecret() {\n return crypto.randomBytes(16).toString('hex')\n }\n\n static generateAppKey() {\n return crypto.randomBytes(16).toString('hex')\n }\n\n /*\n * Signing a token for an application.\n *\n * ── Why this slot takes an object and not the key ───────────────────────\n * An application has two 32-character hex strings on it — `appKey`, the public client id, and\n * `appSecret`, the client secret. A positional `appKey: string` parameter accepts either, and\n * a call site reading `..., application.appSecret, ...` looks no different from the right one.\n * `renewApplicationAccessToken` passed the secret that way from the day it was written, which\n * put the client secret into a payload anyone holding the token can read.\n *\n * Taking `{ appKey }` does not make the wrong value unreachable, but it moves the mistake to\n * where it is visible: the call site has to write the field name, and a bare string no longer\n * type-checks. The caller that genuinely holds only a key (the refresh route, which reads it\n * back out of a token) writes `{ appKey }` and says so.\n */\n static sign(subject, expiresIn, domain, user, client: { appKey?: string }, scope) {\n var application = {\n id: user.id,\n userType: 'application',\n application: {\n appKey: client?.appKey\n },\n status: UserStatus.ACTIVATED,\n domain: {\n subdomain: domain.subdomain\n },\n scope\n }\n\n return jwt.sign(application, SECRET, {\n expiresIn,\n issuer: 'hatiolab.com',\n subject\n })\n }\n\n static generateAccessToken(domain, user, client: { appKey?: string }, scope) {\n /* how to set expiresIn https://github.com/vercel/ms */\n return this.sign('access-token', '30d', domain, user, client, scope)\n }\n\n static generateRefreshToken(domain, user, client: { appKey?: string }, scope) {\n /* how to set expiresIn https://github.com/vercel/ms */\n return this.sign('refresh-token', '1y', domain, user, client, scope)\n }\n\n /* auth-code signing for jsonwebtoken */\n static generateAuthCode(email, appKey, subdomain, scopes, state) {\n var credential = {\n email,\n appKey,\n subdomain,\n scopes,\n state\n }\n\n return jwt.sign(credential, SECRET, {\n expiresIn: '1m'\n })\n }\n\n /* auth-code signing for jsonwebtoken */\n static verifyAuthCode(authcode) {\n return jwt.verify(authcode, SECRET)\n }\n}\n"]}
|
|
@@ -108,6 +108,7 @@ let AuthProviderMutation = class AuthProviderMutation {
|
|
|
108
108
|
};
|
|
109
109
|
exports.AuthProviderMutation = AuthProviderMutation;
|
|
110
110
|
tslib_1.__decorate([
|
|
111
|
+
(0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
|
|
111
112
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
112
113
|
(0, type_graphql_1.Mutation)(returns => auth_provider_js_1.AuthProvider, { description: 'To create new AuthProvider' }),
|
|
113
114
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('authProvider')),
|
|
@@ -117,6 +118,7 @@ tslib_1.__decorate([
|
|
|
117
118
|
tslib_1.__metadata("design:returntype", Promise)
|
|
118
119
|
], AuthProviderMutation.prototype, "createAuthProvider", null);
|
|
119
120
|
tslib_1.__decorate([
|
|
121
|
+
(0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
|
|
120
122
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
121
123
|
(0, type_graphql_1.Mutation)(returns => auth_provider_js_1.AuthProvider, { description: 'To modify AuthProvider information' }),
|
|
122
124
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('id')),
|
|
@@ -127,6 +129,7 @@ tslib_1.__decorate([
|
|
|
127
129
|
tslib_1.__metadata("design:returntype", Promise)
|
|
128
130
|
], AuthProviderMutation.prototype, "updateAuthProvider", null);
|
|
129
131
|
tslib_1.__decorate([
|
|
132
|
+
(0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
|
|
130
133
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
131
134
|
(0, type_graphql_1.Mutation)(returns => [auth_provider_js_1.AuthProvider]),
|
|
132
135
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('patches', type => [auth_provider_type_js_1.AuthProviderPatch])),
|
|
@@ -136,6 +139,7 @@ tslib_1.__decorate([
|
|
|
136
139
|
tslib_1.__metadata("design:returntype", Promise)
|
|
137
140
|
], AuthProviderMutation.prototype, "updateMultipleAuthProvider", null);
|
|
138
141
|
tslib_1.__decorate([
|
|
142
|
+
(0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
|
|
139
143
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
140
144
|
(0, type_graphql_1.Mutation)(returns => Boolean, { description: 'To delete AuthProvider' }),
|
|
141
145
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('id')),
|
|
@@ -145,6 +149,7 @@ tslib_1.__decorate([
|
|
|
145
149
|
tslib_1.__metadata("design:returntype", Promise)
|
|
146
150
|
], AuthProviderMutation.prototype, "deleteAuthProvider", null);
|
|
147
151
|
tslib_1.__decorate([
|
|
152
|
+
(0, type_graphql_1.Directive)('@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)'),
|
|
148
153
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
149
154
|
(0, type_graphql_1.Mutation)(returns => Boolean),
|
|
150
155
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('ids', type => [String])),
|