Skip to content

Commit 1996ebe

Browse files
committed
assing roles to principals functionality
1 parent 9196bd0 commit 1996ebe

File tree

6 files changed

+461
-19
lines changed

6 files changed

+461
-19
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {NextRequest, NextResponse} from 'next/server';
2+
import {apiManagementPrincipalPrincipalRolesUrl} from "@/app/constants";
3+
import {getRealmHeadersFromRequest} from "@/utils/auth";
4+
5+
6+
export async function DELETE(
7+
request: NextRequest,
8+
{params}: { params: Promise<{ principalName: string; principalRoleName: string }> }
9+
) {
10+
try {
11+
const authHeader = request.headers.get('Authorization');
12+
13+
if (!authHeader) {
14+
return NextResponse.json(
15+
{
16+
error: {
17+
message: 'Authorization header is required',
18+
type: 'UnauthorizedError',
19+
code: 401
20+
}
21+
},
22+
{status: 401}
23+
);
24+
}
25+
26+
const {principalName, principalRoleName} = await params;
27+
const url = `${apiManagementPrincipalPrincipalRolesUrl(principalName)}/${encodeURIComponent(principalRoleName)}`;
28+
29+
console.log('Removing principal role from principal:', principalName, principalRoleName);
30+
31+
const realmHeaders = getRealmHeadersFromRequest(request);
32+
33+
const response = await fetch(url, {
34+
method: 'DELETE',
35+
headers: {
36+
'Authorization': authHeader,
37+
'Content-Type': 'application/json',
38+
...realmHeaders,
39+
},
40+
});
41+
42+
if (!response.ok) {
43+
const data = await response.json().catch(() => ({
44+
error: {
45+
message: 'Failed to remove principal role from principal',
46+
type: 'DeleteError',
47+
code: response.status
48+
}
49+
}));
50+
console.error('Backend error response:', data);
51+
return NextResponse.json(data, {status: response.status});
52+
}
53+
54+
console.log('Principal role removed from principal successfully');
55+
return NextResponse.json({success: true}, {status: 204});
56+
} catch (error) {
57+
console.error('Remove principal role from principal proxy error:', error);
58+
return NextResponse.json(
59+
{error: {message: 'Internal server error', type: 'InternalServerError', code: 500}},
60+
{status: 500}
61+
);
62+
}
63+
}
64+

src/app/api/principals/[principalName]/principal-roles/route.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,65 @@ export async function GET(
6464
}
6565
}
6666

67+
export async function PUT(
68+
request: NextRequest,
69+
{params}: { params: Promise<{ principalName: string }> }
70+
) {
71+
try {
72+
const authHeader = request.headers.get('Authorization');
73+
74+
if (!authHeader) {
75+
return NextResponse.json(
76+
{
77+
error: {
78+
message: 'Authorization header is required',
79+
type: 'UnauthorizedError',
80+
code: 401
81+
}
82+
},
83+
{status: 401}
84+
);
85+
}
86+
87+
const {principalName} = await params;
88+
const url = apiManagementPrincipalPrincipalRolesUrl(principalName);
89+
90+
console.log('Assigning principal role to principal:', principalName);
91+
92+
const body = await request.json();
93+
console.log('Request body:', body);
94+
95+
const realmHeaders = getRealmHeadersFromRequest(request);
96+
97+
const response = await fetch(url, {
98+
method: 'PUT',
99+
headers: {
100+
'Authorization': authHeader,
101+
'Content-Type': 'application/json',
102+
...realmHeaders,
103+
},
104+
body: JSON.stringify(body),
105+
});
106+
107+
if (!response.ok) {
108+
const data = await response.json().catch(() => ({
109+
error: {
110+
message: 'Failed to assign principal role',
111+
type: 'AssignError',
112+
code: response.status
113+
}
114+
}));
115+
console.error('Backend error response:', data);
116+
return NextResponse.json(data, {status: response.status});
117+
}
118+
119+
console.log('Principal role assigned successfully');
120+
return NextResponse.json({success: true}, {status: 201});
121+
} catch (error) {
122+
console.error('Assign principal role proxy error:', error);
123+
return NextResponse.json(
124+
{error: {message: 'Internal server error', type: 'InternalServerError', code: 500}},
125+
{status: 500}
126+
);
127+
}
128+
}

