@contember/engine-tenant-api 1.3.0-beta.4 → 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.
Files changed (43) hide show
  1. package/dist/src/TenantContainer.js +1 -1
  2. package/dist/src/TenantContainer.js.map +1 -1
  3. package/dist/src/model/authorization/Identity.d.ts +1 -0
  4. package/dist/src/model/authorization/Identity.d.ts.map +1 -1
  5. package/dist/src/model/authorization/Identity.js +5 -1
  6. package/dist/src/model/authorization/Identity.js.map +1 -1
  7. package/dist/src/model/authorization/PermissionActions.d.ts +6 -0
  8. package/dist/src/model/authorization/PermissionActions.d.ts.map +1 -1
  9. package/dist/src/model/authorization/PermissionActions.js +2 -0
  10. package/dist/src/model/authorization/PermissionActions.js.map +1 -1
  11. package/dist/src/model/authorization/PermissionsFactory.d.ts.map +1 -1
  12. package/dist/src/model/authorization/PermissionsFactory.js +3 -1
  13. package/dist/src/model/authorization/PermissionsFactory.js.map +1 -1
  14. package/dist/src/model/authorization/Roles.d.ts +2 -1
  15. package/dist/src/model/authorization/Roles.d.ts.map +1 -1
  16. package/dist/src/model/authorization/Roles.js +1 -0
  17. package/dist/src/model/authorization/Roles.js.map +1 -1
  18. package/dist/src/model/service/InviteManager.d.ts +4 -2
  19. package/dist/src/model/service/InviteManager.d.ts.map +1 -1
  20. package/dist/src/model/service/InviteManager.js +9 -3
  21. package/dist/src/model/service/InviteManager.js.map +1 -1
  22. package/dist/src/model/service/ProjectMemberManager.d.ts.map +1 -1
  23. package/dist/src/model/service/ProjectMemberManager.js +12 -10
  24. package/dist/src/model/service/ProjectMemberManager.js.map +1 -1
  25. package/dist/src/resolvers/types/IdentityTypeResolver.d.ts.map +1 -1
  26. package/dist/src/resolvers/types/IdentityTypeResolver.js +1 -0
  27. package/dist/src/resolvers/types/IdentityTypeResolver.js.map +1 -1
  28. package/dist/src/schema/index.d.ts +2 -0
  29. package/dist/src/schema/index.d.ts.map +1 -1
  30. package/dist/src/schema/tenant.graphql.js +1 -1
  31. package/dist/src/tsconfig.tsbuildinfo +1 -1
  32. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +12 -12
  34. package/src/TenantContainer.ts +2 -2
  35. package/src/model/authorization/Identity.ts +4 -1
  36. package/src/model/authorization/PermissionActions.ts +2 -0
  37. package/src/model/authorization/PermissionsFactory.ts +4 -1
  38. package/src/model/authorization/Roles.ts +1 -0
  39. package/src/model/service/InviteManager.ts +10 -3
  40. package/src/model/service/ProjectMemberManager.ts +16 -17
  41. package/src/resolvers/types/IdentityTypeResolver.ts +4 -2
  42. package/src/schema/index.ts +2 -0
  43. 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-beta.4",
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-beta.4",
17
- "@contember/config-loader": "1.3.0-beta.4",
18
- "@contember/database": "1.3.0-beta.4",
19
- "@contember/database-migrations": "1.3.0-beta.4",
20
- "@contember/dic": "1.3.0-beta.4",
21
- "@contember/graphql-utils": "1.3.0-beta.4",
22
- "@contember/logger": "1.3.0-beta.4",
23
- "@contember/schema": "1.3.0-beta.4",
24
- "@contember/schema-utils": "1.3.0-beta.4",
25
- "@contember/typesafe": "1.3.0-beta.4",
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-beta.4",
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",
@@ -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
- return await this.memberManager.getEffectiveProjectMemberships(this.dbContext, { slug: projectSlug }, this)
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
  }
@@ -6,4 +6,5 @@ export enum TenantRole {
6
6
  PROJECT_CREATOR = 'project_creator',
7
7
  PROJECT_MEMBER = 'project_member',
8
8
  PROJECT_ADMIN = 'project_admin',
9
+ ENTRYPOINT_DEPLOYER = 'entrypoint_deployer',
9
10
  }
@@ -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, INVITATION_RESET_TOKEN_EXPIRATION_MINUTES))
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, INVITATION_RESET_TOKEN_EXPIRATION_MINUTES))
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 = await Promise.all(
118
- memberships.map(async membership => {
119
- if (!(await verifier(PermissionActions.PROJECT_VIEW_MEMBER([{ role: membership.role, variables: [] }])))) {
120
- return null
121
- }
122
- const variables = await Promise.all(
123
- membership.variables.map(async variable => {
124
- const values = await this.filterProjectMembershipVariableValues(membership, variable, verifier)
125
- return { name: variable.name, values }
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
- return { ...membership, variables }
132
- }),
133
- )
134
- return filteredMemberships.filter(notEmpty)
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
  }
@@ -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 {