Skip to content

权限、Method 与事务

服务端 Method 是 Aras 的业务边界。权限、Server Event、版本、历史和事务都在这里汇合。客户端可以提前提示用户,但最终决定必须来自服务端。

适用版本: IOM、Permission 与事务原则跨版本适用;CCO 和服务器安全 helper 属目标 Release 兼容项。

执行层: 以服务端 C# Method 为主,客户端权限查询只用于界面提示。

主要依据: R31 原厂手册 Unit 9、Unit 12-15,当前官方 Programmer's Guide / AML 文档,11.0 SP9 旧手册 Unit 3、Unit 8,以及本项目提供的 Method、权限、SQL 与 xProperty 素材。

UI 权限提示不等于授权

标注:Classic client compatibility

客户端权限查询适合控制按钮是否可用:

js
function updateActionAvailability(permissionAdapter, itemContext) {
  const allowed = permissionAdapter.canUpdate(itemContext);
  actionButton.disabled = !allowed;
}

permissionAdapter.canUpdate 可以包装目标 Release 的客户端权限 helper,但它的结果只能作为 UX 提示。服务端仍须使用平台 Permission、Lifecycle Permission 和 Server Event 规则拒绝非法更新。

测试时应绕过按钮,直接提交同样的 AML;无权限请求必须仍然失败。

推荐的 Server Method 骨架

标注:Stable

csharp
Innovator inn = this.getInnovator();

try
{
    string itemId = this.getID();
    if (string.IsNullOrEmpty(itemId))
    {
        return inn.newError("缺少当前 Item ID。");
    }

    // 读取、校验和业务写入都使用 IOM。
    return this;
}
catch (Exception)
{
    // 这里应通过项目的受控日志适配器记录详细异常;不要回传给浏览器。
    return inn.newError("处理失败,请联系管理员并提供操作时间。");
}

Method 应明确:

  • 绑定事件和运行阶段;
  • this 是单个 Item、集合、Activity 还是参数容器;
  • 成功返回类型;
  • 错误是否会中止当前事务;
  • 是否允许重复执行。

返回错误即可中止当前请求

校验失败时使用 inn.newError(...) 返回错误,让 Aras 管理事务。不要为了“阻止保存”直接调用数据库回滚函数。

用 IOM 更新,不直改 SQL 表

标注:Stable

csharp
Innovator inn = this.getInnovator();

string targetId = this.getProperty("target_id", "");
string newName = this.getProperty("new_name", "");

if (!System.Text.RegularExpressions.Regex.IsMatch(
        targetId ?? "",
        "^[0-9A-Fa-f]{32}$"))
{
    return inn.newError("target_id 格式无效。");
}

if (string.IsNullOrWhiteSpace(newName) || newName.Length > 128)
{
    return inn.newError("名称不能为空且不能超过 128 个字符。");
}

Item edit = inn.newItem("Example Item", "edit");
edit.setID(targetId);
edit.setProperty("name", newName);
edit = edit.apply();

if (edit.isError())
{
    return inn.newError("更新失败,请联系管理员并提供操作时间。");
}

return edit;

这条请求会继续经过 Aras 的权限、事件、版本和事务逻辑。直接 UPDATE innovator.TABLE 即使给字符串加了引号,也无法补回这些平台行为。

受控权限提升

标注:Private API / 版本化服务器接口

权限提升应是最后手段。先考虑:

  1. 调整正确的 Permission、Team 或 Lifecycle Permission;
  2. 把动作放到拥有明确执行权限的 Server Method;
  3. 只在确实需要完成系统级局部操作时,提升到一个专用、最小权限 Identity。
csharp
Innovator inn = this.getInnovator();
Aras.Server.Security.Identity serviceIdentity =
    Aras.Server.Security.Identity.GetByName("Example Limited Service");

if (serviceIdentity == null)
{
    return inn.newError("服务 Identity 未配置。");
}

using (CCO.Permissions.GrantIdentity(serviceIdentity))
{
    // 只放必须提升权限的最小操作。
    // 操作前仍需验证调用者、目标 Item、状态和允许的字段。
}

return this;

CCO.Permissions.GrantIdentity 属于需按目标 Release 核验的服务器接口,因此标记为 Private API。若该 Release 提供公开替代方案,应优先替换。

不要默认使用超级 Identity

Aras PLM 或 Administrators 写进所有 Method 模板,会让普通业务代码自然获得过大权限。模板默认应当不提权。

必须记录的提升理由

  • 为什么现有 Permission 无法完成需求;
  • 提升到哪个专用 Identity;
  • 哪些 ItemType、动作和属性允许被操作;
  • 如何记录调用人和目标 Item;
  • 异常时如何保证释放;
  • 哪个集成测试证明范围没有扩大。