src/app/principals/page.tsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {UserAddOutlined, UserOutlined} from '@ant-design/icons';
66
import {useAuthenticatedFetch} from '@/hooks/useAuthenticatedFetch';
77
import Principals, {Principal, PrincipalRoleItem} from '@/app/ui/principals';
88
import CreatePrincipalModal from '@/app/ui/create-principal-modal';
9+
import AssignPrincipalRoleModal from '@/app/ui/assign-principal-role-modal';
910

1011
const {Title} = Typography;
1112

@@ -22,6 +23,8 @@ export default function Page() {
2223
const [principalRoles, setPrincipalRoles] = useState<Record<string, PrincipalRoleItem[]>>({});
2324
const [rolesLoading, setRolesLoading] = useState<Record<string, boolean>>({});
2425
const [createModalVisible, setCreateModalVisible] = useState(false);
26+
const [assignRoleModalVisible, setAssignRoleModalVisible] = useState(false);
27+
const [selectedPrincipalForRole, setSelectedPrincipalForRole] = useState<string | null>(null);
2528
const {authenticatedFetch} = useAuthenticatedFetch();
2629

2730
const getPrincipals = useCallback(async (): Promise<Principal[]> => {
@@ -130,6 +133,49 @@ export default function Page() {
130133
}
131134
};
132135

136+
const handleAddRole = (principalName: string) => {
137+
setSelectedPrincipalForRole(principalName);
138+
setAssignRoleModalVisible(true);
139+
};
140+
141+
const handleAssignRoleSuccess = async () => {
142+
if (selectedPrincipalForRole) {
143+
// Refresh the principal roles for the selected principal
144+
setRolesLoading(prev => ({...prev, [selectedPrincipalForRole]: true}));
145+
try {
146+
const roles = await getPrincipalRoles(selectedPrincipalForRole);
147+
setPrincipalRoles(prev => ({...prev, [selectedPrincipalForRole]: roles}));
148+
} finally {
149+
setRolesLoading(prev => ({...prev, [selectedPrincipalForRole]: false}));
150+
}
151+
}
152+
};
153+
154+
const handleDeletePrincipalRole = async (principalName: string, roleName: string) => {
155+
try {
156+
await authenticatedFetch(
157+
`/api/principals/${encodeURIComponent(principalName)}/principal-roles/${encodeURIComponent(roleName)}`,
158+
{
159+
method: 'DELETE',
160+
}
161+
);
162+
163+
message.success(`Role "${roleName}" removed from principal "${principalName}" successfully!`);
164+
165+
// Refresh the principal roles for the selected principal
166+
setRolesLoading(prev => ({...prev, [principalName]: true}));
167+
try {
168+
const roles = await getPrincipalRoles(principalName);
169+
setPrincipalRoles(prev => ({...prev, [principalName]: roles}));
170+
} finally {
171+
setRolesLoading(prev => ({...prev, [principalName]: false}));
172+
}
173+
} catch (error) {
174+
console.error('Error removing principal role:', error);
175+
throw error; // Re-throw to let the modal handle the error display
176+
}
177+
};
178+
133179
useEffect(() => {
134180
async function fetchPrincipals() {
135181
setLoading(true);
@@ -199,13 +245,25 @@ export default function Page() {
199245
rolesLoading={rolesLoading}
200246
onResetCredentials={handleResetCredentials}
201247
onDelete={handleDeletePrincipal}
248+
onAddRole={handleAddRole}
249+
onDeletePrincipalRole={handleDeletePrincipalRole}
202250
/>
203251

204252
<CreatePrincipalModal
205253
visible={createModalVisible}
206254
onClose={() => setCreateModalVisible(false)}
207255
onSuccess={handleCreateSuccess}
208256
/>
257+
258+
<AssignPrincipalRoleModal
259+
visible={assignRoleModalVisible}
260+
principalName={selectedPrincipalForRole}
261+
onClose={() => {
262+
setAssignRoleModalVisible(false);
263+
setSelectedPrincipalForRole(null);
264+
}}
265+
onSuccess={handleAssignRoleSuccess}
266+
/>
209267
</Space>
210268
);
211269
}

0 commit comments

Comments
 (0)