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

180 lines
6.3 KiB
Markdown

# 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 anonymous](https://github.com/halo-dev/halo/blob/main/application/src/main/resources/extensions/role-template-anonymous.yaml)
> - [Role template authenticated](https://github.com/halo-dev/halo/blob/main/application/src/main/resources/extensions/role-template-authenticated.yaml)
> - [Plugin photos role templates (production example)](https://github.com/halo-sigs/plugin-photos/blob/main/src/main/resources/extensions/roleTemplate.yaml)
## Role Template File
Place YAML files in `src/main/resources/extensions/`.
```yaml
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>]`:
```yaml
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`):
```yaml
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:
```yaml
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.
```java
@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:
```java
ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.map(Authentication::getName)
.defaultIfEmpty(AnonymousUserConst.PRINCIPAL);
```
## UI Permissions
Used in frontend route guards:
```ts
meta: {
permissions: ["plugin:my-plugin:person:view"];
}
```
Check at runtime:
```ts
import { utils } from "@halo-dev/ui-shared";
utils.permission.has(["plugin:my-plugin:person:view"]); // true/false
```