Skip to content

feat: Admin/Viewer 역할 분리 (설정 vs 로그 탐색)#1

Open
hpcnt-roa wants to merge 1 commit into
mainfrom
feat/viewer-admin-roles
Open

feat: Admin/Viewer 역할 분리 (설정 vs 로그 탐색)#1
hpcnt-roa wants to merge 1 commit into
mainfrom
feat/viewer-admin-roles

Conversation

@hpcnt-roa

Copy link
Copy Markdown

Summary

  • User에 role: admin | viewer 추가. 팀 최초 가입자·ADMIN_EMAILS는 admin, invite 기본은 viewer.
  • sources / connections / team / alerts / webhooks 쓰기는 admin만 (403).
  • Team Settings 메뉴·Spotlight·/team은 viewer에게 숨김/리다이렉트.
  • local mode는 기존처럼 auth 없음 + 주입 유저 admin.

Test plan

  • ADMIN_EMAILS 없이 첫 register → admin, /me.role=admin
  • invitee join → viewer, sources POST → 403, 로그 search(clickhouse-proxy) → 200
  • ADMIN_EMAILS에 넣은 이메일 invite → admin
  • local mode 배포 시 Team Settings 동작(admin) 유지

상세: FORK.md

Made with Cursor

HPCNT fork: first registrant and ADMIN_EMAILS are admin; invites default
to viewer. Mutations on sources/connections/team/alerts/webhooks require
admin; Team Settings UI is hidden for viewers.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hpcnt-roa hpcnt-roa self-assigned this Jul 21, 2026
@hpcnt-roa
hpcnt-roa requested a review from a team July 21, 2026 07:54

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 4 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit a4a1c5f. Configure here.

void router.replace('/search');
}
}, [hasAdminAccess, router]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Team page shows settings to viewers

High Severity

After switching hasAdminAccess to useIsAdmin(), only the team-name edit control is gated. Tabs still render all Team Settings sections (sources, connections, members, etc.) whenever team is loaded, while the redirect runs in useEffect. Viewers who open /team can see admin UI and trigger those fetches before navigation finishes.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a4a1c5f. Configure here.

return true;
}
// Missing role (legacy) → admin so we don't lock out existing users.
return (me?.role ?? 'admin') === 'admin';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Admin UI shown while loading

Medium Severity

useIsAdmin() treats a missing /me payload the same as legacy users without a role: (me?.role ?? 'admin') === 'admin'. While useMe() is still loading, me is undefined, so viewers briefly count as admin and see Team Settings in nav and Spotlight until /me returns role: 'viewer'.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a4a1c5f. Configure here.

return next();
}
return requireAdmin(req, res, next);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Viewers read team secrets via GET

High Severity

Mounting requireAdminForMutations on /team lets viewers pass all GET requests. GET /team returns the team apiKey, and GET /team/invitations returns invite URLs built from secret tokens—data meant for admins, not log-only viewers.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a4a1c5f. Configure here.

isUserAuthenticated,
requireAdminForMutations,
sourcesRouter,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

External API skips role checks

High Severity

Session routes for /sources, /connections, /team, /alerts, and /webhooks now use requireAdminForMutations, but /api/v2 still authenticates with access keys only. Viewers receive an accessKey from /me and can mutate those resources through v2 while the UI returns 403 on the internal routes.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a4a1c5f. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant