-
Notifications
You must be signed in to change notification settings - Fork 12
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
Feature/bai 1485 manual user access #1624
base: main
Are you sure you want to change the base?
Conversation
… to manually add users to model access
import { getErrorMessage } from 'utils/fetcher' | ||
|
||
interface ManualEntryAccessProps { | ||
accessList: CollaboratorEntry[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This component doesn't have to care about the access list. It can probably have a single prop onAddEntityManually: (entityName: string) => void
. We can let the main EntryAccessInput
component manage the access list logic, though I also think the checks we're doing within handleAddEntityManuallyOnClick
can and should be handled by the backend.
setAccessList: (accessList: CollaboratorEntry[]) => void | ||
} | ||
|
||
export default function ManualEntryAccess({ accessList, setAccessList }: ManualEntryAccessProps) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider a name change to something like ManualEntityInput
as this component isn't responsible for managing entry access in itself.
<AccordionDetails sx={{ p: 0 }}> | ||
<Stack spacing={2} direction={{ xs: 'column', sm: 'row' }}> | ||
<TextField | ||
id='manual-entity-name-select' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't look like this id
is needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot, I think this was left over from when I was using a FormControl to add a label to it .
<Stack spacing={2} direction={{ xs: 'column', sm: 'row' }}> | ||
<TextField | ||
id='manual-entity-name-select' | ||
placeholder='Joe Bloggs' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct me if I'm wrong here, but aren't we expecting users to provide a dn here? I'm not sure this placeholder is useful if that's the case.
Depending on how intuitive it is to know what information should be provided by a user, we may benefit from using a help icon that explains what can be provided.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I appreciate the placeholder might not be that useful. A help icon with more information could be the way to go but I'd imagine we'd need to make it configurable.
@@ -49,9 +49,9 @@ interface UserInformationResponse { | |||
entity: UserInformation | |||
} | |||
|
|||
export function useGetUserInformation(dn: string) { | |||
export function useGetUserInformation(dn: string, kind?: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we give kind
a more specific type? I'm assuming this can be 'user'
or 'group'
but I'm not sure just by looking at it.
@@ -319,6 +322,18 @@ export async function updateModel(user: UserInterface, modelId: string, modelDif | |||
if (modelDiff.settings?.mirror?.destinationModelId && modelDiff.settings?.mirror?.sourceModelId) { | |||
throw BadReq('You cannot select both mirror settings simultaneously.') | |||
} | |||
if (modelDiff.collaborators) { | |||
const invalidUsers = [] | |||
modelDiff.collaborators.forEach(async (collaborator) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't await the promises from this async callback when using .forEach()
as it doesn't return anything. Instead we can use .map()
like so:
await Promise.all(modelDiff.collaborators.map(async (collaborator) => { ... }))
export type UpdateModelParams = Pick<ModelInterface, 'name' | 'teamId' | 'description' | 'visibility'> & { | ||
export type UpdateModelParams = Pick< | ||
ModelInterface, | ||
'name' | 'teamId' | 'description' | 'visibility' | 'collaborators' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
teamId
isn't a valid property anymore, so it looks like this was missed from your other PR. It's a little concerning that this isn't causing TS errors, so can we double check that there's not any more that we've missed?
@@ -319,6 +322,18 @@ export async function updateModel(user: UserInterface, modelId: string, modelDif | |||
if (modelDiff.settings?.mirror?.destinationModelId && modelDiff.settings?.mirror?.sourceModelId) { | |||
throw BadReq('You cannot select both mirror settings simultaneously.') | |||
} | |||
if (modelDiff.collaborators) { | |||
const invalidUsers = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we type this array please?
It's worth noting that not all entities are users, so we should probably use the word "entity" instead of "user". At some point I'd like to get around to fixing this across the app, but it would be nice to at least not add to the tech debt.
I also think we should change this to store the collaboration.entity
so that we can inform users of what has failed by returning exactly what they provided.
const invalidEntities: string[] = []
modelDiff.collaborators.forEach(async (collaborator) => { | ||
const userInformation = await authentication.getUserInformation(collaborator.entity) | ||
if (!userInformation) { | ||
invalidUsers.push(userInformation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned in another comment (https://github.com/gchq/Bailo/pull/1624/files#r1843611009) I think it's better if we store the entity string that's invalid, so we can return that to the user.
invalidEntities.push(collaborator.entity)
} | ||
}) | ||
if (invalidUsers.length > 0) { | ||
throw BadReq('One or more user is invalid') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could tell the user which users are invalid by doing something like:
// We probably need to have the entry kind label formatting logic from the frontend to make this a nice error.
throw BadReq(
`Failed to update ${`model.kind`}. Invalid ${invalidEntities.length === 1 ? 'entity' : 'entities'}: `${invalidEntities.join(', ')}`
)
@@ -63,6 +63,12 @@ export function useGetUserInformation(dn: string) { | |||
} | |||
} | |||
|
|||
export function getUserInformation(dn: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't be needed if we let the backend handle all of the validation.
setErrorMessage('') | ||
if (manualEntityName !== undefined && manualEntityName !== '') { | ||
if (accessList.find((collaborator) => collaborator.entity === `${EntityKind.USER}:${manualEntityName}`)) { | ||
return setErrorMessage(`The requested user has already been added below.`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The backend should handle this, not the frontend. An API user can bypass this check.
if (accessList.find((collaborator) => collaborator.entity === `${EntityKind.USER}:${manualEntityName}`)) { | ||
return setErrorMessage(`The requested user has already been added below.`) | ||
} | ||
const response = await getUserInformation(manualEntityName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be handled by the backend
const updatedAccessList = [...accessList] | ||
const newAccess = { entity: `${EntityKind.USER}:${manualEntityName}`, roles: [] } | ||
updatedAccessList.push(newAccess) | ||
setAccessList(updatedAccessList) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider condensing this like so:
setAccessList([...accessList, { entity: `${EntityKind.USER}:${manualEntityName}`, roles: [] }])
It turns 4 lines into 1 and I think it's also easier to read.
No description provided.