-
Notifications
You must be signed in to change notification settings - Fork 0
chore/casl orphan s3, upload etc #164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
cea6a99
chore: casl setting
rklpoi5678 918c8d0
chore: casl type action setting
rklpoi5678 3d4844d
feat: student-posts.ability
rklpoi5678 287f13f
fix: remove upload router
rklpoi5678 3ad2543
refactor: orphan s3 object shields and cale rbac refactor
rklpoi5678 2d9b030
refactor: upload multer
rklpoi5678 b16bafd
feat: for orphan s3 object
rklpoi5678 db87717
refactor: orphan s3 object defance
rklpoi5678 bbb81c7
feat: mock object
rklpoi5678 1db21c2
fix: non-null assertion on effectiveInstructorId may cause unexpectedโฆ
rklpoi5678 82cd98c
fix: risk of data loss if new file upload fails after deleting old atโฆ
rklpoi5678 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import type { PureAbility } from '@casl/ability'; | ||
| import type { PrismaQuery } from '@casl/prisma'; | ||
| import type { Action } from './actions.js'; | ||
| import type { AppSubjects } from './subjects.js'; | ||
|
|
||
| export type AppAbility = PureAbility<[Action, AppSubjects], PrismaQuery>; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| export const Action = { | ||
| Create: 'create', | ||
| Read: 'read', | ||
| Update: 'update', | ||
| Delete: 'delete', | ||
| UpdateStatus: 'updateStatus', | ||
| List: 'list', | ||
| Manage: 'manage', | ||
| } as const; | ||
|
|
||
| export type Action = (typeof Action)[keyof typeof Action]; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| import { subject } from '@casl/ability'; | ||
| import { defineStudentPostAbility } from './student-post.ability.js'; | ||
| import { Action } from './actions.js'; | ||
| import { UserType } from '../constants/auth.constant.js'; | ||
| import { AuthorRole } from '../constants/posts.constant.js'; | ||
|
|
||
| describe('StudentPost Ability', () => { | ||
| describe('STUDENT ์ญํ ', () => { | ||
| const ability = defineStudentPostAbility({ | ||
| userType: UserType.STUDENT, | ||
| profileId: 'student-1', | ||
| enrollmentIds: ['enrollment-1', 'enrollment-2'], | ||
| }); | ||
|
|
||
| it('์๊ธฐ enrollment์ STUDENT ๊ธ์ ์ฝ์ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| enrollmentId: 'enrollment-1', | ||
| authorRole: AuthorRole.STUDENT, | ||
| instructorId: 'inst-1', | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Read, post)).toBe(true); | ||
| }); | ||
|
|
||
| it('ํ์ธ enrollment์ ๊ธ์ ์ฝ์ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| enrollmentId: 'other-enrollment', | ||
| authorRole: AuthorRole.STUDENT, | ||
| instructorId: 'inst-1', | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Read, post)).toBe(false); | ||
| }); | ||
|
|
||
| it('PARENT๊ฐ ์์ฑํ ๊ธ์ ์ฝ์ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| enrollmentId: 'enrollment-1', | ||
| authorRole: AuthorRole.PARENT, | ||
| instructorId: 'inst-1', | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Read, post)).toBe(false); | ||
| }); | ||
|
|
||
| it('๊ธ์ ์ญ์ ํ ์ ์๋ค (์๊ธฐ enrollment)', () => { | ||
| const post = subject('StudentPost', { | ||
| enrollmentId: 'enrollment-1', | ||
| authorRole: AuthorRole.STUDENT, | ||
| instructorId: 'inst-1', | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Delete, post)).toBe(true); | ||
| }); | ||
| }); | ||
|
|
||
| describe('INSTRUCTOR ์ญํ ', () => { | ||
| const ability = defineStudentPostAbility({ | ||
| userType: UserType.INSTRUCTOR, | ||
| profileId: 'inst-1', | ||
| effectiveInstructorId: 'inst-1', | ||
| }); | ||
|
|
||
| it('๋ด๋น ํ์์ ๊ธ์ ์ฝ์ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| instructorId: 'inst-1', | ||
| enrollmentId: 'enrollment-1', | ||
| authorRole: AuthorRole.STUDENT, | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Read, post)).toBe(true); | ||
| }); | ||
|
|
||
| it('๊ธ์ ์์ /์ญ์ ํ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| instructorId: 'inst-1', | ||
| enrollmentId: 'enrollment-1', | ||
| authorRole: AuthorRole.STUDENT, | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Update, post)).toBe(false); | ||
| expect(ability.can(Action.Delete, post)).toBe(false); | ||
| }); | ||
|
|
||
| it('ํ ๊ฐ์ฌ ๋ด๋น ๊ธ์ ์ฝ์ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| instructorId: 'other-inst', | ||
| enrollmentId: 'enrollment-1', | ||
| authorRole: AuthorRole.STUDENT, | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Read, post)).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| describe('ASSISTANT ์ญํ ', () => { | ||
| const ability = defineStudentPostAbility({ | ||
| userType: UserType.ASSISTANT, | ||
| profileId: 'assi-1', | ||
| effectiveInstructorId: 'inst-1', // ์กฐ๊ต๊ฐ ๋ด๋นํ๋ ๊ฐ์ฌ ID | ||
| }); | ||
|
|
||
| it('๋ด๋น ๊ฐ์ฌ์ ๊ธ์ ์ฝ์ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| instructorId: 'inst-1', | ||
| enrollmentId: 'enrollment-1', | ||
| authorRole: AuthorRole.STUDENT, | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Read, post)).toBe(true); | ||
| }); | ||
|
|
||
| it('ํ ๊ฐ์ฌ ๋ด๋น ๊ธ์ ์ฝ์ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| instructorId: 'other-inst', | ||
| enrollmentId: 'enrollment-1', | ||
| authorRole: AuthorRole.STUDENT, | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Read, post)).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| describe('PARENT ์ญํ ', () => { | ||
| const ability = defineStudentPostAbility({ | ||
| userType: UserType.PARENT, | ||
| profileId: 'parent-1', | ||
| parentEnrollmentIds: ['enrollment-1'], | ||
| }); | ||
|
|
||
| it('์๋ enrollment์ PARENT ๊ธ์ ์ฝ์ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| enrollmentId: 'enrollment-1', | ||
| authorRole: AuthorRole.PARENT, | ||
| instructorId: 'inst-1', | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Read, post)).toBe(true); | ||
| }); | ||
|
|
||
| it('์๋ enrollment์ STUDENT ๊ธ์ ์ฝ์ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| enrollmentId: 'enrollment-1', | ||
| authorRole: AuthorRole.STUDENT, | ||
| instructorId: 'inst-1', | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Read, post)).toBe(false); | ||
| }); | ||
|
|
||
| it('ํ์ธ ์๋ enrollment์ ๊ธ์ ์ฝ์ ์ ์๋ค', () => { | ||
| const post = subject('StudentPost', { | ||
| enrollmentId: 'other-enrollment', | ||
| authorRole: AuthorRole.PARENT, | ||
| instructorId: 'inst-1', | ||
| } as Record<string, unknown>); | ||
| expect(ability.can(Action.Read, post)).toBe(false); | ||
| }); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| import { AbilityBuilder } from '@casl/ability'; | ||
| import { createPrismaAbility } from '@casl/prisma'; | ||
| import { Action as A } from './actions.js'; | ||
| import type { UserType } from '../constants/auth.constant.js'; | ||
| import { UserType as UT } from '../constants/auth.constant.js'; | ||
| import { AuthorRole } from '../constants/posts.constant.js'; | ||
| import type { AppAbility } from './ability.types.js'; | ||
|
|
||
| export interface AbilityContext { | ||
| userType: UserType; | ||
| profileId: string; | ||
| enrollmentIds?: string[]; | ||
| effectiveInstructorId?: string; | ||
| parentEnrollmentIds?: string[]; | ||
| } | ||
|
|
||
| export function defineStudentPostAbility(ctx: AbilityContext): AppAbility { | ||
| const { can, build } = new AbilityBuilder<AppAbility>(createPrismaAbility); | ||
|
|
||
| switch (ctx.userType) { | ||
| case UT.STUDENT: { | ||
| const condition = { | ||
| enrollmentId: { in: ctx.enrollmentIds ?? [] }, | ||
| authorRole: AuthorRole.STUDENT, | ||
| }; | ||
| can(A.Create, 'StudentPost', condition); | ||
| can(A.Read, 'StudentPost', condition); | ||
| can(A.Update, 'StudentPost', condition); | ||
| can(A.Delete, 'StudentPost', condition); | ||
| can(A.UpdateStatus, 'StudentPost', condition); | ||
| can(A.List, 'StudentPost', condition); | ||
| break; | ||
| } | ||
|
|
||
| case UT.INSTRUCTOR: { | ||
| if (!ctx.effectiveInstructorId) { | ||
| // Retrun empty ability - no permissions granted | ||
| break; | ||
| } | ||
| const condition = { instructorId: ctx.effectiveInstructorId }; | ||
| can(A.Read, 'StudentPost', condition); | ||
| can(A.List, 'StudentPost', condition); | ||
| break; | ||
| } | ||
|
|
||
| case UT.ASSISTANT: { | ||
| if (!ctx.effectiveInstructorId) { | ||
| break; | ||
| } | ||
| const condition = { instructorId: ctx.effectiveInstructorId }; | ||
| can(A.Read, 'StudentPost', condition); | ||
| can(A.List, 'StudentPost', condition); | ||
| break; | ||
| } | ||
|
|
||
| case UT.PARENT: { | ||
| const condition = { | ||
| enrollmentId: { in: ctx.parentEnrollmentIds ?? [] }, | ||
| authorRole: AuthorRole.PARENT, | ||
| }; | ||
| can(A.Create, 'StudentPost', condition); | ||
| can(A.Read, 'StudentPost', condition); | ||
| can(A.Update, 'StudentPost', condition); | ||
| can(A.Delete, 'StudentPost', condition); | ||
| can(A.UpdateStatus, 'StudentPost', condition); | ||
| can(A.List, 'StudentPost', condition); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| return build(); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import type { StudentPost } from '../generated/prisma/client.js'; | ||
| import type { Subjects as PrismaSubjects } from '@casl/prisma'; | ||
|
|
||
| export type AppSubjects = | ||
| | PrismaSubjects<{ | ||
| StudentPost: StudentPost; | ||
| }> | ||
| | 'all'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.