@things-factory/auth-base 10.1.278 → 10.1.280
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/package.json +24 -7
- package/CHANGELOG.md +0 -55
- package/dist-client/actions/auth.js.map +0 -1
- package/dist-client/apply-domain-theme.js.map +0 -1
- package/dist-client/auth.js.map +0 -1
- package/dist-client/bootstrap.js.map +0 -1
- package/dist-client/directive/privileged.js.map +0 -1
- package/dist-client/domain-required.js.map +0 -1
- package/dist-client/index.js.map +0 -1
- package/dist-client/profiled.js.map +0 -1
- package/dist-client/reducers/auth.js.map +0 -1
- package/dist-client/tsconfig.tsbuildinfo +0 -1
- package/dist-client/verify-webauthn.js.map +0 -1
- package/dist-server/ai-entry/access-assistant.js.map +0 -1
- package/dist-server/ai-entry/access-entry-targets.js.map +0 -1
- package/dist-server/ai-entry/entry-target-shape.js.map +0 -1
- package/dist-server/constants/error-code.js.map +0 -1
- package/dist-server/constants/error-message.js.map +0 -1
- package/dist-server/constants/max-age.js.map +0 -1
- package/dist-server/controllers/access-request.js.map +0 -1
- package/dist-server/controllers/account-mail.js.map +0 -1
- package/dist-server/controllers/auth.js.map +0 -1
- package/dist-server/controllers/change-pwd.js.map +0 -1
- package/dist-server/controllers/confirm-identity.js.map +0 -1
- package/dist-server/controllers/delete-user.js.map +0 -1
- package/dist-server/controllers/passkey.js.map +0 -1
- package/dist-server/controllers/password-setup.js.map +0 -1
- package/dist-server/controllers/profile.js.map +0 -1
- package/dist-server/controllers/reset-password.js.map +0 -1
- package/dist-server/controllers/signin.js.map +0 -1
- package/dist-server/controllers/signup.js.map +0 -1
- package/dist-server/controllers/unlock-user.js.map +0 -1
- package/dist-server/controllers/utils/find-verification-token.js.map +0 -1
- package/dist-server/controllers/utils/make-verification-token.js.map +0 -1
- package/dist-server/controllers/utils/password-rule.js.map +0 -1
- package/dist-server/controllers/utils/save-verification-token.js.map +0 -1
- package/dist-server/controllers/verification.js.map +0 -1
- package/dist-server/errors/auth-error.js.map +0 -1
- package/dist-server/errors/index.js.map +0 -1
- package/dist-server/errors/user-domain-not-match-error.js.map +0 -1
- package/dist-server/index.js.map +0 -1
- package/dist-server/middlewares/audit-scope-middleware.js.map +0 -1
- package/dist-server/middlewares/authenticate-401-middleware.js.map +0 -1
- package/dist-server/middlewares/bypass-signin-middleware.js.map +0 -1
- package/dist-server/middlewares/domain-authenticate-middleware.js.map +0 -1
- package/dist-server/middlewares/graphql-authenticate-middleware.js.map +0 -1
- package/dist-server/middlewares/index.js.map +0 -1
- package/dist-server/middlewares/jwt-authenticate-middleware.js.map +0 -1
- package/dist-server/middlewares/signin-middleware.js.map +0 -1
- package/dist-server/middlewares/verify-recaptcha-middleware.js.map +0 -1
- package/dist-server/migrations/1548206416130-SeedUser.js.map +0 -1
- package/dist-server/migrations/1566805283882-SeedPrivilege.js.map +0 -1
- package/dist-server/migrations/1745000000000-MigrateDomainOwnersToTable.js.map +0 -1
- package/dist-server/migrations/index.js.map +0 -1
- package/dist-server/role-tasks.js.map +0 -1
- package/dist-server/router/auth-checkin-router.js.map +0 -1
- package/dist-server/router/auth-private-process-router.js.map +0 -1
- package/dist-server/router/auth-public-process-router.js.map +0 -1
- package/dist-server/router/auth-signin-router.js.map +0 -1
- package/dist-server/router/auth-signup-router.js.map +0 -1
- package/dist-server/router/index.js.map +0 -1
- package/dist-server/router/oauth2/index.js.map +0 -1
- package/dist-server/router/oauth2/oauth2-authorize-router.js.map +0 -1
- package/dist-server/router/oauth2/oauth2-router.js.map +0 -1
- package/dist-server/router/oauth2/oauth2-server.js.map +0 -1
- package/dist-server/router/oauth2/passport-oauth2-client-password.js.map +0 -1
- package/dist-server/router/oauth2/passport-refresh-token.js.map +0 -1
- package/dist-server/router/path-base-domain-router.js.map +0 -1
- package/dist-server/router/site-root-router.js.map +0 -1
- package/dist-server/router/webauthn-router.js.map +0 -1
- package/dist-server/routes.js.map +0 -1
- package/dist-server/service/access-request/access-request-mutation.js.map +0 -1
- package/dist-server/service/access-request/access-request-query.js.map +0 -1
- package/dist-server/service/access-request/access-request-types.js.map +0 -1
- package/dist-server/service/access-request/access-request.js.map +0 -1
- package/dist-server/service/access-request/index.js.map +0 -1
- package/dist-server/service/app-binding/app-binding-mutation.js.map +0 -1
- package/dist-server/service/app-binding/app-binding-query.js.map +0 -1
- package/dist-server/service/app-binding/app-binding-types.js.map +0 -1
- package/dist-server/service/app-binding/app-binding.js.map +0 -1
- package/dist-server/service/app-binding/index.js.map +0 -1
- package/dist-server/service/appliance/appliance-mutation.js.map +0 -1
- package/dist-server/service/appliance/appliance-query.js.map +0 -1
- package/dist-server/service/appliance/appliance-types.js.map +0 -1
- package/dist-server/service/appliance/appliance.js.map +0 -1
- package/dist-server/service/appliance/index.js.map +0 -1
- package/dist-server/service/application/application-mutation.js.map +0 -1
- package/dist-server/service/application/application-query.js.map +0 -1
- package/dist-server/service/application/application-types.js.map +0 -1
- package/dist-server/service/application/application.js.map +0 -1
- package/dist-server/service/application/index.js.map +0 -1
- package/dist-server/service/audit/audit-action.js.map +0 -1
- package/dist-server/service/audit/audit-kinds.js.map +0 -1
- package/dist-server/service/audit/audit-query.js.map +0 -1
- package/dist-server/service/audit/audit-recorder.js.map +0 -1
- package/dist-server/service/audit/audit-request.js.map +0 -1
- package/dist-server/service/audit/audit-retention.js.map +0 -1
- package/dist-server/service/audit/audit-subscriber.js.map +0 -1
- package/dist-server/service/audit/audit-target.js.map +0 -1
- package/dist-server/service/audit/audit-types.js.map +0 -1
- package/dist-server/service/audit/audit-work.js.map +0 -1
- package/dist-server/service/audit/index.js.map +0 -1
- package/dist-server/service/auth-provider/auth-provider-mutation.js.map +0 -1
- package/dist-server/service/auth-provider/auth-provider-parameter-spec.js.map +0 -1
- package/dist-server/service/auth-provider/auth-provider-query.js.map +0 -1
- package/dist-server/service/auth-provider/auth-provider-type.js.map +0 -1
- package/dist-server/service/auth-provider/auth-provider.js.map +0 -1
- package/dist-server/service/auth-provider/index.js.map +0 -1
- package/dist-server/service/domain-generator/domain-generator-mutation.js.map +0 -1
- package/dist-server/service/domain-generator/domain-generator-types.js.map +0 -1
- package/dist-server/service/domain-generator/index.js.map +0 -1
- package/dist-server/service/domain-link/domain-link-mutation.js.map +0 -1
- package/dist-server/service/domain-link/domain-link-query.js.map +0 -1
- package/dist-server/service/domain-link/domain-link-types.js.map +0 -1
- package/dist-server/service/domain-link/domain-link.js.map +0 -1
- package/dist-server/service/domain-link/index.js.map +0 -1
- package/dist-server/service/domain-owner/domain-owner-mutation.js.map +0 -1
- package/dist-server/service/domain-owner/domain-owner-query.js.map +0 -1
- package/dist-server/service/domain-owner/domain-owner.js.map +0 -1
- package/dist-server/service/domain-owner/index.js.map +0 -1
- package/dist-server/service/granted-role/granted-role-mutation.js.map +0 -1
- package/dist-server/service/granted-role/granted-role-query.js.map +0 -1
- package/dist-server/service/granted-role/granted-role.js.map +0 -1
- package/dist-server/service/granted-role/index.js.map +0 -1
- package/dist-server/service/identity-confirmation/identity-confirmation.js.map +0 -1
- package/dist-server/service/index.js.map +0 -1
- package/dist-server/service/open-fields.js.map +0 -1
- package/dist-server/service/partner/index.js.map +0 -1
- package/dist-server/service/partner/partner-mutation.js.map +0 -1
- package/dist-server/service/partner/partner-query.js.map +0 -1
- package/dist-server/service/partner/partner-types.js.map +0 -1
- package/dist-server/service/partner/partner.js.map +0 -1
- package/dist-server/service/password-history/index.js.map +0 -1
- package/dist-server/service/password-history/password-history.js.map +0 -1
- package/dist-server/service/privilege/index.js.map +0 -1
- package/dist-server/service/privilege/open-field-rule.js.map +0 -1
- package/dist-server/service/privilege/privilege-axis.js.map +0 -1
- package/dist-server/service/privilege/privilege-deprecation.js.map +0 -1
- package/dist-server/service/privilege/privilege-directive.js.map +0 -1
- package/dist-server/service/privilege/privilege-mutation.js.map +0 -1
- package/dist-server/service/privilege/privilege-query.js.map +0 -1
- package/dist-server/service/privilege/privilege-rejection.js.map +0 -1
- package/dist-server/service/privilege/privilege-sync.js.map +0 -1
- package/dist-server/service/privilege/privilege-types.js.map +0 -1
- package/dist-server/service/privilege/privilege.js.map +0 -1
- package/dist-server/service/role/index.js.map +0 -1
- package/dist-server/service/role/role-mutation.js.map +0 -1
- package/dist-server/service/role/role-privilege-query.js.map +0 -1
- package/dist-server/service/role/role-query.js.map +0 -1
- package/dist-server/service/role/role-types.js.map +0 -1
- package/dist-server/service/role/role.js.map +0 -1
- package/dist-server/service/role-template/index.js.map +0 -1
- package/dist-server/service/role-template/role-task.js.map +0 -1
- package/dist-server/service/role-template/role-template-gap-resolver.js.map +0 -1
- package/dist-server/service/role-template/role-template-mutation.js.map +0 -1
- package/dist-server/service/role-template/role-template-query.js.map +0 -1
- package/dist-server/service/role-template/role-template-types.js.map +0 -1
- package/dist-server/service/role-template/role-template.js.map +0 -1
- package/dist-server/service/user/domain-query.js.map +0 -1
- package/dist-server/service/user/index.js.map +0 -1
- package/dist-server/service/user/user-mutation.js.map +0 -1
- package/dist-server/service/user/user-query.js.map +0 -1
- package/dist-server/service/user/user-types.js.map +0 -1
- package/dist-server/service/user/user.js.map +0 -1
- package/dist-server/service/users-auth-providers/index.js.map +0 -1
- package/dist-server/service/users-auth-providers/users-auth-providers.js.map +0 -1
- package/dist-server/service/verification-token/index.js.map +0 -1
- package/dist-server/service/verification-token/verification-token.js.map +0 -1
- package/dist-server/service/web-auth-credential/index.js.map +0 -1
- package/dist-server/service/web-auth-credential/web-auth-credential-mutation.js.map +0 -1
- package/dist-server/service/web-auth-credential/web-auth-credential-query.js.map +0 -1
- package/dist-server/service/web-auth-credential/web-auth-credential.js.map +0 -1
- package/dist-server/tsconfig.tsbuildinfo +0 -1
- package/dist-server/types.js.map +0 -1
- package/dist-server/utils/accepts.js.map +0 -1
- package/dist-server/utils/access-request-review.js.map +0 -1
- package/dist-server/utils/access-token-cookie.js.map +0 -1
- package/dist-server/utils/account-lock-rule.js.map +0 -1
- package/dist-server/utils/assignable-roles.js.map +0 -1
- package/dist-server/utils/build-auth-url.js.map +0 -1
- package/dist-server/utils/can-user-checkin-domain.js.map +0 -1
- package/dist-server/utils/check-permission.js.map +0 -1
- package/dist-server/utils/check-user-belongs-domain.js.map +0 -1
- package/dist-server/utils/check-user-has-role.js.map +0 -1
- package/dist-server/utils/checkin-domain-facts.js.map +0 -1
- package/dist-server/utils/checkin-domain-rule.js.map +0 -1
- package/dist-server/utils/credential-at-rest.js.map +0 -1
- package/dist-server/utils/credential-features.js.map +0 -1
- package/dist-server/utils/credential-serial-rule.js.map +0 -1
- package/dist-server/utils/domain-ownership.js.map +0 -1
- package/dist-server/utils/encrypt-state.js.map +0 -1
- package/dist-server/utils/get-aes-256-key.js.map +0 -1
- package/dist-server/utils/get-domain-users.js.map +0 -1
- package/dist-server/utils/get-secret.js.map +0 -1
- package/dist-server/utils/get-user-domains.js.map +0 -1
- package/dist-server/utils/lock-duration.js.map +0 -1
- package/dist-server/utils/password-reset-rule.js.map +0 -1
- package/dist-server/utils/profile-user.js.map +0 -1
- package/dist-server/utils/refuse-role-name.js.map +0 -1
- package/dist-server/utils/role-name-standing.js.map +0 -1
- package/dist-server/utils/same-origin-redirect.js.map +0 -1
- package/dist-server/utils/verification-token-rule.js.map +0 -1
- package/spec/unit/checkin-domain.spec.ts +0 -307
- package/spec/unit/checkin-privilege.spec.ts +0 -205
- package/spec/unit/domain-owner-entity.spec.ts +0 -179
- package/spec/unit/domain-owner-granted.spec.ts +0 -215
- package/spec/unit/domain-owner-migration.spec.ts +0 -236
- package/spec/unit/domain-owner-mutation.spec.ts +0 -337
- package/spec/unit/domain-ownership.spec.ts +0 -188
- package/tests/access-assistant-db.test.ts +0 -155
- package/tests/access-entry-targets-db.test.ts +0 -176
- package/tests/access-request-db.test.ts +0 -394
- package/tests/access-request-pg.test.ts +0 -83
- package/tests/access-request-review.test.ts +0 -193
- package/tests/account-lock-db.test.ts +0 -216
- package/tests/account-lock-rule.test.ts +0 -141
- package/tests/account-mail.test.ts +0 -69
- package/tests/activate-page-case.test.ts +0 -53
- package/tests/add-user-gates.test.ts +0 -34
- package/tests/app-binding-db.test.ts +0 -210
- package/tests/app-binding-delete-db.test.ts +0 -188
- package/tests/appliance-credential-state-db.test.ts +0 -207
- package/tests/appliance-delete-db.test.ts +0 -119
- package/tests/appliance-revoke-db.test.ts +0 -294
- package/tests/appliance-schema.test.ts +0 -129
- package/tests/application-delete-db.test.ts +0 -190
- package/tests/application-token-payload-db.test.ts +0 -185
- package/tests/assignable-roles-db.test.ts +0 -219
- package/tests/audit-db.test.ts +0 -410
- package/tests/audit-work.test.ts +0 -58
- package/tests/auth-base-role-tasks.test.ts +0 -58
- package/tests/checkin-domain-rule.test.ts +0 -128
- package/tests/checkin-privilege-seam-db.test.ts +0 -158
- package/tests/compiled.ts +0 -41
- package/tests/confirm-identity-db.test.ts +0 -85
- package/tests/credential-at-rest-db.test.ts +0 -246
- package/tests/credential-features-off-db.test.ts +0 -189
- package/tests/credential-serial-rule.test.ts +0 -84
- package/tests/declare-privileges.test.ts +0 -29
- package/tests/domain-assignees-db.test.ts +0 -95
- package/tests/domain-inheritance-sentinel.test.ts +0 -93
- package/tests/domain-required.test.ts +0 -127
- package/tests/has-privilege-db.test.ts +0 -198
- package/tests/identity-confirmation.test.ts +0 -146
- package/tests/list-inheritance-db.test.ts +0 -127
- package/tests/lock-recovery.test.ts +0 -206
- package/tests/mail-request-answers-db.test.ts +0 -142
- package/tests/open-doors.test.ts +0 -121
- package/tests/open-fields-enforcement.test.ts +0 -215
- package/tests/open-fields-framework.test.ts +0 -212
- package/tests/open-redirect-db.test.ts +0 -143
- package/tests/passkey-authenticator.ts +0 -104
- package/tests/passkey-db.test.ts +0 -295
- package/tests/passkey-signin-route-db.test.ts +0 -139
- package/tests/password-hashing.test.ts +0 -131
- package/tests/password-persistence.test.ts +0 -147
- package/tests/password-reset-rule-db.test.ts +0 -159
- package/tests/password-setup-db.test.ts +0 -215
- package/tests/permission-gate.test.ts +0 -228
- package/tests/picture-reducer.test.ts +0 -25
- package/tests/privilege-axis.test.ts +0 -157
- package/tests/privilege-deprecation.test.ts +0 -65
- package/tests/privilege-directive.test.ts +0 -332
- package/tests/privilege-rejection.test.ts +0 -104
- package/tests/privilege-sync-db.test.ts +0 -58
- package/tests/profile-user.test.ts +0 -24
- package/tests/refusal-cause.test.ts +0 -80
- package/tests/refusals-have-korean.test.ts +0 -24
- package/tests/request-holds.test.ts +0 -53
- package/tests/role-inheritance-db.test.ts +0 -286
- package/tests/role-mutation-db.test.ts +0 -293
- package/tests/role-name-lookup-sentinel.test.ts +0 -79
- package/tests/role-name-standing.test.ts +0 -83
- package/tests/role-privileges-db.test.ts +0 -252
- package/tests/role-task.test.ts +0 -168
- package/tests/role-template-gap-db.test.ts +0 -268
- package/tests/role-template-seed-db.test.ts +0 -180
- package/tests/role-template.test.ts +0 -162
- package/tests/role-users-gate.test.ts +0 -22
- package/tests/signin-policy.test.ts +0 -200
- package/tests/signup-duplicate-db.test.ts +0 -68
- package/tests/token-issuance.test.ts +0 -244
- package/tests/user-roles-field-db.test.ts +0 -134
- package/tests/verification-token-db.test.ts +0 -285
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 체크인 자격 판정 — 실제 엔티티·실제 쿼리 검증.
|
|
3
|
-
*
|
|
4
|
-
* 보안 판정이므로 로직을 재현하지 않고, 빌드된 `@things-factory/auth-base` 의 실제 함수
|
|
5
|
-
* (`getUserDomains` / `canUserCheckinDomain`)를 실제 엔티티 메타데이터(실제 조인 테이블·네이밍
|
|
6
|
-
* 전략)로 만든 sqlite in-memory 스키마에 대해 그대로 실행한다.
|
|
7
|
-
*
|
|
8
|
-
* 주의: 이 스펙은 빌드 산출물(`dist-server`)을 대상으로 한다 — 소스를 고친 뒤에는
|
|
9
|
-
* `npm run build:server` 를 먼저 실행해야 변경이 반영된다.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { DataSource } from 'typeorm'
|
|
13
|
-
|
|
14
|
-
import { NamingStrategy, addDataSource, entities as shellEntities, Domain } from '@things-factory/shell'
|
|
15
|
-
import {
|
|
16
|
-
DomainOwner,
|
|
17
|
-
GrantedRole,
|
|
18
|
-
Role,
|
|
19
|
-
User,
|
|
20
|
-
canUserCheckinDomain,
|
|
21
|
-
entities as authEntities,
|
|
22
|
-
getUserDomains
|
|
23
|
-
} from '@things-factory/auth-base'
|
|
24
|
-
|
|
25
|
-
let dataSource: DataSource
|
|
26
|
-
|
|
27
|
-
async function domain(name: string, parent?: Domain, owner?: string): Promise<Domain> {
|
|
28
|
-
return await dataSource.getRepository(Domain).save({
|
|
29
|
-
name,
|
|
30
|
-
subdomain: name,
|
|
31
|
-
parent,
|
|
32
|
-
owner
|
|
33
|
-
} as any)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function user(name: string, domains: Domain[], roles: Role[] = []): Promise<User> {
|
|
37
|
-
return await dataSource.getRepository(User).save({
|
|
38
|
-
name,
|
|
39
|
-
email: `${name}@test.com`,
|
|
40
|
-
username: name,
|
|
41
|
-
domains,
|
|
42
|
-
roles
|
|
43
|
-
} as any)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function role(name: string, roleDomain: Domain): Promise<Role> {
|
|
47
|
-
return await dataSource.getRepository(Role).save({ name, domain: roleDomain } as any)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** 판정에 쓰이는 도메인은 `context.state.domain` 처럼 온전히 적재된 것이어야 한다. */
|
|
51
|
-
async function reload(target: Domain): Promise<Domain> {
|
|
52
|
-
return await dataSource.getRepository(Domain).findOneBy({ id: target.id })
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function reloadUser(target: User): Promise<User> {
|
|
56
|
-
return await dataSource.getRepository(User).findOne({ where: { id: target.id }, relations: ['domains'] })
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function checkinSubdomains(target: User): Promise<string[]> {
|
|
60
|
-
return (await getUserDomains(await reloadUser(target))).map(d => d.subdomain)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
beforeAll(async () => {
|
|
64
|
-
dataSource = new DataSource({
|
|
65
|
-
type: 'sqlite',
|
|
66
|
-
database: ':memory:',
|
|
67
|
-
synchronize: true,
|
|
68
|
-
dropSchema: true,
|
|
69
|
-
namingStrategy: new NamingStrategy(),
|
|
70
|
-
entities: [...shellEntities, ...authEntities],
|
|
71
|
-
logging: false
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
await dataSource.initialize()
|
|
75
|
-
addDataSource('default', dataSource)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
afterAll(async () => {
|
|
79
|
-
await dataSource?.destroy()
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
beforeEach(async () => {
|
|
83
|
-
/* 스펙 간 격리 — 테이블 이름은 엔티티 메타데이터에서 얻는다 (네이밍 전략에 의존하지 않기 위해). */
|
|
84
|
-
for (const junction of dataSource.entityMetadatas.filter(metadata => metadata.isJunction)) {
|
|
85
|
-
await dataSource.query(`DELETE FROM "${junction.tableName}"`)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
for (const target of [GrantedRole, DomainOwner, Role, User, Domain]) {
|
|
89
|
-
await dataSource.query(`DELETE FROM "${dataSource.getMetadata(target).tableName}"`)
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
describe('부모 도메인으로부터 상속받은 역할', () => {
|
|
94
|
-
it('부여가 일어난 자식 도메인에 체크인할 수 있다', async () => {
|
|
95
|
-
const parent = await domain('parent')
|
|
96
|
-
const child = await domain('child', parent)
|
|
97
|
-
const inheritedRole = await role('operator', parent)
|
|
98
|
-
const member = await user('kim', [child], [inheritedRole])
|
|
99
|
-
|
|
100
|
-
expect(await canUserCheckinDomain(await reloadUser(member), await reload(child))).toBe(true)
|
|
101
|
-
expect(await checkinSubdomains(member)).toEqual(['child'])
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('역할을 정의한 부모 도메인에는 체크인할 수 없다', async () => {
|
|
105
|
-
const parent = await domain('parent')
|
|
106
|
-
const child = await domain('child', parent)
|
|
107
|
-
const inheritedRole = await role('operator', parent)
|
|
108
|
-
const member = await user('kim', [child], [inheritedRole])
|
|
109
|
-
|
|
110
|
-
expect(await canUserCheckinDomain(await reloadUser(member), await reload(parent))).toBe(false)
|
|
111
|
-
expect(await checkinSubdomains(member)).not.toContain('parent')
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it('멤버가 아닌 형제 자식 도메인에는 체크인할 수 없다', async () => {
|
|
115
|
-
const parent = await domain('parent')
|
|
116
|
-
const child = await domain('child', parent)
|
|
117
|
-
const sibling = await domain('sibling', parent)
|
|
118
|
-
const inheritedRole = await role('operator', parent)
|
|
119
|
-
const member = await user('kim', [child], [inheritedRole])
|
|
120
|
-
|
|
121
|
-
expect(await canUserCheckinDomain(await reloadUser(member), await reload(sibling))).toBe(false)
|
|
122
|
-
expect(await checkinSubdomains(member)).toEqual(['child'])
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('부모의 역할을 부모에서 부여받았다면 부모에 체크인한다', async () => {
|
|
126
|
-
const parent = await domain('parent')
|
|
127
|
-
const child = await domain('child', parent)
|
|
128
|
-
const parentRole = await role('operator', parent)
|
|
129
|
-
const member = await user('kim', [parent], [parentRole])
|
|
130
|
-
|
|
131
|
-
expect(await canUserCheckinDomain(await reloadUser(member), await reload(parent))).toBe(true)
|
|
132
|
-
expect(await canUserCheckinDomain(await reloadUser(member), await reload(child))).toBe(false)
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
describe('자기 도메인에 정의된 역할', () => {
|
|
137
|
-
it('멤버이고 역할이 있으면 체크인한다', async () => {
|
|
138
|
-
const target = await domain('acme')
|
|
139
|
-
const ownRole = await role('operator', target)
|
|
140
|
-
const member = await user('kim', [target], [ownRole])
|
|
141
|
-
|
|
142
|
-
expect(await canUserCheckinDomain(await reloadUser(member), await reload(target))).toBe(true)
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
it('멤버이지만 역할이 없으면 체크인하지 못한다', async () => {
|
|
146
|
-
const target = await domain('acme')
|
|
147
|
-
const member = await user('kim', [target], [])
|
|
148
|
-
|
|
149
|
-
expect(await canUserCheckinDomain(await reloadUser(member), await reload(target))).toBe(false)
|
|
150
|
-
expect(await checkinSubdomains(member)).toEqual([])
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('역할이 있지만 멤버가 아니면 체크인하지 못한다', async () => {
|
|
154
|
-
const target = await domain('acme')
|
|
155
|
-
const outsideRole = await role('operator', target)
|
|
156
|
-
const outsider = await user('lee', [], [outsideRole])
|
|
157
|
-
|
|
158
|
-
expect(await canUserCheckinDomain(await reloadUser(outsider), await reload(target))).toBe(false)
|
|
159
|
-
expect(await checkinSubdomains(outsider)).toEqual([])
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
describe('파트너십으로 grant 된 역할', () => {
|
|
164
|
-
it('공급자 도메인의 멤버가 아니어도 체크인한다', async () => {
|
|
165
|
-
const supplier = await domain('supplier')
|
|
166
|
-
const customer = await domain('customer')
|
|
167
|
-
const supplierRole = await role('carrier', supplier)
|
|
168
|
-
await dataSource.getRepository(GrantedRole).save({ role: supplierRole, domain: customer } as any)
|
|
169
|
-
|
|
170
|
-
const customerUser = await user('park', [customer], [supplierRole])
|
|
171
|
-
|
|
172
|
-
expect(await canUserCheckinDomain(await reloadUser(customerUser), await reload(supplier))).toBe(true)
|
|
173
|
-
expect((await checkinSubdomains(customerUser)).sort()).toEqual(['supplier'])
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
it('grant 되지 않은 역할은 파트너 경로를 열지 않는다', async () => {
|
|
177
|
-
const supplier = await domain('supplier')
|
|
178
|
-
const customer = await domain('customer')
|
|
179
|
-
const supplierRole = await role('carrier', supplier)
|
|
180
|
-
|
|
181
|
-
const customerUser = await user('park', [customer], [supplierRole])
|
|
182
|
-
|
|
183
|
-
expect(await canUserCheckinDomain(await reloadUser(customerUser), await reload(supplier))).toBe(false)
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it('다른 도메인에 grant 된 역할은 근거가 되지 않는다', async () => {
|
|
187
|
-
const supplier = await domain('supplier')
|
|
188
|
-
const customer = await domain('customer')
|
|
189
|
-
const stranger = await domain('stranger')
|
|
190
|
-
const supplierRole = await role('carrier', supplier)
|
|
191
|
-
await dataSource.getRepository(GrantedRole).save({ role: supplierRole, domain: stranger } as any)
|
|
192
|
-
|
|
193
|
-
const customerUser = await user('park', [customer], [supplierRole])
|
|
194
|
-
|
|
195
|
-
expect(await canUserCheckinDomain(await reloadUser(customerUser), await reload(supplier))).toBe(false)
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
describe('소유권', () => {
|
|
200
|
-
it('하위 호환 owner 컬럼으로 체크인한다', async () => {
|
|
201
|
-
const target = await domain('acme')
|
|
202
|
-
const owner = await user('boss', [])
|
|
203
|
-
await dataSource.getRepository(Domain).update(target.id, { owner: owner.id })
|
|
204
|
-
|
|
205
|
-
expect(await canUserCheckinDomain(await reloadUser(owner), await reload(target))).toBe(true)
|
|
206
|
-
expect(await checkinSubdomains(owner)).toEqual(['acme'])
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
it('DomainOwner 테이블 기준으로도 체크인한다', async () => {
|
|
210
|
-
const target = await domain('acme')
|
|
211
|
-
const owner = await user('boss', [])
|
|
212
|
-
await dataSource.getRepository(DomainOwner).save({ domain: target, user: owner } as any)
|
|
213
|
-
|
|
214
|
-
expect(await canUserCheckinDomain(await reloadUser(owner), await reload(target))).toBe(true)
|
|
215
|
-
expect(await checkinSubdomains(owner)).toEqual(['acme'])
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
it('다른 사용자가 owner 인 도메인은 체크인하지 못한다', async () => {
|
|
219
|
-
const target = await domain('acme')
|
|
220
|
-
const owner = await user('boss', [])
|
|
221
|
-
const other = await user('lee', [])
|
|
222
|
-
await dataSource.getRepository(Domain).update(target.id, { owner: owner.id })
|
|
223
|
-
|
|
224
|
-
expect(await canUserCheckinDomain(await reloadUser(other), await reload(target))).toBe(false)
|
|
225
|
-
})
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
describe('핫패스 조회 비용', () => {
|
|
229
|
-
/**
|
|
230
|
-
* `canUserCheckinDomain` 은 매 요청 도메인 인증에서 호출된다. 도메인 목록 전체를 계산하지
|
|
231
|
-
* 않고 대상 도메인만 판정하도록 되어 있는데, 이 성질이 깨지면 요청당 비용이 늘어난다.
|
|
232
|
-
*/
|
|
233
|
-
async function countQueries(work: () => Promise<unknown>): Promise<number> {
|
|
234
|
-
let count = 0
|
|
235
|
-
const original = (dataSource as any).logger
|
|
236
|
-
|
|
237
|
-
;(dataSource as any).logger = {
|
|
238
|
-
logQuery: () => {
|
|
239
|
-
count++
|
|
240
|
-
},
|
|
241
|
-
logQueryError: () => {},
|
|
242
|
-
logQuerySlow: () => {},
|
|
243
|
-
logSchemaBuild: () => {},
|
|
244
|
-
logMigration: () => {},
|
|
245
|
-
log: () => {}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
try {
|
|
249
|
-
await work()
|
|
250
|
-
} finally {
|
|
251
|
-
;(dataSource as any).logger = original
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return count
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
it('통과 판정은 1회 조회로 끝난다', async () => {
|
|
258
|
-
const parent = await domain('parent')
|
|
259
|
-
const child = await domain('child', parent)
|
|
260
|
-
const inheritedRole = await role('operator', parent)
|
|
261
|
-
const member = await user('kim', [child], [inheritedRole])
|
|
262
|
-
|
|
263
|
-
const loadedUser = await reloadUser(member)
|
|
264
|
-
const loadedDomain = await reload(child)
|
|
265
|
-
|
|
266
|
-
expect(await countQueries(() => canUserCheckinDomain(loadedUser, loadedDomain))).toBe(1)
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
it('목록 계산은 4회 조회로 끝난다 (경로별 조회가 도메인 행을 직접 반환한다)', async () => {
|
|
270
|
-
const parent = await domain('parent')
|
|
271
|
-
const child = await domain('child', parent)
|
|
272
|
-
const inheritedRole = await role('operator', parent)
|
|
273
|
-
const member = await user('kim', [child], [inheritedRole])
|
|
274
|
-
|
|
275
|
-
const loadedUser = await reloadUser(member)
|
|
276
|
-
|
|
277
|
-
expect(await countQueries(() => getUserDomains(loadedUser))).toBe(4)
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
it('거절 판정도 3회를 넘지 않는다', async () => {
|
|
281
|
-
const target = await domain('acme')
|
|
282
|
-
const outsider = await user('lee', [], [])
|
|
283
|
-
|
|
284
|
-
const loadedUser = await reloadUser(outsider)
|
|
285
|
-
const loadedDomain = await reload(target)
|
|
286
|
-
|
|
287
|
-
expect(await countQueries(() => canUserCheckinDomain(loadedUser, loadedDomain))).toBeLessThanOrEqual(3)
|
|
288
|
-
})
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
describe('여러 도메인이 얽힌 목록 계산', () => {
|
|
292
|
-
it('부여받은 도메인만 목록에 담는다', async () => {
|
|
293
|
-
const parent = await domain('parent')
|
|
294
|
-
const childA = await domain('child-a', parent)
|
|
295
|
-
const childB = await domain('child-b', parent)
|
|
296
|
-
const other = await domain('other')
|
|
297
|
-
|
|
298
|
-
const inheritedRole = await role('operator', parent)
|
|
299
|
-
const childBRole = await role('viewer', childB)
|
|
300
|
-
await role('unassigned', other)
|
|
301
|
-
|
|
302
|
-
/* childA: 상속 역할 부여 / childB: 자기 역할 부여 / other: 멤버이지만 역할 없음 */
|
|
303
|
-
const member = await user('kim', [childA, childB, other], [inheritedRole, childBRole])
|
|
304
|
-
|
|
305
|
-
expect((await checkinSubdomains(member)).sort()).toEqual(['child-a', 'child-b'])
|
|
306
|
-
})
|
|
307
|
-
})
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 권한 판정의 도메인 상속 — 실제 엔티티·실제 쿼리 검증.
|
|
3
|
-
*
|
|
4
|
-
* 역할은 부모 도메인으로부터 상속되므로, 상속 역할을 부여받아 자식 도메인에 체크인한 사용자는
|
|
5
|
-
* 그 역할의 권한을 자식 도메인에서 행사할 수 있어야 한다. 체크인만 열리고 권한이 0 이면
|
|
6
|
-
* 상속 부여는 무의미하다.
|
|
7
|
-
*
|
|
8
|
-
* 주의: 이 스펙은 빌드 산출물(`dist-server`)을 대상으로 한다.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { DataSource } from 'typeorm'
|
|
12
|
-
|
|
13
|
-
import { NamingStrategy, addDataSource, entities as shellEntities, Domain } from '@things-factory/shell'
|
|
14
|
-
import {
|
|
15
|
-
DomainOwner,
|
|
16
|
-
Privilege,
|
|
17
|
-
Role,
|
|
18
|
-
User,
|
|
19
|
-
entities as authEntities,
|
|
20
|
-
getDomainsWithPrivilege
|
|
21
|
-
} from '@things-factory/auth-base'
|
|
22
|
-
|
|
23
|
-
let dataSource: DataSource
|
|
24
|
-
|
|
25
|
-
async function domain(name: string, parent?: Domain): Promise<Domain> {
|
|
26
|
-
return await dataSource.getRepository(Domain).save({ name, subdomain: name, parent } as any)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function privilege(name: string, category: string): Promise<Privilege> {
|
|
30
|
-
return await dataSource.getRepository(Privilege).save({ name, category } as any)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function role(name: string, roleDomain: Domain, privileges: Privilege[]): Promise<Role> {
|
|
34
|
-
return await dataSource.getRepository(Role).save({ name, domain: roleDomain, privileges } as any)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function user(name: string, domains: Domain[], roles: Role[]): Promise<User> {
|
|
38
|
-
return await dataSource.getRepository(User).save({
|
|
39
|
-
name,
|
|
40
|
-
email: `${name}@test.com`,
|
|
41
|
-
username: name,
|
|
42
|
-
domains,
|
|
43
|
-
roles
|
|
44
|
-
} as any)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function reload(target: Domain): Promise<Domain> {
|
|
48
|
-
return await dataSource.getRepository(Domain).findOneBy({ id: target.id })
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function reloadUser(target: User): Promise<User> {
|
|
52
|
-
return await dataSource.getRepository(User).findOne({ where: { id: target.id }, relations: ['domains'] })
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
beforeAll(async () => {
|
|
56
|
-
dataSource = new DataSource({
|
|
57
|
-
type: 'sqlite',
|
|
58
|
-
database: ':memory:',
|
|
59
|
-
synchronize: true,
|
|
60
|
-
dropSchema: true,
|
|
61
|
-
namingStrategy: new NamingStrategy(),
|
|
62
|
-
entities: [...shellEntities, ...authEntities],
|
|
63
|
-
logging: false
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
await dataSource.initialize()
|
|
67
|
-
addDataSource('default', dataSource)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
afterAll(async () => {
|
|
71
|
-
await dataSource?.destroy()
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
beforeEach(async () => {
|
|
75
|
-
for (const junction of dataSource.entityMetadatas.filter(metadata => metadata.isJunction)) {
|
|
76
|
-
await dataSource.query(`DELETE FROM "${junction.tableName}"`)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
for (const target of [DomainOwner, Role, Privilege, User, Domain]) {
|
|
80
|
-
await dataSource.query(`DELETE FROM "${dataSource.getMetadata(target).tableName}"`)
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
describe('User.hasPrivilege — 도메인 상속', () => {
|
|
85
|
-
it('자기 도메인에 정의된 역할의 권한을 행사한다', async () => {
|
|
86
|
-
const target = await domain('acme')
|
|
87
|
-
const query = await privilege('query', 'board')
|
|
88
|
-
const operator = await role('operator', target, [query])
|
|
89
|
-
const member = await user('kim', [target], [operator])
|
|
90
|
-
|
|
91
|
-
expect(await User.hasPrivilege('query', 'board', await reload(target), member)).toBe(true)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('부모 도메인에 정의된 상속 역할의 권한을 자식 도메인에서 행사한다', async () => {
|
|
95
|
-
const parent = await domain('parent')
|
|
96
|
-
const child = await domain('child', parent)
|
|
97
|
-
const query = await privilege('query', 'board')
|
|
98
|
-
const inherited = await role('operator', parent, [query])
|
|
99
|
-
const member = await user('kim', [child], [inherited])
|
|
100
|
-
|
|
101
|
-
expect(await User.hasPrivilege('query', 'board', await reload(child), member)).toBe(true)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('무관한 도메인에 정의된 역할의 권한은 행사하지 못한다', async () => {
|
|
105
|
-
const target = await domain('acme')
|
|
106
|
-
const other = await domain('other')
|
|
107
|
-
const query = await privilege('query', 'board')
|
|
108
|
-
const otherRole = await role('operator', other, [query])
|
|
109
|
-
const member = await user('kim', [target], [otherRole])
|
|
110
|
-
|
|
111
|
-
expect(await User.hasPrivilege('query', 'board', await reload(target), member)).toBe(false)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it('역할이 갖지 않은 권한은 행사하지 못한다', async () => {
|
|
115
|
-
const parent = await domain('parent')
|
|
116
|
-
const child = await domain('child', parent)
|
|
117
|
-
const query = await privilege('query', 'board')
|
|
118
|
-
await privilege('mutation', 'board')
|
|
119
|
-
const inherited = await role('viewer', parent, [query])
|
|
120
|
-
const member = await user('kim', [child], [inherited])
|
|
121
|
-
|
|
122
|
-
expect(await User.hasPrivilege('mutation', 'board', await reload(child), member)).toBe(false)
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('조부모 도메인의 역할까지 거슬러 올라가지는 않는다 (상속은 1단)', async () => {
|
|
126
|
-
const grandParent = await domain('grand')
|
|
127
|
-
const parent = await domain('parent', grandParent)
|
|
128
|
-
const child = await domain('child', parent)
|
|
129
|
-
const query = await privilege('query', 'board')
|
|
130
|
-
const grandRole = await role('operator', grandParent, [query])
|
|
131
|
-
const member = await user('kim', [child], [grandRole])
|
|
132
|
-
|
|
133
|
-
expect(await User.hasPrivilege('query', 'board', await reload(child), member)).toBe(false)
|
|
134
|
-
})
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
describe('User.getPrivilegesByDomain — 도메인 상속', () => {
|
|
138
|
-
it('상속 역할의 권한이 목록에 포함된다', async () => {
|
|
139
|
-
const parent = await domain('parent')
|
|
140
|
-
const child = await domain('child', parent)
|
|
141
|
-
const inheritedPrivilege = await privilege('query', 'board')
|
|
142
|
-
const ownPrivilege = await privilege('mutation', 'worklist')
|
|
143
|
-
const inherited = await role('operator', parent, [inheritedPrivilege])
|
|
144
|
-
const own = await role('editor', child, [ownPrivilege])
|
|
145
|
-
const member = await user('kim', [child], [inherited, own])
|
|
146
|
-
|
|
147
|
-
const privileges = await User.getPrivilegesByDomain(member, await reload(child))
|
|
148
|
-
|
|
149
|
-
expect(privileges).toEqual(
|
|
150
|
-
expect.arrayContaining([
|
|
151
|
-
{ category: 'board', privilege: 'query' },
|
|
152
|
-
{ category: 'worklist', privilege: 'mutation' }
|
|
153
|
-
])
|
|
154
|
-
)
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('무관한 도메인 역할의 권한은 목록에서 제외된다', async () => {
|
|
158
|
-
const target = await domain('acme')
|
|
159
|
-
const other = await domain('other')
|
|
160
|
-
const otherPrivilege = await privilege('query', 'secret')
|
|
161
|
-
const otherRole = await role('operator', other, [otherPrivilege])
|
|
162
|
-
const member = await user('kim', [target, other], [otherRole])
|
|
163
|
-
|
|
164
|
-
const privileges = await User.getPrivilegesByDomain(member, await reload(target))
|
|
165
|
-
|
|
166
|
-
expect(privileges).toEqual([])
|
|
167
|
-
})
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
describe('getDomainsWithPrivilege — 부여 도메인 + 상속', () => {
|
|
171
|
-
it('상속 역할로 권한을 가진 자식 도메인을 반환한다', async () => {
|
|
172
|
-
const parent = await domain('parent')
|
|
173
|
-
const child = await domain('child', parent)
|
|
174
|
-
const query = await privilege('query', 'board')
|
|
175
|
-
const inherited = await role('operator', parent, [query])
|
|
176
|
-
const member = await user('kim', [child], [inherited])
|
|
177
|
-
|
|
178
|
-
const domains = await getDomainsWithPrivilege(await reloadUser(member), 'query', 'board')
|
|
179
|
-
|
|
180
|
-
expect(domains.map(d => d.subdomain)).toEqual(['child'])
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
it('권한 없는 역할만 부여된 도메인은 제외한다', async () => {
|
|
184
|
-
const withPrivilege = await domain('with')
|
|
185
|
-
const without = await domain('without')
|
|
186
|
-
const query = await privilege('query', 'board')
|
|
187
|
-
const granted = await role('operator', withPrivilege, [query])
|
|
188
|
-
const plain = await role('guest', without, [])
|
|
189
|
-
const member = await user('kim', [withPrivilege, without], [granted, plain])
|
|
190
|
-
|
|
191
|
-
const domains = await getDomainsWithPrivilege(await reloadUser(member), 'query', 'board')
|
|
192
|
-
|
|
193
|
-
expect(domains.map(d => d.subdomain)).toEqual(['with'])
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
it('소유 도메인은 권한 판정 없이 포함한다', async () => {
|
|
197
|
-
const owned = await domain('owned')
|
|
198
|
-
const owner = await user('boss', [], [])
|
|
199
|
-
await dataSource.getRepository(Domain).update(owned.id, { owner: owner.id })
|
|
200
|
-
|
|
201
|
-
const domains = await getDomainsWithPrivilege(await reloadUser(owner), 'query', 'board')
|
|
202
|
-
|
|
203
|
-
expect(domains.map(d => d.subdomain)).toEqual(['owned'])
|
|
204
|
-
})
|
|
205
|
-
})
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DomainOwner Entity Tests
|
|
3
|
-
*
|
|
4
|
-
* DomainOwner 엔티티의 저장/조회/유니크 제약/캐스케이드 테스트.
|
|
5
|
-
* EntitySchema 기반 테스트 DB를 사용하므로 실제 @things-factory/shell 의
|
|
6
|
-
* Domain/User 엔티티 대신 테스트 전용 스키마를 사용한다.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { TestDatabase } from '../../../../test/test-database'
|
|
10
|
-
import { withTestTransaction } from '../../../../test/test-context'
|
|
11
|
-
import {
|
|
12
|
-
domainFactory,
|
|
13
|
-
userFactory,
|
|
14
|
-
domainOwnerFactory
|
|
15
|
-
} from '../../../../test/factories'
|
|
16
|
-
import { DomainOwner } from '../../../../test/entities/schemas'
|
|
17
|
-
|
|
18
|
-
describe('DomainOwner Entity', () => {
|
|
19
|
-
let testDb: TestDatabase
|
|
20
|
-
|
|
21
|
-
beforeAll(() => {
|
|
22
|
-
testDb = TestDatabase.getInstance()
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
describe('생성', () => {
|
|
26
|
-
it('기본 DomainOwner 엔트리를 저장할 수 있다', async () => {
|
|
27
|
-
await withTestTransaction(async context => {
|
|
28
|
-
const { tx } = context.state
|
|
29
|
-
|
|
30
|
-
const domain = await domainFactory.create({}, tx)
|
|
31
|
-
const user = await userFactory.create({}, tx)
|
|
32
|
-
|
|
33
|
-
const entry = await domainOwnerFactory.grant(domain, user, {}, tx)
|
|
34
|
-
|
|
35
|
-
expect(entry.id).toBeDefined()
|
|
36
|
-
expect(entry.grantedAt).toBeInstanceOf(Date)
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('grantedBy (부여자) 감사 정보를 저장한다', async () => {
|
|
41
|
-
await withTestTransaction(async context => {
|
|
42
|
-
const { tx } = context.state
|
|
43
|
-
|
|
44
|
-
const domain = await domainFactory.create({}, tx)
|
|
45
|
-
const newOwner = await userFactory.create({ name: 'Alice' }, tx)
|
|
46
|
-
const granter = await userFactory.create({ name: 'Bob the Admin' }, tx)
|
|
47
|
-
|
|
48
|
-
const entry = await domainOwnerFactory.grantBy(
|
|
49
|
-
domain,
|
|
50
|
-
newOwner,
|
|
51
|
-
granter,
|
|
52
|
-
{ reason: 'initial setup' },
|
|
53
|
-
tx
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
const reloaded = await tx.findOne<DomainOwner>('DomainOwner', {
|
|
57
|
-
where: { id: entry.id },
|
|
58
|
-
relations: ['user', 'grantedBy', 'domain']
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
expect(reloaded?.user.id).toBe(newOwner.id)
|
|
62
|
-
expect(reloaded?.grantedBy?.id).toBe(granter.id)
|
|
63
|
-
expect(reloaded?.domain.id).toBe(domain.id)
|
|
64
|
-
expect(reloaded?.reason).toBe('initial setup')
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('reason 은 선택(null)적이다', async () => {
|
|
69
|
-
await withTestTransaction(async context => {
|
|
70
|
-
const { tx } = context.state
|
|
71
|
-
|
|
72
|
-
const domain = await domainFactory.create({}, tx)
|
|
73
|
-
const user = await userFactory.create({}, tx)
|
|
74
|
-
|
|
75
|
-
const entry = await domainOwnerFactory.grant(domain, user, { reason: null as any }, tx)
|
|
76
|
-
|
|
77
|
-
expect(entry.id).toBeDefined()
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
describe('유니크 제약', () => {
|
|
83
|
-
it('같은 (domain, user) 조합은 중복 삽입할 수 없다', async () => {
|
|
84
|
-
await withTestTransaction(async context => {
|
|
85
|
-
const { tx } = context.state
|
|
86
|
-
|
|
87
|
-
const domain = await domainFactory.create({}, tx)
|
|
88
|
-
const user = await userFactory.create({}, tx)
|
|
89
|
-
|
|
90
|
-
await domainOwnerFactory.grant(domain, user, {}, tx)
|
|
91
|
-
|
|
92
|
-
await expect(
|
|
93
|
-
domainOwnerFactory.grant(domain, user, { reason: 'duplicate' }, tx)
|
|
94
|
-
).rejects.toThrow()
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('같은 user는 서로 다른 domain에는 owner로 등록 가능하다', async () => {
|
|
99
|
-
await withTestTransaction(async context => {
|
|
100
|
-
const { tx } = context.state
|
|
101
|
-
|
|
102
|
-
const domainA = await domainFactory.create({ name: 'A' }, tx)
|
|
103
|
-
const domainB = await domainFactory.create({ name: 'B' }, tx)
|
|
104
|
-
const user = await userFactory.create({}, tx)
|
|
105
|
-
|
|
106
|
-
const a = await domainOwnerFactory.grant(domainA, user, {}, tx)
|
|
107
|
-
const b = await domainOwnerFactory.grant(domainB, user, {}, tx)
|
|
108
|
-
|
|
109
|
-
expect(a.id).not.toBe(b.id)
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
it('같은 domain에 여러 user가 owner로 등록 가능하다 (멀티 오너)', async () => {
|
|
114
|
-
await withTestTransaction(async context => {
|
|
115
|
-
const { tx } = context.state
|
|
116
|
-
|
|
117
|
-
const domain = await domainFactory.create({}, tx)
|
|
118
|
-
const u1 = await userFactory.create({ name: 'U1' }, tx)
|
|
119
|
-
const u2 = await userFactory.create({ name: 'U2' }, tx)
|
|
120
|
-
const u3 = await userFactory.create({ name: 'U3' }, tx)
|
|
121
|
-
|
|
122
|
-
await domainOwnerFactory.grant(domain, u1, {}, tx)
|
|
123
|
-
await domainOwnerFactory.grant(domain, u2, {}, tx)
|
|
124
|
-
await domainOwnerFactory.grant(domain, u3, {}, tx)
|
|
125
|
-
|
|
126
|
-
const count = await tx.count('DomainOwner', {
|
|
127
|
-
where: { domain: { id: domain.id } }
|
|
128
|
-
})
|
|
129
|
-
expect(count).toBe(3)
|
|
130
|
-
})
|
|
131
|
-
})
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
describe('CASCADE on delete', () => {
|
|
135
|
-
it('User 삭제 시 관련 DomainOwner 엔트리가 자동 제거된다', async () => {
|
|
136
|
-
// 트랜잭션 밖에서 실행 — CASCADE 동작이 FK 제약에 달려있고
|
|
137
|
-
// sqlite 트랜잭션 롤백에서 깔끔하게 관측하기 어려움.
|
|
138
|
-
const ds = testDb.getDataSource()
|
|
139
|
-
|
|
140
|
-
const domain = await domainFactory.create({ name: 'cascade-user' })
|
|
141
|
-
const user = await userFactory.create({ name: 'to-delete' })
|
|
142
|
-
const entry = await domainOwnerFactory.grant(domain, user)
|
|
143
|
-
|
|
144
|
-
expect(
|
|
145
|
-
await ds.manager.count('DomainOwner', { where: { id: entry.id } })
|
|
146
|
-
).toBe(1)
|
|
147
|
-
|
|
148
|
-
await ds.manager.delete('User', { id: user.id })
|
|
149
|
-
|
|
150
|
-
expect(
|
|
151
|
-
await ds.manager.count('DomainOwner', { where: { id: entry.id } })
|
|
152
|
-
).toBe(0)
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
it('Domain 삭제 시 관련 DomainOwner 엔트리가 자동 제거된다', async () => {
|
|
156
|
-
const ds = testDb.getDataSource()
|
|
157
|
-
|
|
158
|
-
const domain = await domainFactory.create({ name: 'cascade-domain' })
|
|
159
|
-
const u1 = await userFactory.create({ name: 'co-owner-1' })
|
|
160
|
-
const u2 = await userFactory.create({ name: 'co-owner-2' })
|
|
161
|
-
await domainOwnerFactory.grant(domain, u1)
|
|
162
|
-
await domainOwnerFactory.grant(domain, u2)
|
|
163
|
-
|
|
164
|
-
expect(
|
|
165
|
-
await ds.manager.count('DomainOwner', {
|
|
166
|
-
where: { domain: { id: domain.id } }
|
|
167
|
-
})
|
|
168
|
-
).toBe(2)
|
|
169
|
-
|
|
170
|
-
await ds.manager.delete('Domain', { id: domain.id })
|
|
171
|
-
|
|
172
|
-
expect(
|
|
173
|
-
await ds.manager.count('DomainOwner', {
|
|
174
|
-
where: { domain: { id: domain.id } }
|
|
175
|
-
})
|
|
176
|
-
).toBe(0)
|
|
177
|
-
})
|
|
178
|
-
})
|
|
179
|
-
})
|