This commit is contained in:
2025-08-27 12:46:16 -04:00
parent a641b4e2d1
commit dc0ea9d4c7
3 changed files with 66 additions and 66 deletions

View File

@ -49,7 +49,7 @@ const Applications: React.FC = () => {
token_renewal_duration: '24h', token_renewal_duration: '24h',
max_token_duration: '168h', max_token_duration: '168h',
owner: { owner: {
type: 'user', type: 'individual',
name: 'Admin User', name: 'Admin User',
owner: 'admin@example.com', owner: 'admin@example.com',
}, },
@ -82,17 +82,40 @@ const Applications: React.FC = () => {
} }
}; };
const parseDuration = (duration: string): number => {
// Convert duration string like "24h" to seconds
const match = duration.match(/^(\d+)([hmd]?)$/);
if (!match) return 86400; // Default to 24h in seconds
const value = parseInt(match[1]);
const unit = match[2] || 'h';
switch (unit) {
case 'm': return value * 60; // minutes to seconds
case 'h': return value * 3600; // hours to seconds
case 'd': return value * 86400; // days to seconds
default: return value * 3600; // default to hours
}
};
const handleSubmit = async (values: CreateApplicationRequest) => { const handleSubmit = async (values: CreateApplicationRequest) => {
try { try {
// Convert duration strings to seconds for API
const apiValues = {
...values,
token_renewal_duration: parseDuration(values.token_renewal_duration),
max_token_duration: parseDuration(values.max_token_duration),
};
if (editingApp) { if (editingApp) {
await apiService.updateApplication(editingApp.app_id, values); await apiService.updateApplication(editingApp.app_id, apiValues);
notifications.show({ notifications.show({
title: 'Success', title: 'Success',
message: 'Application updated successfully', message: 'Application updated successfully',
color: 'green', color: 'green',
}); });
} else { } else {
await apiService.createApplication(values); await apiService.createApplication(apiValues);
notifications.show({ notifications.show({
title: 'Success', title: 'Success',
message: 'Application created successfully', message: 'Application created successfully',
@ -164,11 +187,8 @@ const Applications: React.FC = () => {
}; };
const appTypeOptions = [ const appTypeOptions = [
{ value: 'web', label: 'Web Application' }, { value: 'static', label: 'Static' },
{ value: 'mobile', label: 'Mobile Application' }, { value: 'user', label: 'User' },
{ value: 'api', label: 'API Service' },
{ value: 'cli', label: 'CLI Tool' },
{ value: 'service', label: 'Background Service' },
]; ];
const rows = applications.map((app) => ( const rows = applications.map((app) => (

View File

@ -27,12 +27,12 @@ const permissionHierarchy: PermissionNode[] = [
{ {
id: 'app', id: 'app',
label: 'Application', label: 'Application',
description: 'Full control of applications', description: 'Access to application management',
children: [ children: [
{ {
id: 'app.read', id: 'app.read',
label: 'Read', label: 'Read',
description: 'View application details', description: 'Read application information',
}, },
{ {
id: 'app.write', id: 'app.write',
@ -49,12 +49,12 @@ const permissionHierarchy: PermissionNode[] = [
{ {
id: 'token', id: 'token',
label: 'Token', label: 'Token',
description: 'Full control of tokens', description: 'Access to token management',
children: [ children: [
{ {
id: 'token.read', id: 'token.read',
label: 'Read', label: 'Read',
description: 'View token details', description: 'Read token information',
}, },
{ {
id: 'token.create', id: 'token.create',
@ -71,39 +71,49 @@ const permissionHierarchy: PermissionNode[] = [
{ {
id: 'repo', id: 'repo',
label: 'Repository', label: 'Repository',
description: 'Full control of repositories', description: 'Access to repository operations',
children: [ children: [
{ {
id: 'repo.read', id: 'repo.read',
label: 'Read', label: 'Read',
description: 'Read repository contents', description: 'Read repository data',
}, },
{ {
id: 'repo.write', id: 'repo.write',
label: 'Write', label: 'Write',
description: 'Push to repositories', description: 'Write to repositories',
}, },
{ {
id: 'repo.admin', id: 'repo.admin',
label: 'Admin', label: 'Admin',
description: 'Full repository administration', description: 'Administrative access to repositories',
}, },
], ],
}, },
{ {
id: 'permission', id: 'permission',
label: 'Permission', label: 'Permission',
description: 'Full control of permissions', description: 'Access to permission management',
children: [ children: [
{ {
id: 'permission.read', id: 'permission.read',
label: 'Read', label: 'Read',
description: 'View permission details', description: 'Read permission information',
}, },
{ {
id: 'permission.write', id: 'permission.write',
label: 'Write', label: 'Write',
description: 'Modify permissions', description: 'Create and update permissions',
},
{
id: 'permission.grant',
label: 'Grant',
description: 'Grant permissions to tokens',
},
{
id: 'permission.revoke',
label: 'Revoke',
description: 'Revoke permissions from tokens',
}, },
], ],
}, },
@ -130,37 +140,16 @@ const PermissionTree: React.FC<PermissionTreeProps> = ({ permissions, onChange }
}); });
}; };
const isChildDisabled = (childId: string): boolean => {
// Find the parent of this child
for (const parent of permissionHierarchy) {
if (parent.children?.some(child => child.id === childId)) {
const wildcardPermission = `${parent.id}.*`;
return permissions.includes(wildcardPermission);
}
}
return false;
};
const getNodeState = (node: PermissionNode): 'checked' | 'indeterminate' | 'unchecked' => { const getNodeState = (node: PermissionNode): 'checked' | 'indeterminate' | 'unchecked' => {
if (!node.children) { if (!node.children) {
// For leaf nodes, check if they're explicitly selected OR their parent wildcard is selected // For leaf nodes, check if they're explicitly selected
const isExplicitlySelected = permissions.includes(node.id); return permissions.includes(node.id) ? 'checked' : 'unchecked';
const isParentWildcardSelected = isChildDisabled(node.id);
return (isExplicitlySelected || isParentWildcardSelected) ? 'checked' : 'unchecked';
} }
// Check if parent wildcard permission exists // For parent nodes, check how many children are selected
const wildcardPermission = `${node.id}.*`; const checkedChildren = node.children.filter(child =>
if (permissions.includes(wildcardPermission)) { permissions.includes(child.id)
return 'checked'; );
}
// Check children states
const checkedChildren = node.children.filter(child => {
const isExplicitlySelected = permissions.includes(child.id);
const isParentWildcardSelected = permissions.includes(wildcardPermission);
return isExplicitlySelected || isParentWildcardSelected;
});
if (checkedChildren.length === 0) { if (checkedChildren.length === 0) {
return 'unchecked'; return 'unchecked';
@ -184,23 +173,19 @@ const PermissionTree: React.FC<PermissionTreeProps> = ({ permissions, onChange }
newPermissions = newPermissions.filter(p => p !== node.id); newPermissions = newPermissions.filter(p => p !== node.id);
} }
} else { } else {
// Parent node // Parent node - add/remove all child permissions individually
const wildcardPermission = `${node.id}.*`;
if (checked) { if (checked) {
// Add wildcard permission and remove specific child permissions // Add all child permissions
if (!newPermissions.includes(wildcardPermission)) { node.children.forEach(child => {
newPermissions.push(wildcardPermission); if (!newPermissions.includes(child.id)) {
} newPermissions.push(child.id);
// Remove specific child permissions as they're covered by wildcard }
});
} else {
// Remove all child permissions
node.children.forEach(child => { node.children.forEach(child => {
newPermissions = newPermissions.filter(p => p !== child.id); newPermissions = newPermissions.filter(p => p !== child.id);
}); });
} else {
// Remove wildcard permission and all child permissions
newPermissions = newPermissions.filter(p =>
p !== wildcardPermission && !node.children!.some(child => child.id === p)
);
} }
} }
@ -246,7 +231,6 @@ const PermissionTree: React.FC<PermissionTreeProps> = ({ permissions, onChange }
onChange={(event) => handleNodeChange(node, event.currentTarget.checked)} onChange={(event) => handleNodeChange(node, event.currentTarget.checked)}
size="sm" size="sm"
style={{ marginTop: '1px' }} style={{ marginTop: '1px' }}
disabled={!node.children && isChildDisabled(node.id)}
/> />
<Box style={{ flex: 1 }}> <Box style={{ flex: 1 }}>
@ -254,7 +238,6 @@ const PermissionTree: React.FC<PermissionTreeProps> = ({ permissions, onChange }
<Text <Text
size="sm" size="sm"
fw={hasChildren ? 600 : 500} fw={hasChildren ? 600 : 500}
c={!node.children && isChildDisabled(node.id) ? 'dimmed' : undefined}
> >
{node.label} {node.label}
{hasChildren && ' (all)'} {hasChildren && ' (all)'}
@ -263,10 +246,7 @@ const PermissionTree: React.FC<PermissionTreeProps> = ({ permissions, onChange }
<Text <Text
size="xs" size="xs"
c="dimmed" c="dimmed"
style={{ style={{ whiteSpace: 'nowrap' }}
whiteSpace: 'nowrap',
opacity: !node.children && isChildDisabled(node.id) ? 0.6 : 1
}}
> >
- {node.description} - {node.description}
</Text> </Text>

View File

@ -56,7 +56,7 @@ const Tokens: React.FC = () => {
initialValues: { initialValues: {
app_id: '', app_id: '',
owner: { owner: {
type: 'user', type: 'individual',
name: 'Admin User', name: 'Admin User',
owner: 'admin@example.com', owner: 'admin@example.com',
}, },