mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-17 16:39:27 +08:00
refactor(oauth): replace agent access naming (#549)
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { AGENT_OAUTH_RESOURCE_SCOPES } from './agent-oauth'
|
||||
import { WEBDAV_API_KEY_PERMISSIONS } from './api-key-templates'
|
||||
import {
|
||||
AuthorizationScope,
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
CANONICAL_AUTHORIZATION_SCOPES,
|
||||
scopePermissions,
|
||||
} from './authorization'
|
||||
import { OAUTH_RESOURCE_SCOPES } from './oauth'
|
||||
|
||||
describe('authorization scope registry', () => {
|
||||
it('uses lowercase resource:action scopes without wildcard semantics', () => {
|
||||
@@ -22,7 +22,7 @@ describe('authorization scope registry', () => {
|
||||
|
||||
it('keeps permanent object purge out of agent-grantable scopes', () => {
|
||||
expect(CANONICAL_AUTHORIZATION_SCOPES).toContain(AuthorizationScope.OBJECTS_PURGE)
|
||||
expect(AGENT_OAUTH_RESOURCE_SCOPES).not.toContain(AuthorizationScope.OBJECTS_PURGE)
|
||||
expect(OAUTH_RESOURCE_SCOPES).not.toContain(AuthorizationScope.OBJECTS_PURGE)
|
||||
expect(scopePermissions([AuthorizationScope.OBJECTS_DELETE])).toEqual({ objects: ['delete'] })
|
||||
})
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@ export const AuthorizationScope = {
|
||||
DOWNLOAD_TASKS_CREATE: 'download-tasks:create',
|
||||
DOWNLOAD_TASKS_CANCEL: 'download-tasks:cancel',
|
||||
SITE_ANALYTICS_READ: 'site-analytics:read',
|
||||
AGENT_OAUTH_GRANTS_READ: 'agent-oauth-grants:read',
|
||||
AGENT_OAUTH_GRANTS_CREATE: 'agent-oauth-grants:create',
|
||||
AGENT_OAUTH_GRANTS_DELETE: 'agent-oauth-grants:delete',
|
||||
OAUTH_GRANTS_READ: 'oauth-grants:read',
|
||||
OAUTH_GRANTS_CREATE: 'oauth-grants:create',
|
||||
OAUTH_GRANTS_DELETE: 'oauth-grants:delete',
|
||||
BACKGROUND_JOBS_READ: 'background-jobs:read',
|
||||
BACKGROUND_JOBS_CREATE: 'background-jobs:create',
|
||||
BACKGROUND_JOBS_UPDATE: 'background-jobs:update',
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { AuthorizationScope } from './authorization'
|
||||
|
||||
export const AGENT_OAUTH_ACCESS_TOKEN_SECONDS = 15 * 60
|
||||
export const AGENT_OAUTH_REFRESH_TOKEN_SECONDS = 30 * 24 * 60 * 60
|
||||
export const AGENT_OAUTH_ACTOR_TOKEN_SECONDS = 5 * 60
|
||||
export const OAUTH_ACCESS_TOKEN_SECONDS = 15 * 60
|
||||
export const OAUTH_REFRESH_TOKEN_SECONDS = 30 * 24 * 60 * 60
|
||||
export const OAUTH_ACTOR_TOKEN_SECONDS = 5 * 60
|
||||
export const JWT_BEARER_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
|
||||
export const TOKEN_EXCHANGE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:token-exchange'
|
||||
export const OAUTH_ACCESS_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:access_token'
|
||||
export const AGENT_ACTOR_RESOURCE = 'urn:zpan:oauth:agent-actor'
|
||||
export const AGENT_OAUTH_STANDARD_SCOPES = ['openid', 'profile', 'email', 'offline_access'] as const
|
||||
export const AGENT_OAUTH_RESOURCE_SCOPES = [
|
||||
export const OAUTH_STANDARD_SCOPES = ['openid', 'profile', 'email', 'offline_access'] as const
|
||||
export const OAUTH_RESOURCE_SCOPES = [
|
||||
AuthorizationScope.OBJECTS_READ,
|
||||
AuthorizationScope.OBJECTS_CREATE,
|
||||
AuthorizationScope.OBJECTS_UPDATE,
|
||||
@@ -20,8 +20,8 @@ export const AGENT_OAUTH_RESOURCE_SCOPES = [
|
||||
AuthorizationScope.QUOTA_PURCHASE,
|
||||
AuthorizationScope.STORAGE_USAGE_READ,
|
||||
] as const
|
||||
export const AGENT_OAUTH_SCOPES = [...AGENT_OAUTH_STANDARD_SCOPES, ...AGENT_OAUTH_RESOURCE_SCOPES] as const
|
||||
export const AGENT_OAUTH_SCOPE_DESCRIPTIONS: Record<(typeof AGENT_OAUTH_RESOURCE_SCOPES)[number], string> = {
|
||||
export const OAUTH_SCOPES = [...OAUTH_STANDARD_SCOPES, ...OAUTH_RESOURCE_SCOPES] as const
|
||||
export const OAUTH_SCOPE_DESCRIPTIONS: Record<(typeof OAUTH_RESOURCE_SCOPES)[number], string> = {
|
||||
[AuthorizationScope.OBJECTS_READ]: 'List, inspect, and download objects',
|
||||
[AuthorizationScope.OBJECTS_CREATE]: 'Create folders and upload objects',
|
||||
[AuthorizationScope.OBJECTS_UPDATE]: 'Rename, move, and copy objects',
|
||||
@@ -1,64 +0,0 @@
|
||||
import { z } from 'zod'
|
||||
import { AGENT_OAUTH_ACCESS_TOKEN_SECONDS, AGENT_OAUTH_REFRESH_TOKEN_SECONDS } from '../agent-oauth'
|
||||
import { oauthResourceScopeSchema } from './oauth-resource'
|
||||
|
||||
export const agentOAuthGrantStatusSchema = z.enum(['active'])
|
||||
export type AgentOAuthGrantStatus = z.infer<typeof agentOAuthGrantStatusSchema>
|
||||
|
||||
export const agentOAuthGrantSchema = z.object({
|
||||
id: z.string(),
|
||||
clientId: z.string(),
|
||||
clientName: z.string(),
|
||||
userId: z.string(),
|
||||
orgId: z.string(),
|
||||
workspaceName: z.string().nullable(),
|
||||
scopes: z.array(oauthResourceScopeSchema),
|
||||
createdAt: z.string(),
|
||||
lastUsedAt: z.string().nullable(),
|
||||
status: agentOAuthGrantStatusSchema,
|
||||
})
|
||||
export type AgentOAuthGrant = z.infer<typeof agentOAuthGrantSchema>
|
||||
|
||||
export const agentOAuthGrantListSchema = z.object({ items: z.array(agentOAuthGrantSchema) })
|
||||
export type AgentOAuthGrantList = z.infer<typeof agentOAuthGrantListSchema>
|
||||
|
||||
export const agentOAuthConsentContextSchema = z.object({
|
||||
clientId: z.string(),
|
||||
clientName: z.string(),
|
||||
instanceOrigin: z.string(),
|
||||
workspace: z.object({
|
||||
id: z.string(),
|
||||
name: z.string().nullable(),
|
||||
}),
|
||||
scopes: z.array(oauthResourceScopeSchema),
|
||||
standardScopes: z.array(z.string()),
|
||||
redirectUri: z.string(),
|
||||
grantLifetime: z.object({
|
||||
accessTokenSeconds: z.number().int().default(AGENT_OAUTH_ACCESS_TOKEN_SECONDS),
|
||||
refreshTokenSeconds: z.number().int().default(AGENT_OAUTH_REFRESH_TOKEN_SECONDS),
|
||||
}),
|
||||
})
|
||||
export type AgentOAuthConsentContext = z.infer<typeof agentOAuthConsentContextSchema>
|
||||
|
||||
export const agentOAuthConsentContextRequestSchema = z.object({
|
||||
oauthQuery: z.string().min(1),
|
||||
})
|
||||
export type AgentOAuthConsentContextRequest = z.infer<typeof agentOAuthConsentContextRequestSchema>
|
||||
|
||||
export const agentOAuthConsentSubmitSchema = z.object({
|
||||
accept: z.boolean(),
|
||||
oauthQuery: z.string().min(1),
|
||||
})
|
||||
export type AgentOAuthConsentSubmit = z.infer<typeof agentOAuthConsentSubmitSchema>
|
||||
|
||||
export const agentOAuthConsentResultSchema = z.object({
|
||||
url: z.string(),
|
||||
})
|
||||
export type AgentOAuthConsentResult = z.infer<typeof agentOAuthConsentResultSchema>
|
||||
|
||||
export function agentOAuthGrantDTO(input: Omit<AgentOAuthGrant, 'status'>): AgentOAuthGrant {
|
||||
return {
|
||||
...input,
|
||||
status: 'active',
|
||||
}
|
||||
}
|
||||
+19
-19
@@ -9,25 +9,6 @@ export {
|
||||
adminAnalyticsTrafficSchema,
|
||||
adminOverviewSchema,
|
||||
} from './admin-analytics'
|
||||
export type {
|
||||
AgentOAuthConsentContext,
|
||||
AgentOAuthConsentContextRequest,
|
||||
AgentOAuthConsentResult,
|
||||
AgentOAuthConsentSubmit,
|
||||
AgentOAuthGrant,
|
||||
AgentOAuthGrantList,
|
||||
AgentOAuthGrantStatus,
|
||||
} from './agent-oauth-grants'
|
||||
export {
|
||||
agentOAuthConsentContextRequestSchema,
|
||||
agentOAuthConsentContextSchema,
|
||||
agentOAuthConsentResultSchema,
|
||||
agentOAuthConsentSubmitSchema,
|
||||
agentOAuthGrantDTO,
|
||||
agentOAuthGrantListSchema,
|
||||
agentOAuthGrantSchema,
|
||||
agentOAuthGrantStatusSchema,
|
||||
} from './agent-oauth-grants'
|
||||
export type {
|
||||
AnnouncementInput,
|
||||
AnnouncementStatus,
|
||||
@@ -162,6 +143,25 @@ export {
|
||||
} from './errors'
|
||||
export type { ListNotificationsQuery } from './notification'
|
||||
export { listNotificationsQuerySchema } from './notification'
|
||||
export type {
|
||||
OAuthConsentContext,
|
||||
OAuthConsentContextRequest,
|
||||
OAuthConsentResult,
|
||||
OAuthConsentSubmit,
|
||||
OAuthGrant,
|
||||
OAuthGrantList,
|
||||
OAuthGrantStatus,
|
||||
} from './oauth-grants'
|
||||
export {
|
||||
oauthConsentContextRequestSchema,
|
||||
oauthConsentContextSchema,
|
||||
oauthConsentResultSchema,
|
||||
oauthConsentSubmitSchema,
|
||||
oauthGrantDTO,
|
||||
oauthGrantListSchema,
|
||||
oauthGrantSchema,
|
||||
oauthGrantStatusSchema,
|
||||
} from './oauth-grants'
|
||||
export type { OAuthResourceScope } from './oauth-resource'
|
||||
export { oauthResourceScopeLabels, oauthResourceScopeSchema } from './oauth-resource'
|
||||
export type { CursorPage, CursorPageQuery, Page, PageQuery } from './pagination'
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { z } from 'zod'
|
||||
import { OAUTH_ACCESS_TOKEN_SECONDS, OAUTH_REFRESH_TOKEN_SECONDS } from '../oauth'
|
||||
import { oauthResourceScopeSchema } from './oauth-resource'
|
||||
|
||||
export const oauthGrantStatusSchema = z.enum(['active'])
|
||||
export type OAuthGrantStatus = z.infer<typeof oauthGrantStatusSchema>
|
||||
|
||||
export const oauthGrantSchema = z.object({
|
||||
id: z.string(),
|
||||
clientId: z.string(),
|
||||
clientName: z.string(),
|
||||
userId: z.string(),
|
||||
orgId: z.string(),
|
||||
workspaceName: z.string().nullable(),
|
||||
scopes: z.array(oauthResourceScopeSchema),
|
||||
createdAt: z.string(),
|
||||
lastUsedAt: z.string().nullable(),
|
||||
status: oauthGrantStatusSchema,
|
||||
})
|
||||
export type OAuthGrant = z.infer<typeof oauthGrantSchema>
|
||||
|
||||
export const oauthGrantListSchema = z.object({ items: z.array(oauthGrantSchema) })
|
||||
export type OAuthGrantList = z.infer<typeof oauthGrantListSchema>
|
||||
|
||||
export const oauthConsentContextSchema = z.object({
|
||||
clientId: z.string(),
|
||||
clientName: z.string(),
|
||||
instanceOrigin: z.string(),
|
||||
workspace: z.object({
|
||||
id: z.string(),
|
||||
name: z.string().nullable(),
|
||||
}),
|
||||
scopes: z.array(oauthResourceScopeSchema),
|
||||
standardScopes: z.array(z.string()),
|
||||
redirectUri: z.string(),
|
||||
grantLifetime: z.object({
|
||||
accessTokenSeconds: z.number().int().default(OAUTH_ACCESS_TOKEN_SECONDS),
|
||||
refreshTokenSeconds: z.number().int().default(OAUTH_REFRESH_TOKEN_SECONDS),
|
||||
}),
|
||||
})
|
||||
export type OAuthConsentContext = z.infer<typeof oauthConsentContextSchema>
|
||||
|
||||
export const oauthConsentContextRequestSchema = z.object({
|
||||
oauthQuery: z.string().min(1),
|
||||
})
|
||||
export type OAuthConsentContextRequest = z.infer<typeof oauthConsentContextRequestSchema>
|
||||
|
||||
export const oauthConsentSubmitSchema = z.object({
|
||||
accept: z.boolean(),
|
||||
oauthQuery: z.string().min(1),
|
||||
})
|
||||
export type OAuthConsentSubmit = z.infer<typeof oauthConsentSubmitSchema>
|
||||
|
||||
export const oauthConsentResultSchema = z.object({
|
||||
url: z.string(),
|
||||
})
|
||||
export type OAuthConsentResult = z.infer<typeof oauthConsentResultSchema>
|
||||
|
||||
export function oauthGrantDTO(input: Omit<OAuthGrant, 'status'>): OAuthGrant {
|
||||
return {
|
||||
...input,
|
||||
status: 'active',
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
import { z } from 'zod'
|
||||
import { AGENT_OAUTH_RESOURCE_SCOPES } from '../agent-oauth'
|
||||
import { AuthorizationScope } from '../authorization'
|
||||
import { OAUTH_RESOURCE_SCOPES } from '../oauth'
|
||||
|
||||
export const oauthResourceScopeSchema = z.enum(AGENT_OAUTH_RESOURCE_SCOPES)
|
||||
export const oauthResourceScopeSchema = z.enum(OAUTH_RESOURCE_SCOPES)
|
||||
export type OAuthResourceScope = z.infer<typeof oauthResourceScopeSchema>
|
||||
|
||||
export const oauthResourceScopeLabels = {
|
||||
[AuthorizationScope.OBJECTS_READ]: 'settings.agentAccess.scope.objectsRead',
|
||||
[AuthorizationScope.OBJECTS_CREATE]: 'settings.agentAccess.scope.objectsCreate',
|
||||
[AuthorizationScope.OBJECTS_UPDATE]: 'settings.agentAccess.scope.objectsUpdate',
|
||||
[AuthorizationScope.OBJECTS_DELETE]: 'settings.agentAccess.scope.objectsDelete',
|
||||
[AuthorizationScope.SHARES_READ]: 'settings.agentAccess.scope.sharesRead',
|
||||
[AuthorizationScope.SHARES_CREATE]: 'settings.agentAccess.scope.sharesCreate',
|
||||
[AuthorizationScope.SHARES_DELETE]: 'settings.agentAccess.scope.sharesDelete',
|
||||
[AuthorizationScope.QUOTA_READ]: 'settings.agentAccess.scope.quotaRead',
|
||||
[AuthorizationScope.QUOTA_PURCHASE]: 'settings.agentAccess.scope.quotaPurchase',
|
||||
[AuthorizationScope.STORAGE_USAGE_READ]: 'settings.agentAccess.scope.storageUsageRead',
|
||||
[AuthorizationScope.OBJECTS_READ]: 'settings.oauthApps.scope.objectsRead',
|
||||
[AuthorizationScope.OBJECTS_CREATE]: 'settings.oauthApps.scope.objectsCreate',
|
||||
[AuthorizationScope.OBJECTS_UPDATE]: 'settings.oauthApps.scope.objectsUpdate',
|
||||
[AuthorizationScope.OBJECTS_DELETE]: 'settings.oauthApps.scope.objectsDelete',
|
||||
[AuthorizationScope.SHARES_READ]: 'settings.oauthApps.scope.sharesRead',
|
||||
[AuthorizationScope.SHARES_CREATE]: 'settings.oauthApps.scope.sharesCreate',
|
||||
[AuthorizationScope.SHARES_DELETE]: 'settings.oauthApps.scope.sharesDelete',
|
||||
[AuthorizationScope.QUOTA_READ]: 'settings.oauthApps.scope.quotaRead',
|
||||
[AuthorizationScope.QUOTA_PURCHASE]: 'settings.oauthApps.scope.quotaPurchase',
|
||||
[AuthorizationScope.STORAGE_USAGE_READ]: 'settings.oauthApps.scope.storageUsageRead',
|
||||
} as const satisfies Record<OAuthResourceScope, string>
|
||||
|
||||
@@ -625,7 +625,7 @@ export interface Announcement {
|
||||
export type AuditActorType =
|
||||
| 'user'
|
||||
| 'api_key'
|
||||
| 'agent_oauth'
|
||||
| 'oauth'
|
||||
| 'agent'
|
||||
| 'anonymous'
|
||||
| 'system'
|
||||
|
||||
Reference in New Issue
Block a user