-
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?
Changes from all commits
a4509ce
4f1836c
d580281
40131db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -305,7 +305,10 @@ export async function updateModelCard( | |
return revision | ||
} | ||
|
||
export type UpdateModelParams = Pick<ModelInterface, 'name' | 'teamId' | 'description' | 'visibility'> & { | ||
export type UpdateModelParams = Pick< | ||
ModelInterface, | ||
'name' | 'teamId' | 'description' | 'visibility' | 'collaborators' | ||
> & { | ||
settings: Partial<ModelInterface['settings']> | ||
} | ||
export async function updateModel(user: UserInterface, modelId: string, modelDiff: Partial<UpdateModelParams>) { | ||
|
@@ -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 commentThe 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
|
||
modelDiff.collaborators.forEach(async (collaborator) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can't await the promises from this async callback when using
|
||
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 commentThe 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.
|
||
} | ||
}) | ||
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 commentThe 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:
|
||
} | ||
} | ||
|
||
const auth = await authorisation.model(user, model, ModelAction.Update) | ||
if (!auth.success) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 commentThe reason will be displayed to describe this comment to others. Learn more. Can we give |
||
const { data, isLoading, error, mutate } = useSWR<UserInformationResponse, ErrorInfo>( | ||
`/api/v2/entity/${dn}/lookup`, | ||
`/api/v2/entity/${dn}/lookup${kind ? `?kind=${kind}` : ''}`, | ||
fetcher, | ||
) | ||
|
||
|
@@ -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 commentThe 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. |
||
return fetch(`/api/v2/entity/${dn}/lookup`, { | ||
headers: { 'Content-Type': 'application/json' }, | ||
}) | ||
} | ||
|
||
interface GetUserTokensResponse { | ||
tokens: TokenInterface[] | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore' | ||
import { Accordion, AccordionDetails, AccordionSummary, Button, Stack, TextField, Typography } from '@mui/material' | ||
import { getUserInformation } from 'actions/user' | ||
import { useState } from 'react' | ||
import MessageAlert from 'src/MessageAlert' | ||
import { CollaboratorEntry, EntityKind } from 'types/types' | ||
import { getErrorMessage } from 'utils/fetcher' | ||
|
||
interface ManualEntryAccessProps { | ||
accessList: CollaboratorEntry[] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
setAccessList: (accessList: CollaboratorEntry[]) => void | ||
} | ||
|
||
export default function ManualEntryAccess({ accessList, setAccessList }: ManualEntryAccessProps) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider a name change to something like |
||
const [manualEntityName, setManualEntityName] = useState('') | ||
const [errorMessage, setErrorMessage] = useState('') | ||
|
||
const handleAddEntityManuallyOnClick = async () => { | ||
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 commentThe 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. |
||
} | ||
const response = await getUserInformation(manualEntityName) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be handled by the backend |
||
if (!response.ok) { | ||
return setErrorMessage(await getErrorMessage(response)) | ||
} | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. Consider condensing this like so:
It turns 4 lines into 1 and I think it's also easier to read. |
||
setManualEntityName('') | ||
} | ||
} | ||
|
||
return ( | ||
<Accordion sx={{ borderTop: 'none' }}> | ||
<AccordionSummary | ||
sx={{ pl: 0, borderTop: 'none' }} | ||
expandIcon={<ExpandMoreIcon />} | ||
aria-controls='manual-user-add-content' | ||
id='manual-user-add-header' | ||
> | ||
<Typography sx={{ mr: 1 }} component='caption'> | ||
Trouble finding a user? Click here to add them manually | ||
</Typography> | ||
</AccordionSummary> | ||
<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 commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't look like this There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 . |
||
placeholder='Joe Bloggs' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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. |
||
size='small' | ||
fullWidth | ||
label='User' | ||
value={manualEntityName} | ||
onChange={(e) => setManualEntityName(e.target.value)} | ||
/> | ||
<Button variant='contained' onClick={handleAddEntityManuallyOnClick} disabled={manualEntityName === ''}> | ||
Add | ||
</Button> | ||
</Stack> | ||
<MessageAlert message={errorMessage} severity='error' /> | ||
</AccordionDetails> | ||
</Accordion> | ||
) | ||
} |
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?