Skip to content

Commit

Permalink
Merge pull request #944 from ynput/941-accessgroups-new-access-groups…
Browse files Browse the repository at this point in the history
…-issues

Project Settings: limited access users UI/UX changes
  • Loading branch information
flynput authored Dec 5, 2024
2 parents 98c16a6 + 515799d commit 0c98ccb
Show file tree
Hide file tree
Showing 24 changed files with 501 additions and 267 deletions.
16 changes: 16 additions & 0 deletions src/api/rest/accessGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ export type ValidationError = {
export type HttpValidationError = {
detail?: ValidationError[]
}
export type StudioManagementPermissions = {
/** Allow users to create new projects */
create_projects?: boolean
/** Allow users to list all users in the studio */
list_all_users?: boolean
}
export type ProjectManagementPermissions = {
/** Allow users to view or edit the project anatomy */
anatomy?: number
/** Allow users to view or assign users to project access groups */
access?: number
/** Allow users to view or edit the project addon settings */
settings?: number
}
export type FolderAccess = {
access_type?: string
/** The path of the folder to allow access to. Required for access_type 'hierarchy and 'children' */
Expand All @@ -83,6 +97,8 @@ export type EndpointsAccessList = {
endpoints?: string[]
}
export type Permissions = {
studio?: StudioManagementPermissions
project?: ProjectManagementPermissions
/** Whitelist folders a user can create */
create?: FolderAccessList
/** Whitelist folders a user can read */
Expand Down
104 changes: 92 additions & 12 deletions src/api/rest/users.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import { RestAPI as api } from '../../services/ayon'
const injectedRtkApi = api.injectEndpoints({
endpoints: (build) => ({
getUserStudioPermissions: build.query<
GetUserStudioPermissionsApiResponse,
GetUserStudioPermissionsApiArg
>({
query: (queryArg) => ({ url: `/api/users/${queryArg.userName}/permissions` }),
}),
getUserProjectPermissions: build.query<
GetUserProjectPermissionsApiResponse,
GetUserProjectPermissionsApiArg
>({
query: (queryArg) => ({
url: `/api/users/${queryArg.userName}/permissions/${queryArg.projectName}`,
}),
}),
getUser: build.query<GetUserApiResponse, GetUserApiArg>({
query: (queryArg) => ({ url: `/api/users/${queryArg.userName}` }),
}),
Expand All @@ -21,6 +35,17 @@ const injectedRtkApi = api.injectEndpoints({
overrideExisting: false,
})
export { injectedRtkApi as api }
export type GetUserStudioPermissionsApiResponse =
/** status 200 Successful Response */ StudioPermissions
export type GetUserStudioPermissionsApiArg = {
userName: string
}
export type GetUserProjectPermissionsApiResponse =
/** status 200 Successful Response */ ProjectPermissions
export type GetUserProjectPermissionsApiArg = {
projectName: string
userName: string
}
export type GetUserApiResponse = /** status 200 Successful Response */
| UserModel
| {
Expand All @@ -39,11 +64,78 @@ export type SetFrontendPreferencesApiArg = {
userName: string
patchData: object
}
export type StudioManagementPermissions = {
/** Allow users to create new projects */
create_projects?: boolean
/** Allow users to list all users in the studio */
list_all_users?: boolean
}
export type StudioPermissions = {
studio?: StudioManagementPermissions
}
export type ErrorResponse = {
code: number
detail: string
}
export type ValidationError = {
loc: (string | number)[]
msg: string
type: string
}
export type HttpValidationError = {
detail?: ValidationError[]
}
export type ProjectManagementPermissions = {
/** Allow users to view or edit the project anatomy */
anatomy?: number
/** Allow users to view or assign users to project access groups */
access?: number
/** Allow users to view or edit the project addon settings */
settings?: number
}
export type FolderAccess = {
access_type?: string
/** The path of the folder to allow access to. Required for access_type 'hierarchy and 'children' */
path?: string
}
export type FolderAccessList = {
enabled?: boolean
access_list?: FolderAccess[]
}
export type AttributeAccessList = {
enabled?: boolean
attributes?: string[]
}
export type EndpointsAccessList = {
enabled?: boolean
endpoints?: string[]
}
export type ProjectPermissions = {
project?: ProjectManagementPermissions
/** Whitelist folders a user can create */
create?: FolderAccessList
/** Whitelist folders a user can read */
read?: FolderAccessList
/** Whitelist folders a user can update */
update?: FolderAccessList
/** Whitelist folders a user can publish to */
publish?: FolderAccessList
/** Whitelist folders a user can delete */
delete?: FolderAccessList
/** Whitelist attributes a user can read */
attrib_read?: AttributeAccessList
/** Whitelist attributes a user can write */
attrib_write?: AttributeAccessList
/** Whitelist REST endpoints a user can access */
endpoints?: EndpointsAccessList
}
export type UserAttribModel = {
fullName?: string
email?: string
avatarUrl?: string
developerMode?: boolean
/** Do they live in Olympus */
god?: boolean
}
export type UserModel = {
/** Name is an unique id of the {entity_name} */
Expand All @@ -58,18 +150,6 @@ export type UserModel = {
/** Time of last update */
updatedAt?: string
}
export type ErrorResponse = {
code: number
detail: string
}
export type ValidationError = {
loc: (string | number)[]
msg: string
type: string
}
export type HttpValidationError = {
detail?: ValidationError[]
}
export type LocationInfo = {
country?: string
subdivision?: string
Expand Down
9 changes: 4 additions & 5 deletions src/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,6 @@ const App = () => {
exact
element={<Navigate replace to="/dashboard/tasks" />}
/>
<Route
path="/manageProjects"
exact
element={<Navigate replace to="/manageProjects/anatomy" />}
/>

<Route
path="/dashboard"
Expand All @@ -207,6 +202,10 @@ const App = () => {
element={<UserDashboardPage />}
/>

<Route
path="/manageProjects"
element={<ProjectManagerPage />}
/>
<Route
path="/manageProjects/:module"
element={<ProjectManagerPage />}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Menu/Menus/AppMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const AppMenu = ({ user, ...props }) => {
const items = [
{
id: 'projectsManager',
link: '/manageProjects/projectSettings',
link: '/manageProjects',
label: 'Projects Settings',
icon: 'settings_applications',
shortcut: 'P+P',
Expand Down
13 changes: 4 additions & 9 deletions src/containers/AddonSettings/AddonSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,6 @@ const AddonSettings = ({ projectName, showSites = false, bypassPermissions = fal
)

const onSelectAddon = (newSelection) => {
console.log('on select addon...', newSelection)
setSelectedAddons(newSelection)
setCurrentSelection(null)
}
Expand All @@ -679,19 +678,15 @@ const AddonSettings = ({ projectName, showSites = false, bypassPermissions = fal
})
}

// console.log('selected addons: ', selectedAddons)

if (isLoading) {
return <LoadingPage />
}

if (!bypassPermissions && !userPermissions.canViewSettings(projectName)) {
return (
<EmptyPlaceholder
icon="settings_alert"
message="You don't have permissions to view the addon settings for this project"
/>
)
return <EmptyPlaceholder
icon="settings_alert"
message="You don't have permission to view the addon settings for this project"
/>
}

return (
Expand Down
4 changes: 4 additions & 0 deletions src/containers/projectList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@ const ProjectList = ({

// When right clicking on the already selected node, we don't want to change the selection
const onContextMenu = (event) => {
const isActiveCallableValue = isActiveCallable ? isActiveCallable(event.data.name) : true
if (!isActiveCallableValue) {
return
}
let newSelection = selection

if (multiselect) {
Expand Down
2 changes: 1 addition & 1 deletion src/context/shortcutsContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function ShortcutsProvider(props) {
// project settings
{
key: 'p+p',
action: () => navigate('/manageProjects/projectSettings?' + searchParams.toString()),
action: () => navigate('/manageProjects'),
},
{
key: 'p+a',
Expand Down
61 changes: 47 additions & 14 deletions src/hooks/useUserProjectPermissions.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import { StudioManagementPermissions, ProjectManagementPermissions } from '@api/rest/users'
import { Module } from '@pages/ProjectManagerPage/mappers'
import { useGetCurrentUserPermissionsQuery } from '@queries/permissions/getPermissions'

type AllProjectsPremissions = {
projects: {
[projectName: string]: {
project: {
anatomy: PermissionLevel
create: boolean
enabled: boolean
settings: PermissionLevel
users: PermissionLevel
}
project: ProjectManagementPermissions
}
}
studio: {
create_project: boolean
}
studio: StudioManagementPermissions
user_level: 'user' | 'admin'
}

Expand All @@ -25,7 +19,8 @@ enum PermissionLevel {
}

export enum UserPermissionsEntity {
users = 'users',
// TODO This needs to be in sync with ProjectManagementPermissions
access = 'access',
anatomy = 'anatomy',
settings = 'settings',
}
Expand All @@ -47,7 +42,7 @@ class UserPermissions {
return false
}

return (this.projectSettingsAreEnabled() && this.permissions.studio.create_project) || false
return (this.projectSettingsAreEnabled() && this.permissions.studio.create_projects) || false
}

getPermissionLevel(type: UserPermissionsEntity, projectName: string): PermissionLevel {
Expand All @@ -72,6 +67,28 @@ class UserPermissions {
return this.permissions.projects[projectName]?.project[type] === PermissionLevel.readWrite
}

canAccessModule({ module, projectName }: { module: string; projectName: string }): boolean {
if (module === Module.siteSettings) {
return true
}

if (module === Module.projectSettings) {
return this.canView(UserPermissionsEntity.settings, projectName)
}

if (module === Module.anatomy) {
return this.canView(UserPermissionsEntity.anatomy, projectName)
}
if (module === Module.projectAccess) {
return this.canView(UserPermissionsEntity.access, projectName)
}
if (module === Module.roots) {
return this.assignedToProject(projectName)
}

return true
}

canView(type: UserPermissionsEntity, projectName: string): boolean {
if (!this.permissions) {
return false
Expand Down Expand Up @@ -104,6 +121,22 @@ class UserPermissions {
return this.canEdit(UserPermissionsEntity.anatomy, projectName)
}

assignedToProject(projectName: string): boolean {
if (!this.permissions) {
return false
}

if (this.hasElevatedPrivileges || !this.projectSettingsAreEnabled()) {
return true
}

if (this.permissions.projects[projectName] !== undefined) {
return true
}

return false
}

canViewSettings(projectName?: string): boolean {
if (projectName) {
return this.canView(UserPermissionsEntity.settings, projectName)
Expand Down Expand Up @@ -140,10 +173,10 @@ class UserPermissions {

canViewUsers(projectName?: string): boolean {
if (projectName) {
return this.canView(UserPermissionsEntity.users, projectName)
return this.canView(UserPermissionsEntity.access, projectName)
}

return this.canViewAny(UserPermissionsEntity.users)
return this.canViewAny(UserPermissionsEntity.access)
}

projectSettingsAreEnabled(): boolean {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/ProjectManagerPage/ProjectAnatomy.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const ProjectAnatomy = ({ projectName, projectList }) => {
) : (
<EmptyPlaceholder
icon="settings_alert"
message="You don't have permissions to view this project's anatomy"
message="You don't have permission to view this project's anatomy"
/>
)}
</ScrollPanel>
Expand Down
Loading

0 comments on commit 0c98ccb

Please sign in to comment.