权限、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
客户端权限查询适合控制按钮是否可用:
function updateActionAvailability(permissionAdapter, itemContext) {
const allowed = permissionAdapter.canUpdate(itemContext);
actionButton.disabled = !allowed;
}2
3
4
permissionAdapter.canUpdate 可以包装目标 Release 的客户端权限 helper,但它的结果只能作为 UX 提示。服务端仍须使用平台 Permission、Lifecycle Permission 和 Server Event 规则拒绝非法更新。
测试时应绕过按钮,直接提交同样的 AML;无权限请求必须仍然失败。
推荐的 Server Method 骨架
标注:Stable
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("处理失败,请联系管理员并提供操作时间。");
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Method 应明确:
- 绑定事件和运行阶段;
this是单个 Item、集合、Activity 还是参数容器;- 成功返回类型;
- 错误是否会中止当前事务;
- 是否允许重复执行。
返回错误即可中止当前请求
校验失败时使用 inn.newError(...) 返回错误,让 Aras 管理事务。不要为了“阻止保存”直接调用数据库回滚函数。
用 IOM 更新,不直改 SQL 表
标注:Stable
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;2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
这条请求会继续经过 Aras 的权限、事件、版本和事务逻辑。直接 UPDATE innovator.TABLE 即使给字符串加了引号,也无法补回这些平台行为。
受控权限提升
标注:Private API / 版本化服务器接口
权限提升应是最后手段。先考虑:
- 调整正确的 Permission、Team 或 Lifecycle Permission;
- 把动作放到拥有明确执行权限的 Server Method;
- 只在确实需要完成系统级局部操作时,提升到一个专用、最小权限 Identity。
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;2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 字符串:
// 不要这样做:值中的 <、&、引号会破坏 AML,且可能改变查询语义。
// string aml = "<Item ...><name>" + userValue + "</name></Item>";2
使用 IOM:
Item query = inn.newItem("Example Item", "get");
query.setAttribute("select", "id,name");
query.setProperty("name", userValue);
Item result = query.apply();2
3
4
IOM 会把属性值作为数据处理。对于必须接收 XML 的接口,应使用 XML DOM API 创建节点;不要手工替换几个字符后宣称“已转义”。
读取与修改 xProperty
标注:Stable(IOM 模式) + 目标 Release 验证
xProperty 的实际名称来自 Extended Classification 配置,不应把环境中的名称硬编码进通用 helper。
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;2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
set="value" 和属性命名要在目标 Release、显式/隐式 xProperty 类型及分类状态下验证。查询时只选择需要的属性,不用 xp-* 拉取全部扩展属性。
错误处理
标注:Stable
用户错误和系统错误应分开:
- 用户可修复:返回具体规则,例如“审核结果不能为空”。
- 配置错误:返回稳定错误码,并在受控日志记录 Method、Item ID 与异常。
- 不把 SQL、服务器路径、连接字符串和完整堆栈返回浏览器。
- 不用空
catch {}吞掉字段、权限或升级错误。
catch (Exception ex)
{
serverLogAdapter.Error(
"EXAMPLE_METHOD_FAILED",
this.getID(),
ex);
return inn.newError(
"操作失败(错误码:EXAMPLE_METHOD_FAILED)。");
}2
3
4
5
6
7
8
9
10
为什么不要这样做
| 原始模式 | 标注 | 风险 |
|---|---|---|
| UI 隐藏/只读充当权限 | Reject | 可被 AML 或其他客户端绕过 |
| 直接 UPDATE Aras 表 | Reject | 绕过权限、事件、历史、版本和缓存 |
| 手动回滚数据库事务 | Reject | 破坏框架事务边界 |
| 默认 Grant 超级 Identity | Reject | 权限扩大且难以审计 |
Grant 后不放在 finally/using | Reject | 异常路径泄漏提升后的权限 |
| 拼接 AML/XML/HTML | Reject | 注入与格式破坏 |
空 catch | Reject | 把版本不兼容误装成“业务正常” |
验证清单
- [ ] 直接 AML 调用无法绕过客户端限制。
- [ ] 无权限、错误状态、重复请求和并发请求均有测试。
- [ ] Method 不直接修改
innovator表,不管理底层事务。 - [ ] 权限提升使用专用 Identity、最小代码块和审计记录。
- [ ] 所有用户值经 IOM、XML DOM 或参数化接口处理。
- [ ] 错误对用户有帮助,但不会泄露系统内部信息。
- [ ] xProperty 白名单、分类和 Permission 都经过验证。
