Files
Serendipity edbf78f236 feat: 初始化 Halo 暗色模式插件
- Halo Plugin 后端(Java/Gradle),含 DarkModePlugin 主类和测试
- Vue 3 + TypeScript 前端 UI,包含主题切换组件和设置页面
- 暗色模式 CSS 变量和覆盖样式(布局/编辑器/表单/滚动条等)
- 设计文档和调查文档
- Halo 插件/主题开发 Agent Skills

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-06 21:03:00 +08:00

6.3 KiB

RBAC & Role Templates

⚠️ Not all plugins need to create RoleTemplate resources. All plugin APIs are restricted to super-admin by default. If your plugin is intended for super-admin use only, there is no need to create or configure RoleTemplates at all.

Only create RoleTemplates in the following scenarios:

  • When certain APIs need to be accessible to other roles (e.g., editor, contributor)
  • When certain APIs need to be publicly accessible to unauthenticated users (anonymous)
  • When the plugin UI requires role-based permission controls

All plugin APIs (auto-generated CRUD + custom) are restricted to super-admin by default. To allow other users access, define role templates.

Source references (Halo main branch):

Role Template File

Place YAML files in src/main/resources/extensions/.

apiVersion: v1alpha1
kind: Role
metadata:
  name: my-plugin-role-view-persons
  labels:
    halo.run/role-template: "true"
  annotations:
    rbac.authorization.halo.run/module: "Persons Management"
    rbac.authorization.halo.run/display-name: "View Persons"
    rbac.authorization.halo.run/ui-permissions: |
      ["plugin:my-plugin:person:view"]
rules:
  - apiGroups: ["my-plugin.halo.run"]
    resources: ["my-plugin/persons"]
    verbs: ["get", "list"]
---
apiVersion: v1alpha1
kind: Role
metadata:
  name: my-plugin-role-manage-persons
  labels:
    halo.run/role-template: "true"
  annotations:
    rbac.authorization.halo.run/dependencies: |
      ["my-plugin-role-view-persons"]
    rbac.authorization.halo.run/module: "Persons Management"
    rbac.authorization.halo.run/display-name: "Manage Persons"
    rbac.authorization.halo.run/ui-permissions: |
      ["plugin:my-plugin:person:manage"]
rules:
  - apiGroups: ["my-plugin.halo.run"]
    resources: ["my-plugin/persons"]
    verbs: ["*"]

Key Rules

Element Description
metadata.name Must use plugin name as prefix to avoid collisions
labels.halo.run/role-template: "true" Required to mark as template
annotations.rbac.authorization.halo.run/dependencies Other roles this role requires
annotations.rbac.authorization.halo.run/module UI grouping name
annotations.rbac.authorization.halo.run/display-name Human-readable name
annotations.rbac.authorization.halo.run/ui-permissions Frontend permission strings

Resource Rules

For APIs matching /apis/<group>/<version>/<resource>[/<name>/<subresource>]:

rules:
  - apiGroups: ["my-plugin.halo.run"]
    resources: ["my-plugin/persons"]
    resourceNames: ["zhangsan"] # optional, for single resource
    verbs: ["get", "list"]

Non-Resource Rules

For APIs not matching resource patterns (e.g., /healthz):

rules:
  - nonResourceURLs: ["/healthz", "/healthz/*"]
    verbs: ["get"]

Verbs

Verb HTTP Method Description
create POST Create new resource
get GET Get single resource (with name in path)
list GET List resources (without name in path)
watch GET (WebSocket) Watch resource changes
update PUT Full update
patch PATCH Partial update
delete DELETE Delete single resource
deletecollection DELETE Delete collection

Aggregation

Merge plugin permissions into existing Halo roles:

metadata:
  labels:
    halo.run/role-template: "true"
    halo.run/hidden: "true" # hide from UI
    rbac.authorization.halo.run/aggregate-to-anonymous: "true"
rules:
  - apiGroups: ["api.my-plugin.halo.run"]
    resources: ["public-data"]
    verbs: ["get", "list"]

Available aggregation targets: anonymous, authenticated, editor, etc.

Special Roles

Role Description
anonymous Unauthenticated visitors
authenticated All logged-in users (minimum permissions)
super-role Full system access
guest No explicit permissions (only anonymous + authenticated)

Web Filters

Add custom WebFlux filters for request interception.

@Component
public class MyFilter implements AdditionalWebFilter {
    @Override
    public int getOrder() { return Ordered.LOWEST_PRECEDENCE; }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        // Runs before security chain
        return chain.filter(exchange);
    }
}

AfterSecurityWebFilter runs after the security chain (for caching, analytics, etc.).

ReactiveSecurityContextHolder

Access the current authentication in reactive code:

ReactiveSecurityContextHolder.getContext()
    .map(SecurityContext::getAuthentication)
    .map(Authentication::getName)
    .defaultIfEmpty(AnonymousUserConst.PRINCIPAL);

UI Permissions

Used in frontend route guards:

meta: {
  permissions: ["plugin:my-plugin:person:view"];
}

Check at runtime:

import { utils } from "@halo-dev/ui-shared";

utils.permission.has(["plugin:my-plugin:person:view"]); // true/false