feat: enforce user roles
enforces user admin role requirement for - creating / deleting / setting role for organization users - creating / deleting / setting role for project users - updating project name - deleting project hides action elements based on role for - admin console - team settings if team is only visible through project membership - add project tile if not team admin - project name text editor if not team / project admin - add redirect from team page if settings only visible through project membership - add redirect from admin console if not org admin role enforcement is handled on the api side through a custom GraphQL directive `hasRole`. on the client side, role information is fetched in the TopNavbar's `me` query and stored in the `UserContext`. there is a custom hook, `useCurrentUser`, that provides a user object with two functions, `isVisibile` & `isAdmin` which is used to check roles in order to render/hide relevant UI elements.
This commit is contained in:
committed by
Jordan Knott
parent
5dbdc20b36
commit
e64f6f8569
@ -97,7 +97,6 @@ type Project {
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
team: Team!
|
||||
owner: Member!
|
||||
taskGroups: [TaskGroup!]!
|
||||
members: [Member!]!
|
||||
labels: [ProjectLabel!]!
|
||||
|
@ -1,3 +1,23 @@
|
||||
enum RoleLevel {
|
||||
ADMIN
|
||||
MEMBER
|
||||
}
|
||||
|
||||
enum ActionLevel {
|
||||
ORG
|
||||
TEAM
|
||||
PROJECT
|
||||
}
|
||||
|
||||
enum ObjectType {
|
||||
ORG
|
||||
TEAM
|
||||
PROJECT
|
||||
TASK
|
||||
}
|
||||
|
||||
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
|
||||
|
||||
type Query {
|
||||
organizations: [Organization!]!
|
||||
users: [UserAccount!]!
|
||||
@ -9,11 +29,27 @@ type Query {
|
||||
teams: [Team!]!
|
||||
labelColors: [LabelColor!]!
|
||||
taskGroups: [TaskGroup!]!
|
||||
me: UserAccount!
|
||||
me: MePayload!
|
||||
}
|
||||
|
||||
type Mutation
|
||||
|
||||
type TeamRole {
|
||||
teamID: UUID!
|
||||
roleCode: RoleCode!
|
||||
}
|
||||
|
||||
type ProjectRole {
|
||||
projectID: UUID!
|
||||
roleCode: RoleCode!
|
||||
}
|
||||
|
||||
type MePayload {
|
||||
user: UserAccount!
|
||||
teamRoles: [TeamRole!]!
|
||||
projectRoles: [ProjectRole!]!
|
||||
}
|
||||
|
||||
input ProjectsFilter {
|
||||
teamID: UUID
|
||||
}
|
||||
@ -23,7 +59,7 @@ input FindUser {
|
||||
}
|
||||
|
||||
input FindProject {
|
||||
projectId: String!
|
||||
projectID: UUID!
|
||||
}
|
||||
|
||||
input FindTask {
|
||||
|
@ -1,7 +1,9 @@
|
||||
extend type Mutation {
|
||||
createProject(input: NewProject!): Project!
|
||||
deleteProject(input: DeleteProject!): DeleteProjectPayload!
|
||||
updateProjectName(input: UpdateProjectName): Project!
|
||||
createProject(input: NewProject!): Project! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM)
|
||||
deleteProject(input: DeleteProject!):
|
||||
DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateProjectName(input: UpdateProjectName):
|
||||
Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
}
|
||||
|
||||
input NewProject {
|
||||
|
@ -1,9 +1,14 @@
|
||||
extend type Mutation {
|
||||
createProjectLabel(input: NewProjectLabel!): ProjectLabel!
|
||||
deleteProjectLabel(input: DeleteProjectLabel!): ProjectLabel!
|
||||
updateProjectLabel(input: UpdateProjectLabel!): ProjectLabel!
|
||||
updateProjectLabelName(input: UpdateProjectLabelName!): ProjectLabel!
|
||||
updateProjectLabelColor(input: UpdateProjectLabelColor!): ProjectLabel!
|
||||
createProjectLabel(input: NewProjectLabel!):
|
||||
ProjectLabel! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
deleteProjectLabel(input: DeleteProjectLabel!):
|
||||
ProjectLabel! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateProjectLabel(input: UpdateProjectLabel!):
|
||||
ProjectLabel! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateProjectLabelName(input: UpdateProjectLabelName!):
|
||||
ProjectLabel! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateProjectLabelColor(input: UpdateProjectLabelColor!):
|
||||
ProjectLabel! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
}
|
||||
|
||||
input NewProjectLabel {
|
||||
|
@ -1,8 +1,10 @@
|
||||
extend type Mutation {
|
||||
createProjectMember(input: CreateProjectMember!): CreateProjectMemberPayload!
|
||||
deleteProjectMember(input: DeleteProjectMember!): DeleteProjectMemberPayload!
|
||||
updateProjectMemberRole(input: UpdateProjectMemberRole!): UpdateProjectMemberRolePayload!
|
||||
setProjectOwner(input: SetProjectOwner!): SetProjectOwnerPayload!
|
||||
createProjectMember(input: CreateProjectMember!):
|
||||
CreateProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
deleteProjectMember(input: DeleteProjectMember!):
|
||||
DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateProjectMemberRole(input: UpdateProjectMemberRole!):
|
||||
UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
}
|
||||
|
||||
input CreateProjectMember {
|
||||
@ -36,13 +38,3 @@ type UpdateProjectMemberRolePayload {
|
||||
ok: Boolean!
|
||||
member: Member!
|
||||
}
|
||||
|
||||
input SetProjectOwner {
|
||||
projectID: UUID!
|
||||
ownerID: UUID!
|
||||
}
|
||||
type SetProjectOwnerPayload {
|
||||
ok: Boolean!
|
||||
prevOwner: Member!
|
||||
newOwner: Member!
|
||||
}
|
||||
|
@ -1,15 +1,24 @@
|
||||
extend type Mutation {
|
||||
createTask(input: NewTask!): Task!
|
||||
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
||||
createTask(input: NewTask!):
|
||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
deleteTask(input: DeleteTaskInput!):
|
||||
DeleteTaskPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
|
||||
updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
|
||||
updateTaskLocation(input: NewTaskLocation!): UpdateTaskLocationPayload!
|
||||
updateTaskName(input: UpdateTaskName!): Task!
|
||||
setTaskComplete(input: SetTaskComplete!): Task!
|
||||
updateTaskDueDate(input: UpdateTaskDueDate!): Task!
|
||||
updateTaskDescription(input: UpdateTaskDescriptionInput!):
|
||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateTaskLocation(input: NewTaskLocation!):
|
||||
UpdateTaskLocationPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateTaskName(input: UpdateTaskName!):
|
||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
setTaskComplete(input: SetTaskComplete!):
|
||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateTaskDueDate(input: UpdateTaskDueDate!):
|
||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
|
||||
assignTask(input: AssignTaskInput): Task!
|
||||
unassignTask(input: UnassignTaskInput): Task!
|
||||
assignTask(input: AssignTaskInput):
|
||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
unassignTask(input: UnassignTaskInput):
|
||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
}
|
||||
|
||||
input NewTask {
|
||||
|
@ -1,14 +1,23 @@
|
||||
extend type Mutation {
|
||||
createTaskChecklist(input: CreateTaskChecklist!): TaskChecklist!
|
||||
deleteTaskChecklist(input: DeleteTaskChecklist!): DeleteTaskChecklistPayload!
|
||||
updateTaskChecklistName(input: UpdateTaskChecklistName!): TaskChecklist!
|
||||
createTaskChecklistItem(input: CreateTaskChecklistItem!): TaskChecklistItem!
|
||||
updateTaskChecklistItemName(input: UpdateTaskChecklistItemName!): TaskChecklistItem!
|
||||
setTaskChecklistItemComplete(input: SetTaskChecklistItemComplete!): TaskChecklistItem!
|
||||
deleteTaskChecklistItem(input: DeleteTaskChecklistItem!): DeleteTaskChecklistItemPayload!
|
||||
createTaskChecklist(input: CreateTaskChecklist!):
|
||||
TaskChecklist! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
deleteTaskChecklist(input: DeleteTaskChecklist!):
|
||||
DeleteTaskChecklistPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateTaskChecklistName(input: UpdateTaskChecklistName!):
|
||||
TaskChecklist! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
createTaskChecklistItem(input: CreateTaskChecklistItem!):
|
||||
TaskChecklistItem! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateTaskChecklistItemName(input: UpdateTaskChecklistItemName!):
|
||||
TaskChecklistItem! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
setTaskChecklistItemComplete(input: SetTaskChecklistItemComplete!):
|
||||
TaskChecklistItem! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
deleteTaskChecklistItem(input: DeleteTaskChecklistItem!):
|
||||
DeleteTaskChecklistItemPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateTaskChecklistLocation(input: UpdateTaskChecklistLocation!):
|
||||
UpdateTaskChecklistLocationPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateTaskChecklistItemLocation(input: UpdateTaskChecklistItemLocation!):
|
||||
UpdateTaskChecklistItemLocationPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
|
||||
updateTaskChecklistLocation(input: UpdateTaskChecklistLocation!): UpdateTaskChecklistLocationPayload!
|
||||
updateTaskChecklistItemLocation(input: UpdateTaskChecklistItemLocation!): UpdateTaskChecklistItemLocationPayload!
|
||||
}
|
||||
|
||||
input UpdateTaskChecklistItemLocation {
|
||||
|
@ -1,8 +1,12 @@
|
||||
extend type Mutation {
|
||||
createTaskGroup(input: NewTaskGroup!): TaskGroup!
|
||||
updateTaskGroupLocation(input: NewTaskGroupLocation!): TaskGroup!
|
||||
updateTaskGroupName(input: UpdateTaskGroupName!): TaskGroup!
|
||||
deleteTaskGroup(input: DeleteTaskGroupInput!): DeleteTaskGroupPayload!
|
||||
createTaskGroup(input: NewTaskGroup!):
|
||||
TaskGroup! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateTaskGroupLocation(input: NewTaskGroupLocation!):
|
||||
TaskGroup! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
updateTaskGroupName(input: UpdateTaskGroupName!):
|
||||
TaskGroup! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
deleteTaskGroup(input: DeleteTaskGroupInput!):
|
||||
DeleteTaskGroupPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
}
|
||||
|
||||
input NewTaskGroupLocation {
|
||||
|
@ -16,7 +16,11 @@ type ToggleTaskLabelPayload {
|
||||
task: Task!
|
||||
}
|
||||
extend type Mutation {
|
||||
addTaskLabel(input: AddTaskLabelInput): Task!
|
||||
removeTaskLabel(input: RemoveTaskLabelInput): Task!
|
||||
toggleTaskLabel(input: ToggleTaskLabelInput!): ToggleTaskLabelPayload!
|
||||
addTaskLabel(input: AddTaskLabelInput):
|
||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
removeTaskLabel(input: RemoveTaskLabelInput):
|
||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
toggleTaskLabel(input: ToggleTaskLabelInput!):
|
||||
ToggleTaskLabelPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
extend type Mutation {
|
||||
deleteTeam(input: DeleteTeam!): DeleteTeamPayload!
|
||||
createTeam(input: NewTeam!): Team!
|
||||
deleteTeam(input: DeleteTeam!):
|
||||
DeleteTeamPayload! @hasRole(roles:[ ADMIN], level: TEAM, type: TEAM)
|
||||
createTeam(input: NewTeam!):
|
||||
Team! @hasRole(roles: [ADMIN], level: ORG, type: ORG)
|
||||
}
|
||||
|
||||
input NewTeam {
|
||||
|
@ -1,8 +1,11 @@
|
||||
extend type Mutation {
|
||||
setTeamOwner(input: SetTeamOwner!): SetTeamOwnerPayload!
|
||||
createTeamMember(input: CreateTeamMember!): CreateTeamMemberPayload!
|
||||
updateTeamMemberRole(input: UpdateTeamMemberRole!): UpdateTeamMemberRolePayload!
|
||||
deleteTeamMember(input: DeleteTeamMember!): DeleteTeamMemberPayload!
|
||||
createTeamMember(input: CreateTeamMember!):
|
||||
CreateTeamMemberPayload! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM)
|
||||
updateTeamMemberRole(input: UpdateTeamMemberRole!):
|
||||
UpdateTeamMemberRolePayload! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM)
|
||||
deleteTeamMember(input: DeleteTeamMember!):
|
||||
DeleteTeamMemberPayload! @hasRole(roles: [ADMIN], level: TEAM, type: TEAM)
|
||||
|
||||
}
|
||||
|
||||
input DeleteTeamMember {
|
||||
@ -35,16 +38,6 @@ input UpdateTeamMemberRole {
|
||||
|
||||
type UpdateTeamMemberRolePayload {
|
||||
ok: Boolean!
|
||||
teamID: UUID!
|
||||
member: Member!
|
||||
}
|
||||
|
||||
input SetTeamOwner {
|
||||
teamID: UUID!
|
||||
userID: UUID!
|
||||
}
|
||||
|
||||
type SetTeamOwnerPayload {
|
||||
ok: Boolean!
|
||||
prevOwner: Member!
|
||||
newOwner: Member!
|
||||
}
|
||||
|
@ -1,12 +1,16 @@
|
||||
extend type Mutation {
|
||||
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
||||
createUserAccount(input: NewUserAccount!): UserAccount!
|
||||
deleteUserAccount(input: DeleteUserAccount!): DeleteUserAccountPayload!
|
||||
createUserAccount(input: NewUserAccount!):
|
||||
UserAccount! @hasRole(roles: [ADMIN], level: ORG, type: ORG)
|
||||
deleteUserAccount(input: DeleteUserAccount!):
|
||||
DeleteUserAccountPayload! @hasRole(roles: [ADMIN], level: ORG, type: ORG)
|
||||
|
||||
logoutUser(input: LogoutUser!): Boolean!
|
||||
clearProfileAvatar: UserAccount!
|
||||
|
||||
updateUserPassword(input: UpdateUserPassword!): UpdateUserPasswordPayload!
|
||||
updateUserRole(input: UpdateUserRole!): UpdateUserRolePayload!
|
||||
updateUserRole(input: UpdateUserRole!):
|
||||
UpdateUserRolePayload! @hasRole(roles: [ADMIN], level: ORG, type: ORG)
|
||||
}
|
||||
|
||||
input UpdateUserPassword {
|
||||
|
Reference in New Issue
Block a user