Files
ayatori/internal/database/adapter/postgresql/management.go
T
panxiao81 bc227bfdb4
Verify / test (pull_request) Successful in 12m41s
Verify / lint (pull_request) Successful in 14m14s
Verify / database-integration (pull_request) Successful in 16m9s
feat: 接入 Instance 原生管理观测与删除保护
2026-09-25 11:35:04 +00:00

59 lines
2.3 KiB
Go

package postgresql
import (
"context"
"git.ddupan.top/panxiao81/ayatori/internal/database/application"
"git.ddupan.top/panxiao81/ayatori/internal/database/domain/instance"
)
// 只读取当前执行角色的属性,不能从可继承的角色成员关系推导 CREATEDB/CREATEROLE。
// 所有事实来自同一条语句;不创建探针数据库,不修改管理账号或持久 schema。
const inspectManagementStatement = `
SELECT
pg_catalog.current_setting('server_version'),
ARRAY(SELECT name::text FROM pg_catalog.pg_available_extensions ORDER BY name),
role.rolsuper,
role.rolcreaterole,
role.rolcreatedb,
pg_catalog.pg_is_in_recovery() OR
pg_catalog.current_setting('transaction_read_only')::boolean
FROM pg_catalog.pg_roles AS role
WHERE role.rolname = current_user`
func (d *database) InspectManagement(ctx context.Context) (application.DatabaseMetadata, error) {
var metadata application.DatabaseMetadata
var superuser, createRole, createDatabase, readOnly bool
err := d.pool.QueryRow(ctx, inspectManagementStatement).Scan(
&metadata.Version, &metadata.AvailableExtensions,
&superuser, &createRole, &createDatabase, &readOnly,
)
if err != nil {
return application.DatabaseMetadata{}, safeError(err, application.ErrObservation)
}
checks := instance.ManagementChecks{
Connection: instance.CheckPassed,
Metadata: instance.CheckPassed,
Roles: nativePrivilege(createRole && !superuser),
Databases: nativePrivilege(createDatabase && !superuser),
// CREATEROLE 可管理自己新建角色的 membership;供应时必须显式取得 SET 权限,
// 再以 owner 操作数据库 ACL。这里不授权操作任意导入角色或他人数据库。
Grants: nativePrivilege(createRole && createDatabase && !superuser),
// 新建数据库 owner 可安装 trusted 扩展。具体扩展仍需逐请求执行和回读,
// 非 trusted 扩展不能因出现在 available 列表就视为可安装。
Extensions: nativePrivilege(createRole && createDatabase && !superuser),
}
if readOnly {
checks.Databases = instance.CheckUnavailable
}
metadata.Management = checks
return metadata, nil
}
func nativePrivilege(allowed bool) instance.CheckResult {
if allowed {
return instance.CheckPassed
}
return instance.CheckInsufficientPrivileges
}