-
This commit is contained in:
@ -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) => (
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user