Skip to content

Commit

Permalink
feat: delete service account tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
rohan-chaturvedi committed Oct 23, 2024
1 parent 92698a3 commit e0b9802
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 16 deletions.
23 changes: 23 additions & 0 deletions backend/backend/graphene/mutations/service_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,26 @@ def mutate(
)

return CreateServiceAccountTokenMutation(token=token)


class DeleteServiceAccountTokenMutation(graphene.Mutation):
class Arguments:
token_id = graphene.ID()

ok = graphene.Boolean()

@classmethod
def mutate(cls, root, info, token_id):
user = info.context.user
token = ServiceAccountToken.objects.get(id=token_id)

if not user_has_permission(
user, "update", "ServiceAccounts", token.service_account.organisation
):
raise GraphQLError(
"You don't have the permissions required to delete Service Tokens in this organisation"
)

token.delete()

return DeleteServiceAccountTokenMutation(ok=True)
2 changes: 2 additions & 0 deletions backend/backend/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
CreateServiceAccountMutation,
CreateServiceAccountTokenMutation,
DeleteServiceAccountMutation,
DeleteServiceAccountTokenMutation,
EnableServiceAccountThirdPartyAuthMutation,
UpdateServiceAccountHandlersMutation,
)
Expand Down Expand Up @@ -777,6 +778,7 @@ class Mutation(graphene.ObjectType):
update_service_account_handlers = UpdateServiceAccountHandlersMutation.Field()
delete_service_account = DeleteServiceAccountMutation.Field()
create_service_account_token = CreateServiceAccountTokenMutation.Field()
delete_service_account_token = DeleteServiceAccountTokenMutation.Field()

