@contember/engine-tenant-api 1.3.0-rc.1 → 1.3.0-rc.2
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/src/TenantContainer.js +1 -1
- package/dist/src/TenantContainer.js.map +1 -1
- package/dist/src/model/authorization/Identity.d.ts +1 -0
- package/dist/src/model/authorization/Identity.d.ts.map +1 -1
- package/dist/src/model/authorization/Identity.js +5 -1
- package/dist/src/model/authorization/Identity.js.map +1 -1
- package/dist/src/model/authorization/PermissionActions.d.ts +6 -0
- package/dist/src/model/authorization/PermissionActions.d.ts.map +1 -1
- package/dist/src/model/authorization/PermissionActions.js +2 -0
- package/dist/src/model/authorization/PermissionActions.js.map +1 -1
- package/dist/src/model/authorization/PermissionsFactory.d.ts.map +1 -1
- package/dist/src/model/authorization/PermissionsFactory.js +3 -1
- package/dist/src/model/authorization/PermissionsFactory.js.map +1 -1
- package/dist/src/model/authorization/Roles.d.ts +2 -1
- package/dist/src/model/authorization/Roles.d.ts.map +1 -1
- package/dist/src/model/authorization/Roles.js +1 -0
- package/dist/src/model/authorization/Roles.js.map +1 -1
- package/dist/src/model/service/InviteManager.d.ts +4 -2
- package/dist/src/model/service/InviteManager.d.ts.map +1 -1
- package/dist/src/model/service/InviteManager.js +9 -3
- package/dist/src/model/service/InviteManager.js.map +1 -1
- package/dist/src/model/service/ProjectMemberManager.d.ts.map +1 -1
- package/dist/src/model/service/ProjectMemberManager.js +12 -10
- package/dist/src/model/service/ProjectMemberManager.js.map +1 -1
- package/dist/src/resolvers/types/IdentityTypeResolver.d.ts.map +1 -1
- package/dist/src/resolvers/types/IdentityTypeResolver.js +1 -0
- package/dist/src/resolvers/types/IdentityTypeResolver.js.map +1 -1
- package/dist/src/schema/index.d.ts +2 -0
- package/dist/src/schema/index.d.ts.map +1 -1
- package/dist/src/schema/tenant.graphql.js +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/src/TenantContainer.ts +2 -2
- package/src/model/authorization/Identity.ts +4 -1
- package/src/model/authorization/PermissionActions.ts +2 -0
- package/src/model/authorization/PermissionsFactory.ts +4 -1
- package/src/model/authorization/Roles.ts +1 -0
- package/src/model/service/InviteManager.ts +10 -3
- package/src/model/service/ProjectMemberManager.ts +16 -17
- package/src/resolvers/types/IdentityTypeResolver.ts +4 -2
- package/src/schema/index.ts +2 -0
- package/src/schema/tenant.graphql.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/engine-tenant-api",
|
|
3
|
-
"version": "1.3.0-rc.
|
|
3
|
+
"version": "1.3.0-rc.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"typings": "dist/src/index.d.ts",
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
"test": "vitest --dir ./tests/cases --no-threads"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@contember/authorization": "1.3.0-rc.
|
|
17
|
-
"@contember/config-loader": "1.3.0-rc.
|
|
18
|
-
"@contember/database": "1.3.0-rc.
|
|
19
|
-
"@contember/database-migrations": "1.3.0-rc.
|
|
20
|
-
"@contember/dic": "1.3.0-rc.
|
|
21
|
-
"@contember/graphql-utils": "1.3.0-rc.
|
|
22
|
-
"@contember/logger": "1.3.0-rc.
|
|
23
|
-
"@contember/schema": "1.3.0-rc.
|
|
24
|
-
"@contember/schema-utils": "1.3.0-rc.
|
|
25
|
-
"@contember/typesafe": "1.3.0-rc.
|
|
16
|
+
"@contember/authorization": "1.3.0-rc.2",
|
|
17
|
+
"@contember/config-loader": "1.3.0-rc.2",
|
|
18
|
+
"@contember/database": "1.3.0-rc.2",
|
|
19
|
+
"@contember/database-migrations": "1.3.0-rc.2",
|
|
20
|
+
"@contember/dic": "1.3.0-rc.2",
|
|
21
|
+
"@contember/graphql-utils": "1.3.0-rc.2",
|
|
22
|
+
"@contember/logger": "1.3.0-rc.2",
|
|
23
|
+
"@contember/schema": "1.3.0-rc.2",
|
|
24
|
+
"@contember/schema-utils": "1.3.0-rc.2",
|
|
25
|
+
"@contember/typesafe": "1.3.0-rc.2",
|
|
26
26
|
"@graphql-tools/utils": "^8.6.13",
|
|
27
27
|
"graphql-tag": "^2.12.5",
|
|
28
28
|
"jose": "^4.10",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"otpauth": "^7.0.6"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@contember/database-tester": "1.3.0-rc.
|
|
35
|
+
"@contember/database-tester": "1.3.0-rc.2",
|
|
36
36
|
"@graphql-codegen/cli": "^2.6.2",
|
|
37
37
|
"@graphql-codegen/typescript": "^2.5.1",
|
|
38
38
|
"@graphql-codegen/typescript-operations": "^2.4.2",
|
package/src/TenantContainer.ts
CHANGED
|
@@ -183,8 +183,8 @@ export class TenantContainerFactory {
|
|
|
183
183
|
new SignInManager(apiKeyManager, providers, otpAuthenticator))
|
|
184
184
|
.addService('membershipValidator', ({ projectSchemaResolver }) =>
|
|
185
185
|
new MembershipValidator(projectSchemaResolver))
|
|
186
|
-
.addService('inviteManager', ({ providers, userMailer }) =>
|
|
187
|
-
new InviteManager(providers, userMailer))
|
|
186
|
+
.addService('inviteManager', ({ providers, userMailer, projectSchemaResolver }) =>
|
|
187
|
+
new InviteManager(providers, userMailer, projectSchemaResolver))
|
|
188
188
|
.addService('otpManager', ({ otpAuthenticator }) =>
|
|
189
189
|
new OtpManager(otpAuthenticator))
|
|
190
190
|
.addService('mailTemplateManager', () =>
|
|
@@ -22,6 +22,8 @@ export class StaticIdentity implements Identity {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export class ProjectAwareIdentity implements Identity {
|
|
25
|
+
private projectMembershipCache: Record<string, Promise<readonly Acl.Membership[]>> = {}
|
|
26
|
+
|
|
25
27
|
constructor(
|
|
26
28
|
public readonly id: string,
|
|
27
29
|
public readonly roles: readonly string[],
|
|
@@ -30,6 +32,7 @@ export class ProjectAwareIdentity implements Identity {
|
|
|
30
32
|
) {}
|
|
31
33
|
|
|
32
34
|
async getProjectMemberships(projectSlug: string): Promise<readonly Acl.Membership[]> {
|
|
33
|
-
|
|
35
|
+
this.projectMembershipCache[projectSlug] ??= this.memberManager.getEffectiveProjectMemberships(this.dbContext, { slug: projectSlug }, this)
|
|
36
|
+
return await this.projectMembershipCache[projectSlug]
|
|
34
37
|
}
|
|
35
38
|
}
|
|
@@ -4,6 +4,7 @@ import { Acl } from '@contember/schema'
|
|
|
4
4
|
namespace PermissionActions {
|
|
5
5
|
export enum Resources {
|
|
6
6
|
system = 'system',
|
|
7
|
+
entrypoint = 'entrypoint',
|
|
7
8
|
person = 'person',
|
|
8
9
|
identity = 'identity',
|
|
9
10
|
project = 'project',
|
|
@@ -37,6 +38,7 @@ namespace PermissionActions {
|
|
|
37
38
|
export const PROJECT_UPDATE = Authorizator.createAction(Resources.project, 'update')
|
|
38
39
|
|
|
39
40
|
export const PROJECT_CREATE = Authorizator.createAction(Resources.project, 'create')
|
|
41
|
+
export const ENTRYPOINT_DEPLOY = Authorizator.createAction(Resources.entrypoint, 'deployEntrypoint')
|
|
40
42
|
|
|
41
43
|
export const PROJECT_VIEW_MEMBER = (memberships: readonly Acl.Membership[]) => Authorizator.createAction(Resources.project, 'viewMembers', { memberships })
|
|
42
44
|
export const PROJECT_ADD_MEMBER = (memberships: readonly Acl.Membership[]) => Authorizator.createAction(Resources.project, 'addMember', { memberships })
|
|
@@ -2,7 +2,7 @@ import { Permissions } from '@contember/authorization'
|
|
|
2
2
|
import { PermissionActions } from './PermissionActions'
|
|
3
3
|
import { TenantRole } from './Roles'
|
|
4
4
|
|
|
5
|
-
const allowedRoles = new Set<string>([TenantRole.LOGIN, TenantRole.PROJECT_ADMIN])
|
|
5
|
+
const allowedRoles = new Set<string>([TenantRole.LOGIN, TenantRole.PROJECT_ADMIN, TenantRole.ENTRYPOINT_DEPLOYER])
|
|
6
6
|
|
|
7
7
|
const projectAdminAllowedInputRoles = ({ roles }: {roles?: readonly string[]}) => {
|
|
8
8
|
return roles === undefined || roles.every(it => allowedRoles.has(it))
|
|
@@ -58,7 +58,10 @@ class PermissionsFactory {
|
|
|
58
58
|
permissions.allow(TenantRole.PROJECT_ADMIN, PermissionActions.IDP_DISABLE)
|
|
59
59
|
permissions.allow(TenantRole.PROJECT_ADMIN, PermissionActions.IDP_ENABLE)
|
|
60
60
|
|
|
61
|
+
permissions.allow(TenantRole.PROJECT_ADMIN, PermissionActions.ENTRYPOINT_DEPLOY)
|
|
62
|
+
|
|
61
63
|
permissions.allow(TenantRole.PROJECT_CREATOR, PermissionActions.PROJECT_CREATE)
|
|
64
|
+
permissions.allow(TenantRole.ENTRYPOINT_DEPLOYER, PermissionActions.ENTRYPOINT_DEPLOY)
|
|
62
65
|
|
|
63
66
|
return permissions
|
|
64
67
|
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from '../commands'
|
|
8
8
|
import { Providers } from '../providers'
|
|
9
9
|
import { PersonQuery, PersonRow } from '../queries'
|
|
10
|
-
import { Project } from '../type'
|
|
10
|
+
import { Project, ProjectSchemaResolver } from '../type'
|
|
11
11
|
import { InviteErrorCode, InviteMethod } from '../../schema'
|
|
12
12
|
import { TenantRole } from '../authorization'
|
|
13
13
|
import { UserMailer } from '../mailing'
|
|
@@ -35,6 +35,7 @@ export class InviteManager {
|
|
|
35
35
|
constructor(
|
|
36
36
|
private readonly providers: Providers,
|
|
37
37
|
private readonly mailer: UserMailer,
|
|
38
|
+
private readonly projectSchemaResolver: ProjectSchemaResolver,
|
|
38
39
|
) {}
|
|
39
40
|
|
|
40
41
|
async invite(
|
|
@@ -70,11 +71,11 @@ export class InviteManager {
|
|
|
70
71
|
password: passwordWrapper,
|
|
71
72
|
}))
|
|
72
73
|
if (method === 'RESET_PASSWORD') {
|
|
73
|
-
const result = await trx.commandBus.execute(new CreatePasswordResetRequestCommand(person.id,
|
|
74
|
+
const result = await trx.commandBus.execute(new CreatePasswordResetRequestCommand(person.id, await this.getInviteExpiration(project.slug)))
|
|
74
75
|
resetToken = result.token
|
|
75
76
|
}
|
|
76
77
|
if (passwordResetTokenHash) {
|
|
77
|
-
await trx.commandBus.execute(new SavePasswordResetRequestCommand(person.id, passwordResetTokenHash,
|
|
78
|
+
await trx.commandBus.execute(new SavePasswordResetRequestCommand(person.id, passwordResetTokenHash, await this.getInviteExpiration(project.slug)))
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
for (const membershipUpdate of createAppendMembershipVariables(memberships)) {
|
|
@@ -98,6 +99,12 @@ export class InviteManager {
|
|
|
98
99
|
return new ResponseOk(new InviteResult(person, isNew))
|
|
99
100
|
})
|
|
100
101
|
}
|
|
102
|
+
|
|
103
|
+
private async getInviteExpiration(projectSlug: string): Promise<number> {
|
|
104
|
+
const schema = await this.projectSchemaResolver.getSchema(projectSlug)
|
|
105
|
+
|
|
106
|
+
return schema?.settings.tenant?.inviteExpirationMinutes ?? INVITATION_RESET_TOKEN_EXPIRATION_MINUTES
|
|
107
|
+
}
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
export type InviteResponse = Response<InviteResult, InviteErrorCode>
|
|
@@ -114,24 +114,23 @@ export class ProjectMemberManager {
|
|
|
114
114
|
memberships: readonly T[],
|
|
115
115
|
verifier: AccessVerifier,
|
|
116
116
|
): Promise<T[]> {
|
|
117
|
-
const filteredMemberships =
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
)
|
|
128
|
-
if (variables.find(it => it.values.length === 0)) {
|
|
129
|
-
return null
|
|
117
|
+
const filteredMemberships: T[] = []
|
|
118
|
+
nextMembership: for (const membership of memberships) {
|
|
119
|
+
if (!(await verifier(PermissionActions.PROJECT_VIEW_MEMBER([{ role: membership.role, variables: [] }])))) {
|
|
120
|
+
continue
|
|
121
|
+
}
|
|
122
|
+
const variables: { values: Acl.MembershipVariable['values']; name: string }[] = []
|
|
123
|
+
for (const variable of membership.variables) {
|
|
124
|
+
const values = await this.filterProjectMembershipVariableValues(membership, variable, verifier)
|
|
125
|
+
if (values.length === 0) {
|
|
126
|
+
continue nextMembership
|
|
130
127
|
}
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
variables.push({ name: variable.name, values })
|
|
129
|
+
}
|
|
130
|
+
filteredMemberships.push({ ...membership, variables })
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return filteredMemberships
|
|
135
134
|
}
|
|
136
135
|
|
|
137
136
|
private async filterProjectMembershipVariableValues(
|
|
@@ -117,6 +117,7 @@ export class IdentityTypeResolver implements IdentityResolvers {
|
|
|
117
117
|
if (parent.permissions) {
|
|
118
118
|
return parent.permissions
|
|
119
119
|
}
|
|
120
|
+
|
|
120
121
|
const permissionsContext = await (async () => {
|
|
121
122
|
const isSelf = parent.id === context.identity.id
|
|
122
123
|
if (isSelf) {
|
|
@@ -128,13 +129,14 @@ export class IdentityTypeResolver implements IdentityResolvers {
|
|
|
128
129
|
}
|
|
129
130
|
return this.permissionContextFactory.create(context.db, { id: parent.id, roles })
|
|
130
131
|
})()
|
|
132
|
+
|
|
131
133
|
if (!permissionsContext) {
|
|
132
134
|
return null
|
|
133
135
|
}
|
|
136
|
+
|
|
134
137
|
return {
|
|
135
138
|
canCreateProject: await permissionsContext.isAllowed({ action: PermissionActions.PROJECT_CREATE }),
|
|
139
|
+
canDeployEntrypoint: await permissionsContext.isAllowed({ action: PermissionActions.ENTRYPOINT_DEPLOY }),
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
142
|
}
|
package/src/schema/index.ts
CHANGED
|
@@ -405,6 +405,7 @@ export type Identity = {
|
|
|
405
405
|
export type IdentityGlobalPermissions = {
|
|
406
406
|
readonly __typename?: 'IdentityGlobalPermissions'
|
|
407
407
|
readonly canCreateProject: Scalars['Boolean']
|
|
408
|
+
readonly canDeployEntrypoint: Scalars['Boolean']
|
|
408
409
|
}
|
|
409
410
|
|
|
410
411
|
export type IdentityProjectRelation = {
|
|
@@ -1812,6 +1813,7 @@ export type IdentityResolvers<ContextType = any, ParentType extends ResolversPar
|
|
|
1812
1813
|
|
|
1813
1814
|
export type IdentityGlobalPermissionsResolvers<ContextType = any, ParentType extends ResolversParentTypes['IdentityGlobalPermissions'] = ResolversParentTypes['IdentityGlobalPermissions']> = {
|
|
1814
1815
|
canCreateProject?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>
|
|
1816
|
+
canDeployEntrypoint?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>
|
|
1815
1817
|
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>
|
|
1816
1818
|
}
|
|
1817
1819
|
|
|
@@ -546,7 +546,6 @@ const schema: DocumentNode = gql`
|
|
|
546
546
|
type AddGlobalIdentityRolesResult {
|
|
547
547
|
identity: Identity!
|
|
548
548
|
}
|
|
549
|
-
|
|
550
549
|
type AddGlobalIdentityRolesError {
|
|
551
550
|
code: AddGlobalIdentityRolesErrorCode!
|
|
552
551
|
developerMessage: String!
|
|
@@ -655,6 +654,7 @@ const schema: DocumentNode = gql`
|
|
|
655
654
|
|
|
656
655
|
type IdentityGlobalPermissions {
|
|
657
656
|
canCreateProject: Boolean!
|
|
657
|
+
canDeployEntrypoint: Boolean!
|
|
658
658
|
}
|
|
659
659
|
|
|
660
660
|
type IdentityProjectRelation {
|