事务边界

标注:Stable(原则)

Server Event 通常运行在 Aras 管理的请求事务中。可靠的 Method:

  • 失败时返回 Error;
  • 不手动提交或回滚共享连接;
  • 不在 OnBefore、On 或 OnAfter 等事务阶段直接执行无法随数据库回滚的外部写入;
  • 对邮件、ERP 等外部动作采用 outbox、队列或可重试设计;
  • 对重复请求使用业务幂等键。

为什么不能手动 Rollback

标注:Reject

直接调用底层 RollbackTransaction() 会改变框架正在管理的连接状态,调用者还可能继续执行后续事件。结果可能是双重回滚、意外异常或部分副作用已经发生。要拒绝操作,返回 inn.newError(...)

安全构建 AML

标注:Stable

不要把用户值插入 XML 字符串:

csharp
// 不要这样做:值中的 <、&、引号会破坏 AML,且可能改变查询语义。
// string aml = "<Item ...><name>" + userValue + "</name></Item>";

使用 IOM:

csharp
Item query = inn.newItem("Example Item", "get");
query.setAttribute("select", "id,name");
query.setProperty("name", userValue);
Item result = query.apply();

IOM 会把属性值作为数据处理。对于必须接收 XML 的接口,应使用 XML DOM API 创建节点;不要手工替换几个字符后宣称“已转义”。

读取与修改 xProperty

标注:Stable(IOM 模式) + 目标 Release 验证

xProperty 的实际名称来自 Extended Classification 配置,不应把环境中的名称硬编码进通用 helper。

csharp
Innovator inn = this.getInnovator();

string partId = this.getProperty("part_id", "");
string propertyName = this.getProperty("xproperty_name", "");
string propertyValue = this.getProperty("xproperty_value", "");

if (!System.Text.RegularExpressions.Regex.IsMatch(
        partId ?? "",
        "^[0-9A-Fa-f]{32}$"))
{
    return inn.newError("part_id 格式无效。");
}

// 只允许项目配置中明确开放的 xProperty。
var allowed = new System.Collections.Generic.HashSet<string>(
    System.StringComparer.OrdinalIgnoreCase)
{
    "xp-example-length"
};

if (!allowed.Contains(propertyName))
{
    return inn.newError("不允许修改此 xProperty。");
}

Item edit = inn.newItem("Part", "edit");
edit.setID(partId);
edit.setProperty(propertyName, propertyValue);
edit.setPropertyAttribute(propertyName, "set", "value");
edit = edit.apply();

if (edit.isError())
{
    return inn.newError("xProperty 更新失败,请联系管理员并提供操作时间。");
}

return edit;

set="value" 和属性命名要在目标 Release、显式/隐式 xProperty 类型及分类状态下验证。查询时只选择需要的属性,不用 xp-* 拉取全部扩展属性。

错误处理

标注:Stable

用户错误和系统错误应分开:

  • 用户可修复:返回具体规则,例如“审核结果不能为空”。
  • 配置错误:返回稳定错误码,并在受控日志记录 Method、Item ID 与异常。
  • 不把 SQL、服务器路径、连接字符串和完整堆栈返回浏览器。
  • 不用空 catch {} 吞掉字段、权限或升级错误。
csharp
catch (Exception ex)
{
    serverLogAdapter.Error(
        "EXAMPLE_METHOD_FAILED",
        this.getID(),
        ex);

    return inn.newError(
        "操作失败(错误码:EXAMPLE_METHOD_FAILED)。");
}

为什么不要这样做

原始模式标注风险
UI 隐藏/只读充当权限Reject可被 AML 或其他客户端绕过
直接 UPDATE Aras 表Reject绕过权限、事件、历史、版本和缓存
手动回滚数据库事务Reject破坏框架事务边界
默认 Grant 超级 IdentityReject权限扩大且难以审计
Grant 后不放在 finally/usingReject异常路径泄漏提升后的权限
拼接 AML/XML/HTMLReject注入与格式破坏
catchReject把版本不兼容误装成“业务正常”

验证清单

  • [ ] 直接 AML 调用无法绕过客户端限制。
  • [ ] 无权限、错误状态、重复请求和并发请求均有测试。
  • [ ] Method 不直接修改 innovator 表,不管理底层事务。
  • [ ] 权限提升使用专用 Identity、最小代码块和审计记录。
  • [ ] 所有用户值经 IOM、XML DOM 或参数化接口处理。
  • [ ] 错误对用户有帮助,但不会泄露系统内部信息。
  • [ ] xProperty 白名单、分类和 Permission 都经过验证。

官方延伸阅读

本站内容仅供学习与参考