diff --git a/frontend/src/pages/AccessControl.tsx b/frontend/src/pages/AccessControl.tsx index 0a13873..2bed898 100644 --- a/frontend/src/pages/AccessControl.tsx +++ b/frontend/src/pages/AccessControl.tsx @@ -15,6 +15,7 @@ import { DialogTitle, } from '@/components/ui/dialog'; import { IconTile } from '@/components/ui/icon-tile'; +import { ConfirmDialog } from '@/components/ui/confirm-dialog'; import { DropdownMenu, DropdownMenuContent, @@ -814,194 +815,125 @@ export function AccessControl() { {/* Delete Key Dialog */} - - - - Delete API Key - - Are you sure you want to delete "{selectedKey?.name}"? Applications using this key - will lose access immediately. - - - - - - - - + {/* Secret Key Dialog */} - - + + - Secret Access Key - - Copy your secret access key now. For security reasons, it cannot be viewed again. - + } tone="primary" size="md" /> +
+ {selectedKey?.name || 'Access key'} + + Reveal and copy this key's credentials. The secret is fetched on demand. + +
-
-
- -
{selectedKey?.name}
-
-
- -
- { - if (selectedKey?.accessKeyId) { - navigator.clipboard.writeText(selectedKey.accessKeyId); - toast.success('Access Key ID copied to clipboard'); - } - }} - > - {selectedKey?.accessKeyId} - - -
-
-
- -
- {isLoadingSecretKey ? ( -
- - Loading secret key... -
- ) : ( - <> - { - if (revealedSecretKey) { - navigator.clipboard.writeText(revealedSecretKey); - toast.success('Secret Access Key copied to clipboard'); - } - }} - > - {revealedSecretKey} - - - - )} -
-
-
+ + + + - +
{/* Key Settings Dialog */} - - + + - Key Settings - {settingsKey?.name} - - Manage activation status and expiration date for this API key - + } tone="primary" size="md" /> +
+ Key settings · {settingsKey?.name} + Manage activation and expiration for this access key. +
-
- {/* Status */} -
- -
- - + +
+ +
+ {(['active', 'inactive'] as const).map((s) => { + const selected = keyStatus === s; + return ( + + ); + })}
-

- Inactive keys cannot be used for authentication +

+ Inactive keys cannot be used for authentication.

- {/* Expiration */} -
- -
- +
+ - {!neverExpires && ( -
- - setExpirationDate(e.target.value)} - className="w-full" - /> -

- Key will automatically become inactive after this date -

-
- )} -
+ {!neverExpires && ( +
+ + setExpirationDate(e.target.value)} + className="w-full" + /> +

+ The key will become inactive after this moment. +

+
+ )}
-
+
- +
@@ -1119,173 +1051,133 @@ export function AccessControl() {
{/* Edit Permissions Dialog */} - - + + - Edit Bucket Permissions - {editingKey?.name} - - Grant this access key permissions on buckets - + } tone="primary" size="md" /> +
+ Bucket permissions · {editingKey?.name} + + Select a bucket, then toggle the scopes this key should have on it. + +
-
- {/* Bucket Selection */} -
- - handleBucketChange(value)}> + Select a bucket… {availableBuckets.map((bucket) => ( {bucket.name} ))} -

- Choose which bucket this key should have permissions on. Current permissions will be displayed when selected. -

- {/* Permissions */} -
- -
-
- setPermissionRead(checked as boolean)} - /> -
- -

- Allows reading objects from the bucket (GetObject, HeadObject, ListObjects) -

-
-
- -
- setPermissionWrite(checked as boolean)} - /> -
- -

- Allows writing and deleting objects in the bucket (PutObject, DeleteObject) -

-
-
- -
- setPermissionOwner(checked as boolean)} - /> -
- -

- Allows managing bucket settings and policies (DeleteBucket, PutBucketPolicy) -

-
-
-
-
- - {/* Current Permissions Info */} - {selectedBucket && editingKey && ( -
- -
- {(() => { - const bucketPermission = editingKey.permissions.find( - perm => perm.bucketName === selectedBucket || perm.bucketId === selectedBucket - ); - - if (bucketPermission) { - const hasPermissions = bucketPermission.read || bucketPermission.write || bucketPermission.owner; - if (hasPermissions) { - return ( -
-

- This key currently has the following permissions on this bucket: -

-
- {bucketPermission.read && ( - Read - )} - {bucketPermission.write && ( - Write - )} - {bucketPermission.owner && ( - Owner - )} -
-

- Modify the checkboxes above to update permissions -

-
- ); - } - } - return ( -

- This key has no permissions on this bucket yet. Select permissions above to grant access. -

- ); - })()} -
-
- )} - - {/* Current Bucket Permissions List */} - {editingKey && editingKey.permissions.length > 0 && ( -
- -
-
- {editingKey.permissions.map((perm, idx) => ( -
- {perm.bucketName} -
- {perm.read && R} - {perm.write && W} - {perm.owner && O} + {selectedBucket && ( + <> +
+ +
+ {[ + { + id: 'edit-permission-read', + label: 'Read', + desc: 'GetObject, HeadObject, ListObjects', + checked: permissionRead, + setChecked: setPermissionRead, + }, + { + id: 'edit-permission-write', + label: 'Write', + desc: 'PutObject, DeleteObject', + checked: permissionWrite, + setChecked: setPermissionWrite, + }, + { + id: 'edit-permission-owner', + label: 'Owner', + desc: 'DeleteBucket, PutBucketPolicy', + checked: permissionOwner, + setChecked: setPermissionOwner, + }, + ].map((p) => ( +
+ ))}
+ + {editingKey && (() => { + const current = editingKey.permissions.find( + (perm) => perm.bucketName === selectedBucket || perm.bucketId === selectedBucket, + ); + const hasAny = current && (current.read || current.write || current.owner); + return ( +
+
+ Currently granted +
+ {hasAny ? ( +
+ {current!.read && Read} + {current!.write && Write} + {current!.owner && Owner} +
+ ) : ( +

+ No permissions on this bucket yet. +

+ )} +
+ ); + })()} + + )} + + {editingKey && editingKey.permissions.length > 0 && ( +
+ +
+ {editingKey.permissions.map((perm, idx) => ( +
+ {perm.bucketName} +
+ {perm.read && R} + {perm.write && W} + {perm.owner && O} +
+
+ ))} +
)} -
+ -