59 lines
2.3 KiB
Go
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
|
|
}
|