@checkstack/auth-frontend 0.0.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +172 -0
- package/package.json +1 -1
- package/src/api.ts +2 -2
- package/src/components/ApplicationsTab.tsx +10 -3
- package/src/components/AuthSettingsPage.tsx +66 -64
- package/src/components/LoginPage.tsx +8 -7
- package/src/components/RegisterPage.tsx +5 -8
- package/src/components/RoleDialog.tsx +43 -37
- package/src/components/RolesTab.tsx +10 -10
- package/src/components/StrategiesTab.tsx +3 -3
- package/src/components/TeamAccessEditor.tsx +557 -0
- package/src/components/TeamsTab.tsx +569 -0
- package/src/components/UsersTab.tsx +2 -2
- package/src/hooks/{usePermissions.ts → useAccessRules.ts} +10 -10
- package/src/index.test.tsx +83 -37
- package/src/index.tsx +33 -51
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,177 @@
|
|
|
1
1
|
# @checkstack/auth-frontend
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9faec1f: # Unified AccessRule Terminology Refactoring
|
|
8
|
+
|
|
9
|
+
This release completes a comprehensive terminology refactoring from "permission" to "accessRule" across the entire codebase, establishing a consistent and modern access control vocabulary.
|
|
10
|
+
|
|
11
|
+
## Changes
|
|
12
|
+
|
|
13
|
+
### Core Infrastructure (`@checkstack/common`)
|
|
14
|
+
|
|
15
|
+
- Introduced `AccessRule` interface as the primary access control type
|
|
16
|
+
- Added `accessPair()` helper for creating read/manage access rule pairs
|
|
17
|
+
- Added `access()` builder for individual access rules
|
|
18
|
+
- Replaced `Permission` type with `AccessRule` throughout
|
|
19
|
+
|
|
20
|
+
### API Changes
|
|
21
|
+
|
|
22
|
+
- `env.registerPermissions()` → `env.registerAccessRules()`
|
|
23
|
+
- `meta.permissions` → `meta.access` in RPC contracts
|
|
24
|
+
- `usePermission()` → `useAccess()` in frontend hooks
|
|
25
|
+
- Route `permission:` field → `accessRule:` field
|
|
26
|
+
|
|
27
|
+
### UI Changes
|
|
28
|
+
|
|
29
|
+
- "Roles & Permissions" tab → "Roles & Access Rules"
|
|
30
|
+
- "You don't have permission..." → "You don't have access..."
|
|
31
|
+
- All permission-related UI text updated
|
|
32
|
+
|
|
33
|
+
### Documentation & Templates
|
|
34
|
+
|
|
35
|
+
- Updated 18 documentation files with AccessRule terminology
|
|
36
|
+
- Updated 7 scaffolding templates with `accessPair()` pattern
|
|
37
|
+
- All code examples use new AccessRule API
|
|
38
|
+
|
|
39
|
+
## Migration Guide
|
|
40
|
+
|
|
41
|
+
### Backend Plugins
|
|
42
|
+
|
|
43
|
+
```diff
|
|
44
|
+
- import { permissionList } from "./permissions";
|
|
45
|
+
- env.registerPermissions(permissionList);
|
|
46
|
+
+ import { accessRules } from "./access";
|
|
47
|
+
+ env.registerAccessRules(accessRules);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### RPC Contracts
|
|
51
|
+
|
|
52
|
+
```diff
|
|
53
|
+
- .meta({ userType: "user", permissions: [permissions.read.id] })
|
|
54
|
+
+ .meta({ userType: "user", access: [access.read] })
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Frontend Hooks
|
|
58
|
+
|
|
59
|
+
```diff
|
|
60
|
+
- const canRead = accessApi.usePermission(permissions.read.id);
|
|
61
|
+
+ const canRead = accessApi.useAccess(access.read);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Routes
|
|
65
|
+
|
|
66
|
+
```diff
|
|
67
|
+
- permission: permissions.entityRead.id,
|
|
68
|
+
+ accessRule: access.read,
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Patch Changes
|
|
72
|
+
|
|
73
|
+
- 95eeec7: # Auto-login after credential registration
|
|
74
|
+
|
|
75
|
+
Users are now automatically logged in after successful registration when using the credential (email & password) authentication strategy.
|
|
76
|
+
|
|
77
|
+
## Changes
|
|
78
|
+
|
|
79
|
+
### Backend (`@checkstack/auth-backend`)
|
|
80
|
+
|
|
81
|
+
- Added `autoSignIn: true` to the `emailAndPassword` configuration in better-auth
|
|
82
|
+
- Users no longer need to manually log in after registration; a session is created immediately upon successful sign-up
|
|
83
|
+
|
|
84
|
+
### Frontend (`@checkstack/auth-frontend`)
|
|
85
|
+
|
|
86
|
+
- Updated `RegisterPage` to use full page navigation after registration to ensure the session state refreshes correctly
|
|
87
|
+
- Updated `LoginPage` to use full page navigation after login to ensure fresh permissions state when switching between users
|
|
88
|
+
|
|
89
|
+
- Updated dependencies [9faec1f]
|
|
90
|
+
- Updated dependencies [f533141]
|
|
91
|
+
- @checkstack/auth-common@0.2.0
|
|
92
|
+
- @checkstack/common@0.2.0
|
|
93
|
+
- @checkstack/frontend-api@0.1.0
|
|
94
|
+
- @checkstack/ui@0.2.0
|
|
95
|
+
|
|
96
|
+
## 0.1.0
|
|
97
|
+
|
|
98
|
+
### Minor Changes
|
|
99
|
+
|
|
100
|
+
- 8e43507: # Teams and Resource-Level Access Control
|
|
101
|
+
|
|
102
|
+
This release introduces a comprehensive Teams system for organizing users and controlling access to resources at a granular level.
|
|
103
|
+
|
|
104
|
+
## Features
|
|
105
|
+
|
|
106
|
+
### Team Management
|
|
107
|
+
|
|
108
|
+
- Create, update, and delete teams with name and description
|
|
109
|
+
- Add/remove users from teams
|
|
110
|
+
- Designate team managers with elevated privileges
|
|
111
|
+
- View team membership and manager status
|
|
112
|
+
|
|
113
|
+
### Resource-Level Access Control
|
|
114
|
+
|
|
115
|
+
- Grant teams access to specific resources (systems, health checks, incidents, maintenances)
|
|
116
|
+
- Configure read-only or manage permissions per team
|
|
117
|
+
- Resource-level "Team Only" mode that restricts access exclusively to team members
|
|
118
|
+
- Separate `resourceAccessSettings` table for resource-level settings (not per-grant)
|
|
119
|
+
- Automatic cleanup of grants when teams are deleted (database cascade)
|
|
120
|
+
|
|
121
|
+
### Middleware Integration
|
|
122
|
+
|
|
123
|
+
- Extended `autoAuthMiddleware` to support resource access checks
|
|
124
|
+
- Single-resource pre-handler validation for detail endpoints
|
|
125
|
+
- Automatic list filtering for collection endpoints
|
|
126
|
+
- S2S endpoints for access verification
|
|
127
|
+
|
|
128
|
+
### Frontend Components
|
|
129
|
+
|
|
130
|
+
- `TeamsTab` component for managing teams in Auth Settings
|
|
131
|
+
- `TeamAccessEditor` component for assigning team access to resources
|
|
132
|
+
- Resource-level "Team Only" toggle in `TeamAccessEditor`
|
|
133
|
+
- Integration into System, Health Check, Incident, and Maintenance editors
|
|
134
|
+
|
|
135
|
+
## Breaking Changes
|
|
136
|
+
|
|
137
|
+
### API Response Format Changes
|
|
138
|
+
|
|
139
|
+
List endpoints now return objects with named keys instead of arrays directly:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// Before
|
|
143
|
+
const systems = await catalogApi.getSystems();
|
|
144
|
+
|
|
145
|
+
// After
|
|
146
|
+
const { systems } = await catalogApi.getSystems();
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Affected endpoints:
|
|
150
|
+
|
|
151
|
+
- `catalog.getSystems` → `{ systems: [...] }`
|
|
152
|
+
- `healthcheck.getConfigurations` → `{ configurations: [...] }`
|
|
153
|
+
- `incident.listIncidents` → `{ incidents: [...] }`
|
|
154
|
+
- `maintenance.listMaintenances` → `{ maintenances: [...] }`
|
|
155
|
+
|
|
156
|
+
### User Identity Enrichment
|
|
157
|
+
|
|
158
|
+
`RealUser` and `ApplicationUser` types now include `teamIds: string[]` field with team memberships.
|
|
159
|
+
|
|
160
|
+
## Documentation
|
|
161
|
+
|
|
162
|
+
See `docs/backend/teams.md` for complete API reference and integration guide.
|
|
163
|
+
|
|
164
|
+
### Patch Changes
|
|
165
|
+
|
|
166
|
+
- 97c5a6b: Fix Radix UI accessibility warning in dialog components by adding visually hidden DialogDescription components
|
|
167
|
+
- Updated dependencies [8e43507]
|
|
168
|
+
- Updated dependencies [97c5a6b]
|
|
169
|
+
- Updated dependencies [8e43507]
|
|
170
|
+
- @checkstack/ui@0.1.0
|
|
171
|
+
- @checkstack/auth-common@0.1.0
|
|
172
|
+
- @checkstack/common@0.1.0
|
|
173
|
+
- @checkstack/frontend-api@0.0.4
|
|
174
|
+
|
|
3
175
|
## 0.0.4
|
|
4
176
|
|
|
5
177
|
### Patch Changes
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -25,10 +25,10 @@ export interface Role {
|
|
|
25
25
|
description?: string | null;
|
|
26
26
|
isSystem?: boolean;
|
|
27
27
|
isAssignable?: boolean; // False for anonymous role - not assignable to users
|
|
28
|
-
|
|
28
|
+
accessRules?: string[];
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export interface
|
|
31
|
+
export interface AccessRuleEntry {
|
|
32
32
|
id: string;
|
|
33
33
|
description?: string;
|
|
34
34
|
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
ConfirmationModal,
|
|
19
19
|
Dialog,
|
|
20
20
|
DialogContent,
|
|
21
|
+
DialogDescription,
|
|
21
22
|
DialogHeader,
|
|
22
23
|
DialogTitle,
|
|
23
24
|
DialogFooter,
|
|
@@ -189,8 +190,8 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
|
|
|
189
190
|
<Alert variant="info" className="mb-4">
|
|
190
191
|
<AlertDescription>
|
|
191
192
|
External applications use API keys to authenticate with the
|
|
192
|
-
Checkstack API. The secret is only shown once when created—store
|
|
193
|
-
securely.
|
|
193
|
+
Checkstack API. The secret is only shown once when created—store
|
|
194
|
+
it securely.
|
|
194
195
|
</AlertDescription>
|
|
195
196
|
</Alert>
|
|
196
197
|
|
|
@@ -302,7 +303,7 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
|
|
|
302
303
|
|
|
303
304
|
{!canManageApplications && (
|
|
304
305
|
<p className="text-xs text-muted-foreground mt-4">
|
|
305
|
-
You don't have
|
|
306
|
+
You don't have access to manage applications.
|
|
306
307
|
</p>
|
|
307
308
|
)}
|
|
308
309
|
</CardContent>
|
|
@@ -346,6 +347,9 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
|
|
|
346
347
|
<DialogTitle>
|
|
347
348
|
Application Secret: {newSecretDialog.applicationName}
|
|
348
349
|
</DialogTitle>
|
|
350
|
+
<DialogDescription className="sr-only">
|
|
351
|
+
Copy your application secret - it will only be shown once
|
|
352
|
+
</DialogDescription>
|
|
349
353
|
</DialogHeader>
|
|
350
354
|
<div className="space-y-4">
|
|
351
355
|
<Alert variant="warning">
|
|
@@ -399,6 +403,9 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
|
|
|
399
403
|
<DialogContent>
|
|
400
404
|
<DialogHeader>
|
|
401
405
|
<DialogTitle>Create Application</DialogTitle>
|
|
406
|
+
<DialogDescription className="sr-only">
|
|
407
|
+
Create a new external application with API key access
|
|
408
|
+
</DialogDescription>
|
|
402
409
|
</DialogHeader>
|
|
403
410
|
<div className="space-y-4">
|
|
404
411
|
<div>
|
|
@@ -1,51 +1,54 @@
|
|
|
1
1
|
import React, { useEffect, useState, useMemo } from "react";
|
|
2
2
|
import { useSearchParams } from "react-router-dom";
|
|
3
|
-
import {
|
|
4
|
-
useApi,
|
|
5
|
-
permissionApiRef,
|
|
6
|
-
rpcApiRef,
|
|
7
|
-
} from "@checkstack/frontend-api";
|
|
3
|
+
import { useApi, accessApiRef, rpcApiRef } from "@checkstack/frontend-api";
|
|
8
4
|
import { PageLayout, useToast, Tabs, TabPanel } from "@checkstack/ui";
|
|
9
|
-
import { authApiRef, AuthUser, Role, AuthStrategy, Permission } from "../api";
|
|
10
5
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
authApiRef,
|
|
7
|
+
AuthUser,
|
|
8
|
+
Role,
|
|
9
|
+
AuthStrategy,
|
|
10
|
+
AccessRuleEntry,
|
|
11
|
+
} from "../api";
|
|
12
|
+
import { authAccess, AuthApi } from "@checkstack/auth-common";
|
|
13
|
+
import { Shield, Settings2, Users, Key, Users2 } from "lucide-react";
|
|
15
14
|
import { UsersTab } from "./UsersTab";
|
|
16
15
|
import { RolesTab } from "./RolesTab";
|
|
17
16
|
import { StrategiesTab } from "./StrategiesTab";
|
|
18
17
|
import { ApplicationsTab } from "./ApplicationsTab";
|
|
18
|
+
import { TeamsTab } from "./TeamsTab";
|
|
19
19
|
|
|
20
20
|
export const AuthSettingsPage: React.FC = () => {
|
|
21
21
|
const authApi = useApi(authApiRef);
|
|
22
22
|
const rpcApi = useApi(rpcApiRef);
|
|
23
23
|
const authClient = rpcApi.forPlugin(AuthApi);
|
|
24
|
-
const
|
|
24
|
+
const accessApi = useApi(accessApiRef);
|
|
25
25
|
const toast = useToast();
|
|
26
26
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
27
27
|
|
|
28
28
|
const session = authApi.useSession();
|
|
29
29
|
|
|
30
30
|
const [activeTab, setActiveTab] = useState<
|
|
31
|
-
"users" | "roles" | "strategies" | "applications"
|
|
31
|
+
"users" | "roles" | "teams" | "strategies" | "applications"
|
|
32
32
|
>("users");
|
|
33
33
|
const [users, setUsers] = useState<(AuthUser & { roles: string[] })[]>([]);
|
|
34
34
|
const [roles, setRoles] = useState<Role[]>([]);
|
|
35
|
-
const [
|
|
35
|
+
const [accessRuleEntries, setAccessRuleEntries] = useState<AccessRuleEntry[]>([]);
|
|
36
36
|
const [strategies, setStrategies] = useState<AuthStrategy[]>([]);
|
|
37
37
|
const [loading, setLoading] = useState(true);
|
|
38
38
|
|
|
39
|
-
const canReadUsers =
|
|
40
|
-
authPermissions.usersRead.id
|
|
41
|
-
);
|
|
39
|
+
const canReadUsers = accessApi.useAccess(authAccess.users.read);
|
|
42
40
|
|
|
43
41
|
// Handle ?tab= URL parameters (from command palette)
|
|
44
42
|
useEffect(() => {
|
|
45
43
|
const tab = searchParams.get("tab");
|
|
46
44
|
|
|
47
|
-
if (
|
|
48
|
-
|
|
45
|
+
if (
|
|
46
|
+
tab &&
|
|
47
|
+
["users", "roles", "teams", "strategies", "applications"].includes(tab)
|
|
48
|
+
) {
|
|
49
|
+
setActiveTab(
|
|
50
|
+
tab as "users" | "roles" | "teams" | "strategies" | "applications"
|
|
51
|
+
);
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
// Clear the URL params after processing
|
|
@@ -56,58 +59,41 @@ export const AuthSettingsPage: React.FC = () => {
|
|
|
56
59
|
}
|
|
57
60
|
}, [searchParams, setSearchParams]);
|
|
58
61
|
|
|
59
|
-
const canManageUsers =
|
|
60
|
-
|
|
61
|
-
);
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
);
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
);
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
);
|
|
74
|
-
const canDeleteRoles = permissionApi.usePermission(
|
|
75
|
-
authPermissions.rolesDelete.id
|
|
76
|
-
);
|
|
77
|
-
const canManageRoles = permissionApi.usePermission(
|
|
78
|
-
authPermissions.rolesManage.id
|
|
79
|
-
);
|
|
80
|
-
const canManageStrategies = permissionApi.usePermission(
|
|
81
|
-
authPermissions.strategiesManage.id
|
|
82
|
-
);
|
|
83
|
-
const canManageRegistration = permissionApi.usePermission(
|
|
84
|
-
authPermissions.registrationManage.id
|
|
85
|
-
);
|
|
86
|
-
const canManageApplications = permissionApi.usePermission(
|
|
87
|
-
authPermissions.applicationsManage.id
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
// Compute loading and permission states for PageLayout
|
|
91
|
-
const permissionsLoading =
|
|
62
|
+
const canManageUsers = accessApi.useAccess(authAccess.users.manage);
|
|
63
|
+
const canCreateUsers = accessApi.useAccess(authAccess.users.create);
|
|
64
|
+
const canReadRoles = accessApi.useAccess(authAccess.roles.read);
|
|
65
|
+
const canCreateRoles = accessApi.useAccess(authAccess.roles.create);
|
|
66
|
+
const canUpdateRoles = accessApi.useAccess(authAccess.roles.update);
|
|
67
|
+
const canDeleteRoles = accessApi.useAccess(authAccess.roles.delete);
|
|
68
|
+
const canManageRoles = accessApi.useAccess(authAccess.roles.manage);
|
|
69
|
+
const canManageStrategies = accessApi.useAccess(authAccess.strategies);
|
|
70
|
+
const canManageRegistration = accessApi.useAccess(authAccess.registration);
|
|
71
|
+
const canManageApplications = accessApi.useAccess(authAccess.applications);
|
|
72
|
+
const canReadTeams = accessApi.useAccess(authAccess.teams.read);
|
|
73
|
+
const canManageTeams = accessApi.useAccess(authAccess.teams.manage);
|
|
74
|
+
|
|
75
|
+
const accessRulesLoading =
|
|
92
76
|
loading ||
|
|
93
77
|
canReadUsers.loading ||
|
|
94
78
|
canReadRoles.loading ||
|
|
95
79
|
canManageStrategies.loading ||
|
|
96
|
-
canManageApplications.loading
|
|
80
|
+
canManageApplications.loading ||
|
|
81
|
+
canReadTeams.loading;
|
|
97
82
|
|
|
98
|
-
const
|
|
83
|
+
const hasAnyAccess =
|
|
99
84
|
canReadUsers.allowed ||
|
|
100
85
|
canReadRoles.allowed ||
|
|
101
86
|
canManageStrategies.allowed ||
|
|
102
|
-
canManageApplications.allowed
|
|
87
|
+
canManageApplications.allowed ||
|
|
88
|
+
canReadTeams.allowed;
|
|
103
89
|
|
|
104
|
-
// Special case: if user is not logged in, show
|
|
105
|
-
const isAllowed = session.data?.user ?
|
|
90
|
+
// Special case: if user is not logged in, show access denied
|
|
91
|
+
const isAllowed = session.data?.user ? hasAnyAccess : false;
|
|
106
92
|
|
|
107
|
-
// Compute visible tabs based on
|
|
93
|
+
// Compute visible tabs based on access rules
|
|
108
94
|
const visibleTabs = useMemo(() => {
|
|
109
95
|
const tabs: Array<{
|
|
110
|
-
id: "users" | "roles" | "strategies" | "applications";
|
|
96
|
+
id: "users" | "roles" | "teams" | "strategies" | "applications";
|
|
111
97
|
label: string;
|
|
112
98
|
icon: React.ReactNode;
|
|
113
99
|
}> = [];
|
|
@@ -120,9 +106,15 @@ export const AuthSettingsPage: React.FC = () => {
|
|
|
120
106
|
if (canReadRoles.allowed)
|
|
121
107
|
tabs.push({
|
|
122
108
|
id: "roles",
|
|
123
|
-
label: "Roles &
|
|
109
|
+
label: "Roles & Access Rules",
|
|
124
110
|
icon: <Shield size={18} />,
|
|
125
111
|
});
|
|
112
|
+
if (canReadTeams.allowed)
|
|
113
|
+
tabs.push({
|
|
114
|
+
id: "teams",
|
|
115
|
+
label: "Teams",
|
|
116
|
+
icon: <Users2 size={18} />,
|
|
117
|
+
});
|
|
126
118
|
if (canManageStrategies.allowed)
|
|
127
119
|
tabs.push({
|
|
128
120
|
id: "strategies",
|
|
@@ -139,6 +131,7 @@ export const AuthSettingsPage: React.FC = () => {
|
|
|
139
131
|
}, [
|
|
140
132
|
canReadUsers.allowed,
|
|
141
133
|
canReadRoles.allowed,
|
|
134
|
+
canReadTeams.allowed,
|
|
142
135
|
canManageStrategies.allowed,
|
|
143
136
|
canManageApplications.allowed,
|
|
144
137
|
]);
|
|
@@ -160,11 +153,11 @@ export const AuthSettingsPage: React.FC = () => {
|
|
|
160
153
|
roles: string[];
|
|
161
154
|
})[];
|
|
162
155
|
const rolesData = await authClient.getRoles();
|
|
163
|
-
const
|
|
156
|
+
const accessRulesData = await authClient.getAccessRules();
|
|
164
157
|
const strategiesData = await authClient.getStrategies();
|
|
165
158
|
setUsers(usersData);
|
|
166
159
|
setRoles(rolesData);
|
|
167
|
-
|
|
160
|
+
setAccessRuleEntries(accessRulesData);
|
|
168
161
|
setStrategies(strategiesData);
|
|
169
162
|
} catch (error: unknown) {
|
|
170
163
|
const message =
|
|
@@ -188,7 +181,7 @@ export const AuthSettingsPage: React.FC = () => {
|
|
|
188
181
|
return (
|
|
189
182
|
<PageLayout
|
|
190
183
|
title="Authentication Settings"
|
|
191
|
-
loading={
|
|
184
|
+
loading={accessRulesLoading}
|
|
192
185
|
allowed={isAllowed}
|
|
193
186
|
>
|
|
194
187
|
<Tabs
|
|
@@ -196,7 +189,7 @@ export const AuthSettingsPage: React.FC = () => {
|
|
|
196
189
|
activeTab={activeTab}
|
|
197
190
|
onTabChange={(tabId) =>
|
|
198
191
|
setActiveTab(
|
|
199
|
-
tabId as "users" | "roles" | "strategies" | "applications"
|
|
192
|
+
tabId as "users" | "roles" | "teams" | "strategies" | "applications"
|
|
200
193
|
)
|
|
201
194
|
}
|
|
202
195
|
className="mb-6"
|
|
@@ -219,7 +212,7 @@ export const AuthSettingsPage: React.FC = () => {
|
|
|
219
212
|
<TabPanel id="roles" activeTab={activeTab}>
|
|
220
213
|
<RolesTab
|
|
221
214
|
roles={roles}
|
|
222
|
-
|
|
215
|
+
accessRulesList={accessRuleEntries}
|
|
223
216
|
userRoleIds={currentUserRoleIds}
|
|
224
217
|
canReadRoles={canReadRoles.allowed}
|
|
225
218
|
canCreateRoles={canCreateRoles.allowed}
|
|
@@ -229,6 +222,15 @@ export const AuthSettingsPage: React.FC = () => {
|
|
|
229
222
|
/>
|
|
230
223
|
</TabPanel>
|
|
231
224
|
|
|
225
|
+
<TabPanel id="teams" activeTab={activeTab}>
|
|
226
|
+
<TeamsTab
|
|
227
|
+
users={users}
|
|
228
|
+
canReadTeams={canReadTeams.allowed}
|
|
229
|
+
canManageTeams={canManageTeams.allowed}
|
|
230
|
+
onDataChange={fetchData}
|
|
231
|
+
/>
|
|
232
|
+
</TabPanel>
|
|
233
|
+
|
|
232
234
|
<TabPanel id="strategies" activeTab={activeTab}>
|
|
233
235
|
<StrategiesTab
|
|
234
236
|
strategies={strategies}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { Link
|
|
2
|
+
import { Link } from "react-router-dom";
|
|
3
3
|
import { LogIn, LogOut, AlertCircle } from "lucide-react";
|
|
4
4
|
import {
|
|
5
5
|
useApi,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
} from "@checkstack/ui";
|
|
39
39
|
import { authApiRef } from "../api";
|
|
40
40
|
import { useEnabledStrategies } from "../hooks/useEnabledStrategies";
|
|
41
|
-
import {
|
|
41
|
+
import { useAccessRules } from "../hooks/useAccessRules";
|
|
42
42
|
import { useAuthClient } from "../lib/auth-client";
|
|
43
43
|
import { SocialProviderButton } from "./SocialProviderButton";
|
|
44
44
|
import { useEffect } from "react";
|
|
@@ -47,7 +47,7 @@ export const LoginPage = () => {
|
|
|
47
47
|
const [email, setEmail] = useState("");
|
|
48
48
|
const [password, setPassword] = useState("");
|
|
49
49
|
const [loading, setLoading] = useState(false);
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
const authApi = useApi(authApiRef);
|
|
52
52
|
const rpcApi = useApi(rpcApiRef);
|
|
53
53
|
const authRpcClient = rpcApi.forPlugin(AuthApi);
|
|
@@ -74,7 +74,8 @@ export const LoginPage = () => {
|
|
|
74
74
|
if (error) {
|
|
75
75
|
console.error("Login failed:", error);
|
|
76
76
|
} else {
|
|
77
|
-
|
|
77
|
+
// Use full page navigation to ensure session/permissions state refreshes
|
|
78
|
+
globalThis.location.href = "/";
|
|
78
79
|
}
|
|
79
80
|
} finally {
|
|
80
81
|
setLoading(false);
|
|
@@ -273,7 +274,7 @@ export const LogoutMenuItem = (_props: UserMenuItemsContext) => {
|
|
|
273
274
|
export const LoginNavbarAction = () => {
|
|
274
275
|
const authApi = useApi(authApiRef);
|
|
275
276
|
const { data: session, isPending } = authApi.useSession();
|
|
276
|
-
const {
|
|
277
|
+
const { accessRules, loading: accessRulesLoading } = useAccessRules();
|
|
277
278
|
const authClient = useAuthClient();
|
|
278
279
|
const [hasCredentialAccount, setHasCredentialAccount] =
|
|
279
280
|
useState<boolean>(false);
|
|
@@ -295,7 +296,7 @@ export const LoginNavbarAction = () => {
|
|
|
295
296
|
});
|
|
296
297
|
}, [session?.user, authClient]);
|
|
297
298
|
|
|
298
|
-
if (isPending ||
|
|
299
|
+
if (isPending || accessRulesLoading || credentialLoading) {
|
|
299
300
|
return <div className="w-20 h-9 bg-muted animate-pulse rounded-full" />;
|
|
300
301
|
}
|
|
301
302
|
|
|
@@ -306,7 +307,7 @@ export const LoginNavbarAction = () => {
|
|
|
306
307
|
);
|
|
307
308
|
const hasBottomItems = bottomExtensions.length > 0;
|
|
308
309
|
const menuContext: UserMenuItemsContext = {
|
|
309
|
-
|
|
310
|
+
accessRules,
|
|
310
311
|
hasCredentialAccount,
|
|
311
312
|
};
|
|
312
313
|
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Link
|
|
2
|
+
import { Link } from "react-router-dom";
|
|
3
3
|
import { AlertCircle } from "lucide-react";
|
|
4
4
|
import { useApi, rpcApiRef } from "@checkstack/frontend-api";
|
|
5
5
|
import { authApiRef } from "../api";
|
|
6
|
-
import {
|
|
7
|
-
AuthApi,
|
|
8
|
-
authRoutes,
|
|
9
|
-
passwordSchema,
|
|
10
|
-
} from "@checkstack/auth-common";
|
|
6
|
+
import { AuthApi, authRoutes, passwordSchema } from "@checkstack/auth-common";
|
|
11
7
|
import { resolveRoute } from "@checkstack/common";
|
|
12
8
|
import {
|
|
13
9
|
Button,
|
|
@@ -35,7 +31,7 @@ export const RegisterPage = () => {
|
|
|
35
31
|
const [password, setPassword] = useState("");
|
|
36
32
|
const [loading, setLoading] = useState(false);
|
|
37
33
|
const [validationErrors, setValidationErrors] = useState<string[]>([]);
|
|
38
|
-
|
|
34
|
+
|
|
39
35
|
const authApi = useApi(authApiRef);
|
|
40
36
|
const rpcApi = useApi(rpcApiRef);
|
|
41
37
|
const authRpcClient = rpcApi.forPlugin(AuthApi);
|
|
@@ -87,7 +83,8 @@ export const RegisterPage = () => {
|
|
|
87
83
|
if (res.error) {
|
|
88
84
|
console.error("Registration failed:", res.error);
|
|
89
85
|
} else {
|
|
90
|
-
|
|
86
|
+
// Use full page navigation to ensure session state refreshes in navbar
|
|
87
|
+
globalThis.location.href = "/";
|
|
91
88
|
}
|
|
92
89
|
} catch (error) {
|
|
93
90
|
console.error("Registration failed:", error);
|