Skip to content

Commit

Permalink
impl user and auth
Browse files Browse the repository at this point in the history
  • Loading branch information
wermarter committed Jan 22, 2024
1 parent 98a0bd3 commit 84babae
Show file tree
Hide file tree
Showing 48 changed files with 679 additions and 168 deletions.
4 changes: 3 additions & 1 deletion apps/hcdc-access-service/src/domain/entity/auth/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ import { BioProductAction } from '../bio-product/auth'
import { TestCategoryAction } from '../test-category/auth'
import { BranchAction } from '../branch/auth'
import { RoleAction } from '../role/auth'
import { UserAction } from '../user/auth'

export const AuthAction = {
BioProduct: stringEnumValues(BioProductAction),
TestCategory: stringEnumValues(TestCategoryAction),
Branch: stringEnumValues(BranchAction),
Role: stringEnumValues(RoleAction),
User: stringEnumValues(UserAction),
} satisfies Record<keyof typeof AuthSubject, string[]>

export const AuthActionValues = Object.values(AuthAction).flat()
export const AuthActionValues = [...new Set(Object.values(AuthAction).flat())]

export type AuthActionUnionType =
(typeof AuthAction)[AuthSubjectUnionType][number]
2 changes: 0 additions & 2 deletions apps/hcdc-access-service/src/domain/entity/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export * from './action'
export * from './subject'
export * from './utils'
export * from './entity'
export * from './example'
3 changes: 3 additions & 0 deletions apps/hcdc-access-service/src/domain/entity/auth/subject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { BioProduct } from '../bio-product'
import { TestCategory } from '../test-category'
import { Branch } from '../branch'
import { Role } from '../role'
import { User } from '../user'

// key-value must be identical for working with '@casl/mongoose'.accessibleBy()
export const AuthSubject = {
BioProduct: 'BioProduct',
TestCategory: 'TestCategory',
Branch: 'Branch',
Role: 'Role',
User: 'User',
} satisfies Record<keyof RecordTypes, keyof RecordTypes>

export type AuthSubjectUnionType = keyof typeof AuthSubject
Expand All @@ -24,4 +26,5 @@ export type SubjectEntityMapping = {
TestCategory: TestCategory
Branch: Branch
Role: Role
User: User
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import '@casl/mongoose'

export enum BioProductAction {
Manage = 'Manage',
Create = 'Create',
Read = 'Read',
Update = 'Update',
Expand Down
1 change: 0 additions & 1 deletion apps/hcdc-access-service/src/domain/entity/branch/auth.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import '@casl/mongoose'

export enum BranchAction {
Manage = 'Manage',
Create = 'Create',
Read = 'Read',
Update = 'Update',
Expand Down
1 change: 1 addition & 0 deletions apps/hcdc-access-service/src/domain/entity/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './test-category'
export * from './role'
export * from './user'
export * from './branch'
export * from './permission-rule'
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MongoQuery } from '@casl/ability'

import { AuthSubject, SubjectEntityMapping } from './subject'
import { AuthAction } from './action'
import { AuthSubject, SubjectEntityMapping } from '../auth/subject'
import { AuthAction } from '../auth/action'

export type PermissionRule<
TSubject extends keyof typeof AuthSubject = keyof typeof AuthSubject,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import { exampleMongoObjectId } from '@diut/nest-core'

import { EntityDataExample } from '../base-entity'
import { AuthActionValues } from './action'
import { AuthActionValues } from '../auth/action'
import { PermissionRule } from './entity'
import { AuthSubjectValues } from './subject'
import { AuthSubjectValues } from '../auth/subject'

export const examplePermissionRule = {
subject: {
example: AuthSubjectValues[0],
enum: AuthSubjectValues,
},
action: {
example: AuthActionValues[0],
enum: AuthActionValues,
},
inverted: {
example: false,
default: false,
required: false,
},
conditions: {
example: { _id: { $eq: exampleMongoObjectId.example } },
example: {},
},
} satisfies EntityDataExample<PermissionRule>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './entity'
export * from './example'
2 changes: 1 addition & 1 deletion apps/hcdc-access-service/src/domain/entity/role/entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BaseEntity } from '../base-entity'
import { Branch } from '../branch'
import { PermissionRule } from '../auth'
import { PermissionRule } from '../permission-rule'

export type Role = BaseEntity & {
index: number
Expand Down
15 changes: 15 additions & 0 deletions apps/hcdc-access-service/src/domain/entity/user/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import '@casl/mongoose'

export enum UserAction {
Manage = 'Manage',
Create = 'Create',
Read = 'Read',
Update = 'Update',
Delete = 'Delete',
}

declare module '@casl/mongoose' {
interface RecordTypes {
User: true
}
}
10 changes: 6 additions & 4 deletions apps/hcdc-access-service/src/domain/entity/user/entity.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PermissionRule } from '../permission-rule'
import { BaseEntity } from '../base-entity'
import { Branch } from '../branch'
// import { Permission } from './permission'
// import { Role } from './role'
import { Role } from '../role'

export type User = BaseEntity & {
username: string
Expand All @@ -10,9 +10,11 @@ export type User = BaseEntity & {
name: string
phoneNumber: string

inlinePermissions: PermissionRule[]

branchIds: string[]
branches?: (Branch | null)[]

// roles: string[] | Role[]
// inlinePermissions: string[] | Permission[]
roleIds: string[]
roles?: (Role | null)[]
}
17 changes: 12 additions & 5 deletions apps/hcdc-access-service/src/domain/entity/user/example.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import { exampleMongoObjectIds } from '@diut/nest-core'

import { EntityDataExample, extractExampleEntity } from '../base-entity'
import { EntityDataExample } from '../base-entity'
import { User } from './entity'
import { exampleBranch } from '../branch'

export const exampleUser = {
username: {
example: 'levana',
},
passwordHash: {
example: 'hashed_password',
},
name: {
example: 'Lê Văn A',
},
phoneNumber: {
example: '1234567890',
},
passwordHash: {
example: 'hashed_password',
inlinePermissions: {
isArray: true,
},
branchIds: exampleMongoObjectIds,
branches: {
example: [extractExampleEntity(exampleBranch)],
required: false,
isArray: true,
},
roleIds: exampleMongoObjectIds,
roles: {
required: false,
isArray: true,
},
} satisfies EntityDataExample<User>
1 change: 1 addition & 0 deletions apps/hcdc-access-service/src/domain/entity/user/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './entity'
export * from './example'
export * from './auth'
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createMongoAbility } from '@casl/ability'
import { Inject } from '@nestjs/common'

import { AuthSubject, BioProductAction } from 'src/domain/entity'
import { PermissionRule, Role } from 'src/domain/entity'
import { EAuthnPayloadUserNotFound } from 'src/domain/exception'
import {
AuthContextData,
Expand All @@ -19,54 +19,24 @@ export class AuthPopulateContextUseCase {
async execute(input: AuthPayload): Promise<AuthContextData> {
const user = await this.userRepository.findOne({
filter: { _id: input.userId },
populates: [{ path: 'branches' }],
populates: [{ path: 'roles', fields: ['permissions'] as (keyof Role)[] }],
})

if (!user) {
throw new EAuthnPayloadUserNotFound()
}

const rolesPermissions: PermissionRule[] = []
user.roles?.forEach((role) => {
if (role != null) {
rolesPermissions.push(...role.permissions)
}
})
const { inlinePermissions } = user
const permissions = [...rolesPermissions, ...inlinePermissions]

// create from user role and direct ability
const ability = createMongoAbility([
{
action: 'manage',
subject: 'all',
},
{
action: BioProductAction.Create,
subject: AuthSubject.BioProduct,
conditions: {
index: {
$lt: 1000,
},
},
},
{
action: BioProductAction.Read,
subject: AuthSubject.BioProduct,
conditions: {
index: {
$gt: 0,
},
},
},
{
action: BioProductAction.Update,
subject: AuthSubject.BioProduct,
conditions: {
index: {
$gt: 2,
},
},
},
{
action: BioProductAction.Delete,
subject: AuthSubject.BioProduct,
conditions: {
index: 5,
},
},
])
const ability = createMongoAbility(permissions)

return { user, ability }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Inject, Injectable } from '@nestjs/common'

import {
AuthSubject,
BioProduct,
BioProductAction,
assertPermission,
Expand Down Expand Up @@ -28,7 +29,12 @@ export class BioProductFindOneUseCase {
const entity = await this.bioProductRepository.findOne(input)

if (entity != null) {
assertPermission(ability, 'BioProduct', BioProductAction.Read, entity)
assertPermission(
ability,
AuthSubject.BioProduct,
BioProductAction.Read,
entity,
)
}

return entity
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Inject, Injectable } from '@nestjs/common'

import { Branch, BranchAction, assertPermission } from 'src/domain/entity'
import {
AuthSubject,
Branch,
BranchAction,
assertPermission,
} from 'src/domain/entity'
import {
AuthContextToken,
BranchRepositoryToken,
Expand All @@ -24,7 +29,7 @@ export class BranchFindOneUseCase {
const entity = await this.branchRepository.findOne(input)

if (entity != null) {
assertPermission(ability, 'Branch', BranchAction.Read, entity)
assertPermission(ability, AuthSubject.Branch, BranchAction.Read, entity)
}

return entity
Expand Down
7 changes: 7 additions & 0 deletions apps/hcdc-access-service/src/domain/use-case/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,10 @@ export * from './branch/update'
export * from './branch/delete'
export * from './branch/search'
export * from './branch/assert-exists'

export * from './user/create'
export * from './user/find-one'
export * from './user/update'
export * from './user/delete'
export * from './user/search'
export * from './user/assert-exists'
15 changes: 14 additions & 1 deletion apps/hcdc-access-service/src/domain/use-case/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ import { BranchDeleteUseCase } from './branch/delete'
import { BranchSearchUseCase } from './branch/search'
import { BranchAssertExistsUseCase } from './branch/assert-exists'

import { AuthMeUseCase } from './auth/me'
import { UserCreateUseCase } from './user/create'
import { UserUpdateUseCase } from './user/update'
import { UserFindOneUseCase } from './user/find-one'
import { UserDeleteUseCase } from './user/delete'
import { UserSearchUseCase } from './user/search'
import { UserAssertExistsUseCase } from './user/assert-exists'

import { AuthMeUseCase } from './auth/me'
import { AuthLoginUseCase } from './auth/login'
import { AuthPopulateContextUseCase } from './auth/populate-context'

Expand Down Expand Up @@ -54,5 +60,12 @@ export const useCaseMetadata: ModuleMetadata = {
RoleDeleteUseCase,
RoleSearchUseCase,
RoleAssertExistsUseCase,

UserCreateUseCase,
UserFindOneUseCase,
UserUpdateUseCase,
UserDeleteUseCase,
UserSearchUseCase,
UserAssertExistsUseCase,
],
}
10 changes: 7 additions & 3 deletions apps/hcdc-access-service/src/domain/use-case/role/find-one.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Inject, Injectable } from '@nestjs/common'

import { Role, RoleAction, assertPermission } from 'src/domain/entity'
import {
AuthSubject,
Role,
RoleAction,
assertPermission,
} from 'src/domain/entity'
import {
AuthContextToken,
RoleRepositoryToken,
Expand All @@ -24,8 +29,7 @@ export class RoleFindOneUseCase {
const entity = await this.roleRepository.findOne(input)

if (entity != null) {
// TODO: check this in assert exists
assertPermission(ability, 'Role', RoleAction.Read, entity)
assertPermission(ability, AuthSubject.Role, RoleAction.Read, entity)
}

return entity
Expand Down
Loading

0 comments on commit 84babae

Please sign in to comment.