init_env_sync = InitEnvSync.Field()
delete_env_sync = DeleteSync.Field()
Expand Down
5 changes: 5 additions & 0 deletions frontend/apollo/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ type Mutation {
updateServiceAccountHandlers(handlers: [ServiceAccountHandlerInput], serviceAccountId: ID): UpdateServiceAccountHandlersMutation
deleteServiceAccount(serviceAccountId: ID): DeleteServiceAccountMutation
createServiceAccountToken(expiry: BigInt, identityKey: String!, name: String!, serviceAccountId: ID, token: String!, wrappedKeyShare: String!): CreateServiceAccountTokenMutation
deleteServiceAccountToken(tokenId: ID): DeleteServiceAccountTokenMutation
initEnvSync(appId: ID, envKeys: [EnvironmentKeyInput]): InitEnvSync
deleteEnvSync(syncId: ID): DeleteSync
triggerSync(syncId: ID): TriggerSync
Expand Down Expand Up @@ -820,6 +821,10 @@ type CreateServiceAccountTokenMutation {
token: ServiceAccountTokenType
}

type DeleteServiceAccountTokenMutation {
ok: Boolean
}

type InitEnvSync {
app: AppType
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { FaTrash } from 'react-icons/fa'
import { DeleteServiceAccountToken } from '@/graphql/mutations/service-accounts/deleteServiceAccountToken.gql'
import { GetServiceAccounts } from '@/graphql/queries/service-accounts/getServiceAccounts.gql'
import { useMutation } from '@apollo/client'
import { toast } from 'react-toastify'
import { useContext, useRef } from 'react'
import { organisationContext } from '@/contexts/organisationContext'
import { ServiceAccountTokenType, ServiceAccountType } from '@/apollo/graphql'
import { Button } from '@/components/common/Button'
import GenericDialog from '@/components/common/GenericDialog'
import { useRouter } from 'next/navigation'

export const DeleteServiceAccountTokenDialog = ({ token }: { token: ServiceAccountTokenType }) => {
const { activeOrganisation: organisation } = useContext(organisationContext)

const dialogRef = useRef<{ closeModal: () => void }>(null)

const [deleteToken] = useMutation(DeleteServiceAccountToken)

const handleDelete = async () => {
const deleted = await deleteToken({
variables: { id: token.id },
refetchQueries: [{ query: GetServiceAccounts, variables: { orgId: organisation!.id } }],
})
if (deleted.data.deleteServiceAccountToken.ok) {
toast.success('Deleted token!')
if (dialogRef.current) dialogRef.current.closeModal()
}
}

return (
<GenericDialog
title={`Delete ${token.name}`}
buttonContent={
<>
<FaTrash /> Delete
</>
}
buttonVariant="danger"
ref={dialogRef}
>
<div className="space-y-4">
<div className="text-neutral-500 py-4">Are you sure you want to delete this token?</div>
<div className="flex justify-end">
<Button variant="danger" onClick={handleDelete}>
<FaTrash /> Delete
</Button>
</div>
</div>
</GenericDialog>
)
}
23 changes: 13 additions & 10 deletions frontend/app/[team]/access/service-accounts/[account]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import { relativeTimeFromDates } from '@/utils/time'
import { useQuery } from '@apollo/client'
import Link from 'next/link'
import { useContext } from 'react'
import { FaBan, FaChevronLeft, FaRobot } from 'react-icons/fa'
import { CreateServiceAccountTokenDialog } from '../_components/CreateServiceAccountTokenDialog'
import { FaBan, FaChevronLeft, FaKey, FaRobot } from 'react-icons/fa'
import { CreateServiceAccountTokenDialog } from './_components/CreateServiceAccountTokenDialog'
import { DeleteServiceAccountDialog } from '../_components/DeleteServiceAccountDialog'
import { ServiceAccountTokenType } from '@/apollo/graphql'
import { Avatar } from '@/components/common/Avatar'
import { EmptyState } from '@/components/common/EmptyState'
import { humanReadableExpiry } from '@/utils/tokens'
import { DeleteServiceAccountTokenDialog } from './_components/DeleteServiceAccountTokenDialog'

export default function ServiceAccount({ params }: { params: { team: string; account: string } }) {
const { activeOrganisation: organisation } = useContext(organisationContext)
Expand Down Expand Up @@ -114,27 +115,29 @@ export default function ServiceAccount({ params }: { params: { team: string; acc
<CreateServiceAccountTokenDialog serviceAccount={account} />
</div>

<div className="space-y-2 divide-y divide-neutral-500/20">
<div className="space-y-2 divide-y divide-neutral-500/20 py-4">
{account.tokens?.map((token: ServiceAccountTokenType) => (
<div key={token!.id} className="grid grid-cols-3 items-center p-2">
<div className="font-medium text-lg text-zinc-900 dark:text-zinc-100">
{token!.name}
<div key={token!.id} className="grid grid-cols-4 gap-2 items-center p-2 group">
<div className="font-medium text-lg text-zinc-900 dark:text-zinc-100 flex items-center gap-2">
<FaKey className="text-neutral-500" /> {token!.name}
</div>

<div className="text-neutral-500 flex items-center gap-1">
<div className="text-neutral-500 text-sm flex items-center gap-1">
<span>Created</span> {relativeTimeFromDates(new Date(token?.createdAt))} by{' '}
<Avatar imagePath={token.createdBy?.avatarUrl} size="sm" />
<span className="font-medium text-zinc-900 dark:text-zinc-100">
{token?.createdBy?.fullName}
</span>
</div>

{/* <div>{new Date(token.expiresAt).toDateString()}</div> */}

<div className="flex items-center gap-1">
<div className="flex items-center gap-1 text-neutral-500 text-sm">
Expires{' '}
{token.expiresAt ? relativeTimeFromDates(new Date(token?.expiresAt)) : 'never'}
</div>

<div className="flex justify-end opacity-0 group-hover:opacity-100 transition ease">
<DeleteServiceAccountTokenDialog token={token} />
</div>
</div>
))}
</div>
Expand Down
7 changes: 1 addition & 6 deletions frontend/app/[team]/access/service-accounts/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { CreateServiceAccountDialog } from './_components/CreateServiceAccountDi
import { FaRobot } from 'react-icons/fa6'
import { relativeTimeFromDates } from '@/utils/time'
import { DeleteServiceAccountDialog } from './_components/DeleteServiceAccountDialog'
import { CreateServiceAccountTokenDialog } from './_components/CreateServiceAccountTokenDialog'
import { CreateServiceAccountTokenDialog } from './[account]/_components/CreateServiceAccountTokenDialog'
import Link from 'next/link'
import { Button } from '@/components/common/Button'

Expand Down Expand Up @@ -85,11 +85,6 @@ export default function ServiceAccounts({ params }: { params: { team: string } }
{relativeTimeFromDates(new Date(account.createdAt))}
</td>

{/* <td className="px-6 py-4 flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition ease">
<CreateServiceAccountTokenDialog serviceAccount={account} />
{userCanDeleteSA && <DeleteServiceAccountDialog account={account} />}
</td> */}

<td className="px-6 py-4">
<Link href={`/${params.team}/access/service-accounts/${account.id}`}>
<Button variant="secondary">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mutation DeleteServiceAccountToken($id: ID!) {
deleteServiceAccountToken(tokenId: $id) {
ok
}
}

0 comments on commit e0b9802

Please sign in to comment.