Skip to content

Commit c706825

Browse files
committed
fix(rbac): update ui having dropped org role
1 parent c0360c7 commit c706825

File tree

6 files changed

+42
-24
lines changed

6 files changed

+42
-24
lines changed

frontend/src/app/invitations/accept/page.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,13 @@ function AcceptInvitationContent() {
201201
<>
202202
<strong>{invitation.inviter_name}</strong> has invited you to
203203
join <strong>{invitation.organization_name}</strong> as a{" "}
204-
<strong>{invitation.role}</strong>.
204+
<strong>{invitation.role_name}</strong>.
205205
</>
206206
) : (
207207
<>
208208
You&apos;ve been invited to join{" "}
209209
<strong>{invitation.organization_name}</strong> as a{" "}
210-
<strong>{invitation.role}</strong>.
210+
<strong>{invitation.role_name}</strong>.
211211
</>
212212
)}
213213
</CardDescription>
@@ -224,7 +224,7 @@ function AcceptInvitationContent() {
224224
<div className="flex justify-between">
225225
<span className="text-muted-foreground">Role</span>
226226
<span className="font-medium capitalize">
227-
{invitation.role}
227+
{invitation.role_name}
228228
</span>
229229
</div>
230230
{invitation.inviter_email && (
@@ -320,12 +320,12 @@ function AcceptInvitationContent() {
320320
{invitation.inviter_name ? (
321321
<>
322322
<strong>{invitation.inviter_name}</strong> has invited you to join
323-
this organization as a <strong>{invitation.role}</strong>.
323+
this organization as a <strong>{invitation.role_name}</strong>.
324324
</>
325325
) : (
326326
<>
327327
You&apos;ve been invited to join this organization as a{" "}
328-
<strong>{invitation.role}</strong>.
328+
<strong>{invitation.role_name}</strong>.
329329
</>
330330
)}
331331
</CardDescription>
@@ -341,7 +341,9 @@ function AcceptInvitationContent() {
341341
</div>
342342
<div className="flex justify-between">
343343
<span className="text-muted-foreground">Role</span>
344-
<span className="font-medium capitalize">{invitation.role}</span>
344+
<span className="font-medium capitalize">
345+
{invitation.role_name}
346+
</span>
345347
</div>
346348
{invitation.inviter_email && (
347349
<div className="flex justify-between">

frontend/src/components/organization/org-invitations-table.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { useForm } from "react-hook-form"
77
import { z } from "zod"
88
import {
99
type OrgInvitationRead,
10-
type OrgRole,
1110
organizationGetInvitationToken,
1211
} from "@/client"
1312
import {
@@ -68,7 +67,7 @@ import { useOrgInvitations } from "@/lib/hooks"
6867

6968
const invitationFormSchema = z.object({
7069
email: z.string().email("Invalid email address"),
71-
role: z.enum(["member", "admin", "owner"]),
70+
role_slug: z.enum(["member", "admin", "owner"]),
7271
})
7372

7473
type InvitationFormValues = z.infer<typeof invitationFormSchema>
@@ -85,15 +84,15 @@ export function OrgInvitationsTable() {
8584
resolver: zodResolver(invitationFormSchema),
8685
defaultValues: {
8786
email: "",
88-
role: "member",
87+
role_slug: "member",
8988
},
9089
})
9190

9291
const handleCreateInvitation = async (values: InvitationFormValues) => {
9392
try {
9493
await createInvitation({
9594
email: values.email,
96-
role: values.role as OrgRole,
95+
role_slug: values.role_slug,
9796
})
9897
form.reset()
9998
setIsCreateDialogOpen(false)
@@ -176,7 +175,7 @@ export function OrgInvitationsTable() {
176175
/>
177176
<FormField
178177
control={form.control}
179-
name="role"
178+
name="role_slug"
180179
render={({ field }) => (
181180
<FormItem>
182181
<FormLabel>Role</FormLabel>
@@ -249,7 +248,7 @@ export function OrgInvitationsTable() {
249248
enableHiding: false,
250249
},
251250
{
252-
accessorKey: "role",
251+
accessorKey: "role_name",
253252
header: ({ column }) => (
254253
<DataTableColumnHeader
255254
className="text-xs"
@@ -259,7 +258,7 @@ export function OrgInvitationsTable() {
259258
),
260259
cell: ({ row }) => (
261260
<div className="text-xs capitalize">
262-
{row.getValue<OrgInvitationRead["role"]>("role")}
261+
{row.getValue<OrgInvitationRead["role_name"]>("role_name")}
263262
</div>
264263
),
265264
enableSorting: true,

frontend/src/components/organization/org-members-table.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function OrgMembersTable() {
5959
const handleChangeRole = async (role: UserRole) => {
6060
try {
6161
if (selectedMember) {
62-
if (selectedMember.role === role) {
62+
if (selectedMember.role_slug === role) {
6363
toast({
6464
title: "Update skipped",
6565
description: `User ${selectedMember.email} is already a ${role} member`,
@@ -161,7 +161,7 @@ export function OrgMembersTable() {
161161
enableHiding: false,
162162
},
163163
{
164-
accessorKey: "role",
164+
accessorKey: "role_slug",
165165
header: ({ column }) => (
166166
<DataTableColumnHeader
167167
className="text-xs"
@@ -171,7 +171,7 @@ export function OrgMembersTable() {
171171
),
172172
cell: ({ row }) => (
173173
<div className="text-xs capitalize">
174-
{row.getValue<OrgMemberRead["role"]>("role")}
174+
{row.getValue<OrgMemberRead["role_slug"]>("role_slug") || "-"}
175175
</div>
176176
),
177177
enableSorting: true,

frontend/src/hooks/use-org-membership.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function useOrgMembership() {
2626

2727
// Check if user has org-level admin/owner role (not platform admin)
2828
const hasOrgAdminRole =
29-
membership?.role === "admin" || membership?.role === "owner"
29+
membership?.role_slug === "admin" || membership?.role_slug === "owner"
3030

3131
// Check if user can administer the org (platform admin OR org admin/owner)
3232
const canAdministerOrg = user?.isPlatformAdmin() || hasOrgAdminRole

tracecat/auth/credentials.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -902,8 +902,6 @@ async def role_dependency_not_req_ws(
902902
)
903903

904904
return Depends(role_dependency_not_req_ws)
905-
else:
906-
raise ValueError(f"Invalid require_workspace value: {require_workspace}")
907905

908906

909907
# --- Platform-level (Superuser) Authentication ---

tracecat/organization/router.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
OrganizationInvitation,
1616
OrganizationMembership,
1717
User,
18+
UserRoleAssignment,
19+
)
20+
from tracecat.db.models import (
21+
Role as RoleModel,
1822
)
1923
from tracecat.exceptions import (
2024
TracecatAuthorizationError,
@@ -111,13 +115,26 @@ async def get_current_org_member(
111115
detail="No organization context",
112116
)
113117

114-
# Query user and membership directly (no admin access required)
118+
# Query user, membership, and org-level role assignment
119+
# We need to left join on UserRoleAssignment and Role to get the org-level role
115120
statement = (
116-
select(User, OrganizationMembership.role)
121+
select(User, RoleModel.id, RoleModel.slug)
117122
.join(
118123
OrganizationMembership,
119124
OrganizationMembership.user_id == User.id, # pyright: ignore[reportArgumentType]
120125
)
126+
.outerjoin(
127+
UserRoleAssignment,
128+
and_(
129+
UserRoleAssignment.user_id == User.id, # pyright: ignore[reportArgumentType]
130+
UserRoleAssignment.organization_id == role.organization_id, # pyright: ignore[reportArgumentType]
131+
UserRoleAssignment.workspace_id.is_(None), # Org-level assignment
132+
),
133+
)
134+
.outerjoin(
135+
RoleModel,
136+
RoleModel.id == UserRoleAssignment.role_id,
137+
)
121138
.where(
122139
and_(
123140
User.id == role.user_id, # pyright: ignore[reportArgumentType]
@@ -129,13 +146,14 @@ async def get_current_org_member(
129146
row = result.tuples().one_or_none()
130147

131148
if row is not None:
132-
user, org_role = row
149+
user, role_id, role_slug = row
133150
return OrgMemberRead(
134151
user_id=user.id,
135152
first_name=user.first_name,
136153
last_name=user.last_name,
137154
email=user.email,
138-
role=org_role,
155+
role_id=role_id,
156+
role_slug=role_slug,
139157
is_active=user.is_active,
140158
is_superuser=user.is_superuser,
141159
is_verified=user.is_verified,
@@ -155,7 +173,8 @@ async def get_current_org_member(
155173
first_name=user.first_name,
156174
last_name=user.last_name,
157175
email=user.email,
158-
role=OrgRole.OWNER, # Superusers have implicit owner role
176+
role_id=None,
177+
role_slug=OrgRole.OWNER, # Superusers have implicit owner role
159178
is_active=user.is_active,
160179
is_superuser=user.is_superuser,
161180
is_verified=user.is_verified,

0 commit comments

Comments
 (0)